Repository navigation
♻️ Buttons: parse line-block text line by line, once - #315
Open
chrisjsewell wants to merge 11 commits into
Open
chrisjsewell wants to merge 11 commits into
chrisjsewell wants to merge 11 commits into
Conversation
…e once #308 recognised a line block in button text by block-parsing the text with nested_parse, checking that the result held only line blocks, and otherwise parsing it again as inline text. A parse has side effects that a fallback cannot undo, so a body that looked like a line block but was not only one was parsed twice: a blank line followed by a deeper bar, paragraph, directive or substitution definition, or a first line deeper than the rest, gave a spurious "Too many autonumbered footnote references" (and an unresolved reference), a "Block quote ends without a blank line" warning, a duplicate explicit target, or a substitution definition leaking into the page. The lines are now found textually: every non-blank line at the first line's indent must start with docutils' line-block marker, and a deeper line directly after a non-blank line continues the previous line. Each line is then parsed once with inline_text into a translatable inline of its own, with the source and line of that content line. Any other text is parsed once as inline text, as before #308, so the parse-twice class is gone by construction, along with the block-quote unwrap, the system-message filter, the node move and the docutils < 0.22 source-info fallback. On valid line blocks the HTML, LaTeX, text, man, texinfo and .pot message output is byte-identical to #308's, on Sphinx 7.4, 8.2 and 9.1; the .pot locations on docutils < 0.22 now name each line instead of the first. All of #308's tests pass unchanged; the malformed bodies are added as regressions (they fail on #308's mechanism), with unit tests of the splitter.
In RST, a directive's content_offset is the offset of its content in the whole input, so it already counts the lines before the directive. The inline path of button-link and button-ref added it to the directive's line anyway, both for the line it gave inline_text and for the gettext location of the text, so the lines before the directive were counted twice. A role error in the text of a button at line 4 of a page was reported at line 9 instead of 6, inside a dropdown it named a line past the end of the file, and for a button in an included file it named the included file's line plus the offset into the including page, attributed to the including page. The text is now located at its first content line, through the state machine, so an included file's text names that file. As for a paragraph, docutils reports the messages of the inline markup at the text's first line. MyST's content_offset counts the lines from the directive, so its line is unchanged (measured identical warnings and .pot on Sphinx 7.4, 8.2 and 9.1). The gettext regression fixture recorded the wrong line (19 for text at line 11) and is corrected.
The docs tell MyST users to break button text with a hard line break (a
backslash at the end of the line). That breaks the line in HTML and LaTeX,
but the text builder joins the lines ("Multi-linebutton text") and the
gettext message contains the raw break markup ("Multi-line<br /> \\ button
text"), the two defects the RST line block avoids. The note now says so,
and points translated projects at the RST line block, which works from MyST
inside an eval-rst block (separate lines in text output, one message per
line).
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #315 +/- ##
==========================================
+ Coverage 92.60% 92.93% +0.33%
==========================================
Files 13 13
Lines 1420 1430 +10
==========================================
+ Hits 1315 1329 +14
+ Misses 105 101 -4
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…cape A single-line RST label written `| Docs | API |` is a one-line line block, so it renders "Docs | API |"; a literal leading bar is written `\|`. Test both, and say so in the button docs.
…ils does The splitter stripped every continuation line, and kept the empty first line of a bare bar. docutils (and so #308's block parse) strips only the common indent of the continuation lines, so they keep their indent relative to each other, and drops a bare bar's empty first line. A continuation line indented deeper than the one before it therefore lost its leading spaces in text, HTML, LaTeX, man and .pot output, and `|` followed by a continuation line gained a leading newline. Both now match main and docutils.
Locating every line at the directive, or at the first line of the line block, passed the whole suite. The gettext test now asserts each line's .pot location, the message test asserts the unknown role is reported at the second line, and a new test checks both for a line-block button in an included file.
Three comments in the #308 tests still described the removed block parse (nodes moved out of it, messages placed beside the line block, a source fallback for docutils < 0.22). Their assertions are unchanged.
Texinfo prints the raw break markup of a MyST hard line break in button text, rather than joining the lines as the text builder does. The note on a leading bar also covers a bare `|` label, which is an empty line block.
…ls does Only spaces indent an RST line, and docutils keeps a line block line's text after the bar as is. The splitter stripped any Unicode whitespace in both places, so `| \xa0text` (a no-break or ideographic space, the one way to indent a line visibly in a button) lost its leading space, and a line starting with a no-break space before a bar was read as an indented bar. Pinned by four split cases and an exact-HTML test, which also pins that a bare bar adds a break and no empty element.
A split case pins that a line at the bar's indent without a bar ends the line block even directly after a bar, so `| Docs` then `More` is not one line. A gettext test pins that a line's message is its source text, markup included (`**hey** there`), not the text it renders.
A two-line label whose second line is plain text is not a line block, so its leading bar is shown.
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.
This replaces the mechanism #308 merged for #156 (a line block in a button's text renders as separate lines) before it ships in 0.8.0. The behaviour on valid input stays the same; only the way the lines are found changes. Thanks to @Cherith1222 for #308: the
sd_line_breaknode and its per-builder rendering, the per-line gettext messages, the docs and the tests all stay as they are.#308 block-parsed the button text with
nested_parse, kept the result if it held only line blocks, and otherwise parsed the text again as inline markup. A parse has side effects that a fallback cannot undo, so text that looked like a line block at its outermost indent but was not only one was parsed twice. A blank line followed by a deeper bar, paragraph, directive or substitution definition, or a first line deeper than the rest, gave a spurious "Too many autonumbered footnote references" (leaving the reference unresolved), a "Block quote ends without a blank line" warning, a duplicate explicit target, or a substitution definition leaking into the page.The first commit finds the lines textually instead. Every non-blank line at the first line's indent must start with docutils' line-block marker, and a deeper line directly after a non-blank line continues the previous line. Each line is then parsed once with
inline_text, into a translatable inline of its own carrying that line's source and line number. A line's text is what docutils gives it: its continuation lines keep their indent relative to each other, and a bare bar's empty first line is dropped (this part landed in a later commit, after review). Any other text is parsed once as inline markup, as before #308. Text can no longer be parsed twice, and the block-quote unwrap, the system-message filter, the node move and the docutils < 0.22 source-info fallback all go away.What was measured, on Sphinx 7.4.7, 8.2.3 and 9.1.1:
|, a blank line between blocks, a nested bar, inline markup and a footnote,button-ref, a button in a dropdown, and an included file. A second page added buttons in table cells and list items, nested sphinx-design directives, and a no-break space after the bar.The second commit fixes an older line-number bug in the inline path. In RST,
content_offsetalready counts the lines before the directive, and the path added it to the directive's line anyway. For a button at line 4 of a page, a role error in its text was reported at line 9 instead of 6. Inside a dropdown it named a line past the end of the file. For a button in an included file, it was blamed on the including page, at the included line plus the include's offset. The .pot locations had the same errors. The text is now located at its first content line through the state machine, which is where docutils reports a paragraph's inline errors. The gettext regression fixture had recorded the wrong line (19 for text at line 11) and is corrected. MyST'scontent_offsetcounts lines from the directive, so the MyST arithmetic is unchanged; its warnings and .pot output were measured identical.The third commit adds a caveat to the docs about breaking button text with a MyST hard line break (a backslash at the end of the line). This works in HTML and LaTeX. The text builder, however, joins the lines ("Multi-linebutton text"), texinfo prints the raw break markup, and the gettext message contains that markup too. The note now says so, and points translated projects at the RST line block, which works from MyST inside an
eval-rstblock.The fourth commit pins what a label that itself starts with a bar does. In RST,
| Docs | API |is a one-line line block, so its leading|is consumed and the button reads "Docs | API |" (a bare|label is an empty line block, so an empty button, as on main). A literal leading bar is written\|. A test covers both forms, and the docs say so after the multi-line example.The later commits come from review. One keeps a continuation line's indent relative to the lines before it, as docutils does: the first commit stripped each continuation line, and kept the empty first line of a bare bar, which changed whitespace inside such lines. One pins each line-block line's own .pot location and message line, which no test had checked, including in an included file. One rewords three comments in #308's tests that still described the block parse; their assertions are unchanged. One keeps the text after a line's bar as docutils does: the splitter stripped any Unicode whitespace there, and counted it as indentation before a bar, so a line starting
|and a no-break or ideographic space lost that space; its tests also pin a bare bar's exact HTML. One pins that a line at the bar's indent without a bar is never a continuation, and that a line's message keeps its markup. The last says in the docs that the leading-bar rule is for a one-line label: a two-line label whose second line is plain text is not a line block, so its bar is shown.Gates: the full test suite passes on Sphinx 7.4.7, 8.2.3 and 9.1.1 (321 passed, 1 skipped), and
pre-commit run --all-filespasses.sphinx-build -nWof the docs is clean for html and man. Locally it is also clean for latex, built withoutsphinx.ext.imgconverterbecause ImageMagick is not installed here.