Preserve semicolons within linkified URLs.

This commit is contained in:
Greyson Parrelli
2026-07-20 13:10:48 -04:00
parent 1f7c42c24b
commit 85261afe42
2 changed files with 7 additions and 3 deletions
@@ -28,11 +28,12 @@ object Linkifier {
private val CLOSING_BRACKETS = mapOf(')' to '(', ']' to '[', '}' to '{')
/**
* Characters we treat as definitely-not-part-of-a-URL when extending past the host. Commas are
* allowed inside a URL, but not when they are acting as a separator before another obvious URL.
* Characters we treat as definitely-not-part-of-a-URL when extending past the host. Commas and
* semicolons are allowed inside a URL, but not when they are acting as a separator before another
* obvious URL.
*/
private const val URL_CHAR =
"(?:[^\\s\\u0085\\u00A0\\u1680\\u2000-\\u200D\\u2028\\u2029\\u202A-\\u202F\\u205F\\u2066-\\u2069\\u3000\\uFEFF<>\"'`,;|\\\\]|,(?!https?://|www\\.))"
"(?:[^\\s\\u0085\\u00A0\\u1680\\u2000-\\u200D\\u2028\\u2029\\u202A-\\u202F\\u205F\\u2066-\\u2069\\u3000\\uFEFF<>\"'`,;|\\\\]|[,;](?!https?://|www\\.))"
/**
* A single domain label: letter/digit, optional letter/digit/hyphen body. Used for intermediate
@@ -105,6 +105,8 @@ class LinkifierTest(private val case: Case) {
Case("trailing underscore is preserved", "https://example.com/path_ tail", listOf(web("https://example.com/path_"))),
Case("comma in url path is preserved", "Go to https://example.com/a,b/c", listOf(web("https://example.com/a,b/c"))),
Case("comma in url query is preserved", "Go to https://example.com/search?q=a,b", listOf(web("https://example.com/search?q=a,b"))),
Case("semicolon in url path is preserved", "https://example.com/pricewatch/compare/2290176;2126182/", listOf(web("https://example.com/pricewatch/compare/2290176;2126182/"))),
Case("semicolon in url query is preserved", "Go to https://example.com/search?a=1;b=2", listOf(web("https://example.com/search?a=1;b=2"))),
// ----- bracket / paren handling -----
Case("trailing closing paren without opener is trimmed", "(see https://signal.org)", listOf(web("https://signal.org"))),
@@ -116,6 +118,7 @@ class LinkifierTest(private val case: Case) {
// ----- multiple URLs in one input -----
Case("two urls separated by text", "First https://a.com then https://b.com", listOf(web("https://a.com"), web("https://b.com"))),
Case("two urls separated only by comma", "https://a.com,https://b.com", listOf(web("https://a.com"), web("https://b.com"))),
Case("two urls separated only by semicolon", "https://a.com;https://b.com", listOf(web("https://a.com"), web("https://b.com"))),
Case(
name = "multi-line text with several urls per line",
input = """