internal/lsp/source: fix completion on final line of a document

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 range end is on a different line here.

Change-Id: Ic545dcb221493530b7e39d2be8eba57b69fb6597
Reviewed-on: https://go-review.googlesource.com/c/tools/+/249706
Run-TryBot: Danish Dua <danishdua@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/internal/lsp/completion.go b/internal/lsp/completion.go
index 754b90d..aa28525 100644
--- a/internal/lsp/completion.go
+++ b/internal/lsp/completion.go
@@ -42,6 +42,17 @@
 	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 3c0ad9f..bca2ac2 100644
--- a/internal/lsp/source/completion.go
+++ b/internal/lsp/source/completion.go
@@ -241,6 +241,10 @@
 	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 1710b77..10b429e 100644
--- a/internal/span/token.go
+++ b/internal/span/token.go
@@ -114,6 +114,8 @@
 	}
 	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
 	}