Repository navigation
fix: recognize generic HTML-attribute processors (th:content, th:for, th:open, ...) - #2
Merged
dozken merged 1 commit intoJul 27, 2026
Conversation
`th:content`, `th:for`, `th:open` and ~160 other valid Standard Dialect attributes were flagged as "Unknown Thymeleaf attribute" because every consumer treated absence from the 42-entry `ATTRS` catalog as invalid. Recognition is now two-tier. `ATTRS` keeps the curated, hand-documented processors; `SPECIFIC_ATTRS` / `BOOLEAN_ATTRS` hold the generic `th:<html-attr>` names transcribed from the Thymeleaf tutorial (§5.2 "Setting value to specific attributes" and §5.5 "Fixed-value boolean attributes"). `classify` resolves the curated tier first, so hand-written docs always win, and also accepts `th:data-*` / `th:aria-*`, which Thymeleaf 3 routes through StandardDefaultAttributesTagProcessor. That same default processor means *any* `th:*` name is technically valid, so "not in the catalog" was never a sound basis for a warning. The diagnostic is re-aimed at typos: it fires only when the name is unrecognized *and* within `MAX_SUGGESTION_DISTANCE` of a real one, and names the intended attribute in the message. `closest_known` and `levenshtein` move from `code_actions` into `thymeleaf` so both features share one search — every warning now has a matching quick fix. The threshold drops from 3 to 2: with the candidate pool grown from 42 to ~200 names, distance 3 matches almost any short word by chance (`th:bogus` would "correct" to `th:cols`). Two edits still covers dropped letters, transpositions and doubled characters. Hover, semantic tokens and completion follow the same classifier, so generic attributes get documentation, highlighting and suggestions. Completion offers both tiers with `sort_text` ranking the curated ones first. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014sSr14H6f4sgB7HAbyugMm
Contributor
Author
No worries - I'll keep using and contrib things when I see fit |
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.
Closes #1.
The problem
Every consumer of the dialect catalog treated "not in the 42-entry
ATTRSlist"as "invalid", so
th:content,th:for,th:open— and ~160 other perfectlylegal attributes — produced
Unknown Thymeleaf attributewarnings.What the Standard Dialect actually accepts
I went to the tutorial
rather than adding the three reported names, because the gap is much wider than
three, and it has a documented shape:
th:<html-attr>processors (
th:content,th:for,th:cite,th:colspan, everyth:on*handler, …).
(
th:open,th:required,th:async,th:hidden, …).Beyond both lists, Thymeleaf 3's
StandardDefaultAttributesTagProcessorhandlesany remaining
th:*name generically — that is howth:data-*customattributes work.
The change
Two-tier recognition in
src/thymeleaf.rs.ATTRSkeeps the curated,hand-documented processors; the two new lists are transcribed verbatim from the
tutorial and get synthesized descriptions.
classify() -> AttrKindresolves thecurated tier first, so hand-written docs always win for names that appear in
both (
th:class,th:href,th:fragment, …).th:data-*/th:aria-*countas known too.
The diagnostic is re-aimed at typos. Since the default attribute processor
accepts any name, "not in a list" was never a sound basis for a warning — but
catching
th:txtgenuinely is useful. So the warning now fires only when a nameis unrecognized and within
MAX_SUGGESTION_DISTANCEof a real one, and itnames the intended attribute:
closest_known+levenshteinmoved fromcode_actionsintothymeleaf, sothe diagnostic and the quick fix share one search. That is now an invariant:
every warning has a matching
Change to ...action.The suggestion threshold drops 3 → 2. With the candidate pool grown from 42
to ~200 names, distance 3 matches almost any short word by chance —
th:boguswould have "corrected" to
th:cols. At distance 2 the separation is clean:th:txtth:textth:contntth:contentth:fragmnetth:fragmentth:opneth:openth:bogusth:colsth:wibbleth:titleThe other consumers follow the same classifier, so generic attributes are no
longer second-class:
hoverdocuments them (via a new ownedAttrInfoso thegeneric tier can synthesize text),
semantic_tokenshighlights them, andcompletionoffers both tiers withsort_textranking curated names first.The bracket-balance ERROR now applies to every
th:*value rather than onlycatalog ones — the value of any Thymeleaf attribute is an expression.
Verification
make checkis clean (fmt,clippy -D warnings, 85 tests — 25 new).Two existing tests asserted a warning on
th:bogus, which is silent by designnow; both were retargeted to
th:txtand additionally assert the message namesits fix. New coverage includes the issue's exact repro producing zero
diagnostics,
th:data-*, the curated-wins-over-generic precedence, and thethreshold boundary cases above.
Also checked against the real
index.html(with the three repro snippets addedto it): the only diagnostic left is the intentional
th:txttypo.Notes for review
HTML attribute index, so they track what Thymeleaf documents rather than what
the HTML spec happens to contain.
MAX_SUGGESTION_DISTANCEis the one judgement call here. I documented thereasoning at the constant; happy to move it if you'd rather be more or less
aggressive.
and clients filter as you type, but say the word if you'd rather keep the menu
curated — it is a one-line change to drop the generic tier from completion
only.