initial commit
This commit is contained in:
92
indexer/scope.go
Normal file
92
indexer/scope.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package indexer
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/odvcencio/gotreesitter"
|
||||
"github.com/odvcencio/gotreesitter/grammars"
|
||||
)
|
||||
|
||||
// packageQueries maps language names to tree-sitter queries that extract the
|
||||
// package/module declaration. The query must capture the package name as @name.
|
||||
var packageQueries = map[string]string{
|
||||
"go": `(package_clause (package_identifier) @name)`,
|
||||
"proto": `(package (full_ident) @name)`,
|
||||
"java": `(package_declaration (scoped_identifier) @name)`,
|
||||
"kotlin": `(package_header (identifier) @name)`,
|
||||
"scala": `(package_clause (identifier) @name)`,
|
||||
"rust": `(mod_item name: (identifier) @name)`,
|
||||
"elixir": `(call target: (dot left: (alias) @name))`, // defmodule
|
||||
"erlang": `(module_attribute name: (atom) @name)`,
|
||||
}
|
||||
|
||||
// ExtractPackage extracts the package/module name from source code.
|
||||
// Falls back to deriving from the file path if no language-specific query exists
|
||||
// or the query finds no match.
|
||||
func ExtractPackage(src []byte, filePath string, entry *grammars.LangEntry) string {
|
||||
if queryStr, ok := packageQueries[entry.Name]; ok {
|
||||
lang := entry.Language()
|
||||
if pkg := runPackageQuery(src, lang, queryStr); pkg != "" {
|
||||
return pkg
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: derive from directory name
|
||||
dir := filepath.Dir(filePath)
|
||||
if dir == "." || dir == "" {
|
||||
return ""
|
||||
}
|
||||
return filepath.Base(dir)
|
||||
}
|
||||
|
||||
func runPackageQuery(src []byte, lang *gotreesitter.Language, queryStr string) string {
|
||||
parser := gotreesitter.NewParser(lang)
|
||||
tree, err := parser.Parse(src)
|
||||
if err != nil || tree == nil || tree.RootNode() == nil {
|
||||
return ""
|
||||
}
|
||||
defer tree.Release()
|
||||
|
||||
query, err := gotreesitter.NewQuery(queryStr, lang)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
cursor := query.Exec(tree.RootNode(), lang, src)
|
||||
for {
|
||||
match, ok := cursor.NextMatch()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
for _, cap := range match.Captures {
|
||||
if cap.Name == "name" {
|
||||
return cap.Node.Text(src)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// IsExported determines if a symbol name is exported/public based on language conventions.
|
||||
func IsExported(name string, langName string) bool {
|
||||
if name == "" {
|
||||
return false
|
||||
}
|
||||
switch langName {
|
||||
case "go":
|
||||
// Go: exported if first letter is uppercase
|
||||
return name[0] >= 'A' && name[0] <= 'Z'
|
||||
case "python":
|
||||
// Python: private if starts with underscore
|
||||
return !strings.HasPrefix(name, "_")
|
||||
case "rust":
|
||||
// Rust: pub is in the AST, but we approximate: starts with uppercase for types
|
||||
// For functions, we can't tell without `pub` keyword — default to true
|
||||
return true
|
||||
default:
|
||||
// Most languages (JS/TS/Java/etc): export/public is a modifier in the AST
|
||||
// We can't reliably determine from name alone — default to nil/unknown
|
||||
return true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user