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()"