Skip to content

fix(adoc): resolve empty-text cross-references corrupting walker context#1110

Open
SudhanshuMatrix wants to merge 1 commit into
vale-cli:v3from
SudhanshuMatrix:fix/adoc-xref-headings
Open

fix(adoc): resolve empty-text cross-references corrupting walker context#1110
SudhanshuMatrix wants to merge 1 commit into
vale-cli:v3from
SudhanshuMatrix:fix/adoc-xref-headings

Conversation

@SudhanshuMatrix

Copy link
Copy Markdown

Description

This PR fixes a silent false-negative bug where rules carrying scope: heading stop matching section titles in an AsciiDoc file if that file combines explicit block anchors with xref: inline macros (having empty link text).

Fixes #1109

Root Cause

  1. Dynamic Target Resolution: During HTML generation, Asciidoctor resolves empty-text cross-references (like xref:id[] or <<id>>) using the target heading's title.
  2. Missing Source Text: Since the resolved text is not written in the raw source paragraph containing the reference, Vale's walker fails to locate the exact phrase inside the paragraph block.
  3. Word-Masking Fallback: As a fallback, Vale starts masking the individual words ("first", "heading", "lowercase") anywhere it can find them in the remaining document context.
  4. Context Corruption: This matches and masks the words inside subsequent headings (e.g., "second heading lowercase" becomes "second @@@@@@@ @@@@@@@@@").
  5. Silent Drop: When Vale later processes these headings, they fail to match the corrupted context, leading to location lookup failures and the alert being silently discarded.

Proposed Changes

We intercept the AsciiDoc content inside lintADoc in internal/lint/adoc.go before it is passed to Asciidoctor, and rewrite empty-text references:

  • xref:target[] -> xref:target[target]
  • <<target>> -> <<target,target>>

This ensures that the text rendered in the HTML tag is the literal target identifier (e.g. #intro or intro), which is present in the raw source code of the paragraph. Vale's walker successfully matches and masks it within the paragraph block, leaving subsequent headings untouched.

How to Test and Verify

  1. Run Vale with a heading-scoped rule against this sample AsciiDoc file (repro.adoc):
    = Document
    
    [[intro]]
    == first heading lowercase
    
    This paragraph links to xref:#intro[].
    
    [[middle]]
    == second heading lowercase
    
    This paragraph links to xref:#intro[].
    
    [[final]]
    == third heading lowercase
    
    This paragraph links to xref:#intro[].

Fixes a bug where rules with 'scope: heading' stopped matching most section
titles in an AsciiDoc file if that file combined explicit block anchors with
'xref:' inline macros having empty link text.

During HTML compilation, Asciidoctor resolves empty-text cross-references
(e.g., 'xref:target[]' or '<<target>>') using the target heading's title.
Since this resolved title text is not present in the raw source paragraph
containing the reference, Vale's walker fails to locate the exact phrase and
falls back to masking individual words across the rest of the document. This
erroneously masks those words at subsequent headings, corrupting their text
in the walker context and causing them to be silently skipped.

We resolve this by pre-processing the AsciiDoc content inside 'lintADoc'
to rewrite empty-text cross-references so that their target text matches the
literal target identifier present in the raw source:
- 'xref:target[]' -> 'xref:target[target]'
- '<<target>>' -> '<<target,target>>'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Heading-scoped rules silently skip sections when a block anchor and an xref: macro coexist in AsciiDoc

1 participant