mirror of https://github.com/golang/go.git
internal/lsp/semantic.go: remove global variable
instead copy the client's token types and modifiers into the options, and reconstruct the inverse map once per LSP request. Change-Id: I4ae5b56a31060882a06b1b6bbc65d26e4085e058 Reviewed-on: https://go-review.googlesource.com/c/tools/+/264320 Run-TryBot: Peter Weinberger <pjw@google.com> gopls-CI: kokoro <noreply+kokoro@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org> Trust: Peter Weinberger <pjw@google.com>
This commit is contained in:
parent
9cf592e881
commit
13b3b30742
|
|
@ -74,9 +74,8 @@ Example: show the semantic tokens for this file:
|
|||
}
|
||||
|
||||
// Run performs the semtok on the files specified by args and prints the
|
||||
// results to stdout. PJW: fix this description
|
||||
// results to stdout in the format described above.
|
||||
func (c *semtok) Run(ctx context.Context, args ...string) error {
|
||||
log.SetFlags(log.Lshortfile)
|
||||
if len(args) != 1 {
|
||||
return fmt.Errorf("expected one file name, got %d", len(args))
|
||||
}
|
||||
|
|
@ -122,7 +121,6 @@ func (c *semtok) Run(ctx context.Context, args ...string) error {
|
|||
Content: buf,
|
||||
Converter: tc,
|
||||
}
|
||||
memo = lsp.SemanticMemo
|
||||
err = decorate(file.uri.Filename(), resp.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -130,8 +128,6 @@ func (c *semtok) Run(ctx context.Context, args ...string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
var memo *lsp.SemMemo
|
||||
|
||||
type mark struct {
|
||||
line, offset int // 1-based, from RangeSpan
|
||||
len int // bytes, not runes
|
||||
|
|
@ -218,8 +214,8 @@ func newMarks(d []float64) []mark {
|
|||
line: spn.Start().Line(),
|
||||
offset: spn.Start().Column(),
|
||||
len: spn.End().Column() - spn.Start().Column(),
|
||||
typ: memo.Type(int(d[5*i+3])),
|
||||
mods: memo.Mods(int(d[5*i+4])),
|
||||
typ: lsp.SemType(int(d[5*i+3])),
|
||||
mods: lsp.SemMods(int(d[5*i+4])),
|
||||
}
|
||||
ans = append(ans, m)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,10 +82,6 @@ func (s *Server) initialize(ctx context.Context, params *protocol.ParamInitializ
|
|||
}
|
||||
}
|
||||
|
||||
if st := params.Capabilities.TextDocument.SemanticTokens; st != nil {
|
||||
rememberToks(st.TokenTypes, st.TokenModifiers)
|
||||
}
|
||||
|
||||
goplsVer := &bytes.Buffer{}
|
||||
debug.PrintVersionInfo(ctx, goplsVer, true, debug.PlainText)
|
||||
|
||||
|
|
@ -136,6 +132,7 @@ func (s *Server) initialize(ctx context.Context, params *protocol.ParamInitializ
|
|||
Version: goplsVer.String(),
|
||||
},
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func (s *Server) initialized(ctx context.Context, params *protocol.InitializedParams) error {
|
||||
|
|
|
|||
|
|
@ -391,8 +391,6 @@ func (r *runner) Format(t *testing.T, spn span.Span) {
|
|||
}
|
||||
|
||||
func (r *runner) SemanticTokens(t *testing.T, spn span.Span) {
|
||||
// no client, so use default
|
||||
rememberToks(SemanticTypes(), SemanticModifiers())
|
||||
uri := spn.URI()
|
||||
filename := uri.Filename()
|
||||
// this is called solely for coverage in semantic.go
|
||||
|
|
|
|||
|
|
@ -69,11 +69,13 @@ func (s *Server) computeSemanticTokens(ctx context.Context, td protocol.TextDocu
|
|||
return nil, pgf.ParseErr
|
||||
}
|
||||
e := &encoded{
|
||||
ctx: ctx,
|
||||
pgf: pgf,
|
||||
rng: rng,
|
||||
ti: info,
|
||||
fset: snapshot.FileSet(),
|
||||
ctx: ctx,
|
||||
pgf: pgf,
|
||||
rng: rng,
|
||||
ti: info,
|
||||
fset: snapshot.FileSet(),
|
||||
tokTypes: s.session.Options().SemanticTypes,
|
||||
tokMods: s.session.Options().SemanticMods,
|
||||
}
|
||||
if err := e.init(); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -174,11 +176,12 @@ type encoded struct {
|
|||
// the generated data
|
||||
items []semItem
|
||||
|
||||
ctx context.Context
|
||||
pgf *source.ParsedGoFile
|
||||
rng *protocol.Range
|
||||
ti *types.Info
|
||||
fset *token.FileSet
|
||||
ctx context.Context
|
||||
tokTypes, tokMods []string
|
||||
pgf *source.ParsedGoFile
|
||||
rng *protocol.Range
|
||||
ti *types.Info
|
||||
fset *token.FileSet
|
||||
// allowed starting and ending token.Pos, set by init
|
||||
// used to avoid looking at declarations not in range
|
||||
start, end token.Pos
|
||||
|
|
@ -509,6 +512,7 @@ func (e *encoded) Data() ([]float64, error) {
|
|||
}
|
||||
return e.items[i].start < e.items[j].start
|
||||
})
|
||||
typeMap, modMap := e.maps()
|
||||
// each semantic token needs five values
|
||||
// (see Integer Encoding for Tokens in the LSP spec)
|
||||
x := make([]float64, 5*len(e.items))
|
||||
|
|
@ -524,10 +528,10 @@ func (e *encoded) Data() ([]float64, error) {
|
|||
x[j+1] = e.items[i].start - e.items[i-1].start
|
||||
}
|
||||
x[j+2] = e.items[i].len
|
||||
x[j+3] = float64(SemanticMemo.TypeMap[e.items[i].typeStr])
|
||||
x[j+3] = float64(typeMap[e.items[i].typeStr])
|
||||
mask := 0
|
||||
for _, s := range e.items[i].mods {
|
||||
mask |= SemanticMemo.ModMap[s]
|
||||
mask |= modMap[s]
|
||||
}
|
||||
x[j+4] = float64(mask)
|
||||
}
|
||||
|
|
@ -565,51 +569,38 @@ func (e *encoded) unexpected(msg string) {
|
|||
panic(msg)
|
||||
}
|
||||
|
||||
// SemMemo supports semantic token translations between numbers and strings
|
||||
type SemMemo struct {
|
||||
tokTypes, tokMods []string
|
||||
// these exported fields are used in the 'gopls semtok' command
|
||||
TypeMap map[tokenType]int
|
||||
ModMap map[string]int
|
||||
}
|
||||
|
||||
var SemanticMemo *SemMemo
|
||||
|
||||
// Type returns a string equivalent of the type, for gopls semtok
|
||||
func (m *SemMemo) Type(n int) string {
|
||||
if n >= 0 && n < len(m.tokTypes) {
|
||||
return m.tokTypes[n]
|
||||
// SemType returns a string equivalent of the type, for gopls semtok
|
||||
func SemType(n int) string {
|
||||
tokTypes := SemanticTypes()
|
||||
tokMods := SemanticModifiers()
|
||||
if n >= 0 && n < len(tokTypes) {
|
||||
return tokTypes[n]
|
||||
}
|
||||
return fmt.Sprintf("?%d[%d,%d]?", n, len(m.tokTypes), len(m.tokMods))
|
||||
return fmt.Sprintf("?%d[%d,%d]?", n, len(tokTypes), len(tokMods))
|
||||
}
|
||||
|
||||
// Mods returns the []string equivalent of the mods, for gopls semtok.
|
||||
func (m *SemMemo) Mods(n int) []string {
|
||||
// SemMods returns the []string equivalent of the mods, for gopls semtok.
|
||||
func SemMods(n int) []string {
|
||||
tokMods := SemanticModifiers()
|
||||
mods := []string{}
|
||||
for i := 0; i < len(m.tokMods); i++ {
|
||||
for i := 0; i < len(tokMods); i++ {
|
||||
if (n & (1 << uint(i))) != 0 {
|
||||
mods = append(mods, m.tokMods[i])
|
||||
mods = append(mods, tokMods[i])
|
||||
}
|
||||
}
|
||||
return mods
|
||||
}
|
||||
|
||||
// save what the client sent
|
||||
func rememberToks(toks []string, mods []string) {
|
||||
SemanticMemo = &SemMemo{
|
||||
tokTypes: toks,
|
||||
tokMods: mods,
|
||||
TypeMap: make(map[tokenType]int),
|
||||
ModMap: make(map[string]int),
|
||||
func (e *encoded) maps() (map[tokenType]int, map[string]int) {
|
||||
tmap := make(map[tokenType]int)
|
||||
mmap := make(map[string]int)
|
||||
for i, t := range e.tokTypes {
|
||||
tmap[tokenType(t)] = i
|
||||
}
|
||||
for i, t := range toks {
|
||||
SemanticMemo.TypeMap[tokenType(t)] = i
|
||||
for i, m := range e.tokMods {
|
||||
mmap[m] = 1 << uint(i) // go 1.12 compatibility
|
||||
}
|
||||
for i, m := range mods {
|
||||
SemanticMemo.ModMap[m] = 1 << uint(i)
|
||||
}
|
||||
// we could have pruned or rearranged them.
|
||||
// But then change the list in cmd.go too
|
||||
return tmap, mmap
|
||||
}
|
||||
|
||||
// SemanticTypes to use in case there is no client, as in the command line, or tests
|
||||
|
|
|
|||
|
|
@ -162,6 +162,8 @@ type ClientOptions struct {
|
|||
PreferredContentFormat protocol.MarkupKind
|
||||
LineFoldingOnly bool
|
||||
HierarchicalDocumentSymbolSupport bool
|
||||
SemanticTypes []string
|
||||
SemanticMods []string
|
||||
}
|
||||
|
||||
// ServerOptions holds LSP-specific configuration that is provided by the
|
||||
|
|
@ -535,6 +537,13 @@ func (o *Options) ForClientCapabilities(caps protocol.ClientCapabilities) {
|
|||
o.LineFoldingOnly = fr.LineFoldingOnly
|
||||
// Check if the client supports hierarchical document symbols.
|
||||
o.HierarchicalDocumentSymbolSupport = caps.TextDocument.DocumentSymbol.HierarchicalDocumentSymbolSupport
|
||||
// Check if the client supports semantic tokens
|
||||
if c := caps.TextDocument.SemanticTokens; c != nil {
|
||||
o.SemanticTypes = c.TokenTypes
|
||||
o.SemanticMods = c.TokenModifiers
|
||||
// we don't need Requests, as we support full functionality
|
||||
// we don't need Formats, as there is only one, for now
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Options) Clone() *Options {
|
||||
|
|
|
|||
|
|
@ -105,8 +105,8 @@ func semanticTokenRegistrations() []protocol.Registration {
|
|||
Legend: protocol.SemanticTokensLegend{
|
||||
// TODO(pjw): trim these to what we use (and an unused one
|
||||
// at position 0 of TokTypes, to catch typos)
|
||||
TokenTypes: SemanticMemo.tokTypes,
|
||||
TokenModifiers: SemanticMemo.tokMods,
|
||||
TokenTypes: SemanticTypes(),
|
||||
TokenModifiers: SemanticModifiers(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue