Skip to content

Commit

Permalink
Fix parsing Windows line directives
Browse files Browse the repository at this point in the history
Also refactor parsing the lines and add tests

closes #45
  • Loading branch information
fatih committed Sep 1, 2019
1 parent 648b773 commit f8a1ef7
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 27 deletions.
87 changes: 60 additions & 27 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -494,33 +494,9 @@ func (c *config) format(file ast.Node, rwErrs error) (string, error) {
return "", err
}

var lines []string
scanner := bufio.NewScanner(&buf)
for scanner.Scan() {
txt := scanner.Text()

// check for any line directive and store it for next iteration to
// re-construct the original file. If it's not a line directive,
// continue consturcting the original file
if !strings.HasPrefix(txt, "//line") {
lines = append(lines, txt)
continue
}

splitted := strings.Split(txt, ":")
if len(splitted) != 2 {
return "", fmt.Errorf("invalid line directive found: %q", txt)
}
lineNr, err := strconv.Atoi(splitted[1])
if err != nil {
return "", err
}

for i := len(lines); i < lineNr-1; i++ {
lines = append(lines, "")
}

lines = lines[:lineNr-1]
lines, err := parseLines(&buf)
if err != nil {
return "", err
}

// prevent selection to be larger than the actual number of lines
Expand Down Expand Up @@ -741,3 +717,60 @@ func (r *rewriteErrors) Append(err error) {

r.errs = append(r.errs, err)
}

// parseLines parses the given buffer and returns a slice of lines
func parseLines(buf io.Reader) ([]string, error) {
var lines []string
scanner := bufio.NewScanner(buf)
for scanner.Scan() {
txt := scanner.Text()

// check for any line directive and store it for next iteration to
// re-construct the original file. If it's not a line directive,
// continue consturcting the original file
if !strings.HasPrefix(txt, "//line") {
lines = append(lines, txt)
continue
}

lineNr, err := split(txt)
if err != nil {
return nil, err
}

for i := len(lines); i < lineNr-1; i++ {
lines = append(lines, "")
}

lines = lines[:lineNr-1]
}

if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("invalid scanner inputl: %s", err)
}

return lines, nil
}

// split splits the given line directive and returns the line number
// see https://golang.org/cmd/compile/#hdr-Compiler_Directives for more
// information
// NOTE(arslan): this only splits the line directive that the go.Parser
// outputs. If the go parser changes the format of the line directive, make
// sure to fix it in the below function
func split(line string) (int, error) {
for i := len(line) - 1; i >= 0; i-- {
if line[i] != ':' {
continue
}

nr, err := strconv.Atoi(line[i+1:])
if err != nil {
return 0, err
}

return nr, nil
}

return 0, fmt.Errorf("couldn't parse line: '%s'", line)
}
68 changes: 68 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"strings"
Expand Down Expand Up @@ -613,3 +614,70 @@ type foo struct {
t.Fatal("expected error")
}
}

func TestParseLines(t *testing.T) {
var tests = []struct {
file string
}{
{file: "line_directive_unix"},
{file: "line_directive_windows"},
}

for _, ts := range tests {
ts := ts

t.Run(ts.file, func(t *testing.T) {
filePath := filepath.Join(fixtureDir, fmt.Sprintf("%s.input", ts.file))

file, err := os.Open(filePath)
if err != nil {
t.Fatal(err)
}
defer file.Close()

out, err := parseLines(file)
if err != nil {
t.Fatal(err)
}

toBytes := func(lines []string) []byte {
var buf bytes.Buffer
for _, line := range lines {
buf.WriteString(line + "\n")
}
return buf.Bytes()
}

got := toBytes(out)

// update golden file if necessary
golden := filepath.Join(fixtureDir, fmt.Sprintf("%s.golden", ts.file))

if *update {
err := ioutil.WriteFile(golden, got, 0644)
if err != nil {
t.Error(err)
}
return
}

// get golden file
want, err := ioutil.ReadFile(golden)
if err != nil {
t.Fatal(err)
}

from, err := ioutil.ReadFile(filePath)
if err != nil {
t.Fatal(err)
}

// compare
if !bytes.Equal(got, want) {
t.Errorf("case %s\ngot:\n====\n\n%s\nwant:\n=====\n\n%s\nfrom:\n=====\n\n%s\n",
ts.file, got, want, from)
}

})
}
}
5 changes: 5 additions & 0 deletions test-fixtures/line_directive_unix.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package a
type x struct {
Foo int `json:"foo"`
bar int
}
8 changes: 8 additions & 0 deletions test-fixtures/line_directive_unix.input
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//line /Users/fatih/test.go:1
package a

//line /Users/fatih/test.go:2
type x struct {
Foo int `json:"foo"`
bar int
}
5 changes: 5 additions & 0 deletions test-fixtures/line_directive_windows.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package a
type x struct {
Foo int `json:"foo"`
bar int
}
8 changes: 8 additions & 0 deletions test-fixtures/line_directive_windows.input
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//line c:\\file\\path\\to\\file.go:1
package a

//line c:\\file\\path\\to\\file.go:2
type x struct {
Foo int `json:"foo"`
bar int
}

0 comments on commit f8a1ef7

Please sign in to comment.