diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 1c816939a8..170a7a2ac4 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -610,7 +610,8 @@ export class SearchAddon implements ITerminalAddon { break; } if (cell.getWidth()) { - offset += cell.getChars().length; + // Treat null characters as whitespace to align with the translateToString API + offset += cell.getCode() === 0 ? 1 : cell.getChars().length; } } lineIndex++; diff --git a/addons/xterm-addon-search/test/SearchAddon.api.ts b/addons/xterm-addon-search/test/SearchAddon.api.ts index e665f54eba..c1305345ab 100644 --- a/addons/xterm-addon-search/test/SearchAddon.api.ts +++ b/addons/xterm-addon-search/test/SearchAddon.api.ts @@ -379,6 +379,21 @@ describe('Search Tests', function(): void { }); }); }); + describe('#3834 lines with null characters before search terms', () => { + // This case can be triggered by the prompt when using starship under conpty + it('should find all matches on a line containing null characters', async () => { + await page.evaluate(` + window.calls = []; + window.search.onDidChangeResults(e => window.calls.push(e)); + `); + // Move cursor forward 1 time to create a null character, as opposed to regular whitespace + await writeSync(page, '\\x1b[CHi Hi'); + assert.strictEqual(await page.evaluate(`window.search.findPrevious('h', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); + assert.deepStrictEqual(await page.evaluate('window.calls'), [ + { resultCount: 2, resultIndex: 1 } + ]); + }); + }); }); function makeData(length: number): string {