fix: translate buffer col to string offset
This commit is contained in:
@@ -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', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user