| 72 | |
| 73 | |
| 74 | def check_fences(path: Path, text: str) -> list[str]: |
| 75 | issues: list[str] = [] |
| 76 | open_fence: tuple[str, int, int] | None = None |
| 77 | for line_no, line in enumerate(text.splitlines(), 1): |
| 78 | match = FENCE_RE.match(line) |
| 79 | if not match: |
| 80 | continue |
| 81 | marker = match.group(1) |
| 82 | char = marker[0] |
| 83 | length = len(marker) |
| 84 | if open_fence is None: |
| 85 | open_fence = (char, length, line_no) |
| 86 | continue |
| 87 | open_char, open_len, _ = open_fence |
| 88 | if char == open_char and length >= open_len: |
| 89 | open_fence = None |
| 90 | if open_fence is not None: |
| 91 | _, _, line_no = open_fence |
| 92 | issues.append(f"{path.relative_to(ROOT)}:{line_no}: unclosed fenced code block") |
| 93 | return issues |
| 94 | |
| 95 | |
| 96 | def is_local_target(raw_target: str) -> bool: |