diff --git a/lib/tickers.py b/lib/tickers.py index 995ffca..5bcb075 100644 --- a/lib/tickers.py +++ b/lib/tickers.py @@ -19,7 +19,9 @@ PROTECTED_TAG_RE = re.compile(r"<(/?)(a|code|pre)\b", re.I) BOLD_TAG_RE = re.compile(r"<(/?)(b|strong)\b", re.I) URL_RE = re.compile(r'https?://[^\s<>"]+') -RAW_ANCHOR_RE = re.compile(r'\s*https?://[^<]*') +_CITE = r'\[\d+\]' +CITE_ONLY_RUN_RE = re.compile(rf"\s*\n\s*({_CITE}(?:\s+{_CITE})*)[ \t]*(?=\n|$)") # lines holding only citations +RAW_ANCHOR_RE =re.compile(r'\s*https?://[^<]*') # "Micron (MU)": up to four capitalised words (or a number) right before a parenthesised ticker. _WORD = r"[A-Z0-9][\w&'’-]*(?:\.[\w&'’-]+)*" NAME_TICKER_RE = re.compile( @@ -94,7 +96,9 @@ def cite(m: re.Match) -> str: return f'[{n}]{tail}' text = RAW_ANCHOR_RE.sub(lambda m: m.group(1), text) - return _rewrite(text, lambda seg, bold: URL_RE.sub(cite, seg)) + text = _rewrite(text, lambda seg, bold: URL_RE.sub(cite, seg)) + # Links the model put on their own lines join the text before them: "volume.[1][2]" + return CITE_ONLY_RUN_RE.sub(lambda m: re.sub(r"(?<=)\s+", "", m.group(1)), text) def link_tickers(text: str) -> str: diff --git a/tests/test_tickers.py b/tests/test_tickers.py index d78890c..c922e70 100644 --- a/tests/test_tickers.py +++ b/tests/test_tickers.py @@ -58,6 +58,14 @@ def test_raw_links_go_behind_citation_numbers(): assert "quote/NVDA" not in out # the ticker inside the URL is not linked +def test_citations_on_their_own_lines_join_the_preceding_text(): + a, b = '[1]', '[2]' + assert link_tickers("Thin volume.\n\nhttps://a.com/1\nhttps://b.com/2") == f"Thin volume.{a}{b}" + assert link_tickers("One.\n\nhttps://a.com/1\n\nTwo.\nhttps://b.com/2") == f"One.{a}\n\nTwo.{b}" + assert link_tickers("Said https://a.com/1 then more") == f"Said {a} then more" # inline: untouched + assert link_tickers("Thin.\nhttps://a.com/1 and more") == f"Thin.\n{a} and more" # not a citation-only line + + def test_link_labelled_with_its_own_url_is_numbered_but_named_links_and_code_are_not(): assert link_tickers('https://y.com/p… ok') == '[1] ok' assert link_tickers('Reuters') == 'Reuters'