Skip to content

Commit

Permalink
fix length calculation of wide unicode chars
Browse files Browse the repository at this point in the history
  • Loading branch information
gera2ld committed Jan 29, 2021
1 parent 28bcd88 commit a57ee6b
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 20 deletions.
49 changes: 29 additions & 20 deletions addons/xterm-addon-search/src/SearchAddon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* @license MIT
*/

import { Terminal, IDisposable, ITerminalAddon, ISelectionPosition } from 'xterm';
import { Terminal, IBufferLine, IDisposable, ITerminalAddon, ISelectionPosition } from 'xterm';

export interface ISearchOptions {
regex?: boolean;
Expand All @@ -21,6 +21,7 @@ export interface ISearchResult {
term: string;
col: number;
row: number;
length: number;
}

const NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\;:"\',./<>?';
Expand Down Expand Up @@ -324,34 +325,42 @@ export class SearchAddon implements ITerminalAddon {
}

const line = terminal.buffer.active.getLine(row);
let { length } = term;

if (line) {
for (let i = 0; i < resultIndex; i++) {
const cell = line.getCell(i);
if (!cell) {
break;
}
// Adjust the searchIndex to normalize emoji into single chars
const char = cell.getChars();
if (char.length > 1) {
resultIndex -= char.length - 1;
}
// Adjust the searchIndex for empty characters following wide unicode
// chars (eg. CJK)
const charWidth = cell.getWidth();
if (charWidth === 0) {
resultIndex++;
}
}
resultIndex = this._stringLengthToBufferSize(line, resultIndex);
length = this._stringLengthToBufferSize(line, length, resultIndex);
}
return {
term,
col: resultIndex,
row
row,
length
};
}
}

private _stringLengthToBufferSize(line: IBufferLine, length: number, start: number = 0): number {
for (let i = 0; i < length; i++) {
const cell = line.getCell(start + i);
if (!cell) {
break;
}
// Adjust the searchIndex to normalize emoji into single chars
const char = cell.getChars();
if (char.length > 1) {
length -= char.length - 1;
}
// Adjust the searchIndex for empty characters following wide unicode
// chars (eg. CJK)
const nextCell = line.getCell(start + i + 1);
if (nextCell && nextCell.getWidth() === 0) {
length++;
}
}
return length;
}

/**
* 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
Expand Down Expand Up @@ -390,7 +399,7 @@ export class SearchAddon implements ITerminalAddon {
terminal.clearSelection();
return false;
}
terminal.select(result.col, result.row, result.term.length);
terminal.select(result.col, result.row, result.length);
// 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) {
let scroll = result.row - terminal.buffer.active.viewportY;
Expand Down
8 changes: 8 additions & 0 deletions addons/xterm-addon-search/test/SearchAddon.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,14 @@ describe('Search Tests', function(): void {
assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'abc');
});

it('Search for result bounding with wide unicode chars', async () => {
await writeSync(page, '中文xxx');
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('xxx')`), true);
assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'xxx');
});

describe('Regression tests', () => {
describe('#2444 wrapped line content not being found', () => {
let fixture: string;
Expand Down

0 comments on commit a57ee6b

Please sign in to comment.