Skip to content

Commit 64e1dd6

Browse files
authored
Add type information to local variables in SCIP index (#169)
For all local symbols (local variables, parameters, etc.), include their Go type information in the SymbolInformation.SignatureDocumentation field. This allows code intelligence features like hover tooltips to show the type of local variables without needing to jump to their definitions. The implementation: - Extends localSymbolInfo struct to store type information alongside symbol ID - Updates createNewLocalSymbol to extract type from types.Object - Includes type string in SignatureDocumentation for each local symbol - Adds display names for better readability in UI
1 parent 7456957 commit 64e1dd6

File tree

2 files changed

+95
-26
lines changed

2 files changed

+95
-26
lines changed

internal/lookup/local.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package lookup
2+
3+
import (
4+
"go/types"
5+
"strings"
6+
)
7+
8+
// Local contains information about a local symbol
9+
type Local struct {
10+
Symbol string
11+
Obj types.Object
12+
}
13+
14+
// SignatureText builds a concise signature string for this local symbol.
15+
func (l *Local) SignatureText() string {
16+
if l.Obj == nil {
17+
return ""
18+
}
19+
20+
var parts []string
21+
22+
switch l.Obj.(type) {
23+
case *types.Const:
24+
parts = append(parts, "const")
25+
case *types.PkgName:
26+
parts = append(parts, "import")
27+
case *types.Var:
28+
parts = append(parts, "var")
29+
}
30+
31+
if name := l.Obj.Name(); name != "" {
32+
parts = append(parts, name)
33+
}
34+
35+
// For PkgName, append the package path instead of type
36+
if pkgName, isPkgName := l.Obj.(*types.PkgName); isPkgName {
37+
if imported := pkgName.Imported(); imported != nil {
38+
parts = append(parts, imported.Path())
39+
}
40+
} else {
41+
if t := l.Obj.Type(); t != nil {
42+
if ts := t.String(); ts != "" {
43+
parts = append(parts, ts)
44+
}
45+
}
46+
}
47+
48+
return strings.Join(parts, " ")
49+
}

internal/visitors/visitor_file.go

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func NewFileVisitor(
4848
pkg: pkg,
4949
file: file,
5050
pkgLookup: pkgLookup,
51-
locals: map[token.Pos]string{},
51+
locals: map[token.Pos]lookup.Local{},
5252
pkgSymbols: pkgSymbols,
5353
globalSymbols: globalSymbols,
5454
occurrences: occurrences,
@@ -77,8 +77,8 @@ type fileVisitor struct {
7777
// soething
7878
pkgLookup loader.PackageLookup
7979

80-
// local definition position to symbol
81-
locals map[token.Pos]string
80+
// local definition position to symbol and its type information
81+
locals map[token.Pos]lookup.Local
8282

8383
// field definition position to symbol for the package
8484
pkgSymbols *lookup.Package
@@ -103,13 +103,19 @@ type fileVisitor struct {
103103
// Implements ast.Visitor
104104
var _ ast.Visitor = &fileVisitor{}
105105

106-
func (f *fileVisitor) createNewLocalSymbol(pos token.Pos) string {
107-
if _, ok := f.locals[pos]; ok {
106+
func (v *fileVisitor) createNewLocalSymbol(pos token.Pos, obj types.Object) string {
107+
if _, ok := v.locals[pos]; ok {
108108
panic("Cannot create a new local symbol for an ident that has already been created")
109109
}
110110

111-
f.locals[pos] = fmt.Sprintf("local %d", len(f.locals))
112-
return f.locals[pos]
111+
symbol := fmt.Sprintf("local %d", len(v.locals))
112+
113+
v.locals[pos] = lookup.Local{
114+
Symbol: symbol,
115+
Obj: obj,
116+
}
117+
118+
return symbol
113119
}
114120

115121
func (v *fileVisitor) Visit(n ast.Node) ast.Visitor {
@@ -127,16 +133,19 @@ func (v *fileVisitor) Visit(n ast.Node) ast.Visitor {
127133
}
128134

129135
if node.Name != nil && node.Name.Name != "." {
130-
sym := v.createNewLocalSymbol(node.Name.Pos())
131-
v.NewDefinition(sym, symbols.RangeFromName(v.pkg.Fset.Position(node.Name.Pos()), node.Name.Name, false))
136+
pkgAlias := v.pkg.TypesInfo.Defs[node.Name]
137+
symName := v.createNewLocalSymbol(node.Name.Pos(), pkgAlias)
138+
rangeFromName := symbols.RangeFromName(
139+
v.pkg.Fset.Position(node.Name.Pos()), node.Name.Name, false)
140+
v.NewDefinition(symName, rangeFromName)
132141

133142
// Save package name override, so that we use the new local symbol
134143
// within this file
135-
v.overrides.pkgNameOverride[newtypes.GetID(importedPackage)] = sym
144+
v.overrides.pkgNameOverride[newtypes.GetID(importedPackage)] = symName
136145
}
137146

138147
position := v.pkg.Fset.Position(node.Path.Pos())
139-
v.emitImportReference(v.globalSymbols, v.doc, position, importedPackage)
148+
v.emitImportReference(v.globalSymbols, position, importedPackage)
140149

141150
return nil
142151

@@ -197,8 +206,8 @@ func (v *fileVisitor) Visit(n ast.Node) ast.Visitor {
197206

198207
// Short circuit on case clauses
199208
if obj, ok := v.overrides.caseClauses[node.Pos()]; ok {
200-
sym := v.createNewLocalSymbol(obj.Pos())
201-
v.NewDefinition(sym, scipRange(position, obj))
209+
symName := v.createNewLocalSymbol(obj.Pos(), obj)
210+
v.NewDefinition(symName, scipRange(position, obj))
202211
return nil
203212
}
204213

@@ -207,16 +216,16 @@ func (v *fileVisitor) Visit(n ast.Node) ast.Visitor {
207216
// Emit Definition
208217
def := info.Defs[node]
209218
if def != nil {
210-
var sym string
219+
var symName string
211220
if pkgSymbols, ok := v.pkgSymbols.GetSymbol(def.Pos()); ok {
212-
sym = pkgSymbols
221+
symName = pkgSymbols
213222
} else if globalSymbol, ok := v.globalSymbols.GetSymbol(v.pkg, def.Pos()); ok {
214-
sym = globalSymbol
223+
symName = globalSymbol
215224
} else {
216-
sym = v.createNewLocalSymbol(def.Pos())
225+
symName = v.createNewLocalSymbol(def.Pos(), def)
217226
}
218227

219-
v.NewDefinition(sym, scipRange(position, def))
228+
v.NewDefinition(symName, scipRange(position, def))
220229
}
221230

222231
// Emit Reference
@@ -228,7 +237,7 @@ func (v *fileVisitor) Visit(n ast.Node) ast.Visitor {
228237
)
229238

230239
if localSymbol, ok := v.locals[ref.Pos()]; ok {
231-
symbol = localSymbol
240+
symbol = localSymbol.Symbol
232241

233242
if _, ok := v.overrides.caseClauses[ref.Pos()]; ok {
234243
overrideType = v.pkg.TypesInfo.TypeOf(node)
@@ -285,17 +294,16 @@ func (v *fileVisitor) Visit(n ast.Node) ast.Visitor {
285294

286295
func (v *fileVisitor) emitImportReference(
287296
globalSymbols *lookup.Global,
288-
doc *document.Document,
289297
position token.Position,
290298
importedPackage *packages.Package,
291299
) {
292300
scipRange := symbols.RangeFromName(position, importedPackage.PkgPath, true)
293-
symbol := globalSymbols.GetPkgNameSymbol(importedPackage)
294-
if symbol == nil {
301+
if scipRange == nil {
295302
handler.ErrOrPanic("Missing symbol for package path: %s", importedPackage.ID)
296303
return
297304
}
298305

306+
symbol := globalSymbols.GetPkgNameSymbol(importedPackage)
299307
if symbol == nil {
300308
handler.ErrOrPanic("Missing symbol information for package: %s", importedPackage.ID)
301309
return
@@ -338,10 +346,22 @@ func (v *fileVisitor) ToScipDocument() *scip.Document {
338346
}
339347

340348
documentSymbols := v.pkgSymbols.SymbolsForFile(documentFile)
341-
for _, localSymbol := range v.locals {
342-
documentSymbols = append(documentSymbols, &scip.SymbolInformation{
343-
Symbol: localSymbol,
344-
})
349+
for _, local := range v.locals {
350+
symbolInfo := &scip.SymbolInformation{
351+
Symbol: local.Symbol,
352+
}
353+
354+
if obj := local.Obj; obj != nil {
355+
symbolInfo.DisplayName = obj.Name()
356+
if txt := local.SignatureText(); txt != "" {
357+
symbolInfo.SignatureDocumentation = &scip.Document{
358+
Language: "go",
359+
Text: txt,
360+
}
361+
}
362+
}
363+
364+
documentSymbols = append(documentSymbols, symbolInfo)
345365
}
346366

347367
return &scip.Document{

0 commit comments

Comments
 (0)