internal/lsp: fix end positions for multi-line comments with CRLF lines

The previous work-around assumed that the end of the comment would be
off by one line, when in reality, it is off by the offset of the number
of \r in the comment, which may result in the comment end position being
off by multiple lines. Work around this by scanning for the end of the
comment position using text/scanner.

Fixes golang/go#42646

Change-Id: Icc33e889546f324c6b65b55a98dea587f84c8f01
Reviewed-on: https://go-review.googlesource.com/c/tools/+/270879
Trust: Rebecca Stambler <rstambler@golang.org>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Rebecca Stambler 2020-11-17 18:05:09 -05:00
Родитель bd56c0adb3
Коммит 598b068a91
2 изменённых файлов: 64 добавлений и 5 удалений

Просмотреть файл

@ -181,3 +181,42 @@ func Hi() {
}
})
}
func TestCRLF_42646(t *testing.T) {
runner.Run(t, "-- main.go --", func(t *testing.T, env *Env) {
want := `package main
import (
"fmt"
)
/*
func upload(c echo.Context) error {
if err := r.ParseForm(); err != nil {
fmt.Fprintf(w, "ParseForm() err: %v", err)
return
}
fmt.Fprintf(w, "POST request successful")
path_ver := r.FormValue("path_ver")
ukclin_ver := r.FormValue("ukclin_ver")
fmt.Fprintf(w, "Name = %s\n", path_ver)
fmt.Fprintf(w, "Address = %s\n", ukclin_ver)
}
*/
func main() {
const server_port = 8080
fmt.Printf("port: %d\n", server_port)
}
`
crlf := strings.ReplaceAll(want, "\n", "\r\n")
env.CreateBuffer("main.go", crlf)
env.OrganizeImports("main.go")
got := env.Editor.BufferText("main.go")
got = strings.ReplaceAll(got, "\r\n", "\n") // convert everything to LF for simplicity
if want != got {
t.Errorf("unexpected content after save:\n%s", tests.Diff(want, got))
}
})
}

Просмотреть файл

@ -14,6 +14,7 @@ import (
"go/parser"
"go/token"
"strings"
"text/scanner"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/imports"
@ -228,12 +229,17 @@ func importPrefix(src []byte) string {
}
for _, c := range f.Comments {
if end := tok.Offset(c.End()); end > importEnd {
// Work-around golang/go#41197: For multi-line comments add +2 to
// the offset. The end position does not account for the */ at the
// end.
startLine := tok.Position(c.Pos()).Line
endLine := tok.Position(c.End()).Line
if end+2 <= tok.Size() && tok.Position(tok.Pos(end+2)).Line == endLine {
end += 2
// Work around golang/go#41197 by checking if the comment might
// contain "\r", and if so, find the actual end position of the
// comment by scanning the content of the file.
startOffset := tok.Offset(c.Pos())
if startLine != endLine && bytes.Contains(src[startOffset:], []byte("\r")) {
if commentEnd := scanForCommentEnd(tok, src[startOffset:]); commentEnd > 0 {
end = startOffset + commentEnd
}
}
importEnd = maybeAdjustToLineEnd(tok.Pos(end), true)
}
@ -244,6 +250,20 @@ func importPrefix(src []byte) string {
return string(src[:importEnd])
}
// scanForCommentEnd returns the offset of the end of the multi-line comment
// at the start of the given byte slice.
func scanForCommentEnd(tok *token.File, src []byte) int {
var s scanner.Scanner
s.Init(bytes.NewReader(src))
s.Mode ^= scanner.SkipComments
t := s.Scan()
if t == scanner.Comment {
return s.Pos().Offset
}
return 0
}
func computeTextEdits(ctx context.Context, snapshot Snapshot, pgf *ParsedGoFile, formatted string) ([]protocol.TextEdit, error) {
_, done := event.Start(ctx, "source.computeTextEdits")
defer done()