Skip to content

fix(markdown): combine adjacent Text nodes when the element is last in its list - #2598

Merged
srawlins merged 1 commit into
dart-lang:mainfrom
hkarmoush:markdown-fix-adjacent-text-combine-2037
Nov 11, 2026
Merged

srawlins merged 1 commit into
dart-lang:mainfrom
hkarmoush:markdown-fix-adjacent-text-combine-2037

Conversation

@hkarmoush

@hkarmoush hkarmoush commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes #2037.

_combineAdjacentText in inline_parser.dart looped with for (var i = 0; i < nodes.length - 1; i++), which never visits the last index of a node list. As a result, when an Element (e.g. <strong>, <em>) happened to be the last node in a list, _combineAdjacentText never recursed into its children, so adjacent Text nodes inside it were never merged.

This is invisible in HTML output, since adjacent text nodes render identically whether merged or not — which is likely why it went unnoticed. It only breaks AST consumers who inspect node structure directly, as the issue reporter did while testing a custom inline syntax.

The reason registering StrikethroughSyntax (or any custom inline syntax) surfaces this even with no ~ in the input: InlineParser switches to a stricter word-matching accelerator regex ([A-Za-z0-9]+(?=\s|$) instead of [ \tA-Za-z0-9]*[A-Za-z0-9](?=\s|$)) whenever document.hasCustomInlineSyntaxes is true. The stricter regex doesn't absorb leading whitespace into its match, so a run like " inside bold." gets split into two Text nodes (" " and "inside bold.") instead of one. Normally _combineAdjacentText would merge them back together, but this pre-existing recursion bug is what exposes the split, since in this exact input the enclosing <strong> element ends up as the last (and only) top-level node.

Fix

Changed the loop bound to i < nodes.length (visiting every index, including the last) and added an explicit i + 1 < nodes.length bounds check on the merge branch, since it now needs to look ahead safely at the final index too.

Test plan

@google-cla

google-cla Bot commented Sep 17, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request fixes a bug in the markdown inline parser where adjacent Text nodes were not combined if they were children of the last node in a list. The fix updates _combineAdjacentText to iterate through all nodes and includes a regression test to verify the behavior. There are no review comments, and the changes look correct and well-tested.

@hkarmoush

Copy link
Copy Markdown
Contributor Author

@googlebot I signed it!

@hkarmoush

Copy link
Copy Markdown
Contributor Author

Two CI notes for whoever gets to this next:

@hkarmoush
hkarmoush force-pushed the markdown-fix-adjacent-text-combine-2037 branch from dc0e1ef to d7a92d5 Compare October 4, 2026 13:33
@hkarmoush
hkarmoush force-pushed the markdown-fix-adjacent-text-combine-2037 branch from d7a92d5 to b195da0 Compare October 26, 2026 00:34
@srawlins

Copy link
Copy Markdown
Member

Sorry, more conflicts. This package is under active development all of a sudden 😅

@hkarmoush
hkarmoush force-pushed the markdown-fix-adjacent-text-combine-2037 branch from b195da0 to 4332f14 Compare November 4, 2026 13:37
…n its list

_combineAdjacentText looped only up to nodes.length - 1, so it never
recursed into an Element sitting at the last index of a node list,
leaving its adjacent Text children unmerged. This was invisible in
HTML output (adjacent text nodes render the same either way) but
broke AST consumers, and only became reachable in the common case of
a single top-level element once a custom inline syntax was registered
(which switches on a stricter word-matching regex that splits plain
text into more Text nodes than usual).

Fixes dart-lang#2037.
@hkarmoush
hkarmoush force-pushed the markdown-fix-adjacent-text-combine-2037 branch from 4332f14 to 11ceb98 Compare November 4, 2026 13:38
@hkarmoush

Copy link
Copy Markdown
Contributor Author

@srawlins this is rebased and green again after the last round of conflicts. Whenever you get a chance to merge it, thanks!

@srawlins
srawlins merged commit 589d844 into dart-lang:main Nov 11, 2026
25 checks passed
copybara-service Bot pushed a commit to dart-lang/sdk that referenced this pull request Nov 12, 2026
Revisions updated by `dart tools/rev_sdk_deps.dart`.

core (https://github.com/dart-lang/core/compare/36f57a9..fa0c6b7):
  fa0c6b77  Fri Nov 6 12:56:58 2026 +0100  Michael Thomsen  Release os_detect 2.0.4 (dart-lang/core#1038)

dartdoc (https://github.com/dart-lang/dartdoc/compare/03ec6df..7e2508c):
  7e2508c6  Wed Nov 11 14:52:22 2026 +0200  Sergey G. Grekhov  Add constructor doc comment tests for enums and extension types (dart-lang/dartdoc#4316)
  03dfb467  Mon Nov 9 11:00:32 2026 +0200  Sergey G. Grekhov  `#3330`. Split test for parameters to separate tests (dart-lang/dartdoc#4314)
  23302d53  Mon Nov 9 10:56:34 2026 +0200  Sergey G. Grekhov  Add tests for primary constructors (dart-lang/dartdoc#4313)

ecosystem (https://github.com/dart-lang/ecosystem/compare/54e1673..1d2b5ff):
  1d2b5ff  Tue Nov 10 09:04:24 2026 -0800  Kevin Moore  feat(repo_manage): add --replace-pattern to report changelog (dart-lang/ecosystem#482)

http (https://github.com/dart-lang/http/compare/4646fe6..2bac218):
  2bac218  Fri Nov 6 17:49:23 2026 -0800  dart-ecosystem-bot[bot]  Tidy: package:cronet_http
  cdf967c  Fri Nov 6 15:40:49 2026 -0800  Brian Quinlan  chore(groundkeeper): use stable for flutter (dart-lang/http#2066)
  6582177  Thu Nov 5 13:04:35 2026 -0800  Brian Quinlan  doc: use timeout as an abort example (dart-lang/http#2062)

native (https://github.com/dart-lang/native/compare/6287745..b21ef2d):
  b21ef2da3  Wed Nov 11 16:07:34 2026 +0530  dhairya05  [json_syntax_generator] Pass the parameter name to ArgumentError.value (dart-lang/native#3787)
  20a607e48  Wed Nov 11 07:28:03 2026 -0300  Daco Harkes  [code_assets] [hooks] [hooks_runner] Adopt `.g.dart` for generated files (dart-lang/native#3790)
  cc974ddec  Wed Nov 11 19:59:39 2026 +1100  Liam Appelbe  [objective_c] Fix `isValidObject` bug on iOS simulator (dart-lang/native#3788)
  f7edbdee3  Sun Nov 8 13:18:25 2026 +1100  Liam Appelbe  [jni] Fix possible receiver NPEs (dart-lang/native#3784)
  0ca7d6745  Fri Nov 6 10:20:21 2026 -0300  Daco Harkes  [code_assets] [hooks_runner] [native_toolchain_c] Prepare for release (dart-lang/native#3781)
  baad15ab4  Fri Nov 6 02:56:52 2026 +0100  Kravchenko Igor  [objective_c] Link Foundation in the native support library (dart-lang/native#3782)

shelf (https://github.com/dart-lang/shelf/compare/9946de5..b175e9f):
  b175e9f  Tue Nov 10 09:14:31 2026 +0530  Muhammad Uvesh  shelf_static: ignore invalid If-Modified-Since headers (dart-lang/shelf#557)
  02b5f94  Sat Nov 7 23:09:28 2026 +0530  dhairya05  Fix typo in shelf_router docs (dart-lang/shelf#556)

test (https://github.com/dart-lang/test/compare/2a9e017..3533ae1):
  3533ae1e  Tue Nov 10 15:06:08 2026 -0800  Nate Bosch  Never retry tearDownAll (dart-lang/test#2815)
  5680b0b1  Fri Nov 6 12:52:11 2026 +0100  Sigurd Meldgaard  Add globalSetup and addGlobalTearDown APIs for cross-suite setup and teardown (dart-lang/test#2714)
  b8dc888a  Thu Nov 5 18:11:37 2026 -0800  Nate Bosch  [checks_codegen] Skip private fields and fields referencing private types (dart-lang/test#2782)
  66e062c4  Thu Nov 5 16:05:36 2026 -0800  Nate Bosch  [checks_codegen] Generate full field types with imports (dart-lang/test#2781)

tools (https://github.com/dart-lang/tools/compare/727fd9c..589d844):
  589d844c4  Wed Nov 11 20:27:32 2026 +0300  Hasan Armoush  fix(markdown): combine adjacent Text nodes when the element is last in its list (dart-lang/tools#2598)

Change-Id: I13acaa7378c86f2c33d88f3c4b163acf2b48ff08
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/576820
Commit-Queue: Nate Bosch <[email protected]>
Reviewed-by: Nicholas Shahan <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

enabling StrikethroughSyntax changes the parsing of unrelated strings

2 participants