diff --git a/internal/lsp/completion.go b/internal/lsp/completion.go index 754b90d641..aa285251b1 100644 --- a/internal/lsp/completion.go +++ b/internal/lsp/completion.go @@ -42,6 +42,17 @@ func (s *Server) completion(ctx context.Context, params *protocol.CompletionPara if err != nil { return nil, err } + // Span treats an end of file as the beginning of the next line, which for + // a final line ending without a newline is incorrect and leads to + // completions being ignored. We adjust the ending in case ange end is on a + // different line here. + // This should be removed after the resolution of golang/go#41029 + if rng.Start.Line != rng.End.Line { + rng.End = protocol.Position{ + Character: rng.Start.Character + float64(len(surrounding.Content())), + Line: rng.Start.Line, + } + } // When using deep completions/fuzzy matching, report results as incomplete so // client fetches updated completions after every key stroke. diff --git a/internal/lsp/source/completion.go b/internal/lsp/source/completion.go index 3c0ad9f179..bca2ac2e64 100644 --- a/internal/lsp/source/completion.go +++ b/internal/lsp/source/completion.go @@ -241,6 +241,10 @@ type Selection struct { mappedRange } +func (p Selection) Content() string { + return p.content +} + func (p Selection) Prefix() string { return p.content[:p.cursor-p.spanRange.Start] } diff --git a/internal/span/token.go b/internal/span/token.go index 1710b7779d..10b429efd1 100644 --- a/internal/span/token.go +++ b/internal/span/token.go @@ -114,6 +114,8 @@ func positionFromOffset(f *token.File, offset int) (string, int, int, error) { } pos := f.Pos(offset) p := f.Position(pos) + // TODO(golang/go#41029): Consider returning line, column instead of line+1, 1 if + // the file's last character is not a newline. if offset == f.Size() { return p.Filename, p.Line + 1, 1, nil }