mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 09:07:40 +00:00
perf(terminal): stop HTTP link backward scans at rows that cannot continue a URL (#35767)
* perf(terminal): stop HTTP link backward scans at rows that cannot continue a URL Framed (hard-wrapped) detection walked back up to 2048 rows per hovered line, building column maps for every URL row; edge-wrapped walked ~54 rows at 80 cols. Stop once a later row cannot be a continuation (borders, prose, blank rows). Table-heavy buffer: provideLinks 1.3-2.1ms -> 36-79us per call. * test(terminal): pin the edge-wrapped HTTP link backward-scan bound
This commit is contained in:
@@ -98,6 +98,15 @@ describe('edge-wrapped terminal HTTP links', () => {
|
||||
}
|
||||
)
|
||||
|
||||
it('stops scanning back at a prose row that cannot continue a URL', () => {
|
||||
const prose = makeBufferLine('see https://example.com and more words')
|
||||
const hovered = makeBufferLine('tail')
|
||||
const getLine = vi.fn((y: number) => (y === 199 ? hovered : prose))
|
||||
|
||||
buildEdgeWrappedHttpLogicalLineCandidates({ getLine }, 200)
|
||||
expect(new Set(getLine.mock.calls.map(([y]) => y)).size).toBeLessThan(5)
|
||||
})
|
||||
|
||||
it('opens a URL when a wide glyph starts the continuation row', () => {
|
||||
const prefix = 'https://example.com/wide/'
|
||||
const firstRow = `${prefix}${'c'.repeat(COLS - 1 - prefix.length)}`
|
||||
|
||||
@@ -8,6 +8,8 @@ const HTTP_STATUS_ROW_PATTERN = /^HTTP\/\d(?:\.\d)?:\d{3}(?:\s|$)/i
|
||||
const VERTICAL_LAYOUT_FRAME_PATTERN = /[│┃║╎╏┆┇┊┋|]/
|
||||
const EMPTY_LABEL_ROW_PATTERN = /^[^\s:][^:]*:$/
|
||||
const LABEL_WITH_SPACING_ROW_PATTERN = /^[^\s:][^:]*:\s/
|
||||
const NON_BLANK_ROW_PATTERN = /\S/
|
||||
const SINGLE_TOKEN_ROW_PATTERN = /^ *\S+\s*$/
|
||||
|
||||
function maxEdgeWrappedHttpRows(lineLength: number, padding: number): number {
|
||||
// Width-2 cells give at least floor(columns / 2) characters per continued row; add two fragment rows to bound scanning by URL length.
|
||||
@@ -147,8 +149,21 @@ export function buildEdgeWrappedHttpLogicalLineCandidates(
|
||||
return translated
|
||||
}
|
||||
const minY = Math.max(0, currentY - maxRows + 1)
|
||||
let laterRowText: string | undefined
|
||||
for (let startY = currentY; startY >= minY; startY--) {
|
||||
// Earlier starts must continue through the later row: frame-free, and one token unless it is the hovered row.
|
||||
if (
|
||||
startY < currentY &&
|
||||
(!laterRowText ||
|
||||
VERTICAL_LAYOUT_FRAME_PATTERN.test(laterRowText) ||
|
||||
!(startY + 1 === currentY ? NON_BLANK_ROW_PATTERN : SINGLE_TOKEN_ROW_PATTERN).test(
|
||||
laterRowText
|
||||
))
|
||||
) {
|
||||
break
|
||||
}
|
||||
const startText = buffer.getLine(startY)?.translateToString(false)
|
||||
laterRowText = startText
|
||||
const schemeIndex = startText ? findHttpSchemeIndex(startText) : -1
|
||||
if (!startText || schemeIndex === -1 || VERTICAL_LAYOUT_FRAME_PATTERN.test(startText)) {
|
||||
continue
|
||||
|
||||
@@ -81,6 +81,19 @@ describe('hard-wrapped terminal HTTP candidate bounds', () => {
|
||||
expect(onTranslateWithColumns).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('stops scanning back at a table border instead of walking every framed row', () => {
|
||||
const rows = Array.from({ length: TERMINAL_HTTP_URL_MAX_HARD_WRAPPED_ROWS }, (_value, index) =>
|
||||
bufferLine(
|
||||
index % 10 === 0 ? '├──────────────────────┤' : `│ https://example.com/${index} │`,
|
||||
() => undefined
|
||||
)
|
||||
)
|
||||
const getLine = vi.fn((y: number) => rows[y])
|
||||
|
||||
buildHardWrappedHttpLogicalLineCandidates({ getLine }, TERMINAL_HTTP_URL_MAX_HARD_WRAPPED_ROWS)
|
||||
expect(new Set(getLine.mock.calls.map(([y]) => y)).size).toBeLessThan(20)
|
||||
})
|
||||
|
||||
it('reconstructs framed URL rows aligned by cells when a fragment has wide characters', () => {
|
||||
const rowTexts = ['|http://a/ab|', '|a你你你你你|', '|tailzzzzzzz|']
|
||||
const rows = rowTexts.map(bufferLineWithCellColumns)
|
||||
|
||||
@@ -11,11 +11,28 @@ const HTTP_FRAGMENT_PATTERN = /^[^\s"'!*(){}|\\^<>`│┃║╎╏┆┇┊┋]*
|
||||
const VERTICAL_LAYOUT_FRAME_PATTERN = /[│┃║╎╏┆┇┊┋|]/
|
||||
const NON_LAYOUT_SUFFIX_PATTERN = /[^\s│┃║╎╏┆┇┊┋|]/
|
||||
const HARD_WRAP_CONTINUATION_SUFFIX_PATTERN = /[/?&=#%+:-]$/
|
||||
const LAYOUT_SUFFIX_CHAR_PATTERN = /[\s│┃║╎╏┆┇┊┋|]/
|
||||
const MIN_HARD_WRAPPED_HTTP_ROWS = 3
|
||||
const MIN_HARD_WRAP_FILL_RATIO = 0.8
|
||||
|
||||
type TranslatedLine = ReturnType<typeof translateLineWithColumns>
|
||||
|
||||
// Continuation rows end in frame + prefix + URL fragment + layout-only suffix with a right frame.
|
||||
function canBeFramedContinuationRow(text: string): boolean {
|
||||
let contentEnd = text.length
|
||||
let hasRightFrame = false
|
||||
while (contentEnd > 0 && LAYOUT_SUFFIX_CHAR_PATTERN.test(text[contentEnd - 1]!)) {
|
||||
hasRightFrame ||= VERTICAL_LAYOUT_FRAME_PATTERN.test(text[contentEnd - 1]!)
|
||||
contentEnd--
|
||||
}
|
||||
return (
|
||||
hasRightFrame &&
|
||||
contentEnd > 0 &&
|
||||
Boolean(HTTP_FRAGMENT_PATTERN.exec(text[contentEnd - 1]!)?.[0]) &&
|
||||
VERTICAL_LAYOUT_FRAME_PATTERN.test(text.slice(0, contentEnd))
|
||||
)
|
||||
}
|
||||
|
||||
function findFrameIndexBefore(text: string, beforeIndex: number): number {
|
||||
for (let index = beforeIndex - 1; index >= 0; index--) {
|
||||
if (VERTICAL_LAYOUT_FRAME_PATTERN.test(text[index]!)) {
|
||||
@@ -189,6 +206,12 @@ export function buildHardWrappedHttpLogicalLineCandidates(
|
||||
if (candidate) {
|
||||
candidates.push(candidate)
|
||||
}
|
||||
// Earlier starts must continue through this row; stop at borders, prose, and blank rows.
|
||||
const rowText =
|
||||
translatedLines.get(startY)?.text ?? buffer.getLine(startY)?.translateToString(false)
|
||||
if (rowText === undefined || !canBeFramedContinuationRow(rowText)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return candidates.sort(
|
||||
(left, right) => right.rows.length - left.rows.length || right.text.length - left.text.length
|
||||
|
||||
Reference in New Issue
Block a user