Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(505)

Unified Diff: src/cmd/gofmt/gofmt.go

Issue 142360043: code review 142360043: go/format, cmd/gofmt: fix issues with partial Go code w... (Closed)
Patch Set: diff -r 8f36a32a9d036f86fb956c8538142eb384bd1a4a https://code.google.com/p/go Created 10 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | src/cmd/gofmt/long_test.go » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/cmd/gofmt/gofmt.go
===================================================================
--- a/src/cmd/gofmt/gofmt.go
+++ b/src/cmd/gofmt/gofmt.go
@@ -87,13 +87,13 @@
return err
}
- file, adjust, err := parse(fileSet, filename, src, stdin)
+ file, sourceAdj, indentAdj, err := parse(fileSet, filename, src, stdin)
if err != nil {
return err
}
if rewrite != nil {
- if adjust == nil {
+ if sourceAdj == nil {
file = rewrite(file)
} else {
fmt.Fprintf(os.Stderr, "warning: rewrite ignored for incomplete programs\n")
@@ -106,15 +106,10 @@
simplify(file)
}
- var buf bytes.Buffer
- err = (&printer.Config{Mode: printerMode, Tabwidth: tabWidth}).Fprint(&buf, fileSet, file)
+ res, err := format(fileSet, file, sourceAdj, indentAdj, src)
if err != nil {
return err
}
- res := buf.Bytes()
- if adjust != nil {
- res = adjust(src, res)
- }
if !bytes.Equal(src, res) {
// formatting has changed
@@ -242,17 +237,19 @@
// parse parses src, which was read from filename,
// as a Go source file or statement list.
-func parse(fset *token.FileSet, filename string, src []byte, stdin bool) (*ast.File, func(orig, src []byte) []byte, error) {
+func parse(fset *token.FileSet, filename string, src []byte, fragmentOk bool) (
+ file *ast.File,
+ sourceAdj func(src []byte, indent int) []byte,
+ indentAdj int,
+ err error,
+) {
// Try as whole source file.
- file, err := parser.ParseFile(fset, filename, src, parserMode)
- if err == nil {
- return file, nil, nil
- }
- // If the error is that the source file didn't begin with a
- // package line and this is standard input, fall through to
+ file, err = parser.ParseFile(fset, filename, src, parserMode)
+ // If there's no error, return. If the error is that the source file didn't begin with a
+ // package line and source fragments are ok, fall through to
// try as a source fragment. Stop and return on any other error.
- if !stdin || !strings.Contains(err.Error(), "expected 'package'") {
- return nil, nil, err
+ if err == nil || !fragmentOk || !strings.Contains(err.Error(), "expected 'package'") {
+ return
}
// If this is a declaration list, make it a source file
@@ -262,19 +259,19 @@
psrc := append([]byte("package p;"), src...)
file, err = parser.ParseFile(fset, filename, psrc, parserMode)
if err == nil {
- adjust := func(orig, src []byte) []byte {
+ sourceAdj = func(src []byte, indent int) []byte {
// Remove the package clause.
// Gofmt has turned the ; into a \n.
- src = src[len("package p\n"):]
- return matchSpace(orig, src)
+ src = src[indent+len("package p\n"):]
+ return bytes.TrimSpace(src)
}
- return file, adjust, nil
+ return
}
// If the error is that the source file didn't begin with a
// declaration, fall through to try as a statement list.
// Stop and return on any other error.
if !strings.Contains(err.Error(), "expected declaration") {
- return nil, nil, err
+ return
}
// If this is a statement list, make it a source file
@@ -285,65 +282,89 @@
fsrc := append(append([]byte("package p; func _() {"), src...), '\n', '}')
file, err = parser.ParseFile(fset, filename, fsrc, parserMode)
if err == nil {
- adjust := func(orig, src []byte) []byte {
+ sourceAdj = func(src []byte, indent int) []byte {
+ // Cap adjusted indent to zero.
+ if indent < 0 {
+ indent = 0
+ }
// Remove the wrapping.
// Gofmt has turned the ; into a \n\n.
- src = src[len("package p\n\nfunc _() {"):]
- src = src[:len(src)-len("\n}\n")]
- // Gofmt has also indented the function body one level.
- // Remove that indent.
- src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1)
- return matchSpace(orig, src)
+ // There will be two non-blank lines with indent, hence 2*indent.
+ src = src[2*indent+len("package p\n\nfunc _() {"):]
+ src = src[:len(src)-(indent+len("\n}\n"))]
+ return bytes.TrimSpace(src)
}
- return file, adjust, nil
+ // Gofmt has also indented the function body one level.
+ // Adjust that with indentAdj.
+ indentAdj = -1
}
- // Failed, and out of options.
- return nil, nil, err
+ // Succeeded, or out of options.
+ return
}
-func cutSpace(b []byte) (before, middle, after []byte) {
- i := 0
- for i < len(b) && (b[i] == ' ' || b[i] == '\t' || b[i] == '\n') {
- i++
+func format(fset *token.FileSet, file *ast.File, sourceAdj func(src []byte, indent int) []byte, indentAdj int, src []byte) ([]byte, error) {
+ if sourceAdj == nil {
+ // Complete source file.
+ var buf bytes.Buffer
+ err := (&printer.Config{Mode: printerMode, Tabwidth: tabWidth}).Fprint(&buf, fset, file)
+ if err != nil {
+ return nil, err
+ }
+ return buf.Bytes(), nil
}
- j := len(b)
- for j > 0 && (b[j-1] == ' ' || b[j-1] == '\t' || b[j-1] == '\n') {
- j--
+
+ // Partial source file.
+ // Determine and prepend leading space.
+ i, j := 0, 0
+ for j < len(src) && isSpace(src[j]) {
+ if src[j] == '\n' {
+ i = j + 1 // byte offset of last line in leading space
+ }
+ j++
}
- if i <= j {
- return b[:i], b[i:j], b[j:]
+ var res []byte
+ res = append(res, src[:i]...)
+
+ // Determine and prepend indentation of first code line.
+ // Spaces are ignored unless there are no tabs,
+ // in which case spaces count as one tab.
+ indent := 0
+ hasSpace := false
+ for _, b := range src[i:j] {
+ switch b {
+ case ' ':
+ hasSpace = true
+ case '\t':
+ indent++
+ }
}
- return nil, nil, b[j:]
+ if indent == 0 && hasSpace {
+ indent = 1
+ }
+ for i := 0; i < indent; i++ {
+ res = append(res, '\t')
+ }
+
+ // Format the source.
+ // Write it without any leading and trailing space.
+ cfg := &printer.Config{Mode: printerMode, Tabwidth: tabWidth}
+ cfg.Indent = indent + indentAdj
+ var buf bytes.Buffer
+ err := cfg.Fprint(&buf, fset, file)
+ if err != nil {
+ return nil, err
+ }
+ res = append(res, sourceAdj(buf.Bytes(), cfg.Indent)...)
+
+ // Determine and append trailing space.
+ i = len(src)
+ for i > 0 && isSpace(src[i-1]) {
+ i--
+ }
+ return append(res, src[i:]...), nil
}
-// matchSpace reformats src to use the same space context as orig.
-// 1) If orig begins with blank lines, matchSpace inserts them at the beginning of src.
-// 2) matchSpace copies the indentation of the first non-blank line in orig
-// to every non-blank line in src.
-// 3) matchSpace copies the trailing space from orig and uses it in place
-// of src's trailing space.
-func matchSpace(orig []byte, src []byte) []byte {
- before, _, after := cutSpace(orig)
- i := bytes.LastIndex(before, []byte{'\n'})
- before, indent := before[:i+1], before[i+1:]
-
- _, src, _ = cutSpace(src)
-
- var b bytes.Buffer
- b.Write(before)
- for len(src) > 0 {
- line := src
- if i := bytes.IndexByte(line, '\n'); i >= 0 {
- line, src = line[:i+1], line[i+1:]
- } else {
- src = nil
- }
- if len(line) > 0 && line[0] != '\n' { // not blank
- b.Write(indent)
- }
- b.Write(line)
- }
- b.Write(after)
- return b.Bytes()
+func isSpace(b byte) bool {
+ return b == ' ' || b == '\t' || b == '\n' || b == '\r'
}
« no previous file with comments | « no previous file | src/cmd/gofmt/long_test.go » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b