|
| 1 | +from bisect import bisect_left |
1 | 2 | import re |
2 | 3 | from typing import Optional, List, Tuple, Match, Pattern, Set |
3 | 4 | import string |
@@ -256,11 +257,11 @@ def parse_ref_link(self, m: Match[str], state: BlockState) -> Optional[int]: |
256 | 257 |
|
257 | 258 | assert href_pos is not None |
258 | 259 |
|
259 | | - _blank = self.BLANK_LINE.search(state.src, href_pos) |
260 | | - if _blank: |
261 | | - max_pos = _blank.start() |
262 | | - else: |
| 260 | + blank_pos = _find_next_blank_line(state, href_pos, self.BLANK_LINE) |
| 261 | + if blank_pos is None: |
263 | 262 | max_pos = state.cursor_max |
| 263 | + else: |
| 264 | + max_pos = blank_pos |
264 | 265 |
|
265 | 266 | title, title_pos = parse_link_title(state.src, href_pos, max_pos) |
266 | 267 | if title_pos: |
@@ -537,6 +538,19 @@ def _parse_block_quote_line(line: str) -> Optional[str]: |
537 | 538 | return _BLOCK_QUOTE_TRIM.sub("", text) |
538 | 539 |
|
539 | 540 |
|
| 541 | +def _find_next_blank_line(state: BlockState, pos: int, pattern: Pattern[str]) -> Optional[int]: |
| 542 | + cache = state.env.get("__blank_line_starts__") |
| 543 | + if cache is None or cache[0] is not state.src: |
| 544 | + cache = (state.src, [m.start() for m in pattern.finditer(state.src)]) |
| 545 | + state.env["__blank_line_starts__"] = cache |
| 546 | + |
| 547 | + positions = cache[1] |
| 548 | + index = bisect_left(positions, pos) |
| 549 | + if index < len(positions): |
| 550 | + return positions[index] |
| 551 | + return None |
| 552 | + |
| 553 | + |
540 | 554 | def _trim_partial_next_line_indent(text: str, end_pos: int) -> int: |
541 | 555 | line_start = text.rfind("\n") + 1 |
542 | 556 | if line_start == 0: |
|
0 commit comments