feat: add YAML support#3757
Open
rodbegbie wants to merge 4 commits into
Open
Conversation
Adds a new harper-yaml crate, following the same standalone pattern as harper-python and harper-html rather than folding into harper-comments. YamlParser lints `#` comments (with the usual spellchecker-ignore-family suppression) and prose-like scalar values (plain, quoted, or block scalars), while leaving structural YAML (keys, identifiers, enum-like values) untouched. The core pieces: - YamlMasker runs two independent tree-sitter passes over the parsed document -- one for comment nodes, one for scalar-value nodes -- and combines the surviving spans. Scalar nodes exclude mapping keys (unquoted keys are the same tree-sitter node kind as values, so a key-vs-value distinction has to be made explicitly by comparing a candidate node's start position against its enclosing mapping pair's start position). - heuristics::is_prose_scalar decides whether a scalar value looks like prose worth checking, as opposed to structural config data: it requires 3+ words and rejects values that are, in their entirety, a single URL/path/version-shaped token (so "v1.2.3" is skipped, but "upgrade to version 1.2.3" is still checked). - DedentLines wraps the inner PlainEnglish parser to parse each line of a scalar independently after trimming its whitespace, avoiding a spurious "double space" false positive that raw multi-line block scalar indentation would otherwise trigger. Closes Automattic#2700. Entire-Checkpoint: 8eae5ee14317
harper-ls now routes the "yaml" language ID directly to YamlParser, the same way it already routes "python" to PythonParser. harper-cli routes .yaml/.yml files the same way it routes .py/.pyi. Entire-Checkpoint: 173be144ecc2
- New fuzz_harper_yaml target, mirroring fuzz_harper_html. - VS Code plugin: register onLanguage:yaml activation and add a YAML case to the language integration test suite. - Document YAML in the language-server support table. Marked "Comments Only: no" (unlike TOML), since YamlParser also lints prose-like scalar values, not just comments. Entire-Checkpoint: 3d4124766057
Address review findings on the YAML support crate: - Remove unreachable dead code in `is_prose_scalar`: under the `word_count >= 3` gate, `looks_url_path_or_version` (bails at >1 word) and `is_snake_or_kebab_case` (bails on whitespace) could never fire. Behaviour is unchanged - the word-count gate already subsumes every single-word URL/path/version/identifier case - but the code and its helpers are gone, so tests no longer imply filtering that isn't there. - Strip YAML block-scalar indicators (`|`, `>`, `|-`, `>+2`, ...) before the word-count gate so a short block-scalar body isn't pushed over the prose threshold by the leading indicator token. - Replace `is_mapping_key`'s byte-position heuristic with tree-sitter's named `key` field, so explicit-key syntax (`? key` / `: value`) no longer misclassifies the key as a lintable value. - Coalesce overlapping spans before building the `Mask`, so adversarial input cannot trigger the `FromIterator` overlap panic (a DoS in harper-ls). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Entire-Checkpoint: 6c181a01b5d3
68ae54b to
48a4cc0
Compare
elijah-potter
requested changes
Jul 6, 2026
elijah-potter
left a comment
Collaborator
There was a problem hiding this comment.
Good stuff! I just have a few nits.
| /// | ||
| /// Comments are never passed through this function — they're | ||
| /// always linted, handled separately in `YamlMasker`. | ||
| pub(crate) fn is_prose_scalar(text: &str) -> bool { |
Collaborator
There was a problem hiding this comment.
The (crate) tag is almost never necessary.
| /// incorrectly triggers Harper's "double space" formatting rule. | ||
| /// De-denting each line before parsing avoids that, without needing to | ||
| /// understand YAML's specific indentation rules. | ||
| pub(crate) struct DedentLines<P> { |
Collaborator
There was a problem hiding this comment.
Same comment. The (crate) tag is superfluous.
Comment on lines
+4
to
+15
| /// Wraps a `Parser`, parsing each line of a (possibly multiline) span | ||
| /// independently after trimming its leading/trailing whitespace, then | ||
| /// stitching the results back together with a single explicit | ||
| /// [`TokenKind::Newline`] token between lines. | ||
| /// | ||
| /// This exists for YAML block/folded scalars: their continuation lines | ||
| /// carry literal indentation (e.g. two leading spaces). Feeding | ||
| /// that raw text straight to a prose parser makes the newline-plus- | ||
| /// indentation read as a run of several space characters, which | ||
| /// incorrectly triggers Harper's "double space" formatting rule. | ||
| /// De-denting each line before parsing avoids that, without needing to | ||
| /// understand YAML's specific indentation rules. |
Collaborator
There was a problem hiding this comment.
This functionality exists for other parsers (i.e. HTML). Would it be possible to centralize the logic? It would make it FAR easier to maintain.
Comment on lines
+6
to
+9
| fuzz_target!(|data: &str| { | ||
| let parser = harper_yaml::YamlParser::default(); | ||
| let _res = parser.parse_str(data); | ||
| }); |
Collaborator
There was a problem hiding this comment.
I'm curious if you've actually run the fuzzer. If so, what were the results?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issues
Closes #2700
Description
Adds YAML support to Harper:
harper-lsandharper-clinow lint.yaml/.ymlfiles, checking#comments and prose-like scalar values while leaving structural YAML (keys, identifiers, enum-like config values) alone.This follows the same standalone-crate pattern as
harper-pythonandharper-html(a newharper-yamlcrate wired directly intoharper-ls/harper-cli), rather than folding intoharper-comments.The three commits build on each other:
harper-yamlcrate itself —YamlMaskerruns two independent tree-sitter passes (comments, scalar values) over the parsed document and combines the surviving spans. Scalar-value detection has to explicitly exclude mapping keys, since unquoted YAML keys are the same tree-sitter node kind as values — this is done by comparing a candidate node's start byte against its enclosingblock_mapping_pair/flow_pair's start byte (a key is always the first token in its pair; a value always starts later, afterkey:). A smallheuristics::is_prose_scalarfunction decides whether a scalar value looks like prose worth checking (3+ words, not solely a URL/path/version-shaped token) versus structural config data. A smallDedentLinesparser wrapper avoids a "double space" false positive that raw YAML block-scalar indentation would otherwise trigger.harper-ls/harper-cli— routes"yaml"/.yaml/.ymlthe same way"python"/.pyalready routes toPythonParser.fuzz_harper_yamltarget (mirroringfuzz_harper_html),onLanguage:yamlactivation plus a VS Code integration test, and a docs table entry.How Has This Been Tested?
cargo test --workspace— full workspace suite passes, including 39 new unit/integration tests inharper-yamlcovering comments, plain/quoted/block scalars, thespellchecker:ignore-family suppression, mapping-key exclusion, trailing same-line comments, and the URL/path/version/identifier heuristics.just test-vscode— the real VS Code extension integration suite (36 specs) passes, exercising the builtharper-lsbinary end to end.harper-cli lintagainst hand-written fixtures.AI Disclosure
If Your PR Implements or Enhances a Linter
Checklist