扫 plain wikilink(alias / 无 RELATION)+ 判断/立场动词,提示应补 RELATION。 跳过:raw 链接(走 anchor 编号)、已合规的 RELATION 链接、自链、 归档区 / index / stub 标签、callout、表格行、代码块、source_summary 顶部元信息块(用 _body_after_first_h2 切)。 词表故意收窄:模糊词("也"、"用"、"用 X 来")会大量误报,不收。 首次接入建议只报警(不接 commit 钩子),看 1-2 周真实命中后再调阈值。
(pages)
| 835 | |
| 836 | |
| 837 | def list_implicit_relations(pages) -> list[dict]: |
| 838 | """扫 plain wikilink(alias / 无 RELATION)+ 判断/立场动词,提示应补 RELATION。 |
| 839 | |
| 840 | 跳过:raw 链接(走 anchor 编号)、已合规的 RELATION 链接、自链、 |
| 841 | 归档区 / index / stub 标签、callout、表格行、代码块、source_summary |
| 842 | 顶部元信息块(用 _body_after_first_h2 切)。 |
| 843 | |
| 844 | 词表故意收窄:模糊词("也"、"用"、"用 X 来")会大量误报,不收。 |
| 845 | 首次接入建议只报警(不接 commit 钩子),看 1-2 周真实命中后再调阈值。 |
| 846 | """ |
| 847 | out = [] |
| 848 | for page in pages: |
| 849 | if _is_exempt(page): |
| 850 | continue |
| 851 | if page.type in BARE_CLAIMS_SKIP_TYPES: |
| 852 | continue |
| 853 | # 跳过 source_summary 顶部元信息块(第一个 H2 之前) |
| 854 | body = _body_after_first_h2(page.raw_content) |
| 855 | for blk in split_blocks(body): |
| 856 | if blk.kind not in ("paragraph", "list", "blockquote"): |
| 857 | continue |
| 858 | scan_text = mask_code_spans(blk.text) |
| 859 | if CALLOUT_RE.search(scan_text): |
| 860 | continue |
| 861 | if scan_text.lstrip().startswith("|"): |
| 862 | continue |
| 863 | # 找 plain wikilink(不扫已合规的 RELATION 链接 + raw 链接 + 自链) |
| 864 | plain_links: list[str] = [] |
| 865 | for m in WIKILINK_RE.finditer(scan_text): |
| 866 | target = m.group(1) or "" |
| 867 | if target.startswith("raw/"): |
| 868 | continue |
| 869 | _, relation = split_alias_or_relation(m.group(3)) |
| 870 | if relation: |
| 871 | continue # 已合规 |
| 872 | normalized = normalize_link_target(target) |
| 873 | if _is_self_link(page.path, normalized): |
| 874 | continue |
| 875 | plain_links.append(m.group(0)) |
| 876 | if not plain_links: |
| 877 | continue |
| 878 | # 找判断动词 |
| 879 | matched_verbs: list[str] = [] |
| 880 | for v in _IMPLICIT_RELATION_VERBS_ZH: |
| 881 | if v in scan_text: |
| 882 | matched_verbs.append(v) |
| 883 | for m in _IMPLICIT_VERB_EN_RE.finditer(scan_text): |
| 884 | matched_verbs.append(m.group(0).lower()) |
| 885 | if not matched_verbs: |
| 886 | continue |
| 887 | preview = re.sub(r"\s+", " ", scan_text).strip() |
| 888 | if len(preview) > 200: |
| 889 | preview = preview[:200] + "…" |
| 890 | out.append({ |
| 891 | "path": page.path, |
| 892 | "title": page.title, |
| 893 | "type": page.type, |
| 894 | "line": blk.line_start, |
no test coverage detected