internal/lsp: fix formatting bug that keeps adding extra newlines

If a file ends with an empty newline, go/token treats the newline as the
final character of the previous line. VSCode, however, treats this as a
final line with no characters. We handle this by determining if the file
we are formatting ends with a newline character and updating the
protocol ranges accordingly.

Change-Id: Id8be0fd776ae65c8f0f937f3e718825e407cb217
Reviewed-on: https://go-review.googlesource.com/c/150338
Reviewed-by: Ian Cottrell <iancottrell@google.com>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Rebecca Stambler 2018-11-19 19:19:47 -05:00
parent fc4f04983f
commit b6bf295893
1 changed files with 18 additions and 3 deletions

View File

@ -277,21 +277,36 @@ func formatRange(ctx context.Context, v *source.View, uri protocol.DocumentURI,
} else {
r = fromProtocolRange(tok, *rng)
}
content, err := f.Read()
if err != nil {
return nil, err
}
edits, err := source.Format(ctx, f, r)
if err != nil {
return nil, err
}
return toProtocolEdits(tok, edits), nil
return toProtocolEdits(tok, content, edits), nil
}
func toProtocolEdits(f *token.File, edits []source.TextEdit) []protocol.TextEdit {
func toProtocolEdits(tok *token.File, content []byte, edits []source.TextEdit) []protocol.TextEdit {
if edits == nil {
return nil
}
// When a file ends with an empty line, the newline character is counted
// as part of the previous line. This causes the formatter to insert
// another unnecessary newline on each formatting. We handle this case by
// checking if the file already ends with a newline character.
hasExtraNewline := content[len(content)-1] == '\n'
result := make([]protocol.TextEdit, len(edits))
for i, edit := range edits {
rng := toProtocolRange(tok, edit.Range)
// If the edit ends at the end of the file, add the extra line.
if hasExtraNewline && tok.Offset(edit.Range.End) == len(content) {
rng.End.Line++
rng.End.Character = 0
}
result[i] = protocol.TextEdit{
Range: toProtocolRange(f, edit.Range),
Range: rng,
NewText: edit.NewText,
}
}