Repository navigation
Conversation
enumerate lists were converted to unordered lists: the items are added through the generic text path, which never sets ListItem.enumerated, so Markdown export rendered "- " bullets instead of "1.", "2.". Mark the direct items of an enumerate as enumerated. Numbering follows an item's position in its list, so each \item now yields exactly one list item: a nested list, and any paragraph after the first one or after a nested list, is added inside the item instead of beside it, as in the other backends. Resolves docling-project#4515 Signed-off-by: pandasuwu <suhani.m024@gmail.com>
|
✅ DCO Check Passed Thanks @pandasuwu, all your commits are properly signed off. 🎉 |
Merge Protections🔴 1 of 2 protections blocking · waiting on 👀 reviews
🔴 Require two reviewer for test updatesWaiting for
This rule is failing.When test data is updated, we require two reviewers
Show 1 satisfied protection🟢 Enforce conventional commitMake sure that we follow https://www.conventionalcommits.org/en/v1.0.0/
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
geoHeil
left a comment
There was a problem hiding this comment.
Please keep each source item as one list item when it contains a display equation or other content that flushes the text buffer. I confirmed the case below on c147215. All 124 LaTeX tests pass, but they do not cover this case.
|
@pandasuwu can you please follow up here? |
Display math, equation and quote environments, and links flush the text buffer, so an \item containing one was still split into several siblings in the list group, each taking a number in an enumerate: "1. First", the formula, "3. continuation", "4. Second". Create the list item first and add the whole \item content inside it; the leading text then becomes the text of the item. Every \item now adds exactly one child to its list, whatever it contains. Signed-off-by: pandasuwu <suhani.m024@gmail.com>
|
@PeterStaar-IBM Done, could u please see my reply to @geoHeil's review above |
The previous commit promoted the leading text of an item by adding it as a child and then deleting it, and dropped empty items the same way. `doc.delete_items()` rescans the whole document, so list conversion took quadratic time: an enumerate of 600 items took about 3 s instead of 0.03 s, and larger documents hit the parse timeout. `_process_nodes` now takes an optional `text_target`: the first text flushed while the target is still empty becomes its text, so no temporary item is created. Whitespace before the first `\item` no longer creates an item, and the remaining empty items are deleted in one call per list. The output is unchanged. Signed-off-by: pandasuwu <suhani.m024@gmail.com>
An empty `\item` (no content, or only a `\label`) added no list item, so every later item of an enumerate took a number one lower than in the typeset document: `1. One`, `2. Three`. Every `\item` now adds a list item, even an empty one. The content before the first `\item` is processed as before: whitespace, comments and macros such as `\setlength` add nothing, and stray text becomes a numbered item. The list code no longer deletes items. Signed-off-by: pandasuwu <suhani.m024@gmail.com>
…ine break
An item starting with `\footnote{...}` or `\newline` added that content as a
child before any text, so the item got no text of its own: `1. ` followed
by the note or an empty line and then the text.
A footnote is attached to the text, so the leading text still becomes the
text of the item and the footnote stays inside it. A `\newline` before any
content has nothing to break and is dropped.
Signed-off-by: pandasuwu <suhani.m024@gmail.com>
geoHeil
left a comment
There was a problem hiding this comment.
The speed regression is fixed: 600 items take 0.016 s on this head, versus 0.016 s on the base. All 135 LaTeX tests pass. One Markdown regression remains for an item that starts with a quote; see below.
Only the outer `_process_nodes` call of an item put the leading text into the list item. A quote, a `center`, another environment or a group processes its content in its own call, so an item starting with one got no text: it was exported as `1. ` followed by `First` on its own line, which a CommonMark parser reads as an empty item and a paragraph that takes in `2. Second`. The text buffer now fills any list item that has no text yet, and no children other than footnotes, whichever call flushes it, so the leading text becomes the text of the item however deeply it is nested. The `text_target` argument is no longer needed. Signed-off-by: pandasuwu <suhani.m024@gmail.com>
geoHeil
left a comment
There was a problem hiding this comment.
The simple quote case is fixed. The block-content and speed fixes also hold. All 138 LaTeX tests pass on 464562c. A leading paragraph break still leaves a text-only item empty; see below.
Ruff, pinned ty 0.0.33 (existing warning policy), Tach, and the lock check pass. The local line check fails on 10 unchanged files. CI fast checks pass; the merge protection still needs two approvals.
A paragraph between blank lines is added by `_process_chars_node` without
going through the text buffer, so an item starting with a blank line kept no
text: `\item`, a blank line, `First` exported as `1. ` followed by `First`,
which a CommonMark parser reads as an empty item and a paragraph that takes
in `2. Second`. The same happened inside a leading quote.
Both paths now add text through `_add_text`, which makes the first paragraph
the text of a list item that has none yet, without a document delete. Text
added to a list item is also split at blank lines, so a group such as
`{First\n\nContinuation}` does not put a blank line into the item text.
Signed-off-by: pandasuwu <suhani.m024@gmail.com>
| ) | ||
| nodes.append(n) | ||
|
|
||
| list_item = doc.add_list_item( |
There was a problem hiding this comment.
[P2] Preserve valid Markdown when a block starts an item.
Reproduce with:
\begin{enumerate}
\item [x=1] First
\item Second
\end{enumerate}
On this head, the first ListItem has the formula and First as children, but Markdown is:
1.
$$x=1$$
First
2. Second
A CommonMark parser reads an empty item, so the item content and numbering are not preserved. Please add a regression test and fix the serializer path, or link the required docling-core fix and keep this PR blocked until the output is valid.
There was a problem hiding this comment.
Thanks @geoHeil, fixed in 75db32d. A display formula that starts an item now goes on the line of the item: an inline group, first in the item, that also takes the text up to the next blank line or other content, as the Word backend does for an equation in a paragraph. \item \[x=1\] First now exports as 1. $x=1$ First\n2. Second, which a CommonMark parser reads as one list with two items.
test_latex_enumerate_item_starting_with_formula covers \[...\] and equation, and checks the line of the item and that Markdown. test_latex_enumerate_line_of_item_starting_with_formula checks that a nested list ends that line and a footnote does not. Both fail on a9f31eb.
There was a problem hiding this comment.
Thanks for the update, @pandasuwu. I checked 75db32dd. The reported formula case now exports correctly. All 145 LaTeX tests pass. The speed fix also holds: 600 items take 0.023 s on this head and 0.022 s on the base, in the same environment.
Blocker (P2): this issue still affects other block content at the start of an item. For example:
\begin{enumerate}
\item \begin{theorem}First\end{theorem}
\item Second
\end{enumerate}The Markdown export is:
1.
**Theorem.**
First
2. Secondmarko reads one empty list item. It puts the theorem text and 2. Second in a paragraph outside the list. Thus, the second item loses its numbering. A check for two parsed list items passes on base a1c5ff2a and fails on this head. A leading verbatim block also leaves an empty item and puts the code outside it.
The document tree and HTML export keep the content inside the first item. The Markdown export must keep that structure too. Please cover these block types and add a test that parses the Markdown and checks both list items. A shared serializer fix may cover these cases. I still recommend changes before merge.
Validation: Python 3.10.19, locked docling-core 2.99.0; pytest -q -p no:tach tests/test_latex passes. Ruff, ty (with the current warning policy), Tach, dprint, and the lock check pass. make check stops at the line limit check on 10 unchanged files; the same check fails on the base. make validate reports no changed files. Hosted CI jobs pass; the merge rule still needs two approvals.
There was a problem hiding this comment.
Thanks @geoHeil. The cause is in the docling-core Markdown serializer: it indents only nested lists, so any other block child of a list item starts at column 0 and ends the item. A backend change can only work around this for content that fits on the line of the item, as for a formula. It cannot do that for a theorem, code or a table. So I opened docling-project/docling-core#888, which indents the block children of a list item by the width of the marker.
With that branch, each of these parses with marko (GFM) as one ordered list with two items, with the block inside item 1:
\item \begin{theorem}First\end{theorem}\item \begin{verbatim}code\end{verbatim}\item \begin{lstlisting}code\end{lstlisting}\item \begin{tabular}{c}a\\b\end{tabular}\item First \begin{verbatim}code\end{verbatim}
For example, the theorem case now exports as:
1.
**Theorem.**
First
2. Secondcdb2ee1 adds test_latex_enumerate_markdown_of_item_with_block. It parses the Markdown and checks one ordered list starting at 1, the blocks of the first item, and 2. Second. With the locked docling-core 2.99.0, all five cases fail, so the test is xfail(strict=True) and refers to the core PR. This PR stays blocked on that fix. When docling-core is bumped, I will:
- remove the xfail marker (strict mode makes the bump fail until I do),
- update the exact Markdown in five list tests, where the continuation line of an item becomes indented (for example
1. First\n Continuation\n2. Second), - regenerate
2305.03393_main.tex.md, where the "Cross cell rule" text becomes indented inside rule 3.
…item An item whose first content is a display formula, such as `\item \[x=1\] First`, had the formula and the text as block children of an item without text. The Markdown export showed `1. ` followed by `$$x=1$$` and `First` on their own lines, which a CommonMark parser reads as an empty item; the next item then loses its numbering. Such a formula now opens the line of the item: an inline group, before any footnotes, that also takes the text up to the next blank line or the next other content, as the Word backend does for an equation in a paragraph. The export is `1. $x=1$ First`. Items that start with text are unchanged. Signed-off-by: pandasuwu <suhani.m024@gmail.com>
A theorem, verbatim, lstlisting or tabular at the start of an enumerate item is exported as an empty item followed by the block at column 0, so a CommonMark parser ends the list. The fix is in docling-core (docling-project/docling-core#888); the test is xfail until it is bumped. Signed-off-by: pandasuwu <suhani.m024@gmail.com>
AI triage (advisory)Checked commit The PR fixes the LaTeX backend to set Topic labels added: CI safety: no concern found. A maintainer must still approve CI. 2 related pull request(s) in the same code
Reference data changes
Assessment: expected. The The |
|
Thanks for the updates. I rechecked One existing blocker remains: block content can leave its list item in Markdown. I ran the five cases in docling-core #888 is still open. Please keep this PR on hold until that fix is released and required here. Then remove the expected-failure marker and update the affected tests and reference output. The backend changes otherwise look good in the cases I checked. Ruff, ty with the current warning policy, Tach, dprint, and the lock check pass. |
|
Hello @geoHeil, I saw that docling-project/docling-core#888 will be dropped for docling-project/docling-core#830 casue the latter handles two more inline cases. So this PR now has to wait until #830 is merged and released. |
enumeratelists were converted to unordered lists (#4515). The LaTeX backend adds list items through the generic text path (doc.add_text(label=DocItemLabel.LIST_ITEM)), which creates them withenumerated=False, and nothing in the backend set it forenumerate. Markdown export therefore rendered-bullets, and the repository's ground truth recorded it:example_01.tex.mdshows the items "First item in ordered list" and "Second item in ordered list" as bullets.Input:
Before:
After:
Fix. The items of an
enumerateare created withenumerated=True; a nested list keeps its own kind.That alone is not enough, because docling-core numbers an item by its position among the children of its list group, and the backend could add more than one child per
\item. Anything that flushes the text buffer started a new sibling in the list:\[...\],equation), a quote, or a link (\href), together with the text after it.With numbering on, each of these would take a number, e.g.
1. First, the formula,3. continuation,4. Secondfor\item First \[x=1\] continuation. The new_process_list_itemtherefore creates the list item first and adds the whole content of the\iteminside it; the leading text then becomes the text of the item itself. Every\itemnow adds exactly one child to its list, whatever it contains. This is the structure the Markdown and HTML backends already produce for nested lists.The first paragraph of an item is written into the list item as it is added, whether it comes from the text buffer or is a paragraph between blank lines, and whichever call adds it. So it also becomes the text of the item after a leading blank line or when it is nested in a quote, another environment or a group, and no temporary item is created and deleted. Text added to a list item is split at blank lines, since a blank line inside the item text would end the list in a Markdown export.
doc.delete_items()rescans the whole document, and calling it per item made list conversion quadratic; the list code no longer deletes items.backend.convert()on anenumerateof 600 items takes 0.03 s, the same as on main.An empty
\item(no content, or only a\label) used to add no list item, so every later item would take a number one lower than in the typeset document. Every\itemnow adds a list item, even an empty one. The content before the first\itemis processed as before: whitespace, comments and macros such as\setlengthadd nothing, and stray text becomes a numbered item.An item that starts with
\footnote{...}or\newlinekeeps its text: a footnote is attached to the text and stays inside the item, and a\newlinebefore any content has nothing to break and is dropped.A display formula that starts an item, as in
\item \[x=1\] First, goes on the line of the item: an inline group that also takes the text up to the next blank line or other content, as the Word backend does for an equation in a paragraph. The Markdown export is1. $x=1$ Firstinstead of1.followed by the formula on its own line, which a CommonMark parser read as an empty item.Tests.
test_latex_list_enumerateonly assertedlen(list_items) >= 2; it now checks the items are enumerated and the Markdown is1. Alpha\n2. Beta. Nine new tests, all failing on main:test_latex_enumerate_numbering_with_nested_contentcovers a nested list, text after it, a second paragraph and a nestedenumerate, and checks the outer list is numbered 1 to 4.test_latex_enumerate_item_with_block_contentcovers an item containing display math, anequation, an\hrefand aquote, and checks the list has two direct items numbered 1 and 2.test_latex_enumerate_item_starting_with_footnote_or_newlinechecks that an item starting with a footnote or a\newlinekeeps its text, with the footnote inside the item.test_latex_enumerate_item_starting_with_nested_textcovers an item starting with a quote, a quote in a group and acenter, and checks the Markdown is1. First\n2. Second.test_latex_enumerate_item_starting_with_paragraph_breakcovers an item whose first paragraph follows a blank line, on its own, in a quote and in a group, and checks the item text, its children and the Markdown1. First\nContinuation\n2. Second.test_latex_enumerate_item_starting_with_formulacovers an item starting with\[...\]and with anequation, and checks the line of the item and the Markdown1. $x=1$ First\n2. Second.test_latex_enumerate_line_of_item_starting_with_formulachecks that a nested list ends the line of such an item and a footnote does not.test_latex_enumerate_empty_item_keeps_numberingcovers an empty\itemand one with only a\label, and checks the item after them is numbered 4.test_latex_enumerate_content_before_first_itemcovers a comment,\setlength, a\labeland stray text before the first\item.Ground truth. Regenerated with
DOCLING_GEN_TEST_DATA=1; only list structure and numbering change:example_01.tex: the ordered list is numbered (1.,2.).2305.03393(OTSL): the six syntax rules are numbered 1 to 6. The "Cross cell rule" item has a blank line between its title and its text, which used to produce a separate list item; that text is now inside rule 3, so the rules are not numbered 1 to 7.pytest tests/test_latex: all other tests pass, andtest_e2e_latex_conversionsregenerates the committed ground truth unchanged, also when merged with current main. The core test suite has the same results as on main apart from the new tests.prek runon the changed files: passed.Issue resolved by this Pull Request:
Resolves #4515
Checklist: