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)
Validator Changes (crates/nemo-config/src/validator.rs)
Validation Error Improvements
Testing
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.rs — ConfigValidator, needs to propagate locations
crates/nemo-config/src/error.rs — ValidationError, already has location field
crates/nemo-config/src/location.rs — SourceLocation, already exists
crates/nemo-config/src/path.rs — ConfigPath, used for error paths
crates/nemo-config/src/value.rs — Value type, may need LocatedValue wrapper
Stack Base
- Stack on:
main
- Blocked by: none (foundation issue)
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-lspserver (#62) needs to produce LSP diagnostics with range information so editors can show squiggly lines at error locations. Currently:ValidationErrorhas alocation: Option<SourceLocation>field, but it is always set toNoneby the validatorquick_xmlwhich tracks byte positions (Reader::buffer_position()), but this data is never attached to the parsedValuetreeConfigPathtracks logical paths (root.layout.button.0.label) but not document positionsWithout 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)quick_xmlevents and stores them in the parse treeSourceLocationpointing to its start tagSourceLocationpointing to the attribute valueSourceLocationdata survives the parse → process_root pipelineValidator Changes (
crates/nemo-config/src/validator.rs)validate_against_schemaaccepts optional source location contextValidationErrorincludes the source location of the attribute (if available), otherwise the element locationmissing_requirederrors point to the element that is missing the fieldtype_mismatcherrors point to the attribute value with the wrong typevalidate_property→validate_rulecallsValidation Error Improvements
ValidationErrorconstructors accept an optionalSourceLocationparameterwith_location()lets callers attach location after constructionlocationfield is populated in all error paths where position data is availableTesting
with_location_tracking(true)produces values with attached locationsmissing_requirederrortype_mismatcherrorrule_violation(OneOf) errorDesign 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.Relevant Files
crates/nemo-config/src/xml_parser.rs— XML parser, needs to capture quick_xml positionscrates/nemo-config/src/validator.rs—ConfigValidator, needs to propagate locationscrates/nemo-config/src/error.rs—ValidationError, already has location fieldcrates/nemo-config/src/location.rs—SourceLocation, already existscrates/nemo-config/src/path.rs—ConfigPath, used for error pathscrates/nemo-config/src/value.rs—Valuetype, may needLocatedValuewrapperStack Base
main