diff --git a/humanfriendly/terminal/html.py b/humanfriendly/terminal/html.py index 4214e09..2004957 100644 --- a/humanfriendly/terminal/html.py +++ b/humanfriendly/terminal/html.py @@ -204,7 +204,7 @@ def handle_data(self, data): if self.link_url: # Link text is captured literally so that we can reliably check # whether the text and the URL of the link are the same string. - self.link_text = data + self.link_text += data elif self.callback and self.preformatted_text_level == 0: # Text that is not part of a link and not preformatted text is # passed to the user defined callback to allow for arbitrary @@ -227,7 +227,7 @@ def handle_endtag(self, tag): self.stack.pop(-1) new_style = self.current_style if tag == 'a': - if self.urls_match(self.link_text, self.link_url): + if not self.link_url or self.urls_match(self.link_text, self.link_url): # Don't render the URL when it's part of the link text. self.emit_style(new_style) else: @@ -237,6 +237,8 @@ def handle_endtag(self, tag): self.output.write(self.render_url(self.link_url)) self.emit_style(new_style) self.output.write(')') + self.link_url = None + self.link_text = None else: self.emit_style(new_style) if tag in ('code', 'pre'): @@ -270,6 +272,7 @@ def handle_starttag(self, tag, attrs): # this is the most intuitive way to present a link in a plain text # interface). self.link_url = next((v for n, v in attrs if n == 'href'), '') + self.link_text = '' elif tag == 'b' or tag == 'strong': self.push_styles(bold=True) elif tag == 'br': diff --git a/humanfriendly/tests.py b/humanfriendly/tests.py index 72dad99..c4fc2c9 100644 --- a/humanfriendly/tests.py +++ b/humanfriendly/tests.py @@ -1078,6 +1078,24 @@ def callback(text): title="VPS hosting" rel="external">True ''') + def test_html_callback_after_link(self): + """Text callbacks resume after each hyperlink closes.""" + result = html_to_ansi('before link after', callback=lambda text: text.upper()) + self.assertEqual(ansi_strip(result), 'BEFORE link (url) AFTER') + + def test_html_link_text_in_multiple_fragments(self): + """Nested markup contributes all link text to URL comparison.""" + result = html_to_ansi('https://example.com') + self.assertEqual(ansi_strip(result), 'https://example.com') + + def test_html_empty_links(self): + """Empty anchors do not reuse previous link text or raise errors.""" + result = html_to_ansi('first') + self.assertEqual(ansi_strip(result), 'first (second)') + self.assertEqual(ansi_strip(html_to_ansi('')), ' (url)') + self.assertEqual(ansi_strip(html_to_ansi('text')), 'text') + self.assertEqual(ansi_strip(html_to_ansi('text')), 'text') + def test_generate_output(self): """Test the :func:`humanfriendly.terminal.output()` function.""" text = "Standard output generated by output()"