Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

120 lines
3.1 KiB
Go

package languages
import (
"bytes"
"encoding/xml"
"io"
"path"
"strings"
"github.com/zzet/gortex/internal/graph"
"github.com/zzet/gortex/internal/parser"
)
// XAMLExtractor extracts structure from XAML / AXAML markup — the
// declarative UI layer of WPF, UWP, MAUI, WinUI and Avalonia .NET
// apps. XAML is well-formed XML, so it is parsed with encoding/xml
// rather than tree-sitter.
//
// What it surfaces:
// - the file node, stamped with x:Class (the code-behind type the
// view is bound to);
// - one node per x:Name'd control, so a control referenced from
// code-behind resolves to a real graph node;
// - {Binding …} expressions, recorded on the owning control so the
// data-binding surface is queryable.
type XAMLExtractor struct{}
// NewXAMLExtractor constructs a XAMLExtractor.
func NewXAMLExtractor() *XAMLExtractor { return &XAMLExtractor{} }
func (e *XAMLExtractor) Language() string { return "xaml" }
func (e *XAMLExtractor) Extensions() []string { return []string{".xaml", ".axaml"} }
// Extract parses XAML markup. A malformed document yields whatever was
// decoded before the error — never a hard failure.
func (e *XAMLExtractor) Extract(filePath string, src []byte) (*parser.ExtractionResult, error) {
result := &parser.ExtractionResult{}
fileNode := &graph.Node{
ID: filePath,
Kind: graph.KindFile,
Name: path.Base(filePath),
FilePath: filePath,
Language: "xaml",
}
result.Nodes = append(result.Nodes, fileNode)
dec := xml.NewDecoder(bytes.NewReader(src))
dec.Strict = false
rootSeen := false
seenName := map[string]bool{}
for {
tok, err := dec.Token()
if err == io.EOF || err != nil {
break // EOF or malformed — keep what was decoded
}
se, ok := tok.(xml.StartElement)
if !ok {
continue
}
line := 1 + bytes.Count(src[:clampOffset(dec.InputOffset(), len(src))], []byte{'\n'})
if !rootSeen {
rootSeen = true
if cls := xamlAttr(se, "Class"); cls != "" {
fileNode.Meta = map[string]any{"xaml_class": cls}
}
}
name := xamlAttr(se, "Name")
if name == "" || seenName[name] {
continue
}
seenName[name] = true
ctrl := &graph.Node{
ID: filePath + "::" + name,
Kind: graph.KindVariable,
Name: name,
FilePath: filePath,
StartLine: line,
EndLine: line,
Language: "xaml",
Meta: map[string]any{"xaml_element": se.Name.Local},
}
var bindings []string
for _, a := range se.Attr {
if v := strings.TrimSpace(a.Value); strings.HasPrefix(v, "{Binding") {
bindings = append(bindings, a.Name.Local+"="+v)
}
}
if len(bindings) > 0 {
ctrl.Meta["xaml_bindings"] = bindings
}
result.Nodes = append(result.Nodes, ctrl)
}
return result, nil
}
// xamlAttr returns the value of the attribute with the given local
// name (namespace-agnostic, so both `x:Name` and `Name` match "Name").
func xamlAttr(se xml.StartElement, local string) string {
for _, a := range se.Attr {
if a.Name.Local == local {
return strings.TrimSpace(a.Value)
}
}
return ""
}
func clampOffset(off int64, n int) int64 {
if off > int64(n) {
return int64(n)
}
if off < 0 {
return 0
}
return off
}