mirror of
https://github.com/lukaszraczylo/filepuff-mcp.git
synced 2026-10-12 04:54:27 +00:00
refactor: remove dead scaffolding from parser and edit
edit.ValidateLanguage had zero callers (only its own godoc referenced it) — removed. yaml_json.go: contrary to the audit's claim, the file is NOT entirely dead — ParseYAML/ParseJSON are live via the Parse() dispatch (parser.go:198-200) and are kept with their tests. Removed only the genuinely unreachable scaffolding that nothing constructs or calls: the YAMLNode/JSONNode types, WalkYAML, GetYAMLNodeText, GetYAMLNodeLocation, QueryYAML, QueryJSON, ValidateYAML, ValidateJSON, and ToSitterTree, plus their now-orphaned tests. YAML/JSON parsing behavior is unchanged (TestParseYAML/TestParseJSON/TestRegistryParse_YAML_JSON still pass).
This commit is contained in:
@@ -881,14 +881,3 @@ func truncateString(s string, maxLen int) string {
|
|||||||
}
|
}
|
||||||
return s[:util.RuneBoundary(s, maxLen-3)] + "..."
|
return s[:util.RuneBoundary(s, maxLen-3)] + "..."
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateLanguage checks if AST editing is supported for a file.
|
|
||||||
// Returns nil for supported languages, error for unsupported.
|
|
||||||
// Note: Text-based editing is always available regardless of this check.
|
|
||||||
func ValidateLanguage(filename string) error {
|
|
||||||
lang := protocol.DetectLanguage(filename)
|
|
||||||
if lang == protocol.LangUnknown {
|
|
||||||
return fmt.Errorf("unsupported file type for AST editing: %s (text-based editing is available)", filename)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,70 +4,45 @@ package parser
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
"github.com/lukaszraczylo/mcp-filepuff/pkg/errors"
|
"github.com/lukaszraczylo/mcp-filepuff/pkg/errors"
|
||||||
"github.com/lukaszraczylo/mcp-filepuff/pkg/protocol"
|
"github.com/lukaszraczylo/mcp-filepuff/pkg/protocol"
|
||||||
sitter "github.com/smacker/go-tree-sitter"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// YAMLNode wraps yaml.Node to provide tree-sitter-like interface
|
// ParseYAML parses YAML content and returns a tree-sitter-compatible result.
|
||||||
type YAMLNode struct {
|
// YAML does not use tree-sitter, so the returned ParseResult.Tree is nil.
|
||||||
*yaml.Node
|
|
||||||
Content []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSONNode represents a JSON AST node
|
|
||||||
type JSONNode struct {
|
|
||||||
Value any
|
|
||||||
Type string
|
|
||||||
Children []*JSONNode
|
|
||||||
Line int
|
|
||||||
Column int
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseYAML parses YAML content and returns a tree-sitter-compatible result
|
|
||||||
func (r *Registry) ParseYAML(ctx context.Context, filename string, content []byte) (*ParseResult, error) {
|
func (r *Registry) ParseYAML(ctx context.Context, filename string, content []byte) (*ParseResult, error) {
|
||||||
// Check file size
|
|
||||||
if len(content) > maxFileSize {
|
if len(content) > maxFileSize {
|
||||||
return nil, errors.NewFileTooLarge(filename, int64(len(content)), maxFileSize)
|
return nil, errors.NewFileTooLarge(filename, int64(len(content)), maxFileSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse YAML
|
|
||||||
var root yaml.Node
|
var root yaml.Node
|
||||||
if err := yaml.Unmarshal(content, &root); err != nil {
|
if err := yaml.Unmarshal(content, &root); err != nil {
|
||||||
return nil, errors.NewParseError("yaml", filename, err)
|
return nil, errors.NewParseError("yaml", filename, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract syntax errors from YAML parse
|
|
||||||
syntaxErrors := extractYAMLErrors()
|
|
||||||
|
|
||||||
// Create a pseudo tree-sitter tree for compatibility
|
|
||||||
// We'll use nil for the tree since YAML doesn't use tree-sitter
|
|
||||||
return &ParseResult{
|
return &ParseResult{
|
||||||
Tree: nil, // YAML uses yaml.Node instead
|
Tree: nil, // YAML uses yaml.Node instead of tree-sitter
|
||||||
Language: protocol.LangYAML,
|
Language: protocol.LangYAML,
|
||||||
Errors: syntaxErrors,
|
Errors: extractYAMLErrors(),
|
||||||
Content: content,
|
Content: content,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseJSON parses JSON content and returns a tree-sitter-compatible result
|
// ParseJSON parses JSON content and returns a tree-sitter-compatible result.
|
||||||
|
// JSON does not use tree-sitter, so the returned ParseResult.Tree is nil.
|
||||||
func (r *Registry) ParseJSON(ctx context.Context, filename string, content []byte) (*ParseResult, error) {
|
func (r *Registry) ParseJSON(ctx context.Context, filename string, content []byte) (*ParseResult, error) {
|
||||||
// Check file size
|
|
||||||
if len(content) > maxFileSize {
|
if len(content) > maxFileSize {
|
||||||
return nil, errors.NewFileTooLarge(filename, int64(len(content)), maxFileSize)
|
return nil, errors.NewFileTooLarge(filename, int64(len(content)), maxFileSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse JSON to validate syntax
|
|
||||||
var jsonData any
|
var jsonData any
|
||||||
if err := json.Unmarshal(content, &jsonData); err != nil {
|
if err := json.Unmarshal(content, &jsonData); err != nil {
|
||||||
return nil, errors.NewParseError("json", filename, err)
|
return nil, errors.NewParseError("json", filename, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON parsing succeeded, no syntax errors
|
|
||||||
return &ParseResult{
|
return &ParseResult{
|
||||||
Tree: nil, // JSON uses native Go structures
|
Tree: nil, // JSON uses native Go structures
|
||||||
Language: protocol.LangJSON,
|
Language: protocol.LangJSON,
|
||||||
@@ -76,120 +51,8 @@ func (r *Registry) ParseJSON(ctx context.Context, filename string, content []byt
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractYAMLErrors extracts errors from YAML nodes
|
// extractYAMLErrors returns YAML syntax errors. yaml.Unmarshal already reports
|
||||||
|
// parse errors, so reaching here means the document was syntactically valid.
|
||||||
func extractYAMLErrors() []SyntaxError {
|
func extractYAMLErrors() []SyntaxError {
|
||||||
// YAML parser already validates during unmarshal
|
|
||||||
// If we got here, there are no syntax errors
|
|
||||||
// However, we could add semantic validation here in the future
|
|
||||||
return []SyntaxError{}
|
return []SyntaxError{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WalkYAML walks a YAML AST and calls fn for each node
|
|
||||||
func WalkYAML(node *yaml.Node, fn func(*yaml.Node) bool) {
|
|
||||||
if node == nil || !fn(node) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, child := range node.Content {
|
|
||||||
WalkYAML(child, fn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetYAMLNodeText returns the text representation of a YAML node
|
|
||||||
func GetYAMLNodeText(node *yaml.Node) string {
|
|
||||||
if node == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
switch node.Kind {
|
|
||||||
case yaml.DocumentNode:
|
|
||||||
if len(node.Content) > 0 {
|
|
||||||
return GetYAMLNodeText(node.Content[0])
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
case yaml.MappingNode:
|
|
||||||
return node.Value
|
|
||||||
case yaml.SequenceNode:
|
|
||||||
return node.Value
|
|
||||||
case yaml.ScalarNode:
|
|
||||||
return node.Value
|
|
||||||
case yaml.AliasNode:
|
|
||||||
return node.Value
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetYAMLNodeLocation returns the location of a YAML node
|
|
||||||
func GetYAMLNodeLocation(node *yaml.Node) protocol.Location {
|
|
||||||
if node == nil {
|
|
||||||
return protocol.Location{Line: 1, Column: 1}
|
|
||||||
}
|
|
||||||
|
|
||||||
return protocol.Location{
|
|
||||||
Line: node.Line,
|
|
||||||
Column: node.Column,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// QueryYAML performs a simple query on YAML content
|
|
||||||
// Example: "$.metadata.name" to find the name field in metadata
|
|
||||||
func QueryYAML(content []byte, query string) ([]*yaml.Node, error) {
|
|
||||||
var root yaml.Node
|
|
||||||
if err := yaml.Unmarshal(content, &root); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to parse YAML: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple path-based query implementation
|
|
||||||
// This is a basic implementation - can be extended with more sophisticated queries
|
|
||||||
var results []*yaml.Node
|
|
||||||
|
|
||||||
WalkYAML(&root, func(node *yaml.Node) bool {
|
|
||||||
if node.Value == query || node.Tag == query {
|
|
||||||
results = append(results, node)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return results, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// QueryJSON performs a simple query on JSON content
|
|
||||||
func QueryJSON(content []byte, query string) ([]any, error) {
|
|
||||||
var data any
|
|
||||||
if err := json.Unmarshal(content, &data); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to parse JSON: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Basic implementation - can be extended with JSONPath support
|
|
||||||
var results []any
|
|
||||||
|
|
||||||
// For now, just validate that it's valid JSON
|
|
||||||
results = append(results, data)
|
|
||||||
|
|
||||||
return results, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateYAML validates YAML content without parsing to full AST
|
|
||||||
func ValidateYAML(content []byte) error {
|
|
||||||
var node yaml.Node
|
|
||||||
if err := yaml.Unmarshal(content, &node); err != nil {
|
|
||||||
return fmt.Errorf("YAML validation failed: %w", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateJSON validates JSON content
|
|
||||||
func ValidateJSON(content []byte) error {
|
|
||||||
var data any
|
|
||||||
if err := json.Unmarshal(content, &data); err != nil {
|
|
||||||
return fmt.Errorf("JSON validation failed: %w", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSitterTree is a placeholder that returns nil for YAML/JSON
|
|
||||||
// These formats don't use tree-sitter, but we keep this for interface compatibility
|
|
||||||
func (yn *YAMLNode) ToSitterTree() *sitter.Tree {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
|
|
||||||
"github.com/lukaszraczylo/mcp-filepuff/pkg/protocol"
|
"github.com/lukaszraczylo/mcp-filepuff/pkg/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -202,82 +200,3 @@ version: 1.0.0`)
|
|||||||
t.Errorf("expected YAML language for .yml extension, got %s", ymlResult.Language)
|
t.Errorf("expected YAML language for .yml extension, got %s", ymlResult.Language)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWalkYAML(t *testing.T) {
|
|
||||||
content := []byte(`metadata:
|
|
||||||
name: test
|
|
||||||
labels:
|
|
||||||
app: myapp
|
|
||||||
env: prod`)
|
|
||||||
|
|
||||||
var root yaml.Node
|
|
||||||
if err := yaml.Unmarshal(content, &root); err != nil {
|
|
||||||
t.Fatalf("failed to parse YAML: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeCount := 0
|
|
||||||
WalkYAML(&root, func(node *yaml.Node) bool {
|
|
||||||
nodeCount++
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
if nodeCount == 0 {
|
|
||||||
t.Error("expected to visit nodes, but count is 0")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValidateYAML(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
content []byte
|
|
||||||
shouldError bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "valid YAML",
|
|
||||||
content: []byte("name: test\nvalue: 100"),
|
|
||||||
shouldError: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid YAML",
|
|
||||||
content: []byte("name: test\n bad:\n[unclosed"),
|
|
||||||
shouldError: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
err := ValidateYAML(tt.content)
|
|
||||||
if (err != nil) != tt.shouldError {
|
|
||||||
t.Errorf("ValidateYAML() error = %v, shouldError = %v", err, tt.shouldError)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValidateJSON(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
content []byte
|
|
||||||
shouldError bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "valid JSON",
|
|
||||||
content: []byte(`{"name": "test", "value": 100}`),
|
|
||||||
shouldError: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid JSON",
|
|
||||||
content: []byte(`{"name": "test", "value": 100`),
|
|
||||||
shouldError: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
err := ValidateJSON(tt.content)
|
|
||||||
if (err != nil) != tt.shouldError {
|
|
||||||
t.Errorf("ValidateJSON() error = %v, shouldError = %v", err, tt.shouldError)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user