Skip to content

Commit 55a3adf

Browse files
authored
LSP improve import completions (#4103)
1 parent 96152df commit 55a3adf

File tree

2 files changed

+61
-8
lines changed

2 files changed

+61
-8
lines changed

private/buf/buflsp/completion.go

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ func completionItemsForDeclPath(ctx context.Context, file *file, declPath []ast.
9696
case ast.DeclKindPackage:
9797
return completionItemsForPackage(ctx, file, decl.AsPackage())
9898
case ast.DeclKindImport:
99-
return completionItemsForImport(ctx, file)
99+
return completionItemsForImport(ctx, file, decl.AsImport(), position)
100100
default:
101101
file.lsp.logger.DebugContext(ctx, "completion: unknown declaration type", slog.String("kind", decl.Kind().String()))
102102
return nil
@@ -208,19 +208,69 @@ func completionItemsForDef(ctx context.Context, file *file, declPath []ast.DeclA
208208
// completionItemsForImport returns completion items for import declarations.
209209
//
210210
// Suggest all importable files.
211-
func completionItemsForImport(ctx context.Context, file *file) []protocol.CompletionItem {
211+
func completionItemsForImport(ctx context.Context, file *file, declImport ast.DeclImport, position protocol.Position) []protocol.CompletionItem {
212212
file.lsp.logger.DebugContext(ctx, "completion: import declaration", slog.Int("importable_count", len(file.importToFile)))
213213

214-
items := make([]protocol.CompletionItem, 0, len(file.importToFile))
214+
positionLocation := file.file.InverseLocation(int(position.Line)+1, int(position.Character)+1, positionalEncoding)
215+
offset := positionLocation.Offset
216+
217+
importPathSpan := declImport.ImportPath().Span()
218+
start, end := importPathSpan.Start, importPathSpan.End
219+
importPathText := importPathSpan.Text()
220+
221+
// Break on newlines to split an unintended capture.
222+
if index := strings.IndexByte(importPathText, '\n'); index != -1 {
223+
end = start + index
224+
importPathText = importPathText[:index]
225+
}
226+
227+
if start > offset || offset > end {
228+
file.lsp.logger.Debug("completion: outside import expr range",
229+
slog.String("import", importPathText),
230+
slog.Int("start", start),
231+
slog.Int("offset", offset),
232+
slog.Int("end", end),
233+
slog.Int("line", int(position.Line)),
234+
slog.Int("character", int(position.Character)),
235+
)
236+
return nil
237+
}
238+
239+
index := offset - start
240+
prefix := importPathText[:index]
241+
suffix := importPathText[index:]
242+
243+
var items []protocol.CompletionItem
215244
for importPath := range file.importToFile {
245+
suggest := fmt.Sprintf("%q", importPath)
246+
if !strings.HasPrefix(suggest, prefix) {
247+
file.lsp.logger.Debug("completion: skipping on prefix",
248+
slog.String("import", importPathText),
249+
slog.String("prefix", prefix),
250+
slog.String("suggest", suggest),
251+
)
252+
continue
253+
}
254+
if !strings.HasSuffix(suggest, suffix) {
255+
file.lsp.logger.Debug("completion: skipping on suffix",
256+
slog.String("import", importPathText),
257+
slog.String("suffix", prefix),
258+
slog.String("suggest", suggest),
259+
)
260+
continue
261+
}
216262
items = append(items, protocol.CompletionItem{
217-
Label: " \"" + importPath + "\";",
263+
Label: importPath,
218264
Kind: protocol.CompletionItemKindFile,
265+
TextEdit: &protocol.TextEdit{
266+
Range: protocol.Range{
267+
Start: position,
268+
End: position,
269+
},
270+
NewText: suggest[len(prefix) : len(suggest)-len(suffix)],
271+
},
219272
})
220273
}
221-
slices.SortFunc(items, func(a, b protocol.CompletionItem) int {
222-
return strings.Compare(strings.ToLower(a.Label), strings.ToLower(b.Label))
223-
})
224274
return items
225275
}
226276

private/buf/buflsp/server.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func (s *server) Initialize(
123123
},
124124
CompletionProvider: &protocol.CompletionOptions{
125125
ResolveProvider: true,
126-
TriggerCharacters: []string{" ", ".", "("},
126+
TriggerCharacters: []string{" ", ".", "(", "\"", "/"},
127127
},
128128
DefinitionProvider: &protocol.DefinitionOptions{
129129
WorkDoneProgressOptions: protocol.WorkDoneProgressOptions{WorkDoneProgress: true},
@@ -455,6 +455,9 @@ func (s *server) Completion(
455455
return nil, nil
456456
}
457457
items := getCompletionItems(ctx, file, params.Position)
458+
if len(items) == 0 {
459+
return nil, nil
460+
}
458461
return &protocol.CompletionList{Items: items}, nil
459462
}
460463

0 commit comments

Comments
 (0)