Skip to content

fix(md): keep backslash escapes inside the surrounding text - #4991

Open
Krina-Paghadar wants to merge 2 commits into
docling-project:mainfrom
Krina-Paghadar:fix/md-backslash-escapes
Open

Krina-Paghadar wants to merge 2 commits into
docling-project:mainfrom
Krina-Paghadar:fix/md-backslash-escapes

Conversation

@Krina-Paghadar

@Krina-Paghadar Krina-Paghadar commented Oct 23, 2026 •

Copy link
Copy Markdown

Issue resolved by this Pull Request:
Resolves #4990

marko parses a backslash escape (snake\_case, \$5, 1\.) as a Literal node between two RawText nodes. The Markdown backend made one text item per node and wrapped them in an inline group, and the exporters join the items of an inline group with a space. The text came out as snake _ case.

This change merges RawText/Literal runs that touch each other into one run (_merge_adjacent_text_runs) before the items are created. An escaped & stays literal text. Inline code and emphasis stay separate items.

Checklist:

  • Documentation has been updated, if necessary.
  • Examples have been added, if necessary.
  • Tests have been added, if necessary.

Review evidence:

Before: snake\_case here exports as snake \_ case here; 1\. not a list exports as 1 . not a list. After: snake_case here, 1. not a list.

Checks: the new test test_backslash_escapes_stay_in_their_text_run fails without the change and passes with it. In tests/test_backend_markdown.py, 40 pass and 3 fail, and the same 3 fail on main (docling-core 2.104.0, see #4982). ruff check and ruff format pass.

Marko splits an escaped character (snake\_case, \$5, 1\.) into its own
Literal node. Each node became a separate text item of an inline group and
the exporters join those items with a space, so the text came out as
"snake _ case" and "1 . not a list". Merge text runs that touch each other
before the items are created.

Signed-off-by: Krina-Paghadar <krina007.paghadar@gmail.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 23, 2026 10:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@github-actions

Copy link
Copy Markdown
Contributor

✅ DCO Check Passed

Thanks @Krina-Paghadar, all your commits are properly signed off. 🎉

@mergify

mergify Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Merge Protections

🔴 1 of 2 protections blocking · waiting on 👀 reviews

Protection Waiting on
🔴 Require two reviewer for test updates 👀 reviews
🟢 Enforce conventional commit —

🔴 Require two reviewer for test updates

Waiting for

  • #approved-reviews-by >= 2
This rule is failing.

When test data is updated, we require two reviewers

  • #approved-reviews-by >= 2

Show 1 satisfied protection

🟢 Enforce conventional commit

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?(!)?:

@github-actions github-actions Bot added ai:ci-safe AI triage found no CI-safety concern (advisory) bug Something isn't working markdown issue related to markdown backend labels Oct 23, 2026
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

AI triage (advisory)

Checked commit 48f1cf3de3. The analysis read the diff only. It did not run code from this pull request.

The PR fixes backslash escapes in the Markdown backend. It adds _merge_adjacent_text_runs, which merges consecutive RawText/Literal AST nodes into one node before items are created, so snake\_case exports as snake_case instead of snake _ case.

CI safety: no concern found. A maintainer must still approve CI.

3 related pull request(s) in the same code
PR State Signal Assessment
#4935 open changes the same code related: PR 4935 fixes GFM task-list checkbox markers in md_backend.py and touches the same test file, but solves a different problem with a different code change.
#4848 open changes the same code related: PR 4848 rewrites HTML-block handling in md_backend.py and adds many new tests, but it targets a completely different problem and code path.
#4984 closed also references issue #4982 related: PR 4984 also references issue #4982 and changes md_backend.py, but it fixes HTML-block restoration after docling-core escaping, not backslash escapes.

Reference data changes

Change Files
text 4
  • text tests/data/md/groundtruth/escaped_characters.md.json: item attributes changed
  • text tests/data/md/groundtruth/inline_and_formatting.md.yaml: +91 / -91 lines
  • text tests/data/md/groundtruth/mixed_without_h1.md.md: +1 / -1 lines
  • text tests/data/md/groundtruth/mixed_without_h1.md.yaml: +2 / -2 lines

Assessment: expected. The fix merges previously split text nodes, so existing reference outputs that recorded the wrong spaced text (e.g. snake _ case) are updated to the correct merged text. The four changed reference files match what the new merge logic would produce.

The ai:* labels are advisory. They do not approve CI and they do not add tests:full. A new push runs this check again.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI first review (advisory): no blocker found

This review covers the full diff at 57075d90dd. It read the code and did not run it.

The PR fixes a real bug: marko splits backslash escapes into separate Literal nodes, causing exporters to add spaces around them. The merge-before-walk approach is correct. The main thing to check is the &amp; round-trip: a Literal('&') node is stored as &amp; in the merged RawText so that the later unescape() call at md_backend.py:884 converts it back to &; the test at line 1333 confirms this path.

A maintainer makes the merge decision. This review never approves the pull request.

@github-actions github-actions Bot added the ai:review-lgtm AI first review found no blocker or major issue (advisory) label Oct 23, 2026
@Krina-Paghadar

Copy link
Copy Markdown
Author

Ready for review. The 3 failing Markdown tests also fail on main with docling-core 2.104.0 (see #4982). Happy to adjust anything.

@codecov

codecov Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.90909% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
docling/backend/md_backend.py 90.90% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@PeterStaar-IBM

Copy link
Copy Markdown
Member

@Krina-Paghadar we needto fix the regression-tests

The groundtruth held the old output of the backend: "2 . strange" for an
escaped marker, and inline groups numbered in the old order. Regenerated with
docling-core 2.103.1 (the locked version) and DOCLING_GEN_TEST_DATA=1. The
only content change is "2. strange" and the marker of an ordered list item
that has an escaped character. The other lines are renumbered group
references.

Signed-off-by: Krina-Paghadar <krina007.paghadar@gmail.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added ai:ci-safe AI triage found no CI-safety concern (advisory) and removed ai:ci-safe AI triage found no CI-safety concern (advisory) ai:review-lgtm AI first review found no blocker or major issue (advisory) labels Oct 25, 2026
@Krina-Paghadar

Krina-Paghadar commented Oct 25, 2026 •

Copy link
Copy Markdown
Author

Thanks @PeterStaar-IBM. The stored groundtruth still held the old buggy output (2 . strange). I regenerated it with the locked docling-core 2.103.1 (DOCLING_GEN_TEST_DATA=1) and pushed it in 48f1cf3. The content changes are 2. strange and one ordered list item that now gets the marker 1.. The rest is renumbered group references. All Markdown tests pass locally with the locked version. Could you please re-run the CI?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai:ci-safe AI triage found no CI-safety concern (advisory) bug Something isn't working markdown issue related to markdown backend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Markdown backend: backslash escapes split the text, snake\_case is exported as snake _ case

3 participants