зеркало из https://github.com/golang/tools.git
gopls/internal/golang: add semantic tokens for control labels
Fixes golang/go#65494 Change-Id: Id044cd12a5c48bb3d5dc8f91657febdc431811b4 Reviewed-on: https://go-review.googlesource.com/c/tools/+/562244 Reviewed-by: Peter Weinberger <pjw@google.com> Reviewed-by: Robert Findley <rfindley@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Родитель
0d171942e7
Коммит
32d313923a
|
@ -231,7 +231,9 @@ func (tv *tokenVisitor) inspect(n ast.Node) (descend bool) {
|
||||||
case *ast.BlockStmt:
|
case *ast.BlockStmt:
|
||||||
case *ast.BranchStmt:
|
case *ast.BranchStmt:
|
||||||
tv.token(n.TokPos, len(n.Tok.String()), semtok.TokKeyword, nil)
|
tv.token(n.TokPos, len(n.Tok.String()), semtok.TokKeyword, nil)
|
||||||
// There's no semantic encoding for labels
|
if n.Label != nil {
|
||||||
|
tv.token(n.Label.Pos(), len(n.Label.Name), semtok.TokLabel, nil)
|
||||||
|
}
|
||||||
case *ast.CallExpr:
|
case *ast.CallExpr:
|
||||||
if n.Ellipsis.IsValid() {
|
if n.Ellipsis.IsValid() {
|
||||||
tv.token(n.Ellipsis, len("..."), semtok.TokOperator, nil)
|
tv.token(n.Ellipsis, len("..."), semtok.TokOperator, nil)
|
||||||
|
@ -303,6 +305,7 @@ func (tv *tokenVisitor) inspect(n ast.Node) (descend bool) {
|
||||||
tv.token(n.Interface, len("interface"), semtok.TokKeyword, nil)
|
tv.token(n.Interface, len("interface"), semtok.TokKeyword, nil)
|
||||||
case *ast.KeyValueExpr:
|
case *ast.KeyValueExpr:
|
||||||
case *ast.LabeledStmt:
|
case *ast.LabeledStmt:
|
||||||
|
tv.token(n.Label.Pos(), len(n.Label.Name), semtok.TokLabel, []string{"definition"})
|
||||||
case *ast.MapType:
|
case *ast.MapType:
|
||||||
tv.token(n.Map, len("map"), semtok.TokKeyword, nil)
|
tv.token(n.Map, len("map"), semtok.TokKeyword, nil)
|
||||||
case *ast.ParenExpr:
|
case *ast.ParenExpr:
|
||||||
|
@ -411,7 +414,7 @@ func (tv *tokenVisitor) ident(id *ast.Ident) {
|
||||||
case *types.Func:
|
case *types.Func:
|
||||||
emit(semtok.TokFunction)
|
emit(semtok.TokFunction)
|
||||||
case *types.Label:
|
case *types.Label:
|
||||||
// nothing to map it to
|
// Labels are reliably covered by the syntax traversal.
|
||||||
case *types.Nil:
|
case *types.Nil:
|
||||||
// nil is a predeclared identifier
|
// nil is a predeclared identifier
|
||||||
emit(semtok.TokVariable, "readonly", "defaultLibrary")
|
emit(semtok.TokVariable, "readonly", "defaultLibrary")
|
||||||
|
@ -571,8 +574,14 @@ func (tv *tokenVisitor) unkIdent(id *ast.Ident) (semtok.TokenType, []string) {
|
||||||
return semtok.TokMethod, def
|
return semtok.TokMethod, def
|
||||||
}
|
}
|
||||||
return semtok.TokVariable, nil
|
return semtok.TokVariable, nil
|
||||||
case *ast.LabeledStmt, *ast.BranchStmt:
|
case *ast.LabeledStmt:
|
||||||
// nothing to report
|
if id == parent.Label {
|
||||||
|
return semtok.TokLabel, def
|
||||||
|
}
|
||||||
|
case *ast.BranchStmt:
|
||||||
|
if id == parent.Label {
|
||||||
|
return semtok.TokLabel, nil
|
||||||
|
}
|
||||||
case *ast.CompositeLit:
|
case *ast.CompositeLit:
|
||||||
if parent.Type == id {
|
if parent.Type == id {
|
||||||
return semtok.TokType, nil
|
return semtok.TokType, nil
|
||||||
|
@ -604,7 +613,17 @@ func isDeprecated(n *ast.CommentGroup) bool {
|
||||||
|
|
||||||
// definitionFor handles a defining identifier.
|
// definitionFor handles a defining identifier.
|
||||||
func (tv *tokenVisitor) definitionFor(id *ast.Ident, obj types.Object) (semtok.TokenType, []string) {
|
func (tv *tokenVisitor) definitionFor(id *ast.Ident, obj types.Object) (semtok.TokenType, []string) {
|
||||||
// PJW: obj == types.Label? probably a nothing
|
// The definition of a types.Label cannot be found by
|
||||||
|
// ascending the syntax tree, and doing so will reach the
|
||||||
|
// FuncDecl, causing us to misinterpret the label as a
|
||||||
|
// parameter (#65494).
|
||||||
|
//
|
||||||
|
// However, labels are reliably covered by the syntax
|
||||||
|
// traversal, so we don't need to use type information.
|
||||||
|
if is[*types.Label](obj) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
// PJW: look into replacing these syntactic tests with types more generally
|
// PJW: look into replacing these syntactic tests with types more generally
|
||||||
modifiers := []string{"definition"}
|
modifiers := []string{"definition"}
|
||||||
for i := len(tv.stack) - 1; i >= 0; i-- {
|
for i := len(tv.stack) - 1; i >= 0; i-- {
|
||||||
|
|
|
@ -8,7 +8,7 @@ package protocol
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
// SemanticTypes to use in case there is no client, as in the command line, or tests
|
// SemanticTypes to use in case there is no client, as in the command line, or tests.
|
||||||
func SemanticTypes() []string {
|
func SemanticTypes() []string {
|
||||||
return semanticTypes[:]
|
return semanticTypes[:]
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,9 @@ type Token struct {
|
||||||
type TokenType string
|
type TokenType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// These are the tokens defined by LSP 3.17, but a client is
|
||||||
|
// free to send its own set; any tokens that the server emits
|
||||||
|
// that are not in this set are simply not encoded in the bitfield.
|
||||||
TokNamespace TokenType = "namespace"
|
TokNamespace TokenType = "namespace"
|
||||||
TokType TokenType = "type"
|
TokType TokenType = "type"
|
||||||
TokInterface TokenType = "interface"
|
TokInterface TokenType = "interface"
|
||||||
|
@ -32,6 +35,10 @@ const (
|
||||||
TokNumber TokenType = "number"
|
TokNumber TokenType = "number"
|
||||||
TokOperator TokenType = "operator"
|
TokOperator TokenType = "operator"
|
||||||
TokMacro TokenType = "macro" // for templates
|
TokMacro TokenType = "macro" // for templates
|
||||||
|
|
||||||
|
// not part of LSP 3.17 (even though JS has labels)
|
||||||
|
// https://github.com/microsoft/vscode-languageserver-node/issues/1422
|
||||||
|
TokLabel TokenType = "label"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Encode returns the LSP encoding of a sequence of tokens.
|
// Encode returns the LSP encoding of a sequence of tokens.
|
||||||
|
|
|
@ -337,6 +337,8 @@ func clientCapabilities(cfg EditorConfig) (protocol.ClientCapabilities, error) {
|
||||||
"struct", "typeParameter", "parameter", "variable", "property", "enumMember",
|
"struct", "typeParameter", "parameter", "variable", "property", "enumMember",
|
||||||
"event", "function", "method", "macro", "keyword", "modifier", "comment",
|
"event", "function", "method", "macro", "keyword", "modifier", "comment",
|
||||||
"string", "number", "regexp", "operator",
|
"string", "number", "regexp", "operator",
|
||||||
|
// Additional types supported by this client:
|
||||||
|
"label",
|
||||||
}
|
}
|
||||||
capabilities.TextDocument.SemanticTokens.TokenModifiers = []string{
|
capabilities.TextDocument.SemanticTokens.TokenModifiers = []string{
|
||||||
"declaration", "definition", "readonly", "static",
|
"declaration", "definition", "readonly", "static",
|
||||||
|
|
|
@ -17,3 +17,13 @@ func F() { //@token("F", "function", "definition")
|
||||||
_ = x //@token("x", "variable", "")
|
_ = x //@token("x", "variable", "")
|
||||||
_ = F //@token("F", "function", "")
|
_ = F //@token("F", "function", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// A goto's label cannot be found by ascending the syntax tree.
|
||||||
|
goto loop //@ token("goto", "keyword", ""), token("loop", "label", "")
|
||||||
|
|
||||||
|
loop: //@token("loop", "label", "definition")
|
||||||
|
for {
|
||||||
|
continue loop //@ token("continue", "keyword", ""), token("loop", "label", "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче