mirror of
https://github.com/lukaszraczylo/filepuff-mcp.git
synced 2026-10-12 04:54:27 +00:00
fix(parser): keep signature in decorated Python skeleton
skeletonPythonNode took lines[0] of a decorated_definition, which is the first @decorator line, dropping the def/class signature entirely. Now emit the decorator line(s) then recurse into the wrapped definition so its signature is preserved. Adds skeleton_test.go (decorated + plain function).
This commit is contained in:
@@ -215,7 +215,34 @@ func skeletonPythonNode(node *sitter.Node, content []byte, sb *strings.Builder,
|
||||
skeletonPythonNode(child, content, sb, indent)
|
||||
}
|
||||
return
|
||||
case "function_definition", "decorated_definition":
|
||||
case "decorated_definition":
|
||||
// Emit the decorator line(s), then render the wrapped definition so its
|
||||
// signature (def/class) is preserved — taking lines[0] of the whole node
|
||||
// would keep only the first decorator and drop the signature entirely.
|
||||
var def *sitter.Node
|
||||
for i := 0; i < int(node.NamedChildCount()); i++ {
|
||||
if child := node.NamedChild(i); child != nil {
|
||||
if t := child.Type(); t == "function_definition" || t == "class_definition" {
|
||||
def = child
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if def != nil {
|
||||
decText := strings.TrimRight(string(content[node.StartByte():def.StartByte()]), "\n")
|
||||
for _, dl := range strings.Split(decText, "\n") {
|
||||
if strings.TrimSpace(dl) == "" {
|
||||
continue
|
||||
}
|
||||
sb.WriteString(indent)
|
||||
sb.WriteString(dl)
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
skeletonPythonNode(def, content, sb, indent)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case "function_definition":
|
||||
nodeText := string(content[node.StartByte():node.EndByte()])
|
||||
lines := strings.SplitN(nodeText, "\n", 2)
|
||||
sb.WriteString(indent)
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// A decorated Python function must keep BOTH its decorator(s) and its def
|
||||
// signature in the skeleton — previously only the first decorator survived and
|
||||
// the signature was dropped.
|
||||
func TestSkeletonPythonDecoratedFunction(t *testing.T) {
|
||||
r := NewRegistry()
|
||||
defer r.Close()
|
||||
|
||||
src := "@app.route(\"/\")\n" +
|
||||
"def home():\n" +
|
||||
" return \"hi\"\n"
|
||||
|
||||
out, _, err := SkeletonFile(context.Background(), r, "app.py", []byte(src))
|
||||
if err != nil {
|
||||
t.Fatalf("SkeletonFile error: %v", err)
|
||||
}
|
||||
if !strings.Contains(out, "@app.route(\"/\")") {
|
||||
t.Errorf("skeleton lost decorator, got:\n%s", out)
|
||||
}
|
||||
if !strings.Contains(out, "def home():") {
|
||||
t.Errorf("skeleton lost def signature, got:\n%s", out)
|
||||
}
|
||||
if strings.Contains(out, "return \"hi\"") {
|
||||
t.Errorf("skeleton should elide the body, got:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
// A plain (undecorated) Python function still renders its signature + body stub.
|
||||
func TestSkeletonPythonPlainFunction(t *testing.T) {
|
||||
r := NewRegistry()
|
||||
defer r.Close()
|
||||
|
||||
src := "def add(a, b):\n return a + b\n"
|
||||
out, _, err := SkeletonFile(context.Background(), r, "m.py", []byte(src))
|
||||
if err != nil {
|
||||
t.Fatalf("SkeletonFile error: %v", err)
|
||||
}
|
||||
if !strings.Contains(out, "def add(a, b):") {
|
||||
t.Errorf("skeleton lost signature, got:\n%s", out)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user