Skip to content

Add source location tracking to XML parser and validator for LSP diagnostics #69

@geoffjay

Description

@geoffjay

Summary

Enhance the XML parser and config validator to track and propagate source locations (line/column) so that LSP diagnostics can point to the exact position of errors in Nemo XML files.

Context

The nemo-lsp server (#62) needs to produce LSP diagnostics with range information so editors can show squiggly lines at error locations. Currently:

  • ValidationError has a location: Option<SourceLocation> field, but it is always set to None by the validator
  • The XML parser uses quick_xml which tracks byte positions (Reader::buffer_position()), but this data is never attached to the parsed Value tree
  • ConfigPath tracks logical paths (root.layout.button.0.label) but not document positions

Without this, the LSP can report what is wrong ("missing required field 'label'") but not where (line 42, column 5).

Acceptance Criteria

XML Parser Changes (crates/nemo-config/src/xml_parser.rs)

  • The parser captures line/column positions from quick_xml events and stores them in the parse tree
  • Each parsed element carries a SourceLocation pointing to its start tag
  • Each parsed attribute carries a SourceLocation pointing to the attribute value
  • Location data is optional (backward compatible — parsing without location tracking still works)
  • The SourceLocation data survives the parse → process_root pipeline

Validator Changes (crates/nemo-config/src/validator.rs)

  • validate_against_schema accepts optional source location context
  • When a property fails validation, the ValidationError includes the source location of the attribute (if available), otherwise the element location
  • missing_required errors point to the element that is missing the field
  • type_mismatch errors point to the attribute value with the wrong type
  • Location is propagated through validate_propertyvalidate_rule calls

Validation Error Improvements

  • All ValidationError constructors accept an optional SourceLocation parameter
  • A convenience method with_location() lets callers attach location after construction
  • The location field is populated in all error paths where position data is available

Testing

  • Unit test: parsing XML with with_location_tracking(true) produces values with attached locations
  • Unit test: validator populates location in missing_required error
  • Unit test: validator populates location in type_mismatch error
  • Unit test: validator populates location in rule_violation (OneOf) error
  • Unit test: unknown property error includes element location

Design Notes

Two approaches for storing locations in the parse tree:

Option A: Wrapper value type — Create LocatedValue { value: Value, location: SourceLocation }. More invasive, changes all downstream code.

Option B: Sidecar map — Store a HashMap<ConfigPath, SourceLocation> alongside the parse tree. Less invasive, but requires matching paths.

Recommended: Option A (wrapper type) because it keeps the location bound to the value and can't get out of sync. The Value::as_object(), Value::as_str(), etc. accessors still work on the inner value.

// Proposed approach
pub struct LocatedValue {
    pub value: Value,
    pub location: Option<SourceLocation>,
}

Relevant Files

  • crates/nemo-config/src/xml_parser.rs — XML parser, needs to capture quick_xml positions
  • crates/nemo-config/src/validator.rsConfigValidator, needs to propagate locations
  • crates/nemo-config/src/error.rsValidationError, already has location field
  • crates/nemo-config/src/location.rsSourceLocation, already exists
  • crates/nemo-config/src/path.rsConfigPath, used for error paths
  • crates/nemo-config/src/value.rsValue type, may need LocatedValue wrapper

Stack Base

  • Stack on: main
  • Blocked by: none (foundation issue)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions