Skip to content

Commit 0bbf04a

Browse files
committed
perf(md013): optimize standalone link check using binary search on LintContext
Replaces regex-based checks with O(log N) binary search over pre-parsed links in LintContext. This includes a fallback check for unbracketed link/image destinations containing spaces (e.g. Jekyll/Liquid template paths) which are not parsed as single links by pulldown-cmark. If the exact range lookup fails, we check for a partial parsed link starting at the line's beginning and verify that the remainder is a single parenthesized group. Note: This drops the standalone link exemption for link-shaped lines inside fenced code blocks, as LintContext does not index links inside fenced code blocks. Assisted-by: Antigravity with Gemini
1 parent 4e70093 commit 0bbf04a

3 files changed

Lines changed: 271 additions & 159 deletions

File tree

src/lint_context/link_parser.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,10 @@ pub(super) fn parse_links_images_pulldown<'a>(
141141
content,
142142
options,
143143
Some(|link: BrokenLink<'_>| {
144+
let span_end = extend_collapsed_byte_end(content, link.link_type, link.span.end);
144145
broken_links.push(BrokenLinkInfo {
145146
reference: link.reference.to_string(),
146-
span: link.span.clone(),
147+
span: link.span.start..span_end,
147148
link_type: link.link_type,
148149
});
149150
None

src/rules/md013_line_length.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ impl Rule for MD013LineLength {
503503
// Lines whose only content is a link/image are exempt.
504504
// After stripping list markers, blockquote markers, and emphasis,
505505
// if only a link or image remains, there is no way to shorten it.
506-
if is_standalone_link_or_image_line(line) {
506+
if is_standalone_link_or_image_line(ctx, line_number) {
507507
continue;
508508
}
509509

@@ -894,7 +894,7 @@ impl MD013LineLength {
894894
// A standalone link/image line is exempt from MD013 (non-strict mode),
895895
// so it must end the blockquote paragraph rather than be absorbed into
896896
// it, mirroring the top-level paragraph reflow boundary.
897-
|| (!strict && is_standalone_link_or_image_line(content))
897+
|| (!strict && is_standalone_link_or_image_line(ctx, line_num))
898898
}
899899

900900
#[allow(clippy::too_many_arguments)]
@@ -1270,7 +1270,7 @@ impl MD013LineLength {
12701270
body_text.starts_with('[') && body_text.contains("]:") && LINK_REF_PATTERN.is_match(body_text);
12711271
let raw_marker_line = lines[start_idx];
12721272
let body_is_unwrappable = is_link_ref_def
1273-
|| (!config.strict && is_standalone_link_or_image_line(raw_marker_line))
1273+
|| (!config.strict && is_standalone_link_or_image_line(ctx, start_idx + 1))
12741274
|| (!config.strict && is_html_only_line(raw_marker_line));
12751275
if body_is_unwrappable {
12761276
return (None, next_idx);
@@ -1538,7 +1538,7 @@ impl MD013LineLength {
15381538
|| is_link_ref_def
15391539
|| ctx.line_info(line_num).is_some_and(|info| info.is_div_marker)
15401540
|| is_html_only_line(lines[i])
1541-
|| (!config.strict && is_standalone_link_or_image_line(lines[i]))
1541+
|| (!config.strict && is_standalone_link_or_image_line(ctx, line_num))
15421542
{
15431543
i += 1;
15441544
continue;
@@ -2413,7 +2413,7 @@ impl MD013LineLength {
24132413
// standalone link/image lines are exempt when strict mode is off.
24142414
// Also checks content after stripping list markers, since list item
24152415
// continuation lines may contain link ref defs.
2416-
let is_exempt_line = |raw_line: &str, _line_num: usize| -> bool {
2416+
let is_exempt_line = |raw_line: &str, line_num: usize| -> bool {
24172417
let trimmed = raw_line.trim();
24182418
// Link reference definitions: always exempt
24192419
if trimmed.starts_with('[') && trimmed.contains("]:") && LINK_REF_PATTERN.is_match(trimmed) {
@@ -2431,7 +2431,7 @@ impl MD013LineLength {
24312431
}
24322432
}
24332433
// Standalone link/image lines: exempt when not strict
2434-
if !config.strict && is_standalone_link_or_image_line(raw_line) {
2434+
if !config.strict && is_standalone_link_or_image_line(ctx, line_num) {
24352435
return true;
24362436
}
24372437
// HTML-only lines: exempt when not strict
@@ -3347,7 +3347,7 @@ impl MD013LineLength {
33473347
|| ctx.line_info(next_line_num).is_some_and(|info| info.is_div_marker)
33483348
|| is_html_only_line(next_line)
33493349
|| self.line_in_multiline_math_block(next_line_num, ctx)
3350-
|| (!config.strict && is_standalone_link_or_image_line(next_line))
3350+
|| (!config.strict && is_standalone_link_or_image_line(ctx, next_line_num))
33513351
{
33523352
break;
33533353
}

0 commit comments

Comments
 (0)