From 98df123772120179846dbe20280e26b427d64dd6 Mon Sep 17 00:00:00 2001 From: Clint J Edwards Date: Tue, 10 Dec 2019 21:40:37 +0000 Subject: [PATCH] internal/lsp: add comment completions for exported vars This should provide simple name completions for comments above exported variables. Can be activated with `ctrl+space` within a comment. Pretty new, so all help is welcome. Fixes #34010 Change-Id: I1c8f71baa3beaa22ec5fd9fd4a531284a8d125f3 GitHub-Last-Rev: a9868eb69dc587cb4579268b2c3ae46932702641 GitHub-Pull-Request: golang/tools#166 Reviewed-on: https://go-review.googlesource.com/c/tools/+/197879 Reviewed-by: Rebecca Stambler --- internal/lsp/source/completion.go | 57 ++++++++++++++++++--- internal/lsp/testdata/complit/complit.go.in | 19 ++++--- internal/lsp/testdata/summary.txt.golden | 2 +- 3 files changed, 63 insertions(+), 15 deletions(-) diff --git a/internal/lsp/source/completion.go b/internal/lsp/source/completion.go index 72d3805e07c..d9d22a893e4 100644 --- a/internal/lsp/source/completion.go +++ b/internal/lsp/source/completion.go @@ -417,12 +417,7 @@ func Completion(ctx context.Context, snapshot Snapshot, f File, pos protocol.Pos if path == nil { return nil, nil, errors.Errorf("cannot find node enclosing position") } - // Skip completion inside comments. - for _, g := range file.Comments { - if g.Pos() <= rng.Start && rng.Start <= g.End() { - return nil, nil, nil - } - } + // Skip completion inside any kind of literal. if _, ok := path[0].(*ast.BasicLit); ok { return nil, nil, nil @@ -460,6 +455,14 @@ func Completion(ctx context.Context, snapshot Snapshot, f File, pos protocol.Pos c.expectedType = expectedType(c) + // If we're inside a comment return comment completions + for _, comment := range file.Comments { + if comment.Pos() <= rng.Start && rng.Start <= comment.End() { + c.populateCommentCompletions(comment) + return c.items, c.getSurrounding(), nil + } + } + // Struct literals are handled entirely separately. if c.wantStructFieldCompletions() { if err := c.structLiteralFieldName(); err != nil { @@ -534,6 +537,48 @@ func Completion(ctx context.Context, snapshot Snapshot, f File, pos protocol.Pos return c.items, c.getSurrounding(), nil } +// populateCommentCompletions returns completions for an exported variable immediately preceeding comment +func (c *completer) populateCommentCompletions(comment *ast.CommentGroup) { + + // Using the comment position find the line after + fset := c.snapshot.View().Session().Cache().FileSet() + file := fset.File(comment.Pos()) + if file == nil { + return + } + + line := file.Line(comment.Pos()) + nextLinePos := file.LineStart(line + 1) + if !nextLinePos.IsValid() { + return + } + + // Using the next line pos, grab and parse the exported variable on that line + for _, n := range c.file.Decls { + if n.Pos() != nextLinePos { + continue + } + switch node := n.(type) { + case *ast.GenDecl: + if node.Tok != token.VAR { + return + } + for _, spec := range node.Specs { + if value, ok := spec.(*ast.ValueSpec); ok { + for _, name := range value.Names { + if name.Name == "_" || !name.IsExported() { + continue + } + + exportedVar := c.pkg.GetTypesInfo().ObjectOf(name) + c.found(exportedVar, stdScore, nil) + } + } + } + } + } +} + func (c *completer) wantStructFieldCompletions() bool { clInfo := c.enclosingCompositeLiteral if clInfo == nil { diff --git a/internal/lsp/testdata/complit/complit.go.in b/internal/lsp/testdata/complit/complit.go.in index d3fd657e034..6a8fc5988cc 100644 --- a/internal/lsp/testdata/complit/complit.go.in +++ b/internal/lsp/testdata/complit/complit.go.in @@ -1,12 +1,15 @@ package complit +// //@complete(" ", cVar) +var C string //@item(cVar, "C", "string", "var") + type position struct { //@item(structPosition, "position", "struct{...}", "struct") X, Y int //@item(fieldX, "X", "int", "field"),item(fieldY, "Y", "int", "field") } func _() { _ = position{ - //@complete("", fieldX, fieldY, structPosition) + //@complete("", fieldX, fieldY, cVar, structPosition) } _ = position{ X: 1, @@ -18,7 +21,7 @@ func _() { } _ = []*position{ { - //@complete("", fieldX, fieldY, structPosition) + //@complete("", fieldX, fieldY, cVar, structPosition) }, } } @@ -34,7 +37,7 @@ func _() { } _ = map[int]int{ - //@complete("", abVar, aaVar, structPosition) + //@complete("", abVar, aaVar, cVar, structPosition) } _ = []string{a: ""} //@complete(":", abVar, aaVar) @@ -42,10 +45,10 @@ func _() { _ = position{X: a} //@complete("}", abVar, aaVar) _ = position{a} //@complete("}", abVar, aaVar) - _ = position{a, } //@complete("}", abVar, aaVar, structPosition) + _ = position{a, } //@complete("}", abVar, aaVar, cVar, structPosition) - _ = []int{a} //@complete("a", abVar, aaVar, structPosition) - _ = [1]int{a} //@complete("a", abVar, aaVar, structPosition) + _ = []int{a} //@complete("a", abVar, aaVar, cVar, structPosition) + _ = [1]int{a} //@complete("a", abVar, aaVar, cVar, structPosition) type myStruct struct { AA int //@item(fieldAA, "AA", "int", "field") @@ -70,7 +73,7 @@ func _() { func _() { _ := position{ - X: 1, //@complete("X", fieldX),complete(" 1", structPosition) - Y: , //@complete(":", fieldY),complete(" ,", structPosition) + X: 1, //@complete("X", fieldX),complete(" 1", cVar, structPosition) + Y: , //@complete(":", fieldY),complete(" ,", cVar, structPosition) } } diff --git a/internal/lsp/testdata/summary.txt.golden b/internal/lsp/testdata/summary.txt.golden index 1ab98b8347a..2a0b15d62c9 100644 --- a/internal/lsp/testdata/summary.txt.golden +++ b/internal/lsp/testdata/summary.txt.golden @@ -1,5 +1,5 @@ -- summary -- -CompletionsCount = 219 +CompletionsCount = 220 CompletionSnippetCount = 51 UnimportedCompletionsCount = 4 DeepCompletionsCount = 5