Repository navigation
Conversation
PR HealthLicense Headers ✔️
All source files should start with a license header. Unrelated files missing license headers
This check can be disabled by tagging the PR with Breaking changes ✔️
This check can be disabled by tagging the PR with Unused Dependencies ✔️
For details on how to fix these, see dependency_validator. This check can be disabled by tagging the PR with
Coverage
|
| File | Coverage |
|---|---|
| pkgs/markdown/lib/src/inline_syntaxes/footnote_ref_syntax.dart | 💚 100 % |
| pkgs/markdown/lib/src/inline_syntaxes/link_syntax.dart | 💔 98 % ⬇️ 0 % |
This check for test coverage is informational (issues shown here will not fail the PR).
This check can be disabled by tagging the PR with skip-coverage-check.
API leaks ✔️
The following packages contain symbols visible in the public API, but not exported by the library. Export these symbols or remove them from your publicly visible API.
| Package | Leaked API symbol | Leaking sources |
|---|
This check can be disabled by tagging the PR with skip-leaking-check.
Changelog Entry ✔️
| Package | Changed Files |
|---|
Changes to files need to be accounted for in their respective changelogs.
This check can be disabled by tagging the PR with skip-changelog-check (for example, for non-user-visible changes when the package version is already at -wip).
There was a problem hiding this comment.
Code Review
This pull request optimizes reference link parsing in LinkSyntax by avoiding eager slicing and normalization of link labels, and enforces the 999-character CommonMark limit on reference link labels. The reviewer feedback correctly points out that the 999-character limit applies strictly to the link label, not the link text, and recommends removing the allowLinkBuilder parameter and its associated restrictions on link text length. Additionally, the reviewer suggests improving DartDoc comments to align with Effective Dart guidelines and adding test cases to verify the resolution of links with long text but valid short labels.
| Map<String, LinkReference> linkReferences, { | ||
| required List<Node> Function() getChildren, | ||
| bool allowLinkBuilder = true, | ||
| }) { |
There was a problem hiding this comment.
| String? title, { | ||
| bool? secondary, | ||
| bool allowLinkBuilder = true, | ||
| }) { |
| getChildren: getChildren, | ||
| allowLinkBuilder: allowLinkBuilder, | ||
| ); |
| test('reference link labels over 999 characters are not resolved', () { | ||
| final label999 = 'a' * 999; | ||
| final label1000 = 'a' * 1000; | ||
| expect( | ||
| markdownToHtml('[$label999]: /url\n\n[text][$label999]'), | ||
| '<p><a href="/url">text</a></p>\n', | ||
| ); | ||
| expect( | ||
| markdownToHtml('[$label999]: /url\n\n[text][$label1000]'), | ||
| '<p>[text][$label1000]</p>\n', | ||
| ); | ||
| expect( | ||
| markdownToHtml('[$label1000]', linkResolver: (name, [_]) => Text(name)), | ||
| '<p>[$label1000]</p>\n', | ||
| ); | ||
| }); |
There was a problem hiding this comment.
Add test cases to verify that full reference links with link text over 999 characters (but with a valid label <= 999 characters) are correctly resolved, both with standard link references and with custom link resolvers.
test('reference link labels over 999 characters are not resolved', () {
final label999 = 'a' * 999;
final label1000 = 'a' * 1000;
final text1000 = 'b' * 1000;
expect(
markdownToHtml('[$label999]: /url\n\n[text][$label999]'),
'<p><a href="/url">text</a></p>\n',
);
expect(
markdownToHtml('[$label999]: /url\n\n[text][$label1000]'),
'<p>[text][$label1000]</p>\n',
);
expect(
markdownToHtml('[$label1000]', linkResolver: (name, [_]) => Text(name)),
'<p>[$label1000]</p>\n',
);
expect(
markdownToHtml('[$label999]: /url\n\n[$text1000][$label999]'),
'<p><a href="/url">$text1000</a></p>\n',
);
expect(
markdownToHtml('[$text1000][label]', linkResolver: (name, [title]) => Text('$name:$title')),
'<p>label:$text1000</p>\n',
);
});4d9d2d4 to
0628223
Compare
0628223 to
a79e416
Compare
davidmorgan
left a comment
There was a problem hiding this comment.
The label allocation is now deferred, and the 999-character cap matches CommonMark. Head's suite passes, and the new regression test fails on the base lib as expected. Output on a set of reference, image, emphasis, footnote and case-folding inputs is unchanged except for labels over 999 characters.
Nested brackets are still quadratic when no link resolves. nodeIndex at inline_parser.dart:157 is computed with _tree.lastIndexWhere before close(), so with no matching reference every ] scans back over the whole tree. That file isn't in the diff, so the comment goes here. nodeIndex is only needed on success: computing it inside getChildren, with a fallback on the success path when getChildren never ran, removes the scan. Timings for '[' * n + 'x' + ']' * n with no references:
n this PR with nodeIndex deferred
8000 326 ms 64 ms
16000 1124 ms 18 ms
32000 5628 ms 25 ms
The timing patch was not run against the suite.
Checked: base eee87b1 and head a79e416; the suite, analyze and format; head's regression tests over the base lib; output diffs on both; and the timings above.
| ) { | ||
| // A link label can have at most 999 characters inside the square | ||
| // brackets. See https://spec.commonmark.org/0.30/#link-label. | ||
| if (labelEnd <= labelStart || labelEnd - labelStart > 999) { |
There was a problem hiding this comment.
Behaviour change: x [] y with a linkResolver used to call the resolver with an empty name, and render its result. Now the resolver isn't called and [] stays literal. That matches CommonMark, since a label needs a non-whitespace character, but it may be worth a CHANGELOG line next to the 999 entry.
| // TODO(srawlins): only check 999 characters, for performance reasons? | ||
| // A link label can have at most 999 characters inside the square | ||
| // brackets. See https://spec.commonmark.org/0.30/#link-label. | ||
| if (parser.pos - start > 999) return null; |
There was a problem hiding this comment.
The inline cap counts raw characters. The definition side, LinkParser.parseLabel, still miscounts escapes, so [<998 a>\[]: /url is still accepted as a definition, as before. A shortcut reference to it, [<998 a>\[], used to link; now it's over the inline cap and renders as literal text. It's an edge case, but the two sides now disagree at the boundary. Counting the same way in parseLabel would line them up.
Defer slicing and normalizing reference link labels in
LinkSyntax.closeuntil a reference link or customlinkBuilderactually needs the label, and enforce CommonMark's 999-character limit on reference link labels.