From 426b8d15f57592f836ff6f51aab6163d532d5153 Mon Sep 17 00:00:00 2001 From: chatman-media Date: Tue, 30 Jun 2026 04:16:27 +0700 Subject: [PATCH] Fix URL lexer dropping the host and path when a port is present When a URL had an explicit port (http://localhost:8080/api), lex_hostport scanned for the end of the port starting from the beginning of the host instead of after the colon. The host's first character is a letter (or the scan stops at the first dot), so the search for the first non-digit returned almost immediately and the port was treated as zero-length. The URL token then ended right after the scheme, leaving the host, port, and path to be tokenized as ordinary prose and spell-checked. Scan the port digits from just after the colon, and leave the colon unconsumed when no digits follow it. --- harper-core/src/lexing/url.rs | 39 +++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/harper-core/src/lexing/url.rs b/harper-core/src/lexing/url.rs index f07114c2a8..99919b394d 100644 --- a/harper-core/src/lexing/url.rs +++ b/harper-core/src/lexing/url.rs @@ -86,17 +86,19 @@ fn lex_hostport(source: &[char]) -> Option { let hostname_end = lex_hostname(source)?; if source.get(hostname_end) == Some(&':') { - Some( - source - .iter() - .enumerate() - .find(|(_, c)| !{ - let c = **c; - c.is_ascii_digit() - }) - .map(|(i, _)| i) - .unwrap_or(source.len()), - ) + // Scan the port number, which begins right after the ':'. + let port_start = hostname_end + 1; + let port_len = source[port_start..] + .iter() + .take_while(|c| c.is_ascii_digit()) + .count(); + + if port_len == 0 { + // A ':' that isn't followed by a port; don't consume it. + Some(hostname_end) + } else { + Some(port_start + port_len) + } } else { Some(hostname_end) } @@ -239,6 +241,21 @@ mod tests { assert_consumes_full("https://github.com/nodesource/distributions#debinstall") } + #[test] + fn consumes_port() { + assert_consumes_full("http://localhost:8080") + } + + #[test] + fn consumes_port_with_path() { + assert_consumes_full("http://localhost:8080/api/v1") + } + + #[test] + fn consumes_ip_with_port() { + assert_consumes_full("http://127.0.0.1:3000/health") + } + /// Tests that the URL parser will not throw a panic under some random /// situations. #[test]