(text: str)
| 1042 | |
| 1043 | |
| 1044 | def _extract_math_blocks(text: str) -> tuple[list[dict[str, object]], list[dict[str, object]]]: |
| 1045 | sanitized = _strip_fenced_code_preserve_newlines(text) |
| 1046 | blocks: list[dict[str, object]] = [] |
| 1047 | issues: list[dict[str, object]] = [] |
| 1048 | consumed_lines: set[int] = set() |
| 1049 | |
| 1050 | block_pattern = re.compile(r"(?<!\\)\$\$(.+?)(?<!\\)\$\$", flags=re.DOTALL) |
| 1051 | for match in block_pattern.finditer(sanitized): |
| 1052 | start = match.start() |
| 1053 | line_number = _line_number_from_offset(sanitized, start) |
| 1054 | content = match.group(1).strip() |
| 1055 | blocks.append( |
| 1056 | { |
| 1057 | "kind": "block", |
| 1058 | "line_number": line_number, |
| 1059 | "content": content, |
| 1060 | "snippet": _formula_snippet(content), |
| 1061 | } |
| 1062 | ) |
| 1063 | line_span = match.group(0).count("\n") |
| 1064 | for extra in range(line_span + 1): |
| 1065 | consumed_lines.add(line_number + extra) |
| 1066 | |
| 1067 | delimiter_positions = [m.start() for m in re.finditer(r"(?<!\\)\$\$", sanitized)] |
| 1068 | if len(delimiter_positions) % 2 == 1: |
| 1069 | offset = delimiter_positions[-1] |
| 1070 | issues.append( |
| 1071 | { |
| 1072 | "line_number": _line_number_from_offset(sanitized, offset), |
| 1073 | "snippet": "$$", |
| 1074 | "reason": "unclosed_math_delimiter", |
| 1075 | } |
| 1076 | ) |
| 1077 | |
| 1078 | inline_pattern = re.compile(r"(?<!\\)(?<!\$)\$(?!\$)(.+?)(?<!\\)\$(?!\$)") |
| 1079 | for idx, line in enumerate(sanitized.splitlines(), start=1): |
| 1080 | if idx in consumed_lines: |
| 1081 | continue |
| 1082 | for match in inline_pattern.finditer(line): |
| 1083 | content = match.group(1).strip() |
| 1084 | if not content: |
| 1085 | continue |
| 1086 | blocks.append( |
| 1087 | { |
| 1088 | "kind": "inline", |
| 1089 | "line_number": idx, |
| 1090 | "content": content, |
| 1091 | "snippet": _formula_snippet(content), |
| 1092 | } |
| 1093 | ) |
| 1094 | if len(re.findall(r"(?<!\\)(?<!\$)\$(?!\$)", line)) % 2 == 1: |
| 1095 | issues.append( |
| 1096 | { |
| 1097 | "line_number": idx, |
| 1098 | "snippet": line.strip(), |
| 1099 | "reason": "unclosed_math_delimiter", |
| 1100 | } |
| 1101 | ) |
no test coverage detected