Commit Graph

6 Commits

Author SHA1 Message Date
741e65cae1 feat(model): TreeNode editing helpers (attributes, shallow copy, order, normalize)
Add the DOM/tree editing primitives from the audit that the editor use
case needs:

- Attribute helpers: attributeValue(_:), setAttribute(_:_:) (update in
  place or append), removeAttribute(_:).
- shallowCopy() copies a node alone, complementing the deep copy().
- precedes(_:) compares two nodes in document order (ancestor before
  descendant, earlier sibling before later; unrelated nodes compare false).
- normalize() coalesces adjacent text children, drops empty text, and
  recurses into elements; CDATA is left intact.

First-class namespace/DOCTYPE/entity-reference nodes (an invasive
TreeNodeKind change) and cross-document adoption/ownerDocument (no
document-identity model) are left for a follow-up.

Closes #90
2026-06-08 15:19:33 +02:00
d667b4cf1e feat(editor): structured quick-fixes from completions
Slice 4 of #94 (completes the epic). Quick-fixes are derived from the structured
schema completions, never by parsing a diagnostic message:

- TreeNode gains contentRange (the span between an element's start and end tags),
  captured by the ranged reader, so an inserted attribute or child is placed
  exactly.
- QuickFix and TextEdit model an editor edit (a replacement over a source range;
  an insertion is a zero-width range).
- QuickFixEngine turns a Completions into fixes: a required-but-absent attribute
  becomes an insertion before the start tag's >, and a still-expected required
  child becomes an insertion before the end tag. Schema.Document.quickFixes(at:in:)
  is the entry over a ranged tree.

Tests apply a fix and re-validate: adding the required id, or inserting the
required qty, yields XML the schema then accepts; a complete element offers none.

#94 (editor integration) is complete: ranged recovering read, unified lint,
schema completions, and quick-fixes.
2026-06-08 13:35:33 +02:00
4659ea33c8 feat(editor): source spans on the tree, mapping findings to positions
Slice 1 of #94 (editor integration). The keystone: a located validation finding
can now be mapped back to a source range.

- TreeNode gains a sourceRange (start/end Mark); SourceRange is a half-open span.
- PureXML.readTree builds a mutable, parent-aware, best-effort tree whose every
  node carries its span (an element from the < of its start tag to past its end
  tag; leaves over the text they consumed). Never throws, recovers in place,
  returns located diagnostics.
- TreeNode.node(at:) navigates a validation coding path to its node using the
  same canonical convention the validators build paths with (a sibling index
  only when a name repeats), and sourceRange(at:) returns the span. So
  Document.validate's ValidationError.codingPath resolves to characters in the
  document.

Tests prove the spans, the navigation, the end-to-end bridge (an XSD finding to
a source range), and that ranged reading of invalid input never crashes.
2026-06-08 13:14:29 +02:00
15de0c5344 feat(model): mutable parent-aware tree (libxml2 tree.h parity)
Add PureXML.Model.TreeNode, a reference-type document tree that knows its
parent and siblings. Navigation goes up and sideways (parent, nextSibling,
previousSibling, ancestors, root, elementChildren, stringValue) and editing
is in place (append, insert before/after, removeFromParent, replace, copy).
Children are held strongly and the parent weakly; attaching a node detaches
it from any prior parent and a node can never become its own ancestor.
parseTree builds one from XML; TreeNode.node converts back for serialization.

Closes #31
2026-06-08 03:13:59 +02:00
43f82312e6 feat(parser): resolve XML namespaces at the start-element boundary
Add namespace resolution following the XML Namespaces rules, reimplemented in
Swift from libxml2's SAX2 model; no upstream code copied.

- QualifiedName gains an optional resolved namespaceURI (nil for names built
  directly or in no namespace), with a resolved(namespaceURI:) helper.
- NamespaceContext maintains a scope stack of xmlns bindings as elements open
  and close, resolving element and attribute names against it.
- A default namespace applies to unprefixed element names but never to
  attributes; the xml prefix is built in; xmlns declarations are preserved as
  attributes so documents round-trip; an unbound prefix raises
  ParseError.undefinedNamespacePrefix. End-tag matching stays lexical.
- Namespace-free documents are unchanged (URIs remain nil).
- 7 namespace tests; 39 total.
2026-06-08 01:03:29 +02:00
708390242c chore: scaffold PureXML pure-Swift XML package
Root Swift package mirroring PureYAML's structure, rules, and verification
gates. Includes the XML node model (Node, Element, Attribute, QualifiedName),
a working emitter (Serializer) with escaping and pretty/compact options, and
structural validation. The parser surface is stable; parse() raises
notImplemented until the scanner lands, pinned by a test.

- dependency-free, namespaced under PureXML
- portability and style gates carried over (style, namespacing, forbidden
  patterns, swiftformat, swiftlint, linux, wasm, windows CI)
- 11 tests passing
2026-06-07 23:20:20 +02:00