fix: translate buffer col to string offset

This commit is contained in:
Gerald
2021-02-22 16:23:08 +08:00
parent 6f6378aaf2
commit ae037bdac0
2 changed files with 53 additions and 17 deletions
+41 -14
View File
@@ -21,7 +21,7 @@ export interface ISearchResult {
term: string; term: string;
col: number; col: number;
row: number; row: number;
length: number; size: number;
} }
const NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\;:"\',./<>?'; const NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\;:"\',./<>?';
@@ -283,6 +283,7 @@ export class SearchAddon implements ITerminalAddon {
} }
} }
const offset = this._bufferColsToStringOffset(row, col);
const searchTerm = searchOptions.caseSensitive ? term : term.toLowerCase(); const searchTerm = searchOptions.caseSensitive ? term : term.toLowerCase();
const searchStringLine = searchOptions.caseSensitive ? stringLine : stringLine.toLowerCase(); const searchStringLine = searchOptions.caseSensitive ? stringLine : stringLine.toLowerCase();
@@ -291,26 +292,26 @@ export class SearchAddon implements ITerminalAddon {
const searchRegex = RegExp(searchTerm, 'g'); const searchRegex = RegExp(searchTerm, 'g');
let foundTerm: RegExpExecArray | null; let foundTerm: RegExpExecArray | null;
if (isReverseSearch) { if (isReverseSearch) {
// This loop will get the resultIndex of the _last_ regex match in the range 0..col // This loop will get the resultIndex of the _last_ regex match in the range 0..offset
while (foundTerm = searchRegex.exec(searchStringLine.slice(0, col))) { while (foundTerm = searchRegex.exec(searchStringLine.slice(0, offset))) {
resultIndex = searchRegex.lastIndex - foundTerm[0].length; resultIndex = searchRegex.lastIndex - foundTerm[0].length;
term = foundTerm[0]; term = foundTerm[0];
searchRegex.lastIndex -= (term.length - 1); searchRegex.lastIndex -= (term.length - 1);
} }
} else { } else {
foundTerm = searchRegex.exec(searchStringLine.slice(col)); foundTerm = searchRegex.exec(searchStringLine.slice(offset));
if (foundTerm && foundTerm[0].length > 0) { if (foundTerm && foundTerm[0].length > 0) {
resultIndex = col + (searchRegex.lastIndex - foundTerm[0].length); resultIndex = offset + (searchRegex.lastIndex - foundTerm[0].length);
term = foundTerm[0]; term = foundTerm[0];
} }
} }
} else { } else {
if (isReverseSearch) { if (isReverseSearch) {
if (col - searchTerm.length >= 0) { if (offset - searchTerm.length >= 0) {
resultIndex = searchStringLine.lastIndexOf(searchTerm, col - searchTerm.length); resultIndex = searchStringLine.lastIndexOf(searchTerm, offset - searchTerm.length);
} }
} else { } else {
resultIndex = searchStringLine.indexOf(searchTerm, col); resultIndex = searchStringLine.indexOf(searchTerm, offset);
} }
} }
@@ -325,17 +326,18 @@ export class SearchAddon implements ITerminalAddon {
} }
const line = terminal.buffer.active.getLine(row); const line = terminal.buffer.active.getLine(row);
let { length } = term; let size = term.length;
let col = 0;
if (line) { if (line) {
resultIndex = this._stringLengthToBufferSize(line, resultIndex); col = this._stringLengthToBufferSize(line, resultIndex);
length = this._stringLengthToBufferSize(line, length, resultIndex); size = this._stringLengthToBufferSize(line, term.length, col);
} }
return { return {
term, term,
col: resultIndex, col,
row, row,
length size
}; };
} }
} }
@@ -361,6 +363,31 @@ export class SearchAddon implements ITerminalAddon {
return length; return length;
} }
private _bufferColsToStringOffset(startRow: number, cols: number): number {
const terminal = this._terminal!;
let lineIndex = startRow;
let offset = 0;
let line = terminal.buffer.active.getLine(lineIndex);
while (cols > 0 && line) {
for (let i = 0; i < cols && i < terminal.cols; i++) {
const cell = line.getCell(i);
if (!cell) {
break;
}
if (cell.getWidth()) {
offset += cell.getChars().length;
}
}
lineIndex++;
line = terminal.buffer.active.getLine(lineIndex);
if (line && !line.isWrapped) {
break;
}
cols -= terminal.cols;
}
return offset;
}
/** /**
* Translates a buffer line to a string, including subsequent lines if they are wraps. * Translates a buffer line to a string, including subsequent lines if they are wraps.
* Wide characters will count as two columns in the resulting string. This * Wide characters will count as two columns in the resulting string. This
@@ -399,7 +426,7 @@ export class SearchAddon implements ITerminalAddon {
terminal.clearSelection(); terminal.clearSelection();
return false; return false;
} }
terminal.select(result.col, result.row, result.length); terminal.select(result.col, result.row, result.size);
// If it is not in the viewport then we scroll else it just gets selected // If it is not in the viewport then we scroll else it just gets selected
if (result.row >= (terminal.buffer.active.viewportY + terminal.rows) || result.row < terminal.buffer.active.viewportY) { if (result.row >= (terminal.buffer.active.viewportY + terminal.rows) || result.row < terminal.buffer.active.viewportY) {
let scroll = result.row - terminal.buffer.active.viewportY; let scroll = result.row - terminal.buffer.active.viewportY;
@@ -107,11 +107,20 @@ describe('Search Tests', function(): void {
}); });
it('Search for result bounding with wide unicode chars', async () => { it('Search for result bounding with wide unicode chars', async () => {
await writeSync(page, '中文xxx'); await writeSync(page, '中文xx𝄞𝄞');
assert.deepEqual(await page.evaluate(`window.search.findNext('中')`), true); assert.deepEqual(await page.evaluate(`window.search.findNext('中')`), true);
assert.deepEqual(await page.evaluate(`window.term.getSelection()`), '中'); assert.deepEqual(await page.evaluate(`window.term.getSelection()`), '中');
assert.deepEqual(await page.evaluate(`window.search.findNext('xxx')`), true); assert.deepEqual(await page.evaluate(`window.search.findNext('xx')`), true);
assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'xxx'); assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'xx');
assert.deepEqual(await page.evaluate(`window.search.findNext('𝄞')`), true);
assert.deepEqual(await page.evaluate(`window.term.getSelection()`), '𝄞');
assert.deepEqual(await page.evaluate(`window.search.findNext('𝄞')`), true);
assert.deepEqual(await page.evaluate(`window.term.getSelectionPosition()`), {
startRow: 0,
endRow: 0,
startColumn: 7,
endColumn: 8
});
}); });
describe('Regression tests', () => { describe('Regression tests', () => {