Skip to content

Commit 01c46c0

Browse files
committed
gopls: semantic tokens should report modifiers for the top-level type constructor of each symbol
These new semantic type modifiers will report in type definition, type embedding, type alias, variable definition like `var foo Bar`'s `Bar', parameter type and return type
1 parent 0734f62 commit 01c46c0

File tree

4 files changed

+52
-15
lines changed

4 files changed

+52
-15
lines changed

gopls/doc/semantictokens.md

+8-3
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,14 @@ The references to *object* refer to the
7272
1. __`keyword`__ All Go [keywords](https://golang.org/ref/spec#Keywords) are marked `keyword`.
7373
1. __`namespace`__ All package names are marked `namespace`. In an import, if there is an
7474
alias, it would be marked. Otherwise the last component of the import path is marked.
75-
1. __`type`__ Objects of type ```types.TypeName``` are marked `type`.
76-
If they are also ```types.Basic```
75+
1. __`type`__ Objects of type ```types.TypeName``` are marked `type`, it also reports
76+
various top-level type constructor modifiers.
77+
If they are ```types.Basic```
7778
the modifier is `defaultLibrary`. (And in ```type B struct{C}```, ```B``` has modifier `definition`.)
79+
If they are ```types.Interface```
80+
the modifier is `interface`,
81+
same as `struct`, `signature`, `pointer`, `array`, `map`, `slice`, `chan`,
82+
together with `interface`, they are the 8 additional modifiers gopls supports.
7883
1. __`parameter`__ The formal arguments in ```ast.FuncDecl``` and ```ast.FuncType``` nodes are marked `parameter`.
7984
1. __`variable`__ Identifiers in the
8085
scope of ```const``` are modified with `readonly`. ```nil``` is usually a `variable` modified with both
@@ -121,4 +126,4 @@ While a file is being edited it may temporarily contain either
121126
parsing errors or type errors. In this case gopls cannot determine some (or maybe any)
122127
of the semantic tokens. To avoid weird flickering it is the responsibility
123128
of clients to maintain the semantic token information
124-
in the unedited part of the file, and they do.
129+
in the unedited part of the file, and they do.

gopls/internal/golang/semtok.go

+41-12
Original file line numberDiff line numberDiff line change
@@ -210,18 +210,18 @@ func (tv *tokenVisitor) comment(c *ast.Comment, importByName map[string]*types.P
210210
}
211211
}
212212

213-
tokenTypeByObject := func(obj types.Object) semtok.TokenType {
213+
tokenTypeByObject := func(obj types.Object) (semtok.TokenType, []string) {
214214
switch obj.(type) {
215215
case *types.PkgName:
216-
return semtok.TokNamespace
216+
return semtok.TokNamespace, nil
217217
case *types.Func:
218-
return semtok.TokFunction
218+
return semtok.TokFunction, nil
219219
case *types.TypeName:
220-
return semtok.TokType
220+
return underlineType(obj)
221221
case *types.Const, *types.Var:
222-
return semtok.TokVariable
222+
return semtok.TokVariable, nil
223223
default:
224-
return semtok.TokComment
224+
return semtok.TokComment, nil
225225
}
226226
}
227227

@@ -244,7 +244,8 @@ func (tv *tokenVisitor) comment(c *ast.Comment, importByName map[string]*types.P
244244
}
245245
id, rest, _ := strings.Cut(name, ".")
246246
name = rest
247-
tv.token(offset, len(id), tokenTypeByObject(obj), nil)
247+
tok, mods := tokenTypeByObject(obj)
248+
tv.token(offset, len(id), tok, mods)
248249
offset += token.Pos(len(id))
249250
}
250251
last = idx[3]
@@ -483,6 +484,31 @@ func (tv *tokenVisitor) inspect(n ast.Node) (descend bool) {
483484
return true
484485
}
485486

487+
func underlineType(obj types.Object) (semtok.TokenType, []string) {
488+
switch obj.Type().Underlying().(type) {
489+
case *types.Interface:
490+
return semtok.TokType, []string{"interface"}
491+
case *types.Struct:
492+
return semtok.TokType, []string{"struct"}
493+
case *types.Signature:
494+
return semtok.TokType, []string{"signature"}
495+
case *types.Pointer:
496+
return semtok.TokType, []string{"pointer"}
497+
case *types.Array:
498+
return semtok.TokType, []string{"array"}
499+
case *types.Map:
500+
return semtok.TokType, []string{"map"}
501+
case *types.Slice:
502+
return semtok.TokType, []string{"slice"}
503+
case *types.Chan:
504+
return semtok.TokType, []string{"chan"}
505+
case *types.Basic:
506+
return semtok.TokType, []string{"defaultLibrary"}
507+
default:
508+
return semtok.TokType, nil
509+
}
510+
}
511+
486512
func (tv *tokenVisitor) ident(id *ast.Ident) {
487513
var obj types.Object
488514

@@ -535,10 +561,9 @@ func (tv *tokenVisitor) ident(id *ast.Ident) {
535561
case *types.TypeName: // could be a TypeParam
536562
if is[*types.TypeParam](aliases.Unalias(obj.Type())) {
537563
emit(semtok.TokTypeParam)
538-
} else if is[*types.Basic](obj.Type()) {
539-
emit(semtok.TokType, "defaultLibrary")
540564
} else {
541-
emit(semtok.TokType)
565+
tok, mods := underlineType(obj)
566+
emit(tok, mods...)
542567
}
543568
case *types.Var:
544569
if is[*types.Signature](aliases.Unalias(obj.Type())) {
@@ -795,11 +820,15 @@ func (tv *tokenVisitor) definitionFor(id *ast.Ident, obj types.Object) (semtok.T
795820
if fld, ok := fldm.(*ast.Field); ok {
796821
// if len(fld.names) == 0 this is a semtok.TokType, being used
797822
if len(fld.Names) == 0 {
798-
return semtok.TokType, nil
823+
tok, mods := underlineType(obj)
824+
modifiers = append(modifiers, mods...)
825+
return tok, modifiers
799826
}
800827
return semtok.TokVariable, modifiers
801828
}
802-
return semtok.TokType, modifiers
829+
tok, mods := underlineType(obj)
830+
modifiers = append(modifiers, mods...)
831+
return tok, modifiers
803832
}
804833
}
805834
// can't happen

gopls/internal/protocol/semantic.go

+1
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,6 @@ var (
5252
semanticModifiers = [...]string{
5353
"declaration", "definition", "readonly", "static",
5454
"deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary",
55+
"interface", "struct", "signature", "pointer", "array", "map", "slice", "chan",
5556
}
5657
)

gopls/internal/test/integration/fake/editor.go

+2
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,8 @@ func clientCapabilities(cfg EditorConfig) (protocol.ClientCapabilities, error) {
353353
capabilities.TextDocument.SemanticTokens.TokenModifiers = []string{
354354
"declaration", "definition", "readonly", "static",
355355
"deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary",
356+
// Additional modifiers supported by this client:
357+
"interface", "struct", "signature", "pointer", "array", "map", "slice", "chan",
356358
}
357359
// The LSP tests have historically enabled this flag,
358360
// but really we should test both ways for older editors.

0 commit comments

Comments
 (0)