Skip to content

Commit f6fe602

Browse files
authored
Prefer the component's own domain page when a docs content match is ambiguous (#2609)
1 parent cf47e29 commit f6fe602

2 files changed

Lines changed: 57 additions & 11 deletions

File tree

script/sync_components.py

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2256,8 +2256,14 @@ def _page_anchor_set(text: str) -> frozenset[str]:
22562256

22572257

22582258
def _find_component_section(text: str, component_id: str) -> str | None:
2259+
"""Anchor slug of the section documenting *component_id* inside a docs page."""
2260+
found = _find_component_section_kinded(text, component_id)
2261+
return found[0] if found else None
2262+
2263+
2264+
def _find_component_section_kinded(text: str, component_id: str) -> tuple[str, bool] | None:
22592265
"""
2260-
Anchor slug of the section documenting *component_id* inside a docs page.
2266+
``(slug, from_example)`` of the section documenting *component_id* inside a docs page.
22612267
22622268
Matched on the first config example naming it (``- platform: <stem>``
22632269
for a dotted id, a top-level ``<id>:`` key otherwise) → the nearest
@@ -2273,11 +2279,11 @@ def _find_component_section(text: str, component_id: str) -> str | None:
22732279
headings, _ = _page_anchor_index(text)
22742280
if m := example.search(body):
22752281
preceding = [slug for start, slug in headings if start <= m.start()]
2276-
return preceding[-1] if preceding else ""
2282+
return (preceding[-1] if preceding else ""), True
22772283
names = {stem, stem.replace("_", "-")}
22782284
for _, slug in headings:
22792285
if slug in names:
2280-
return slug
2286+
return slug, False
22812287
return None
22822288

22832289

@@ -2339,16 +2345,23 @@ def _find_docs_page_by_content(
23392345
23402346
A config-example match wins and carries the section slug; a component
23412347
documented without one (a supported-platforms table row) matches on an
2342-
inline ``<stem>`` code span with no slug. Either scan bails unless
2343-
exactly one page matches.
2348+
inline ``<stem>`` code span with no slug. Section matches rank
2349+
config-example pages above stem-heading pages, then narrow an ambiguous
2350+
set to pages under the component's own domain directory; either scan
2351+
bails unless exactly one page remains.
23442352
"""
2345-
matches: list[tuple[str, str]] = []
2353+
example_matches: list[tuple[str, str]] = []
2354+
heading_matches: list[tuple[str, str]] = []
23462355
for path, text in pages.items():
2347-
slug = _find_component_section(text, component_id)
2348-
if slug is not None:
2349-
matches.append((path, slug))
2350-
if len(matches) > 1:
2351-
break
2356+
found = _find_component_section_kinded(text, component_id)
2357+
if found is None:
2358+
continue
2359+
slug, from_example = found
2360+
(example_matches if from_example else heading_matches).append((path, slug))
2361+
matches = example_matches or heading_matches
2362+
if len(matches) > 1 and "." in component_id:
2363+
domain = component_id.split(".", 1)[0]
2364+
matches = [m for m in matches if m[0].split("/", 1)[0] == domain] or matches
23522365
if len(matches) == 1:
23532366
return matches[0]
23542367
# Blockquote mentions are commentary about the component, not its docs.

tests/test_sync_components_docs_url_validation.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,39 @@ def test_ambiguous_config_example_yields_no_link() -> None:
108108
assert _resolve_docs_url("", "weikai", {"a": _WEIKAI_PAGE, "b": _WEIKAI_PAGE}) == ("", None)
109109

110110

111+
def test_ambiguous_config_example_prefers_own_domain_page() -> None:
112+
pages = {
113+
"sensor/xiaomi_ble": _XIAOMI_PAGE,
114+
"bk72xx_ble_tracker": _XIAOMI_PAGE,
115+
"ln882h_ble_tracker": _XIAOMI_PAGE,
116+
}
117+
url, anchor = _resolve_docs_url("", "sensor.xiaomi_lywsd03mmc", pages)
118+
assert url == "https://esphome.io/components/sensor/xiaomi_ble"
119+
assert anchor == ("sensor/xiaomi_ble", "lywsd03mmc")
120+
121+
122+
def test_ambiguous_config_example_within_own_domain_yields_no_link() -> None:
123+
pages = {"sensor/a": _XIAOMI_PAGE, "sensor/b": _XIAOMI_PAGE}
124+
assert _resolve_docs_url("", "sensor.xiaomi_lywsd03mmc", pages) == ("", None)
125+
126+
127+
def test_ambiguous_stem_heading_prefers_own_domain_page() -> None:
128+
pages = {"sensor/p": "## Foo\n", "light/q": "## Foo\n"}
129+
url, anchor = _resolve_docs_url("", "sensor.foo", pages)
130+
assert url == "https://esphome.io/components/sensor/p"
131+
assert anchor == ("sensor/p", "foo")
132+
133+
134+
def test_config_example_outranks_own_domain_stem_heading() -> None:
135+
pages = {
136+
"sensor/other": "## Xiaomi Lywsd03mmc\n\ntext\n",
137+
"ble/real": _XIAOMI_PAGE,
138+
}
139+
url, anchor = _resolve_docs_url("", "sensor.xiaomi_lywsd03mmc", pages)
140+
assert url == "https://esphome.io/components/ble/real"
141+
assert anchor == ("ble/real", "lywsd03mmc")
142+
143+
111144
def test_undocumented_component_yields_no_link() -> None:
112145
assert _resolve_docs_url("", "climate.coolix", {"light": ""}) == ("", None)
113146

0 commit comments

Comments
 (0)