mirror of https://github.com/golang/go.git
97 lines
2.6 KiB
Go
97 lines
2.6 KiB
Go
// Copyright 2021 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.
|
|
|
|
package template
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"regexp"
|
|
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
|
"golang.org/x/tools/internal/lsp/source"
|
|
)
|
|
|
|
func Highlight(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, loc protocol.Position) ([]protocol.DocumentHighlight, error) {
|
|
buf, err := fh.Read()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p := parseBuffer(buf)
|
|
pos := p.FromPosition(loc)
|
|
var ans []protocol.DocumentHighlight
|
|
if p.ParseErr == nil {
|
|
for _, s := range p.symbols {
|
|
if s.start <= pos && pos < s.start+s.length {
|
|
return markSymbols(p, s)
|
|
}
|
|
}
|
|
}
|
|
// these tokens exist whether or not there was a parse error
|
|
// (symbols require a successful parse)
|
|
for _, tok := range p.tokens {
|
|
if tok.Start <= pos && pos < tok.End {
|
|
wordAt := findWordAt(p, pos)
|
|
if len(wordAt) > 0 {
|
|
return markWordInToken(p, wordAt)
|
|
}
|
|
}
|
|
}
|
|
// find the 'word' at pos, etc: someday
|
|
// until then we get the default action, which doesn't respect word boundaries
|
|
return ans, nil
|
|
}
|
|
|
|
func markSymbols(p *Parsed, sym symbol) ([]protocol.DocumentHighlight, error) {
|
|
var ans []protocol.DocumentHighlight
|
|
for _, s := range p.symbols {
|
|
if s.name == sym.name {
|
|
kind := protocol.Read
|
|
if s.vardef {
|
|
kind = protocol.Write
|
|
}
|
|
ans = append(ans, protocol.DocumentHighlight{
|
|
Range: p.Range(s.start, s.length),
|
|
Kind: kind,
|
|
})
|
|
}
|
|
}
|
|
return ans, nil
|
|
}
|
|
|
|
// A token is {{...}}, and this marks words in the token that equal the give word
|
|
func markWordInToken(p *Parsed, wordAt string) ([]protocol.DocumentHighlight, error) {
|
|
var ans []protocol.DocumentHighlight
|
|
pat, err := regexp.Compile(fmt.Sprintf(`\b%s\b`, wordAt))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%q: unmatchable word (%v)", wordAt, err)
|
|
}
|
|
for _, tok := range p.tokens {
|
|
got := pat.FindAllIndex(p.buf[tok.Start:tok.End], -1)
|
|
for i := 0; i < len(got); i++ {
|
|
ans = append(ans, protocol.DocumentHighlight{
|
|
Range: p.Range(got[i][0], got[i][1]-got[i][0]),
|
|
Kind: protocol.Text,
|
|
})
|
|
}
|
|
}
|
|
return ans, nil
|
|
}
|
|
|
|
var wordRe = regexp.MustCompile(`[$]?\w+$`)
|
|
var moreRe = regexp.MustCompile(`^[$]?\w+`)
|
|
|
|
// findWordAt finds the word the cursor is in (meaning in or just before)
|
|
func findWordAt(p *Parsed, pos int) string {
|
|
if pos >= len(p.buf) {
|
|
return "" // can't happen, as we are called with pos < tok.End
|
|
}
|
|
after := moreRe.Find(p.buf[pos:])
|
|
if len(after) == 0 {
|
|
return "" // end of the word
|
|
}
|
|
got := wordRe.Find(p.buf[:pos+len(after)])
|
|
return string(got)
|
|
}
|