Skip to content

Commit 32b25f9

Browse files
committed
Strip #:~:text= directives when validating anchor links
With `file.md#:~:text=focus_on_text`, there should be nothing to validate. With `file.md#anchor:~:text=focus_on_text`, 'anchor' is the part to validate. Based on https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Fragment/Text_fragments#syntax
1 parent 65ed6c3 commit 32b25f9

File tree

2 files changed

+36
-8
lines changed

2 files changed

+36
-8
lines changed

properdocs/structure/pages.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,10 @@ def validate_anchor_links(self, *, files: Files, log_level: int) -> None:
315315
continue
316316
context = ""
317317
if to_file == self.file:
318-
problem = "there is no such anchor on this page"
318+
if original_link.endswith('#' + anchor):
319+
problem = "there is no such anchor on this page"
320+
else:
321+
problem = f"there is no anchor '#{anchor}' on this page"
319322
if anchor.startswith('fnref:'):
320323
context = " This seems to be a footnote that is never referenced."
321324
else:
@@ -416,6 +419,16 @@ def _possible_target_uris(
416419
yield guess
417420
tried.add(guess)
418421

422+
def register_anchor(self, *, file: File, anchor: str, url: str) -> None:
423+
if not anchor:
424+
return
425+
# Detect https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Fragment/Text_fragments#syntax
426+
if (index := anchor.find(':~:')) > -1:
427+
if index == 0:
428+
return # This is entirely just a directive, no anchor.
429+
anchor = anchor[:index]
430+
self.links_to_anchors.setdefault(file, {}).setdefault(anchor, url)
431+
419432
def path_to_url(self, url: str) -> str:
420433
scheme, netloc, path, query, anchor = urlsplit(url)
421434

@@ -433,9 +446,8 @@ def path_to_url(self, url: str) -> str:
433446
elif AMP_SUBSTITUTE in url: # AMP_SUBSTITUTE is used internally by Markdown only for email.
434447
return url
435448
elif not path: # Self-link containing only query or anchor.
436-
if anchor:
437-
# Register that the page links to itself with an anchor.
438-
self.links_to_anchors.setdefault(self.file, {}).setdefault(anchor, url)
449+
# Register that the page links to itself with an anchor.
450+
self.register_anchor(file=self.file, anchor=anchor, url=url)
439451
return url
440452

441453
path = urlunquote(path)
@@ -498,9 +510,8 @@ def path_to_url(self, url: str) -> str:
498510
assert target_uri is not None
499511
assert target_file is not None
500512

501-
if anchor:
502-
# Register that this page links to the target file with an anchor.
503-
self.links_to_anchors.setdefault(target_file, {}).setdefault(anchor, url)
513+
# Register that this page links to the target file with an anchor.
514+
self.register_anchor(file=target_file, anchor=anchor, url=url)
504515

505516
if target_file.inclusion.is_excluded():
506517
if self.file.inclusion.is_excluded():

properdocs/tests/build_tests.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,7 @@ def test_anchor_no_warning_with_html(self, site_dir, docs_dir):
776776
}
777777
)
778778
@tempdir()
779-
def test_anchor_warning_and_query(self, site_dir, docs_dir):
779+
def test_anchor_and_query_warning(self, site_dir, docs_dir):
780780
cfg = load_config(docs_dir=docs_dir, site_dir=site_dir, validation={'anchors': 'info'})
781781

782782
expected_logs = '''
@@ -806,6 +806,23 @@ def test_anchor_warning_for_footnote(self, site_dir, docs_dir):
806806
with self._assert_build_logs(expected_logs):
807807
build.build(cfg)
808808

809+
@tempdir(
810+
files={
811+
'test/foo.md': '# page1 heading\n\n[bar](bar.md#page1-heading:~:text=a)\n\n[just text](#:~:text=text)',
812+
'test/bar.md': '# page2 heading\n\n[aaa](#a:~:text=a)\n\n[bbb](#page2-heading:~:text=a)',
813+
}
814+
)
815+
@tempdir()
816+
def test_anchor_with_directive_warnings(self, site_dir, docs_dir):
817+
cfg = load_config(docs_dir=docs_dir, site_dir=site_dir, validation={'anchors': 'warn'})
818+
819+
expected_logs = '''
820+
WARNING:Doc file 'test/bar.md' contains a link '#a:~:text=a', but there is no anchor '#a' on this page.
821+
WARNING:Doc file 'test/foo.md' contains a link 'bar.md#page1-heading:~:text=a', but the doc 'test/bar.md' does not contain an anchor '#page1-heading'.
822+
'''
823+
with self._assert_build_logs(expected_logs):
824+
build.build(cfg)
825+
809826
@tempdir(
810827
files={
811828
'foo.md': 'page1 content',

0 commit comments

Comments
 (0)