mirror of
https://github.com/stablyai/orca.git
synced 2026-10-23 00:03:52 +00:00
fix(terminal): end a url at CJK punctuation instead of swallowing it
Terminal URL detection treated every non-ASCII character as part of the url, so Japanese or Chinese text written straight after one was absorbed into the link. A line like PR: https://github.com/org/repo/pull/12345(作成済み・マージ待ち) underlined the whole run and opened the annotation percent-encoded onto the end of the url, which 404s. The body terminator listed only ASCII codes, so nothing at or above 0x80 could end a url. Terminating on all non-ASCII is the obvious repair and is wrong: a url path may legitimately carry unencoded CJK, and the wrapped-url tests cover exactly that - a real multi-line url whose continuation row is a Chinese path segment. That repair was written first and broke four of them. The distinction is punctuation, not ASCII. Non-ASCII punctuation, symbols and separators end a url; letters do not. Full-width brackets, an ideographic space, a full-width comma and the katakana middle dot are prose; 文档 in a path is not. Adds the extraction test file the module never had, covering the reporter's three cases, the CJK-path case that must keep working, and ASCII behaviour as a regression guard. Closes #10571
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { extractTerminalHttpLinks } from './terminal-http-url-extraction'
|
||||
|
||||
const urlsIn = (line: string): string[] => extractTerminalHttpLinks(line).map((link) => link.url)
|
||||
|
||||
describe('extractTerminalHttpLinks', () => {
|
||||
it('stops at a full-width parenthesis instead of swallowing the annotation', () => {
|
||||
expect(
|
||||
urlsIn('- PR: https://github.com/stablyai/orca/pull/12345(作成済み・マージ待ち)')
|
||||
).toEqual(['https://github.com/stablyai/orca/pull/12345'])
|
||||
})
|
||||
|
||||
it('stops at a full-width closing quote', () => {
|
||||
expect(urlsIn('詳細は「https://example.com/docs」を参照')).toEqual(['https://example.com/docs'])
|
||||
})
|
||||
|
||||
it('stops at an ideographic space', () => {
|
||||
expect(urlsIn('PR: https://example.com/a (全角スペース区切り)')).toEqual([
|
||||
'https://example.com/a'
|
||||
])
|
||||
})
|
||||
|
||||
it('keeps unencoded CJK letters that are genuinely part of the path', () => {
|
||||
// Why: wrapped URLs with Chinese path segments are supported; only punctuation ends a url.
|
||||
expect(urlsIn('https://example.com/文档/路径')).toEqual([
|
||||
new URL('https://example.com/文档/路径').toString()
|
||||
])
|
||||
})
|
||||
|
||||
it('stops at a full-width comma and full stop between a url and prose', () => {
|
||||
expect(urlsIn('https://example.com/a、次へ')).toEqual(['https://example.com/a'])
|
||||
expect(urlsIn('https://example.com/b。')).toEqual(['https://example.com/b'])
|
||||
})
|
||||
|
||||
it('still extracts a plain ascii url and keeps its own punctuation', () => {
|
||||
expect(urlsIn('see https://example.com/a_b-c?d=1&e=2#f end')).toEqual([
|
||||
'https://example.com/a_b-c?d=1&e=2#f'
|
||||
])
|
||||
})
|
||||
|
||||
it('still trims ascii trailing punctuation', () => {
|
||||
expect(urlsIn('see https://example.com/a.')).toEqual(['https://example.com/a'])
|
||||
})
|
||||
})
|
||||
@@ -98,6 +98,7 @@ function trimHttpUrlTrailingPunctuation(
|
||||
|
||||
function isHttpUrlBodyTerminator(code: number): boolean {
|
||||
return (
|
||||
isNonAsciiUrlTerminator(code) ||
|
||||
isAsciiWhitespace(code) ||
|
||||
code === 0x22 ||
|
||||
code === 0x27 ||
|
||||
@@ -142,6 +143,14 @@ function isHttpUrlTrailingPunctuation(code: number): boolean {
|
||||
)
|
||||
}
|
||||
|
||||
// Why not all non-ascii: a url path may legitimately carry unencoded CJK (see the wrapped-url
|
||||
// tests), so only punctuation and spaces end it - full-width brackets, an ideographic space, `\u30fb`.
|
||||
const NON_ASCII_URL_TERMINATOR = /[\p{P}\p{S}\p{Z}]/u
|
||||
|
||||
function isNonAsciiUrlTerminator(code: number): boolean {
|
||||
return code > 0x7e && NON_ASCII_URL_TERMINATOR.test(String.fromCharCode(code))
|
||||
}
|
||||
|
||||
function isAsciiWhitespace(code: number): boolean {
|
||||
return code === 9 || code === 10 || code === 11 || code === 12 || code === 13 || code === 32
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user