Repository: fatih/gomodifytags (go)

Issue:
Wrong output when the file isn't gofmt'd
With the following file:

```
$ cat tags.go
package a
type x struct {
    Foo int
    bar int
}
```

The tool gives the wrong results.

Should be start=2:

```
$ gomodifytags -format json -file tags.go -transform snakecase -line 3,4 -add-tags json -add-options json=
{
  "start": 3,
  "end": 4,
  "lines": [
    "type x struct {",
    "\tFoo int `json:\"foo\"`"
  ]
}
```

Should be start=2, and one line is missing from the output:

```
$ gomodifytags -format json -file tags.go -transform snakecase -line 3,3 -add-tags json -add-options json=
{
  "start": 3,
  "end": 3,
  "lines": [
    "type x struct {"
  ]
}
```

start is correct, but the last line (`}`) missing:
```
$ gomodifytags -format json -file tags.go -transform snakecase -offset 23 -add-tags json -add-options json=
{
  "start": 2,
  "end": 5,
  "lines": [
    "",
    "type x struct {",
    "\tFoo int `json:\"foo\"`",
    "\tbar int `json:\"bar\"`"
  ]
}
```

It seems that the tool operates on the file *as if* they're gofmt'd.

Pull request description:
Fix outputting correct information for nonformatted source
This makes sure to return the correct lines for the `--json` format if
the given input is not formatted (gofmt'd).

fixes #39

Patch:
diff --git a/README.md b/README.md
index 7329b12..94c57ee 100644
--- a/README.md
+++ b/README.md
@@ -152,7 +152,6 @@ We currently support the following transformations:
 * `camelcase`: `"BaseDomain"` -> `"baseDomain"`
 * `lispcase`:  `"BaseDomain"` -> `"base-domain"`
 * `pascalcase`:  `"BaseDomain"` -> `"BaseDomain"`
-* `keep`:  keeps the original field name
 
 You can also pass a static value for each fields. This is useful if you use Go
 packages that validates the struct fields or extract values for certain
diff --git a/main.go b/main.go
index 0690fb7..547a551 100644
--- a/main.go
+++ b/main.go
@@ -10,6 +10,7 @@ import (
 	"go/ast"
 	"go/format"
 	"go/parser"
+	"go/printer"
 	"go/token"
 	"io"
 	"io/ioutil"
@@ -101,7 +102,7 @@ func realMain() error {
 		flagOverride  = flag.Bool("override", false, "Override current tags when adding tags")
 		flagTransform = flag.String("transform", "snakecase",
 			"Transform adds a transform rule when adding tags."+
-				" Current options: [snakecase, camelcase, lispcase, pascalcase, keep]")
+				" Current options: [snakecase, camelcase, lispcase, pascalcase]")
 		flagSort = flag.Bool("sort", false,
 			"Sort sorts the tags in increasing order according to the key name")
 
@@ -380,8 +381,6 @@ func (c *config) addTags(fieldName string, tags *structtag.Tags) (*structtag.Tag
 		}
 
 		name = strings.Join(titled, "")
-	case "keep":
-		name = fieldName
 	default:
 		unknown = true
 	}
@@ -483,19 +482,46 @@ func (c *config) format(file ast.Node, rwErrs error) (string, error) {
 		// other declarations in between them. Printing the file and cutting
 		// the selection is the easier and simpler to do.
 		var buf bytes.Buffer
-		err := format.Node(&buf, c.fset, file)
+
+		// this is the default config from `format.Node()`, but we add
+		// `printer.SourcePos` to get the original source position of the
+		// modified lines
+		cfg := printer.Config{Mode: printer.SourcePos | printer.UseSpaces | printer.TabIndent, Tabwidth: 8}
+		err := cfg.Fprint(&buf, c.fset, file)
 		if err != nil {
 			return "", err
 		}
 
 		var lines []string
-		scanner := bufio.NewScanner(bytes.NewBufferString(buf.String()))
+		scanner := bufio.NewScanner(&buf)
 		for scanner.Scan() {
-			lines = append(lines, scanner.Text())
+			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]
 		}
 
-		// prevent selection to be larger than the actual number of
-		// lines
+		// prevent selection to be larger than the actual number of lines
 		if c.start > len(lines) || c.end > len(lines) {
 			return "", errors.New("line selection is invalid")
 		}


Test patch:
diff --git a/main_test.go b/main_test.go
index b611c43..bc1720c 100644
--- a/main_test.go
+++ b/main_test.go
@@ -303,6 +303,15 @@ func TestRewrite(t *testing.T) {
 				transform: "pascalcase",
 			},
 		},
+		{
+			file: "not_formatted",
+			cfg: &config{
+				add:       []string{"json"},
+				output:    "source",
+				line:      "3,4",
+				transform: "snakecase",
+			},
+		},
 	}
 
 	for _, ts := range test {
@@ -419,6 +428,48 @@ func TestJSON(t *testing.T) {
 				line: "4,7",
 			},
 		},
+		{
+			file: "json_not_formatted",
+			cfg: &config{
+				add:  []string{"json"},
+				line: "3,4",
+			},
+		},
+		{
+			file: "json_not_formatted_2",
+			cfg: &config{
+				add:  []string{"json"},
+				line: "3,3",
+			},
+		},
+		{
+			file: "json_not_formatted_3",
+			cfg: &config{
+				add:    []string{"json"},
+				offset: 23,
+			},
+		},
+		{
+			file: "json_not_formatted_4",
+			cfg: &config{
+				add:    []string{"json"},
+				offset: 51,
+			},
+		},
+		{
+			file: "json_not_formatted_5",
+			cfg: &config{
+				add:    []string{"json"},
+				offset: 29,
+			},
+		},
+		{
+			file: "json_not_formatted_6",
+			cfg: &config{
+				add:  []string{"json"},
+				line: "2,54",
+			},
+		},
 	}
 
 	for _, ts := range test {
diff --git a/test-fixtures/json_not_formatted.golden b/test-fixtures/json_not_formatted.golden
new file mode 100644
index 0000000..5506b01
--- /dev/null
+++ b/test-fixtures/json_not_formatted.golden
@@ -0,0 +1,8 @@
+{
+  "start": 3,
+  "end": 4,
+  "lines": [
+    "\tFoo int `json:\"foo\"`",
+    "\tbar int `json:\"bar\"`"
+  ]
+}
\ No newline at end of file
diff --git a/test-fixtures/json_not_formatted.input b/test-fixtures/json_not_formatted.input
new file mode 100644
index 0000000..1b107cd
--- /dev/null
+++ b/test-fixtures/json_not_formatted.input
@@ -0,0 +1,5 @@
+package a
+type x struct {
+    Foo int
+    bar int
+}
diff --git a/test-fixtures/json_not_formatted_2.golden b/test-fixtures/json_not_formatted_2.golden
new file mode 100644
index 0000000..bcb75e0
--- /dev/null
+++ b/test-fixtures/json_not_formatted_2.golden
@@ -0,0 +1,7 @@
+{
+  "start": 3,
+  "end": 3,
+  "lines": [
+    "\tFoo int `json:\"foo\"`"
+  ]
+}
\ No newline at end of file
diff --git a/test-fixtures/json_not_formatted_2.input b/test-fixtures/json_not_formatted_2.input
new file mode 100644
index 0000000..1b107cd
--- /dev/null
+++ b/test-fixtures/json_not_formatted_2.input
@@ -0,0 +1,5 @@
+package a
+type x struct {
+    Foo int
+    bar int
+}
diff --git a/test-fixtures/json_not_formatted_3.golden b/test-fixtures/json_not_formatted_3.golden
new file mode 100644
index 0000000..9acd13b
--- /dev/null
+++ b/test-fixtures/json_not_formatted_3.golden
@@ -0,0 +1,10 @@
+{
+  "start": 2,
+  "end": 5,
+  "lines": [
+    "type x struct {",
+    "\tFoo int `json:\"foo\"`",
+    "\tbar int `json:\"bar\"`",
+    "}"
+  ]
+}
\ No newline at end of file
diff --git a/test-fixtures/json_not_formatted_3.input b/test-fixtures/json_not_formatted_3.input
new file mode 100644
index 0000000..1b107cd
--- /dev/null
+++ b/test-fixtures/json_not_formatted_3.input
@@ -0,0 +1,5 @@
+package a
+type x struct {
+    Foo int
+    bar int
+}
diff --git a/test-fixtures/json_not_formatted_4.golden b/test-fixtures/json_not_formatted_4.golden
new file mode 100644
index 0000000..0047c35
--- /dev/null
+++ b/test-fixtures/json_not_formatted_4.golden
@@ -0,0 +1,11 @@
+{
+  "start": 2,
+  "end": 6,
+  "lines": [
+    "type x struct {",
+    "\tFoo int `json:\"foo\"`",
+    "\tbar int `json:\"bar\"`",
+    "",
+    "}"
+  ]
+}
\ No newline at end of file
diff --git a/test-fixtures/json_not_formatted_4.input b/test-fixtures/json_not_formatted_4.input
new file mode 100644
index 0000000..7a04cab
--- /dev/null
+++ b/test-fixtures/json_not_formatted_4.input
@@ -0,0 +1,6 @@
+package a
+type x struct {
+    Foo int
+    bar int
+
+}
diff --git a/test-fixtures/json_not_formatted_5.golden b/test-fixtures/json_not_formatted_5.golden
new file mode 100644
index 0000000..d49e7e1
--- /dev/null
+++ b/test-fixtures/json_not_formatted_5.golden
@@ -0,0 +1,16 @@
+{
+  "start": 3,
+  "end": 12,
+  "lines": [
+    "type x struct {",
+    "",
+    "",
+    "",
+    "",
+    "",
+    " Foo int `json:\"foo\"`",
+    "\tbar string `json:\"bar\"`",
+    "",
+    "}"
+  ]
+}
\ No newline at end of file
diff --git a/test-fixtures/json_not_formatted_5.input b/test-fixtures/json_not_formatted_5.input
new file mode 100644
index 0000000..d00a825
--- /dev/null
+++ b/test-fixtures/json_not_formatted_5.input
@@ -0,0 +1,12 @@
+package a
+
+type x struct {
+
+
+
+
+
+	Foo int
+	bar string
+
+}
diff --git a/test-fixtures/json_not_formatted_6.golden b/test-fixtures/json_not_formatted_6.golden
new file mode 100644
index 0000000..718ff6e
--- /dev/null
+++ b/test-fixtures/json_not_formatted_6.golden
@@ -0,0 +1,59 @@
+{
+  "start": 2,
+  "end": 54,
+  "lines": [
+    "",
+    "type x struct {",
+    "\tFoo int    `json:\"foo\"`",
+    "\tbar string `json:\"bar\"`",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "}",
+    "",
+    "func main() {",
+    "",
+    "",
+    "",
+    "",
+    " fmt.Println(\"---\")",
+    "\tfmt.Println(\"---\")",
+    "\tfmt.Println(\"---\")",
+    "\tfmt.Println(\"zeynep\")",
+    "\tfmt.Println(\"alper\")",
+    "\tfmt.Println(\"miray\")",
+    "\tfmt.Println(\"---\")",
+    "\tfmt.Println(\"---\")",
+    "\tfmt.Println(\"---\")",
+    "",
+    "",
+    "",
+    "}",
+    "",
+    "type b struct {",
+    "\tFoo int    `json:\"foo\"`",
+    "\tbar string `json:\"bar\"`",
+    "",
+    "}",
+    "",
+    "type b struct {",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    "",
+    " Foo int `json:\"foo\"`",
+    "\tbar string `json:\"bar\"`",
+    "",
+    "}"
+  ]
+}
\ No newline at end of file
diff --git a/test-fixtures/json_not_formatted_6.input b/test-fixtures/json_not_formatted_6.input
new file mode 100644
index 0000000..1a11546
--- /dev/null
+++ b/test-fixtures/json_not_formatted_6.input
@@ -0,0 +1,54 @@
+package main
+
+type x struct {
+	Foo int
+	bar string
+
+
+
+
+
+
+
+}
+
+func main() {
+
+
+
+
+	fmt.Println("---")
+	fmt.Println("---")
+	fmt.Println("---")
+	fmt.Println("zeynep")
+	fmt.Println("alper")
+	fmt.Println("miray")
+	fmt.Println("---")
+	fmt.Println("---")
+	fmt.Println("---")
+
+
+
+}
+
+type b struct {
+	Foo int
+	bar string
+
+}
+
+type b struct {
+
+
+
+
+
+
+
+
+
+
+	Foo int
+	bar string
+
+}
diff --git a/test-fixtures/not_formatted.golden b/test-fixtures/not_formatted.golden
new file mode 100644
index 0000000..91948de
--- /dev/null
+++ b/test-fixtures/not_formatted.golden
@@ -0,0 +1,6 @@
+package a
+
+type x struct {
+	Foo int `json:"foo"`
+	bar int `json:"bar"`
+}
diff --git a/test-fixtures/not_formatted.input b/test-fixtures/not_formatted.input
new file mode 100644
index 0000000..1b107cd
--- /dev/null
+++ b/test-fixtures/not_formatted.input
@@ -0,0 +1,5 @@
+package a
+type x struct {
+    Foo int
+    bar int
+}