Skip to content

Commit

Permalink
Update editor reading/writing mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
mkchoi212 committed May 21, 2018
1 parent afe8327 commit 7d9024a
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 38 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ before_install:
- go get -t -v ./...

script:
- go test -race -coverprofile=coverage.txt -covermode=atomic `go list ./...`
- go test -race -coverprofile=coverage.txt -covermode=atomic `go list ./... | grep -v testhelper`

after_success:
- bash <(curl -s https://codecov.io/bash)
Expand Down
40 changes: 26 additions & 14 deletions editor/content.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import (
"strings"
)

// Content holds all I/O related logic of the editor
// Content represents the lines read from a io.Reader
// `c` holds the actual lines and
// `reader` contains the io.Reader representation of the lines
type Content struct {
c []string
reader io.Reader
Expand All @@ -18,30 +20,40 @@ func (c Content) Read(b []byte) (int, error) {
}

func (c Content) String() string {
return strings.Join(c.c, "\n")
return strings.Join(c.c, "")
}

// contentFromReader creates a new `Content` object
// by scanning an io.Reader using a bufio.SplitFunc
func contentFromReader(content io.Reader, split bufio.SplitFunc) (Content, error) {
c := Content{}
scanner := bufio.NewScanner(content)
scanner.Split(split)

for scanner.Scan() {
c.c = append(c.c, scanner.Text())
// filled with the lines from the provided Reader
// It returns an error if anything other than io.EOF is raised
func contentFromReader(content io.Reader) (c Content, err error) {
reader := bufio.NewReader(content)

for {
line, err := reader.ReadString('\n')
c.c = append(c.c, line)

if err != nil {
break
}
}

if err != nil && err != io.EOF {
return
}
c.reader = strings.NewReader(c.String())

return c, scanner.Err()
c.reader = strings.NewReader(c.String())
return
}

func contentFromFile(filename string, split bufio.SplitFunc) (Content, error) {
// contentFromFile reads the content from the file
// It returns an error if the file does not exist
func contentFromFile(filename string) (Content, error) {
file, err := os.Open(filename)
if err != nil {
return Content{}, err
}
defer file.Close()

return contentFromReader(file, split)
return contentFromReader(file)
}
9 changes: 4 additions & 5 deletions editor/content_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package editor

import (
"bufio"
"io/ioutil"
"strings"
"testing"
Expand All @@ -10,7 +9,7 @@ import (
)

func TestUseContentAsReader(t *testing.T) {
c, err := contentFromReader(strings.NewReader("foo\nbar"), bufio.ScanLines)
c, err := contentFromReader(strings.NewReader("foo\nbar"))
testhelper.Ok(t, err)

byteCnt, err := ioutil.ReadAll(c)
Expand All @@ -24,8 +23,8 @@ func TestCreateContentFromFile(t *testing.T) {
_, err = f.Write([]byte("foo\nbar"))
testhelper.Ok(t, err)

c, err := contentFromFile(f.Name(), bufio.ScanLines)
c, err := contentFromFile(f.Name())

testhelper.Equals(t, c.c[0], "foo")
testhelper.Equals(t, c.c[1], "bar")
testhelper.Equals(t, "foo\n", c.c[0])
testhelper.Equals(t, "bar", c.c[1])
}
29 changes: 11 additions & 18 deletions editor/editor.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ package editor
// https://github.com/kioopi/extedit

import (
"bufio"
"io"
"io/ioutil"
"os"
Expand All @@ -16,19 +15,16 @@ import (

const defaultEditor = "vim"

type Session struct {
input Content
SplitFunc bufio.SplitFunc
}
var execCommand = exec.Command

// Open starts a text-editor with the contents of content.
// It returns edited content after user closes the editor
// Open starts a text-editor with lines from `Content`
// It returns the manually edited lines from the text-editor when the user closes the editor
func Open(c *conflict.Conflict) (output []string, err error) {
s := &Session{SplitFunc: bufio.ScanLines}
lines := c.File.Lines[c.Start : c.End+1]

content := strings.NewReader(strings.Join(lines, ""))
input, err := contentFromReader(content, s.SplitFunc)
input, err := contentFromReader(content)

if err != nil {
return
}
Expand All @@ -44,20 +40,17 @@ func Open(c *conflict.Conflict) (output []string, err error) {
return
}

newContent, err := contentFromFile(fileName, s.SplitFunc)
newContent, err := contentFromFile(fileName)
if err != nil {
return
}

output = newContent.c
for i := range output {
output[i] = output[i] + "\n"
}
return
}

// writeTmpFile writes content to a temporary file and returns
// the path to the file
// writeTmpFile writes content to a temporary file and returns the path to the file
// It returns an error if the temporary file cannot be created
func writeTmpFile(content io.Reader) (string, error) {
f, err := ioutil.TempFile("", "")
if err != nil {
Expand All @@ -69,14 +62,14 @@ func writeTmpFile(content io.Reader) (string, error) {
return f.Name(), nil
}

// editorCmd creates a os/exec.Cmd to open
// filename in an editor ready to be run()
// editorCmd returns a os/exec.Cmd to open the provided file
func editorCmd(filename string) *exec.Cmd {
editorPath := os.Getenv("EDITOR")
if editorPath == "" {
editorPath = defaultEditor
}
editor := exec.Command(editorPath, filename)

editor := execCommand(editorPath, filename)

editor.Stdin = os.Stdin
editor.Stdout = os.Stdout
Expand Down
36 changes: 36 additions & 0 deletions editor/editor_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package editor

import (
"os"
"os/exec"
"strings"
"testing"

"github.com/mkchoi212/fac/conflict"
"github.com/mkchoi212/fac/testhelper"
)

Expand All @@ -19,3 +22,36 @@ func TestWriteTmpFile(t *testing.T) {
testhelper.Ok(t, err)
testhelper.Assert(t, name != "", "tmp file name should not be empty")
}

func TestOpen(t *testing.T) {
execCommand = mockExecCommand
defer func() { execCommand = exec.Command }()

f := conflict.File{AbsolutePath: "testdata/lorem_ipsum"}
err := f.Read()
testhelper.Ok(t, err)

c := conflict.Conflict{File: &f, Start: 0, End: 5}
output, err := Open(&c)

testhelper.Ok(t, err)
testhelper.Equals(t, f.Lines, output)
}

func TestHelperProcess(t *testing.T) {
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
return
}

os.Exit(0)
}

// Allows us to mock exec.Command, thanks to
// https://npf.io/2015/06/testing-exec-command/
func mockExecCommand(command string, args ...string) *exec.Cmd {
cs := []string{"-test.run=TestHelperProcess", "--", command}
cs = append(cs, args...)
cmd := exec.Command(os.Args[0], cs...)
cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
return cmd
}

0 comments on commit 7d9024a

Please sign in to comment.