blob: fc269c38759f8b03a10a6488845baf76d342c6d3 [file] [log] [blame]
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// The semtok package provides an encoder for LSP's semantic tokens.
package semtok
import "sort"
// A Token provides the extent and semantics of a token.
type Token struct {
Line, Start uint32
Len uint32
Type TokenType
Modifiers []Modifier
}
type TokenType string
const (
// These are the tokens defined by LSP 3.18, 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.
//
// If you add or uncomment a token type, document it in
// gopls/doc/features/passive.md#semantic-tokens.
TokComment TokenType = "comment" // for a comment
TokFunction TokenType = "function" // for a function
TokKeyword TokenType = "keyword" // for a keyword
TokLabel TokenType = "label" // for a control label (LSP 3.18)
TokMacro TokenType = "macro" // for text/template tokens
TokMethod TokenType = "method" // for a method
TokNamespace TokenType = "namespace" // for an imported package name
TokNumber TokenType = "number" // for a numeric literal
TokOperator TokenType = "operator" // for an operator
TokParameter TokenType = "parameter" // for a parameter variable
TokString TokenType = "string" // for a string literal
TokType TokenType = "type" // for a type name (plus other uses)
TokTypeParam TokenType = "typeParameter" // for a type parameter
TokVariable TokenType = "variable" // for a var or const
// TokClass TokenType = "class"
// TokDecorator TokenType = "decorator"
// TokEnum TokenType = "enum"
// TokEnumMember TokenType = "enumMember"
// TokEvent TokenType = "event"
// TokInterface TokenType = "interface"
// TokModifier TokenType = "modifier"
// TokProperty TokenType = "property"
// TokRegexp TokenType = "regexp"
// TokStruct TokenType = "struct"
)
type Modifier string
const (
// LSP 3.18 standard modifiers
// As with TokenTypes, clients get only the modifiers they request.
//
// If you add or uncomment a modifier, document it in
// gopls/doc/features/passive.md#semantic-tokens.
ModDefaultLibrary Modifier = "defaultLibrary" // for predeclared symbols
ModDefinition Modifier = "definition" // for the declaring identifier of a symbol
ModReadonly Modifier = "readonly" // for constants (TokVariable)
// ModAbstract Modifier = "abstract"
// ModAsync Modifier = "async"
// ModDeclaration Modifier = "declaration"
// ModDeprecated Modifier = "deprecated"
// ModDocumentation Modifier = "documentation"
// ModModification Modifier = "modification"
// ModStatic Modifier = "static"
// non-standard modifiers
//
// Since the type of a symbol is orthogonal to its kind,
// (e.g. a variable can have function type),
// we use modifiers for the top-level type constructor.
ModArray Modifier = "array"
ModBool Modifier = "bool"
ModChan Modifier = "chan"
ModInterface Modifier = "interface"
ModMap Modifier = "map"
ModNumber Modifier = "number"
ModPointer Modifier = "pointer"
ModSignature Modifier = "signature" // for function types
ModSlice Modifier = "slice"
ModString Modifier = "string"
ModStruct Modifier = "struct"
)
// Encode returns the LSP encoding of a sequence of tokens.
// The noStrings, noNumbers options cause strings, numbers to be skipped.
// The lists of types and modifiers determines the bitfield encoding.
func Encode(
tokens []Token,
noStrings, noNumbers bool,
types, modifiers []string) []uint32 {
// binary operators, at least, will be out of order
sort.Slice(tokens, func(i, j int) bool {
if tokens[i].Line != tokens[j].Line {
return tokens[i].Line < tokens[j].Line
}
return tokens[i].Start < tokens[j].Start
})
typeMap := make(map[TokenType]int)
for i, t := range types {
typeMap[TokenType(t)] = i
}
modMap := make(map[Modifier]int)
for i, m := range modifiers {
modMap[Modifier(m)] = 1 << i
}
// each semantic token needs five values
// (see Integer Encoding for Tokens in the LSP spec)
x := make([]uint32, 5*len(tokens))
var j int
var last Token
for i := 0; i < len(tokens); i++ {
item := tokens[i]
typ, ok := typeMap[item.Type]
if !ok {
continue // client doesn't want typeStr
}
if item.Type == TokString && noStrings {
continue
}
if item.Type == TokNumber && noNumbers {
continue
}
if j == 0 {
x[0] = tokens[0].Line
} else {
x[j] = item.Line - last.Line
}
x[j+1] = item.Start
if j > 0 && x[j] == 0 {
x[j+1] = item.Start - last.Start
}
x[j+2] = item.Len
x[j+3] = uint32(typ)
mask := 0
for _, s := range item.Modifiers {
// modMap[s] is 0 if the client doesn't want this modifier
mask |= modMap[s]
}
x[j+4] = uint32(mask)
j += 5
last = item
}
return x[:j]
}