mirror of
https://github.com/lukaszraczylo/filepuff-mcp.git
synced 2026-10-17 05:47:33 +00:00
Ho hum.
This commit is contained in:
@@ -0,0 +1,474 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"github.com/lukaszraczylo/mcp-filepuff/pkg/protocol"
|
||||
sitter "github.com/smacker/go-tree-sitter"
|
||||
)
|
||||
|
||||
// ExtractSymbols extracts symbols from a parsed tree.
|
||||
func ExtractSymbols(tree *sitter.Tree, content []byte, lang protocol.Language, filename string) []protocol.Symbol {
|
||||
if tree == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
root := tree.RootNode()
|
||||
if root == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch lang {
|
||||
case protocol.LangGo:
|
||||
return extractGoSymbols(root, content, filename)
|
||||
case protocol.LangTypeScript, protocol.LangJavaScript:
|
||||
return extractJSSymbols(root, content, filename)
|
||||
case protocol.LangPython:
|
||||
return extractPythonSymbols(root, content, filename)
|
||||
case protocol.LangC, protocol.LangCpp:
|
||||
return extractCSymbols(root, content, filename)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// extractGoSymbols extracts symbols from Go code.
|
||||
func extractGoSymbols(root *sitter.Node, content []byte, filename string) []protocol.Symbol {
|
||||
var symbols []protocol.Symbol
|
||||
|
||||
WalkTree(root, func(n *sitter.Node) bool {
|
||||
var symbol *protocol.Symbol
|
||||
|
||||
switch n.Type() {
|
||||
case "function_declaration":
|
||||
symbol = extractGoFunction(n, content, filename)
|
||||
case "method_declaration":
|
||||
symbol = extractGoMethod(n, content, filename)
|
||||
case "type_declaration":
|
||||
symbol = extractGoType(n, content, filename)
|
||||
case "const_declaration", "var_declaration":
|
||||
syms := extractGoVarConst(n, content, filename)
|
||||
symbols = append(symbols, syms...)
|
||||
return true
|
||||
}
|
||||
|
||||
if symbol != nil {
|
||||
if doc := ExtractDocComment(n, content, protocol.LangGo); doc != nil {
|
||||
symbol.Doc = FormatDocComment(doc)
|
||||
}
|
||||
symbols = append(symbols, *symbol)
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return symbols
|
||||
}
|
||||
|
||||
func extractGoFunction(n *sitter.Node, content []byte, filename string) *protocol.Symbol {
|
||||
nameNode := n.ChildByFieldName("name")
|
||||
if nameNode == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &protocol.Symbol{
|
||||
Name: GetNodeText(nameNode, content),
|
||||
Kind: protocol.SymbolFunction,
|
||||
Location: NodeLocation(n, filename),
|
||||
}
|
||||
}
|
||||
|
||||
func extractGoMethod(n *sitter.Node, content []byte, filename string) *protocol.Symbol {
|
||||
nameNode := n.ChildByFieldName("name")
|
||||
if nameNode == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get receiver type
|
||||
receiver := n.ChildByFieldName("receiver")
|
||||
receiverType := ""
|
||||
if receiver != nil {
|
||||
// Find the type in the receiver
|
||||
WalkTree(receiver, func(node *sitter.Node) bool {
|
||||
if node.Type() == "type_identifier" {
|
||||
receiverType = GetNodeText(node, content)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
name := GetNodeText(nameNode, content)
|
||||
if receiverType != "" {
|
||||
name = "(" + receiverType + ")." + name
|
||||
}
|
||||
|
||||
return &protocol.Symbol{
|
||||
Name: name,
|
||||
Kind: protocol.SymbolMethod,
|
||||
Location: NodeLocation(n, filename),
|
||||
}
|
||||
}
|
||||
|
||||
func extractGoType(n *sitter.Node, content []byte, filename string) *protocol.Symbol {
|
||||
// Find type_spec child
|
||||
for i := 0; i < int(n.NamedChildCount()); i++ {
|
||||
child := n.NamedChild(i)
|
||||
if child != nil && child.Type() == "type_spec" {
|
||||
nameNode := child.ChildByFieldName("name")
|
||||
if nameNode == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
kind := protocol.SymbolType
|
||||
typeNode := child.ChildByFieldName("type")
|
||||
if typeNode != nil {
|
||||
switch typeNode.Type() {
|
||||
case "struct_type":
|
||||
kind = protocol.SymbolStruct
|
||||
case "interface_type":
|
||||
kind = protocol.SymbolInterface
|
||||
}
|
||||
}
|
||||
|
||||
return &protocol.Symbol{
|
||||
Name: GetNodeText(nameNode, content),
|
||||
Kind: kind,
|
||||
Location: NodeLocation(child, filename),
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractGoVarConst(n *sitter.Node, content []byte, filename string) []protocol.Symbol {
|
||||
var symbols []protocol.Symbol
|
||||
kind := protocol.SymbolVariable
|
||||
if n.Type() == "const_declaration" {
|
||||
kind = protocol.SymbolConstant
|
||||
}
|
||||
|
||||
WalkTree(n, func(node *sitter.Node) bool {
|
||||
if node.Type() == "const_spec" || node.Type() == "var_spec" {
|
||||
nameNode := node.ChildByFieldName("name")
|
||||
if nameNode != nil {
|
||||
symbols = append(symbols, protocol.Symbol{
|
||||
Name: GetNodeText(nameNode, content),
|
||||
Kind: kind,
|
||||
Location: NodeLocation(node, filename),
|
||||
})
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return symbols
|
||||
}
|
||||
|
||||
// extractJSSymbols extracts symbols from JavaScript/TypeScript code.
|
||||
func extractJSSymbols(root *sitter.Node, content []byte, filename string) []protocol.Symbol {
|
||||
var symbols []protocol.Symbol
|
||||
|
||||
WalkTree(root, func(n *sitter.Node) bool {
|
||||
var symbol *protocol.Symbol
|
||||
|
||||
switch n.Type() {
|
||||
case "function_declaration":
|
||||
symbol = extractJSFunction(n, content, filename)
|
||||
case "class_declaration":
|
||||
symbol = extractJSClass(n, content, filename)
|
||||
case "method_definition":
|
||||
symbol = extractJSMethod(n, content, filename)
|
||||
case "lexical_declaration", "variable_declaration":
|
||||
syms := extractJSVariable(n, content, filename)
|
||||
symbols = append(symbols, syms...)
|
||||
return true
|
||||
case "interface_declaration":
|
||||
symbol = extractTSInterface(n, content, filename)
|
||||
case "type_alias_declaration":
|
||||
symbol = extractTSTypeAlias(n, content, filename)
|
||||
}
|
||||
|
||||
if symbol != nil {
|
||||
if doc := ExtractDocComment(n, content, protocol.LangJavaScript); doc != nil {
|
||||
symbol.Doc = FormatDocComment(doc)
|
||||
}
|
||||
symbols = append(symbols, *symbol)
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return symbols
|
||||
}
|
||||
|
||||
func extractJSFunction(n *sitter.Node, content []byte, filename string) *protocol.Symbol {
|
||||
nameNode := n.ChildByFieldName("name")
|
||||
if nameNode == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &protocol.Symbol{
|
||||
Name: GetNodeText(nameNode, content),
|
||||
Kind: protocol.SymbolFunction,
|
||||
Location: NodeLocation(n, filename),
|
||||
}
|
||||
}
|
||||
|
||||
func extractJSClass(n *sitter.Node, content []byte, filename string) *protocol.Symbol {
|
||||
nameNode := n.ChildByFieldName("name")
|
||||
if nameNode == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &protocol.Symbol{
|
||||
Name: GetNodeText(nameNode, content),
|
||||
Kind: protocol.SymbolClass,
|
||||
Location: NodeLocation(n, filename),
|
||||
}
|
||||
}
|
||||
|
||||
func extractJSMethod(n *sitter.Node, content []byte, filename string) *protocol.Symbol {
|
||||
nameNode := n.ChildByFieldName("name")
|
||||
if nameNode == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &protocol.Symbol{
|
||||
Name: GetNodeText(nameNode, content),
|
||||
Kind: protocol.SymbolMethod,
|
||||
Location: NodeLocation(n, filename),
|
||||
}
|
||||
}
|
||||
|
||||
func extractJSVariable(n *sitter.Node, content []byte, filename string) []protocol.Symbol {
|
||||
var symbols []protocol.Symbol
|
||||
|
||||
WalkTree(n, func(node *sitter.Node) bool {
|
||||
if node.Type() == "variable_declarator" {
|
||||
nameNode := node.ChildByFieldName("name")
|
||||
if nameNode != nil {
|
||||
symbols = append(symbols, protocol.Symbol{
|
||||
Name: GetNodeText(nameNode, content),
|
||||
Kind: protocol.SymbolVariable,
|
||||
Location: NodeLocation(node, filename),
|
||||
})
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return symbols
|
||||
}
|
||||
|
||||
func extractTSInterface(n *sitter.Node, content []byte, filename string) *protocol.Symbol {
|
||||
nameNode := n.ChildByFieldName("name")
|
||||
if nameNode == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &protocol.Symbol{
|
||||
Name: GetNodeText(nameNode, content),
|
||||
Kind: protocol.SymbolInterface,
|
||||
Location: NodeLocation(n, filename),
|
||||
}
|
||||
}
|
||||
|
||||
func extractTSTypeAlias(n *sitter.Node, content []byte, filename string) *protocol.Symbol {
|
||||
nameNode := n.ChildByFieldName("name")
|
||||
if nameNode == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &protocol.Symbol{
|
||||
Name: GetNodeText(nameNode, content),
|
||||
Kind: protocol.SymbolType,
|
||||
Location: NodeLocation(n, filename),
|
||||
}
|
||||
}
|
||||
|
||||
// extractPythonSymbols extracts symbols from Python code.
|
||||
func extractPythonSymbols(root *sitter.Node, content []byte, filename string) []protocol.Symbol {
|
||||
var symbols []protocol.Symbol
|
||||
|
||||
WalkTree(root, func(n *sitter.Node) bool {
|
||||
var symbol *protocol.Symbol
|
||||
|
||||
switch n.Type() {
|
||||
case "function_definition":
|
||||
symbol = extractPythonFunction(n, content, filename)
|
||||
case "class_definition":
|
||||
symbol = extractPythonClass(n, content, filename)
|
||||
}
|
||||
|
||||
if symbol != nil {
|
||||
if doc := ExtractDocComment(n, content, protocol.LangPython); doc != nil {
|
||||
symbol.Doc = FormatDocComment(doc)
|
||||
}
|
||||
symbols = append(symbols, *symbol)
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return symbols
|
||||
}
|
||||
|
||||
func extractPythonFunction(n *sitter.Node, content []byte, filename string) *protocol.Symbol {
|
||||
nameNode := n.ChildByFieldName("name")
|
||||
if nameNode == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if this is a method (inside a class)
|
||||
parent := n.Parent()
|
||||
kind := protocol.SymbolFunction
|
||||
if parent != nil && parent.Type() == "block" {
|
||||
grandparent := parent.Parent()
|
||||
if grandparent != nil && grandparent.Type() == "class_definition" {
|
||||
kind = protocol.SymbolMethod
|
||||
}
|
||||
}
|
||||
|
||||
return &protocol.Symbol{
|
||||
Name: GetNodeText(nameNode, content),
|
||||
Kind: kind,
|
||||
Location: NodeLocation(n, filename),
|
||||
}
|
||||
}
|
||||
|
||||
func extractPythonClass(n *sitter.Node, content []byte, filename string) *protocol.Symbol {
|
||||
nameNode := n.ChildByFieldName("name")
|
||||
if nameNode == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &protocol.Symbol{
|
||||
Name: GetNodeText(nameNode, content),
|
||||
Kind: protocol.SymbolClass,
|
||||
Location: NodeLocation(n, filename),
|
||||
}
|
||||
}
|
||||
|
||||
// extractCSymbols extracts symbols from C/C++ code.
|
||||
func extractCSymbols(root *sitter.Node, content []byte, filename string) []protocol.Symbol {
|
||||
var symbols []protocol.Symbol
|
||||
|
||||
WalkTree(root, func(n *sitter.Node) bool {
|
||||
var symbol *protocol.Symbol
|
||||
|
||||
switch n.Type() {
|
||||
case "function_definition":
|
||||
symbol = extractCFunction(n, content, filename)
|
||||
case "struct_specifier":
|
||||
symbol = extractCStruct(n, content, filename)
|
||||
case "class_specifier":
|
||||
symbol = extractCppClass(n, content, filename)
|
||||
case "declaration":
|
||||
// Could be function declaration or variable
|
||||
if hasFunctionDeclarator(n) {
|
||||
symbol = extractCFunctionDecl(n, content, filename)
|
||||
}
|
||||
}
|
||||
|
||||
if symbol != nil {
|
||||
if doc := ExtractDocComment(n, content, protocol.LangC); doc != nil {
|
||||
symbol.Doc = FormatDocComment(doc)
|
||||
}
|
||||
symbols = append(symbols, *symbol)
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return symbols
|
||||
}
|
||||
|
||||
func extractCFunction(n *sitter.Node, content []byte, filename string) *protocol.Symbol {
|
||||
declarator := n.ChildByFieldName("declarator")
|
||||
if declarator == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Find the function name within the declarator
|
||||
var name string
|
||||
WalkTree(declarator, func(node *sitter.Node) bool {
|
||||
if node.Type() == "identifier" {
|
||||
name = GetNodeText(node, content)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if name == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &protocol.Symbol{
|
||||
Name: name,
|
||||
Kind: protocol.SymbolFunction,
|
||||
Location: NodeLocation(n, filename),
|
||||
}
|
||||
}
|
||||
|
||||
func extractCStruct(n *sitter.Node, content []byte, filename string) *protocol.Symbol {
|
||||
nameNode := n.ChildByFieldName("name")
|
||||
if nameNode == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &protocol.Symbol{
|
||||
Name: GetNodeText(nameNode, content),
|
||||
Kind: protocol.SymbolStruct,
|
||||
Location: NodeLocation(n, filename),
|
||||
}
|
||||
}
|
||||
|
||||
func extractCppClass(n *sitter.Node, content []byte, filename string) *protocol.Symbol {
|
||||
nameNode := n.ChildByFieldName("name")
|
||||
if nameNode == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &protocol.Symbol{
|
||||
Name: GetNodeText(nameNode, content),
|
||||
Kind: protocol.SymbolClass,
|
||||
Location: NodeLocation(n, filename),
|
||||
}
|
||||
}
|
||||
|
||||
func extractCFunctionDecl(n *sitter.Node, content []byte, filename string) *protocol.Symbol {
|
||||
declarator := n.ChildByFieldName("declarator")
|
||||
if declarator == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var name string
|
||||
WalkTree(declarator, func(node *sitter.Node) bool {
|
||||
if node.Type() == "identifier" {
|
||||
name = GetNodeText(node, content)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if name == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &protocol.Symbol{
|
||||
Name: name,
|
||||
Kind: protocol.SymbolFunction,
|
||||
Location: NodeLocation(n, filename),
|
||||
}
|
||||
}
|
||||
|
||||
func hasFunctionDeclarator(n *sitter.Node) bool {
|
||||
found := false
|
||||
WalkTree(n, func(node *sitter.Node) bool {
|
||||
if node.Type() == "function_declarator" {
|
||||
found = true
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
return found
|
||||
}
|
||||
Reference in New Issue
Block a user