ollie/tools/codeintel/internal/codeintel/symbols.go

78 lines
1.7 KiB
Go

package codeintel
import (
"path/filepath"
"strings"
ts "github.com/tree-sitter/go-tree-sitter"
)
type Symbol struct {
Name, Kind, Path string
Line int
Depth int
}
func Symbols(d *Document) []Symbol {
var out []Symbol
kinds := SymbolKinds()
var visit func(*ts.Node, int)
visit = func(n *ts.Node, depth int) {
kind := n.Kind()
if kinds[kind] {
name := symbolName(n, d.Source)
if name != "" {
out = append(out, Symbol{Name: name, Kind: kind, Path: d.Path, Line: Line(n), Depth: depth})
}
}
for i := uint(0); i < n.NamedChildCount(); i++ {
visit(n.NamedChild(i), depth+1)
}
}
visit(d.Root, 0)
return out
}
func symbolName(n *ts.Node, source []byte) string {
for _, field := range []string{"name", "declarator", "left"} {
child := n.ChildByFieldName(field)
if child != nil {
name := strings.TrimSpace(Text(child, source))
if name != "" && !strings.ContainsAny(name, "{}();") {
return name
}
}
}
for i := uint(0); i < n.NamedChildCount(); i++ {
child := n.NamedChild(i)
kind := child.Kind()
if kind == "identifier" || strings.HasSuffix(kind, "_name") || kind == "property_identifier" || kind == "type_identifier" {
return strings.TrimSpace(Text(child, source))
}
}
return ""
}
func MatchSymbols(root string, query string) ([]Symbol, error) {
files, err := Files(root)
if err != nil {
return nil, err
}
var out []Symbol
for _, file := range files {
d, err := Parse(file.Path, file.Language)
if err != nil {
continue
}
syms := Symbols(d)
d.Close()
for _, s := range syms {
if query == "" || strings.Contains(strings.ToLower(s.Name), strings.ToLower(query)) {
s.Path, _ = filepath.Rel(root, s.Path)
out = append(out, s)
}
}
}
return out, nil
}