134 lines
4.5 KiB
Go
134 lines
4.5 KiB
Go
package codeintel
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
|
|
yaml "github.com/tree-sitter-grammars/tree-sitter-yaml/bindings/go"
|
|
ts "github.com/tree-sitter/go-tree-sitter"
|
|
c "github.com/tree-sitter/tree-sitter-c/bindings/go"
|
|
cpp "github.com/tree-sitter/tree-sitter-cpp/bindings/go"
|
|
golang "github.com/tree-sitter/tree-sitter-go/bindings/go"
|
|
javascript "github.com/tree-sitter/tree-sitter-javascript/bindings/go"
|
|
jsonlang "github.com/tree-sitter/tree-sitter-json/bindings/go"
|
|
markdown "github.com/tree-sitter/tree-sitter-markdown/bindings/go"
|
|
php "github.com/tree-sitter/tree-sitter-php/bindings/go"
|
|
python "github.com/tree-sitter/tree-sitter-python/bindings/go"
|
|
rust "github.com/tree-sitter/tree-sitter-rust/bindings/go"
|
|
typescript "github.com/tree-sitter/tree-sitter-typescript/bindings/go"
|
|
)
|
|
|
|
type Language struct {
|
|
Name string
|
|
Extensions []string
|
|
Grammar *ts.Language
|
|
}
|
|
|
|
var languages = []Language{
|
|
{"go", []string{".go"}, ts.NewLanguage(golang.Language())},
|
|
{"javascript", []string{".js", ".jsx", ".mjs", ".cjs"}, ts.NewLanguage(javascript.Language())},
|
|
{"typescript", []string{".ts"}, ts.NewLanguage(typescript.LanguageTypescript())},
|
|
{"tsx", []string{".tsx"}, ts.NewLanguage(typescript.LanguageTSX())},
|
|
{"python", []string{".py"}, ts.NewLanguage(python.Language())},
|
|
{"rust", []string{".rs"}, ts.NewLanguage(rust.Language())},
|
|
{"c", []string{".c", ".h"}, ts.NewLanguage(c.Language())},
|
|
{"cpp", []string{".cc", ".cpp", ".cxx", ".hpp", ".hxx"}, ts.NewLanguage(cpp.Language())},
|
|
{"json", []string{".json"}, ts.NewLanguage(jsonlang.Language())},
|
|
{"yaml", []string{".yaml", ".yml"}, ts.NewLanguage(yaml.Language())},
|
|
{"markdown", []string{".md", ".markdown"}, ts.NewLanguage(markdown.Language())},
|
|
{"php", []string{".php"}, ts.NewLanguage(php.LanguagePHP())},
|
|
}
|
|
|
|
func LanguageFor(path, name string) (Language, bool) {
|
|
if name != "" {
|
|
for _, l := range languages {
|
|
if l.Name == name {
|
|
return l, true
|
|
}
|
|
}
|
|
return Language{}, false
|
|
}
|
|
ext := strings.ToLower(filepath.Ext(path))
|
|
for _, l := range languages {
|
|
for _, e := range l.Extensions {
|
|
if e == ext {
|
|
return l, true
|
|
}
|
|
}
|
|
}
|
|
return Language{}, false
|
|
}
|
|
func Languages() []string {
|
|
out := make([]string, len(languages))
|
|
for i, l := range languages {
|
|
out[i] = l.Name
|
|
}
|
|
return out
|
|
}
|
|
|
|
type Document struct {
|
|
Path, Language string
|
|
Source []byte
|
|
Tree *ts.Tree
|
|
Root *ts.Node
|
|
}
|
|
|
|
func Parse(path, language string) (*Document, error) {
|
|
l, ok := LanguageFor(path, language)
|
|
if !ok {
|
|
return nil, fmt.Errorf("unsupported language or extension: %s", path)
|
|
}
|
|
src, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p := ts.NewParser()
|
|
defer p.Close()
|
|
if err := p.SetLanguage(l.Grammar); err != nil {
|
|
return nil, err
|
|
}
|
|
tree := p.Parse(src, nil)
|
|
if tree == nil {
|
|
return nil, fmt.Errorf("parse failed: %s", path)
|
|
}
|
|
return &Document{Path: path, Language: l.Name, Source: src, Tree: tree, Root: tree.RootNode()}, nil
|
|
}
|
|
func (d *Document) Close() { d.Tree.Close() }
|
|
|
|
type File struct{ Path, Language string }
|
|
|
|
func Files(root string) ([]File, error) {
|
|
var out []File
|
|
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if info.IsDir() {
|
|
if path != root && (info.Name() == ".git" || info.Name() == "vendor" || info.Name() == "node_modules" || info.Name() == ".kiro") {
|
|
return filepath.SkipDir
|
|
}
|
|
return nil
|
|
}
|
|
if l, ok := LanguageFor(path, ""); ok {
|
|
out = append(out, File{path, l.Name})
|
|
}
|
|
return nil
|
|
})
|
|
sort.Slice(out, func(i, j int) bool { return out[i].Path < out[j].Path })
|
|
return out, err
|
|
}
|
|
func Text(n *ts.Node, source []byte) string { return n.Utf8Text(source) }
|
|
func Line(n *ts.Node) int { return int(n.StartPosition().Row) + 1 }
|
|
func SymbolKinds() map[string]bool {
|
|
return map[string]bool{"function_declaration": true, "method_declaration": true, "func_literal": true, "type_declaration": true, "type_spec": true, "class_declaration": true, "class_definition": true, "struct_item": true, "enum_item": true, "interface_declaration": true, "namespace_definition": true, "function_definition": true, "function_item": true, "const_declaration": true, "var_declaration": true, "lexical_declaration": true, "variable_declaration": true, "import_declaration": true, "use_declaration": true, "callable_declaration": true, "property_declaration": true, "atx_heading": true, "setext_heading": true}
|
|
}
|
|
func Walk(n *ts.Node, fn func(*ts.Node)) {
|
|
fn(n)
|
|
for i := uint(0); i < n.NamedChildCount(); i++ {
|
|
Walk(n.NamedChild(i), fn)
|
|
}
|
|
}
|