From d812e8c994a2ef15c0f2819581db51fa07fe5b05 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Fri, 4 Feb 2011 17:34:16 -0800 Subject: [PATCH] go/printer, gofmt: smarter handling of multi-line raw strings If a multi-line raw string is the first token on a line, it should not be indented because the following lines (belonging to the raw string) are not indented either. Adjusted src of ebnf/ebnf_test.go manually as it now is formatted as expected. gofmt -w src misc Fixes #1072. R=r CC=golang-dev https://golang.org/cl/4119056 --- src/pkg/ebnf/ebnf_test.go | 50 +++++------ src/pkg/go/printer/printer.go | 87 +++++++++++++++---- .../go/printer/testdata/expressions.golden | 71 +++++++++++++++ src/pkg/go/printer/testdata/expressions.input | 79 +++++++++++++++++ src/pkg/go/printer/testdata/expressions.raw | 72 ++++++++++++++- 5 files changed, 315 insertions(+), 44 deletions(-) diff --git a/src/pkg/ebnf/ebnf_test.go b/src/pkg/ebnf/ebnf_test.go index bbe530c278..69ad5fed1c 100644 --- a/src/pkg/ebnf/ebnf_test.go +++ b/src/pkg/ebnf/ebnf_test.go @@ -15,31 +15,31 @@ var fset = token.NewFileSet() var grammars = []string{ - `Program = . - `, - - `Program = foo . - foo = "foo" . - `, - - `Program = "a" | "b" "c" . - `, - - `Program = "a" ... "z" . - `, - - `Program = Song . - Song = { Note } . - Note = Do | (Re | Mi | Fa | So | La) | Ti . - Do = "c" . - Re = "d" . - Mi = "e" . - Fa = "f" . - So = "g" . - La = "a" . - Ti = ti . - ti = "b" . - `, +`Program = . +`, + +`Program = foo . +foo = "foo" . +`, + +`Program = "a" | "b" "c" . +`, + +`Program = "a" ... "z" . +`, + +`Program = Song . + Song = { Note } . + Note = Do | (Re | Mi | Fa | So | La) | Ti . + Do = "c" . + Re = "d" . + Mi = "e" . + Fa = "f" . + So = "g" . + La = "a" . + Ti = ti . + ti = "b" . +`, } diff --git a/src/pkg/go/printer/printer.go b/src/pkg/go/printer/printer.go index 34b0c4e2dc..2790a5c34c 100644 --- a/src/pkg/go/printer/printer.go +++ b/src/pkg/go/printer/printer.go @@ -34,6 +34,12 @@ const ( ) +const ( + esc2 = '\xfe' // an escape byte that cannot occur in regular UTF-8 + _ = 1 / (esc2 - tabwriter.Escape) // cause compiler error if esc2 == tabwriter.Escape +) + + var ( esc = []byte{tabwriter.Escape} htab = []byte{'\t'} @@ -843,6 +849,19 @@ func (p *printer) print(args ...interface{}) { escData = append(escData, tabwriter.Escape) data = escData tok = x.Kind + // If we have a raw string that spans multiple lines and + // the opening quote (`) is on a line preceded only by + // indentation, we don't want to write that indentation + // because the following lines of the raw string are not + // indented. It's easiest to correct the output at the end + // via the trimmer (because of the complex handling of + // white space). + // Mark multi-line raw strings by replacing the opening + // quote with esc2 and have the trimmer take care of fixing + // it up. (Do this _after_ making a copy of data!) + if data[1] == '`' && bytes.IndexByte(data, '\n') > 0 { + data[1] = esc2 + } case token.Token: s := x.String() if mayCombine(p.lastTok, s[0]) { @@ -927,21 +946,26 @@ func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) { // through unchanged. // type trimmer struct { - output io.Writer - space bytes.Buffer - state int + output io.Writer + state int + space bytes.Buffer + hasText bool } // trimmer is implemented as a state machine. // It can be in one of the following states: const ( - inSpace = iota - inEscape - inText + inSpace = iota // inside space + atEscape // inside space and the last char was an opening tabwriter.Escape + inEscape // inside text bracketed by tabwriter.Escapes + inText // inside text ) +var backquote = []byte{'`'} + + // Design note: It is tempting to eliminate extra blanks occurring in // whitespace in this function as it could simplify some // of the blanks logic in the node printing functions. @@ -949,7 +973,13 @@ const ( // the tabwriter. func (p *trimmer) Write(data []byte) (n int, err os.Error) { - m := 0 // if p.state != inSpace, data[m:n] is unwritten + // invariants: + // p.state == inSpace, atEscape: + // p.space is unwritten + // p.hasText indicates if there is any text on this line + // p.state == inEscape, inText: + // data[m:n] is unwritten + m := 0 var b byte for n, b = range data { if b == '\v' { @@ -960,37 +990,55 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) { switch b { case '\t', ' ': p.space.WriteByte(b) // WriteByte returns no errors - case '\f', '\n': + case '\n', '\f': p.space.Reset() // discard trailing space _, err = p.output.Write(newlines[0:1]) // write newline + p.hasText = false case tabwriter.Escape: - _, err = p.output.Write(p.space.Bytes()) - p.space.Reset() - p.state = inEscape - m = n + 1 // drop tabwriter.Escape + p.state = atEscape default: _, err = p.output.Write(p.space.Bytes()) - p.space.Reset() p.state = inText m = n } + case atEscape: + // discard indentation if we have a multi-line raw string + // (see printer.print for details) + if b != esc2 || p.hasText { + _, err = p.output.Write(p.space.Bytes()) + } + p.state = inEscape + m = n + if b == esc2 { + _, err = p.output.Write(backquote) // convert back + m++ + } case inEscape: if b == tabwriter.Escape { _, err = p.output.Write(data[m:n]) p.state = inSpace + p.space.Reset() + p.hasText = true } case inText: switch b { case '\t', ' ': _, err = p.output.Write(data[m:n]) p.state = inSpace + p.space.Reset() p.space.WriteByte(b) // WriteByte returns no errors - case '\f': - data[n] = '\n' // convert to newline + p.hasText = true + case '\n', '\f': + _, err = p.output.Write(data[m:n]) + p.state = inSpace + p.space.Reset() + _, err = p.output.Write(newlines[0:1]) // write newline + p.hasText = false case tabwriter.Escape: _, err = p.output.Write(data[m:n]) - p.state = inEscape - m = n + 1 // drop tabwriter.Escape + p.state = atEscape + p.space.Reset() + p.hasText = true } } if err != nil { @@ -999,9 +1047,12 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) { } n = len(data) - if p.state != inSpace { + switch p.state { + case inEscape, inText: _, err = p.output.Write(data[m:n]) p.state = inSpace + p.space.Reset() + p.hasText = true } return diff --git a/src/pkg/go/printer/testdata/expressions.golden b/src/pkg/go/printer/testdata/expressions.golden index 882c7624c0..7f18f338a6 100644 --- a/src/pkg/go/printer/testdata/expressions.golden +++ b/src/pkg/go/printer/testdata/expressions.golden @@ -247,6 +247,77 @@ they must not be removed` } +func _() { + // smart handling of indentation for multi-line raw strings + var _ = `` + var _ = `foo` + var _ = `foo +bar` + + var _ = `` + var _ = `foo` + var _ = + // the next line should not be indented +`foo +bar` + + var _ = // comment + `` + var _ = // comment + `foo` + var _ = // comment + // the next line should not be indented +`foo +bar` + + var _ = /* comment */ `` + var _ = /* comment */ `foo` + var _ = /* comment */ `foo +bar` + + var _ = /* comment */ + `` + var _ = /* comment */ + `foo` + var _ = /* comment */ + // the next line should not be indented +`foo +bar` + + var board = []int( +`........... +........... +....●●●.... +....●●●.... +..●●●●●●●.. +..●●●○●●●.. +..●●●●●●●.. +....●●●.... +....●●●.... +........... +........... +`) + + var state = S{ + "foo", + // the next line should not be indented +`........... +........... +....●●●.... +....●●●.... +..●●●●●●●.. +..●●●○●●●.. +..●●●●●●●.. +....●●●.... +....●●●.... +........... +........... +`, + "bar", + } +} + + func _() { // one-line function literals (body is on a single line) _ = func() {} diff --git a/src/pkg/go/printer/testdata/expressions.input b/src/pkg/go/printer/testdata/expressions.input index 647706b092..6bcd9b5f89 100644 --- a/src/pkg/go/printer/testdata/expressions.input +++ b/src/pkg/go/printer/testdata/expressions.input @@ -243,6 +243,85 @@ they must not be removed` } +func _() { + // smart handling of indentation for multi-line raw strings + var _ = `` + var _ = `foo` + var _ = `foo +bar` + + +var _ = + `` +var _ = + `foo` +var _ = + // the next line should not be indented + `foo +bar` + + + var _ = // comment + `` + var _ = // comment + `foo` + var _ = // comment + // the next line should not be indented + `foo +bar` + + +var _ = /* comment */ `` +var _ = /* comment */ `foo` +var _ = /* comment */ `foo +bar` + + + var _ = /* comment */ + `` + var _ = /* comment */ + `foo` + var _ = /* comment */ + // the next line should not be indented + `foo +bar` + + +var board = []int( + `........... +........... +....●●●.... +....●●●.... +..●●●●●●●.. +..●●●○●●●.. +..●●●●●●●.. +....●●●.... +....●●●.... +........... +........... +`) + + + var state = S{ + "foo", + // the next line should not be indented + `........... +........... +....●●●.... +....●●●.... +..●●●●●●●.. +..●●●○●●●.. +..●●●●●●●.. +....●●●.... +....●●●.... +........... +........... +`, + "bar", + } +} + + func _() { // one-line function literals (body is on a single line) _ = func() {} diff --git a/src/pkg/go/printer/testdata/expressions.raw b/src/pkg/go/printer/testdata/expressions.raw index 62be00cc30..f1944c94bb 100644 --- a/src/pkg/go/printer/testdata/expressions.raw +++ b/src/pkg/go/printer/testdata/expressions.raw @@ -243,7 +243,77 @@ func _() { _ = `foo bar` _ = `three spaces before the end of the line starting here: -they must not be removed` +they must not be removed` } + + +func _() { + // smart handling of indentation for multi-line raw strings + var _ = `` + var _ = `foo` + var _ = `foo +bar` + + var _ = `` + var _ = `foo` + var _ = + // the next line should not be indented +`foo +bar` + + var _ = // comment + `` + var _ = // comment + `foo` + var _ = // comment + // the next line should not be indented +`foo +bar` + + var _ = /* comment */ `` + var _ = /* comment */ `foo` + var _ = /* comment */ `foo +bar` + + var _ = /* comment */ + `` + var _ = /* comment */ + `foo` + var _ = /* comment */ + // the next line should not be indented +`foo +bar` + + var board = []int( +`........... +........... +....●●●.... +....●●●.... +..●●●●●●●.. +..●●●○●●●.. +..●●●●●●●.. +....●●●.... +....●●●.... +........... +........... +`) + + var state = S{ + "foo", + // the next line should not be indented +`........... +........... +....●●●.... +....●●●.... +..●●●●●●●.. +..●●●○●●●.. +..●●●●●●●.. +....●●●.... +....●●●.... +........... +........... +`, + "bar", + } } -- 2.50.0