From ab9e92fcf1d5ed9b20592f354a1b623f1abff257 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Fri, 19 Jul 2019 17:45:39 -0700 Subject: [PATCH 01/15] Initial search tests --- .../xterm-addon-search/src/SearchAddon.api.ts | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 addons/xterm-addon-search/src/SearchAddon.api.ts diff --git a/addons/xterm-addon-search/src/SearchAddon.api.ts b/addons/xterm-addon-search/src/SearchAddon.api.ts new file mode 100644 index 00000000..188f7c3f --- /dev/null +++ b/addons/xterm-addon-search/src/SearchAddon.api.ts @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import * as puppeteer from 'puppeteer'; +import { assert } from 'chai'; +import { ITerminalOptions } from 'xterm'; + +const APP = 'http://127.0.0.1:3000/test'; + +let browser: puppeteer.Browser; +let page: puppeteer.Page; +const width = 800; +const height = 600; + +describe.only('Broken Tests', function (): void { + this.timeout(200000); + + before(async function (): Promise { + browser = await puppeteer.launch({ + headless: process.argv.indexOf('--headless') !== -1, + slowMo: 80, + args: [`--window-size=${width},${height}`] + }); + page = (await browser.pages())[0]; + await page.setViewport({ width, height }); + await page.goto(APP); + await openTerminal(); + await page.evaluate(`window.search = new SearchAddon();`); + await page.evaluate(`window.term.loadAddon(window.search);`); + }); + + after(() => { + // browser.close(); + }); + + beforeEach(async () => { + await page.evaluate(`window.term.reset()`); + }); + + it('Simple Search', async () => { + await writeSync('dafhdjfldshafhldsahfkjhldhjkftestlhfdsakjfhdjhlfdsjkafhjdlk'); + assert.deepEqual(await page.evaluate(`window.search.findNext('test')`), true); + assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'test'); + }); + + it('Scrolling Search', async () => { + let dataString = ''; + for (let i = 0; i < 100; i++) { + if (i === 52) { + dataString += '$^1_3{}test$#'; + } + dataString += makeData(50); + } + await writeSync(dataString); + assert.deepEqual(await page.evaluate(`window.search.findNext('$^1_3{}test$#')`), true); + assert.deepEqual(await page.evaluate(`window.term.getSelection()`), '$^1_3{}test$#'); + }); +}); + +async function openTerminal(options: ITerminalOptions = {}): Promise { + await page.evaluate(`window.term = new Terminal(${JSON.stringify(options)})`); + await page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`); + if (options.rendererType === 'dom') { + await page.waitForSelector('.xterm-rows'); + } else { + await page.waitForSelector('.xterm-text-layer'); + } +} + +async function writeSync(data: string): Promise { + await page.evaluate(`window.term.write('${data}');`); + while (true) { + if (await page.evaluate(`window.term._core.writeBuffer.length === 0`)) { + break; + } + } +} + +function makeData(length: number): string { + let result = ''; + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * characters.length)); + } + return result; +} From 7860dccdcf30b116cdc7ea3cf997d2a325fcbaee Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Wed, 24 Jul 2019 14:53:14 -0700 Subject: [PATCH 02/15] Search addon tests --- addons/xterm-addon-search/src/SearchAddon.api.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.api.ts b/addons/xterm-addon-search/src/SearchAddon.api.ts index 188f7c3f..9ba79686 100644 --- a/addons/xterm-addon-search/src/SearchAddon.api.ts +++ b/addons/xterm-addon-search/src/SearchAddon.api.ts @@ -14,7 +14,7 @@ let page: puppeteer.Page; const width = 800; const height = 600; -describe.only('Broken Tests', function (): void { +describe.only('Search Tests', function (): void { this.timeout(200000); before(async function (): Promise { @@ -32,7 +32,7 @@ describe.only('Broken Tests', function (): void { }); after(() => { - // browser.close(); + browser.close(); }); beforeEach(async () => { From cae3d59e2367ea74ac5fe88186b50e4d35b5e79b Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Wed, 24 Jul 2019 15:09:59 -0700 Subject: [PATCH 03/15] Change search addon behavior --- addons/xterm-addon-search/src/SearchAddon.ts | 65 +++----------------- 1 file changed, 7 insertions(+), 58 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 8fcfc690..054b97cd 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -58,7 +58,7 @@ export class SearchAddon implements ITerminalAddon { } let startCol: number = 0; - let startRow = this._terminal.buffer.viewportY; + let startRow = 0; if (this._terminal.hasSelection()) { const incremental = searchOptions ? searchOptions.incremental : false; @@ -72,16 +72,9 @@ export class SearchAddon implements ITerminalAddon { this._initLinesCache(); // A row that has isWrapped = false - let findingRow = startRow; + const findingRow = startRow; // index of beginning column that _findInLine need to scan. - let cumulativeCols = startCol; - // If startRow is wrapped row, scan for unwrapped row above. - // So we can start matching on wrapped line from long unwrapped line. - let currentLine = this._terminal.buffer.getLine(findingRow); - while (currentLine && currentLine.isWrapped) { - cumulativeCols += this._terminal.cols; - currentLine = this._terminal.buffer.getLine(--findingRow); - } + const cumulativeCols = startCol; // Search startRow let result = this._findInLine(term, findingRow, cumulativeCols, searchOptions); @@ -89,7 +82,7 @@ export class SearchAddon implements ITerminalAddon { // Search from startRow + 1 to end if (!result) { - for (let y = startRow + 1; y < this._terminal.buffer.baseY + this._terminal.rows; y++) { + for (let y = startRow; y < this._terminal.buffer.baseY + this._terminal.rows; y++) { // If the current line is wrapped line, increase index of column to ignore the previous scan // Otherwise, reset beginning column index to zero with set new unwrapped line index @@ -100,17 +93,6 @@ export class SearchAddon implements ITerminalAddon { } } - // Search from the top to the startRow (search the whole startRow again in - // case startCol > 0) - if (!result) { - for (let y = 0; y < findingRow; y++) { - result = this._findInLine(term, y, 0, searchOptions); - if (result) { - break; - } - } - } - // Set selection and scroll if a result was found return this._selectResult(result); } @@ -133,7 +115,7 @@ export class SearchAddon implements ITerminalAddon { } const isReverseSearch = true; - let startRow = this._terminal.buffer.viewportY + this._terminal.rows - 1; + let startRow = this._terminal.buffer.baseY + this._terminal.rows; let startCol = this._terminal.cols; if (this._terminal.hasSelection()) { @@ -150,44 +132,11 @@ export class SearchAddon implements ITerminalAddon { // Search from startRow - 1 to top if (!result) { - // If the line is wrapped line, increase number of columns that is needed to be scanned - // Se we can scan on wrapped line from unwrapped line - let cumulativeCols = this._terminal.cols; - if (this._terminal.buffer.getLine(startRow)!.isWrapped) { - cumulativeCols += startCol; - } - for (let y = startRow - 1; y >= 0; y--) { - result = this._findInLine(term, y, cumulativeCols, searchOptions, isReverseSearch); + for (let y = startRow; y >= 0; y--) { + result = this._findInLine(term, y, startCol, searchOptions, isReverseSearch); if (result) { break; } - // If the current line is wrapped line, increase scanning range, - // preparing for scanning on unwrapped line - const line = this._terminal.buffer.getLine(y); - if (line && line.isWrapped) { - cumulativeCols += this._terminal.cols; - } else { - cumulativeCols = this._terminal.cols; - } - } - } - - // Search from the bottom to startRow (search the whole startRow again in - // case startCol > 0) - if (!result) { - const searchFrom = this._terminal.buffer.baseY + this._terminal.rows - 1; - let cumulativeCols = this._terminal.cols; - for (let y = searchFrom; y >= startRow; y--) { - result = this._findInLine(term, y, cumulativeCols, searchOptions, isReverseSearch); - if (result) { - break; - } - const line = this._terminal.buffer.getLine(y); - if (line && line.isWrapped) { - cumulativeCols += this._terminal.cols; - } else { - cumulativeCols = this._terminal.cols; - } } } From f3f52e6920dba1fef5f27d5740ac0dcc3d8861d6 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Wed, 24 Jul 2019 15:30:09 -0700 Subject: [PATCH 04/15] Changed default Search Addon behavior --- addons/xterm-addon-search/src/SearchAddon.ts | 23 ++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 054b97cd..bd962ee5 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -82,7 +82,7 @@ export class SearchAddon implements ITerminalAddon { // Search from startRow + 1 to end if (!result) { - for (let y = startRow; y < this._terminal.buffer.baseY + this._terminal.rows; y++) { + for (let y = startRow + 1; y < this._terminal.buffer.baseY + this._terminal.rows; y++) { // If the current line is wrapped line, increase index of column to ignore the previous scan // Otherwise, reset beginning column index to zero with set new unwrapped line index @@ -92,6 +92,15 @@ export class SearchAddon implements ITerminalAddon { } } } + // If we hit the bottom and didn't search from the very top wrap back up + if (!result && startRow !== 0) { + for (let y = 0; y < startRow; y++) { + result = this._findInLine(term, y, 0, searchOptions); + if (result) { + break; + } + } + } // Set selection and scroll if a result was found return this._selectResult(result); @@ -132,7 +141,17 @@ export class SearchAddon implements ITerminalAddon { // Search from startRow - 1 to top if (!result) { - for (let y = startRow; y >= 0; y--) { + startCol = this._terminal.cols; + for (let y = startRow - 1; y >= 0; y--) { + result = this._findInLine(term, y, startCol, searchOptions, isReverseSearch); + if (result) { + break; + } + } + } + // If we hit the top and didn't search from the very bottom wrap back down + if (!result && startRow !== (this._terminal.buffer.baseY + this._terminal.rows)) { + for (let y = (this._terminal.buffer.baseY + this._terminal.rows); y > startRow; y--) { result = this._findInLine(term, y, startCol, searchOptions, isReverseSearch); if (result) { break; From d104f39a9d886413f6d05d890222838ddf0cb8bb Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Wed, 24 Jul 2019 16:01:07 -0700 Subject: [PATCH 05/15] Incremental previous search --- addons/xterm-addon-search/src/SearchAddon.ts | 7 ++++--- demo/client.ts | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index bd962ee5..b3b430a5 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -126,10 +126,11 @@ export class SearchAddon implements ITerminalAddon { const isReverseSearch = true; let startRow = this._terminal.buffer.baseY + this._terminal.rows; let startCol = this._terminal.cols; - + let result: ISearchResult | undefined = undefined; + const incremental = searchOptions ? searchOptions.incremental : false; if (this._terminal.hasSelection()) { - // Start from the selection start if there is a selection const currentSelection = this._terminal.getSelectionPosition()!; + // Start from selection start if there is a selection startRow = currentSelection.startRow; startCol = currentSelection.startColumn; } @@ -137,7 +138,7 @@ export class SearchAddon implements ITerminalAddon { this._initLinesCache(); // Search startRow - let result = this._findInLine(term, startRow, startCol, searchOptions, isReverseSearch); + result = incremental ? this._findInLine(term, startRow, startCol, searchOptions, false) : this._findInLine(term, startRow, startCol, searchOptions, isReverseSearch); // Search from startRow - 1 to top if (!result) { diff --git a/demo/client.ts b/demo/client.ts index a28a720c..104fa49a 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -140,9 +140,9 @@ function createTerminal(): void { }); addDomListener(actionElements.findPrevious, 'keyup', (e) => { - if (e.key === `Enter`) { - searchAddon.findPrevious(actionElements.findPrevious.value, getSearchOptions()); - } + const searchOptions = getSearchOptions(); + searchOptions.incremental = e.key !== `Enter`; + searchAddon.findPrevious(actionElements.findPrevious.value, searchOptions); }); // fit is called within a setTimeout, cols and rows need this. From db936701f48e30871a2b8af711664a311214b2cf Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 24 Jul 2019 16:09:57 -0700 Subject: [PATCH 06/15] Include incremental in getSearchOptions --- demo/client.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/demo/client.ts b/demo/client.ts index 104fa49a..040292e4 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -58,11 +58,12 @@ function setPadding(): void { term.fit(); } -function getSearchOptions(): ISearchOptions { +function getSearchOptions(e: KeyboardEvent): ISearchOptions { return { regex: (document.getElementById('regex') as HTMLInputElement).checked, wholeWord: (document.getElementById('whole-word') as HTMLInputElement).checked, - caseSensitive: (document.getElementById('case-sensitive') as HTMLInputElement).checked + caseSensitive: (document.getElementById('case-sensitive') as HTMLInputElement).checked, + incremental: e.key !== `Enter` }; } @@ -134,15 +135,11 @@ function createTerminal(): void { addDomListener(paddingElement, 'change', setPadding); addDomListener(actionElements.findNext, 'keyup', (e) => { - const searchOptions = getSearchOptions(); - searchOptions.incremental = e.key !== `Enter`; - searchAddon.findNext(actionElements.findNext.value, searchOptions); + searchAddon.findNext(actionElements.findNext.value, getSearchOptions(e)); }); addDomListener(actionElements.findPrevious, 'keyup', (e) => { - const searchOptions = getSearchOptions(); - searchOptions.incremental = e.key !== `Enter`; - searchAddon.findPrevious(actionElements.findPrevious.value, searchOptions); + searchAddon.findPrevious(actionElements.findPrevious.value, getSearchOptions(e)); }); // fit is called within a setTimeout, cols and rows need this. From ea72a3b594c2d67da0b3d4b1f1c485ab8cd57e39 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 25 Jul 2019 08:50:37 -0700 Subject: [PATCH 07/15] Fix incremental search bug --- addons/xterm-addon-search/src/SearchAddon.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index b3b430a5..2b8c4234 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -137,8 +137,14 @@ export class SearchAddon implements ITerminalAddon { this._initLinesCache(); - // Search startRow - result = incremental ? this._findInLine(term, startRow, startCol, searchOptions, false) : this._findInLine(term, startRow, startCol, searchOptions, isReverseSearch); + if (incremental) { + result = this._findInLine(term, startRow, startCol, searchOptions, false); + if (!(result && result.row === startRow && result.col === startCol)) { + result = this._findInLine(term, startRow, startCol, searchOptions, true); + } + } else { + result = this._findInLine(term, startRow, startCol, searchOptions, isReverseSearch); + } // Search from startRow - 1 to top if (!result) { From a1be1f0b3564b02171b8fa444d38c39d35868df8 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 25 Jul 2019 09:02:32 -0700 Subject: [PATCH 08/15] Code cleanup --- addons/xterm-addon-search/src/SearchAddon.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 2b8c4234..e9f8a9b4 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -57,7 +57,7 @@ export class SearchAddon implements ITerminalAddon { return false; } - let startCol: number = 0; + let startCol = 0; let startRow = 0; if (this._terminal.hasSelection()) { @@ -71,13 +71,8 @@ export class SearchAddon implements ITerminalAddon { this._initLinesCache(); - // A row that has isWrapped = false - const findingRow = startRow; - // index of beginning column that _findInLine need to scan. - const cumulativeCols = startCol; - // Search startRow - let result = this._findInLine(term, findingRow, cumulativeCols, searchOptions); + let result = this._findInLine(term, startRow, startCol, searchOptions); // Search from startRow + 1 to end if (!result) { From 7479131747c72a8ab21affdccd3c3d1918f7b9d7 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 25 Jul 2019 10:05:25 -0700 Subject: [PATCH 09/15] Cancel keydown events when not in screenReaderMode This caused issues with embedder keybinding systems firing when they shouldn't be. The fix is to only allow it in screenReaderMode as a compromise so keys are echoed. --- src/Terminal.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index d08ae73c..49b451ba 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -1568,10 +1568,19 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this.textarea.value = ''; } - this._keyDownHandled = true; this._onKey.fire({ key: result.key, domEvent: event }); this.showCursor(); this._coreService.triggerDataEvent(result.key, true); + + // Cancel events when not in screen reader mode so events don't get bubbled up and handled by + // other listeners. When screen reader mode is enabled, this could cause issues if the event + // is handled at a higher level, this is a compromise in order to echo keys to the screen + // reader. + if (!this.optionsService.options.screenReaderMode) { + return this.cancel(event, true); + } + + this._keyDownHandled = true; } private _isThirdLevelShift(browser: IBrowser, ev: IKeyboardEvent): boolean { From 41f418e91e163072aaafb64232c35bbd07739494 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 25 Jul 2019 10:13:40 -0700 Subject: [PATCH 10/15] Revert tests to how they used to be --- src/Terminal.test.ts | 39 +++++++++++---------------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index 2de54396..714257f9 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -556,16 +556,12 @@ describe('Terminal', () => { afterEach(() => term.browser.isMac = originalIsMac); it('should interfere with the alt key on keyDown', () => { - (term)._keyDownHandled = false; evKeyDown.altKey = true; evKeyDown.keyCode = 81; - term.keyDown(evKeyDown); - assert.equal((term)._keyDownHandled, true); - (term)._keyDownHandled = false; + assert.equal(term.keyDown(evKeyDown), false); evKeyDown.altKey = true; evKeyDown.keyCode = 192; - term.keyDown(evKeyDown); - assert.equal((term)._keyDownHandled, true); + assert.equal(term.keyDown(evKeyDown), false); }); }); @@ -578,29 +574,22 @@ describe('Terminal', () => { afterEach(() => term.browser.isMac = originalIsMac); it('should not interfere with the alt key on keyDown', () => { - (term)._keyDownHandled = false; evKeyDown.altKey = true; evKeyDown.keyCode = 81; - term.keyDown(evKeyDown); - assert.equal((term)._keyDownHandled, false); - (term)._keyDownHandled = false; + assert.equal(term.keyDown(evKeyDown), true); evKeyDown.altKey = true; evKeyDown.keyCode = 192; term.keyDown(evKeyDown); - assert.equal((term)._keyDownHandled, false); + assert.equal(term.keyDown(evKeyDown), true); }); it('should interfere with the alt + arrow keys', () => { - (term)._keyDownHandled = false; evKeyDown.altKey = true; evKeyDown.keyCode = 37; - term.keyDown(evKeyDown); - assert.equal((term)._keyDownHandled, true); - (term)._keyDownHandled = false; + assert.equal(term.keyDown(evKeyDown), false); evKeyDown.altKey = true; evKeyDown.keyCode = 39; - term.keyDown(evKeyDown); - assert.equal((term)._keyDownHandled, true); + assert.equal(term.keyDown(evKeyDown), false); }); it('should emit key with alt + key on keyPress', (done) => { @@ -652,32 +641,26 @@ describe('Terminal', () => { afterEach(() => term.browser.isWindows = originalIsWindows); it('should not interfere with the alt + ctrl key on keyDown', () => { - (term)._keyDownHandled = false; evKeyPress.altKey = true; evKeyPress.ctrlKey = true; evKeyPress.keyCode = 81; - term.keyDown(evKeyPress); - assert.equal((term)._keyDownHandled, false); - (term)._keyDownHandled = false; + assert.equal(term.keyDown(evKeyPress), true); evKeyDown.altKey = true; evKeyDown.ctrlKey = true; evKeyDown.keyCode = 81; term.keyDown(evKeyDown); - assert.equal((term)._keyDownHandled, false); + assert.equal(term.keyDown(evKeyPress), true); }); - it('should interefere with the alt + ctrl + arrow keys', () => { + it('should interfere with the alt + ctrl + arrow keys', () => { evKeyDown.altKey = true; evKeyDown.ctrlKey = true; - (term)._keyDownHandled = false; evKeyDown.keyCode = 37; - term.keyDown(evKeyDown); - assert.equal((term)._keyDownHandled, true); - (term)._keyDownHandled = false; + assert.equal(term.keyDown(evKeyDown), false); evKeyDown.keyCode = 39; term.keyDown(evKeyDown); - assert.equal((term)._keyDownHandled, true); + assert.equal(term.keyDown(evKeyDown), false); }); it('should emit key with alt + ctrl + key on keyPress', (done) => { From bb4c694f8d626b4a2d66ceac8de42855b5bca99b Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 25 Jul 2019 10:33:33 -0700 Subject: [PATCH 11/15] More search tests --- .../xterm-addon-search/src/SearchAddon.api.ts | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/addons/xterm-addon-search/src/SearchAddon.api.ts b/addons/xterm-addon-search/src/SearchAddon.api.ts index 9ba79686..f2c4faea 100644 --- a/addons/xterm-addon-search/src/SearchAddon.api.ts +++ b/addons/xterm-addon-search/src/SearchAddon.api.ts @@ -57,6 +57,45 @@ describe.only('Search Tests', function (): void { assert.deepEqual(await page.evaluate(`window.search.findNext('$^1_3{}test$#')`), true); assert.deepEqual(await page.evaluate(`window.term.getSelection()`), '$^1_3{}test$#'); }); + it ('Incremental Find Previous', async () => { + await page.evaluate(`window.term.writeln('package.jsonc\\n')`); + await writeSync('package.json pack package.lock'); + await page.evaluate(`window.search.findPrevious('pack', {incremental: true})`); + let line: string = await page.evaluate(`window.term.buffer.getLine(window.term.getSelectionPosition().startRow).translateToString()`); + let selectionPosition: {startColumn: number, startRow: number, endColumn: number, endRow: number} = await page.evaluate(`window.term.getSelectionPosition()`); + // We look further ahead in the line to ensure that pack was selected from package.lock + assert.deepEqual(line.substring(selectionPosition.startColumn, selectionPosition.endColumn + 8), 'package.lock'); + await page.evaluate(`window.search.findPrevious('package.j', {incremental: true})`); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(line.substring(selectionPosition.startColumn, selectionPosition.endColumn + 3), 'package.json'); + await page.evaluate(`window.search.findPrevious('package.jsonc', {incremental: true})`); + // We have to reevaluate line because it should have switched starting rows at this point + line = await page.evaluate(`window.term.buffer.getLine(window.term.getSelectionPosition().startRow).translateToString()`); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(line.substring(selectionPosition.startColumn, selectionPosition.endColumn), 'package.jsonc'); + }); + it ('Incremental Find Next', async () => { + await page.evaluate(`window.term.writeln('package.lock pack package.json package.ups\\n')`); + await writeSync('package.jsonc'); + await page.evaluate(`window.search.findNext('pack', {incremental: true})`); + let line: string = await page.evaluate(`window.term.buffer.getLine(window.term.getSelectionPosition().startRow).translateToString()`); + let selectionPosition: {startColumn: number, startRow: number, endColumn: number, endRow: number} = await page.evaluate(`window.term.getSelectionPosition()`); + // We look further ahead in the line to ensure that pack was selected from package.lock + assert.deepEqual(line.substring(selectionPosition.startColumn, selectionPosition.endColumn + 8), 'package.lock'); + await page.evaluate(`window.search.findNext('package.j', {incremental: true})`); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(line.substring(selectionPosition.startColumn, selectionPosition.endColumn + 3), 'package.json'); + await page.evaluate(`window.search.findNext('package.jsonc', {incremental: true})`); + // We have to reevaluate line because it should have switched starting rows at this point + line = await page.evaluate(`window.term.buffer.getLine(window.term.getSelectionPosition().startRow).translateToString()`); + selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`); + assert.deepEqual(line.substring(selectionPosition.startColumn, selectionPosition.endColumn), 'package.jsonc'); + }); + it ('Simple Regex', async () => { + await writeSync('abc123def'); + await page.evaluate(`window.search.findNext('[a-z]+', {regex: true})`); + assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'abc'); + }); }); async function openTerminal(options: ITerminalOptions = {}): Promise { From 6869184864b7a36fde100d330dc5872ee6b23d1c Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 25 Jul 2019 10:39:11 -0700 Subject: [PATCH 12/15] More RegEx tests --- addons/xterm-addon-search/src/SearchAddon.api.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.api.ts b/addons/xterm-addon-search/src/SearchAddon.api.ts index f2c4faea..8f559cac 100644 --- a/addons/xterm-addon-search/src/SearchAddon.api.ts +++ b/addons/xterm-addon-search/src/SearchAddon.api.ts @@ -92,9 +92,11 @@ describe.only('Search Tests', function (): void { assert.deepEqual(line.substring(selectionPosition.startColumn, selectionPosition.endColumn), 'package.jsonc'); }); it ('Simple Regex', async () => { - await writeSync('abc123def'); + await writeSync('abc123defABCD'); await page.evaluate(`window.search.findNext('[a-z]+', {regex: true})`); assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'abc'); + await page.evaluate(`window.search.findNext('[A-Z]+', {regex: true})`); + assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'ABCD'); }); }); From dcb39a658c33dd4965e1f2b8d0f8ecdeddc1c553 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 25 Jul 2019 10:41:55 -0700 Subject: [PATCH 13/15] Case sensitivity enabled --- addons/xterm-addon-search/src/SearchAddon.api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.api.ts b/addons/xterm-addon-search/src/SearchAddon.api.ts index 8f559cac..0f502417 100644 --- a/addons/xterm-addon-search/src/SearchAddon.api.ts +++ b/addons/xterm-addon-search/src/SearchAddon.api.ts @@ -95,7 +95,7 @@ describe.only('Search Tests', function (): void { await writeSync('abc123defABCD'); await page.evaluate(`window.search.findNext('[a-z]+', {regex: true})`); assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'abc'); - await page.evaluate(`window.search.findNext('[A-Z]+', {regex: true})`); + await page.evaluate(`window.search.findNext('[A-Z]+', {regex: true, caseSensitive: true})`); assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'ABCD'); }); }); From 3f53f0ed29cc53d154cf35d88235616e790547a3 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 25 Jul 2019 10:57:03 -0700 Subject: [PATCH 14/15] Remove describe.only --- addons/xterm-addon-search/src/SearchAddon.api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.api.ts b/addons/xterm-addon-search/src/SearchAddon.api.ts index 0f502417..f970fcc8 100644 --- a/addons/xterm-addon-search/src/SearchAddon.api.ts +++ b/addons/xterm-addon-search/src/SearchAddon.api.ts @@ -14,7 +14,7 @@ let page: puppeteer.Page; const width = 800; const height = 600; -describe.only('Search Tests', function (): void { +describe('Search Tests', function (): void { this.timeout(200000); before(async function (): Promise { From 3dc52184e5b4d66519ec26b7571d554c22b83e74 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 25 Jul 2019 16:49:26 -0700 Subject: [PATCH 15/15] Fix NPE on dispose when it hasn't been opened --- src/Terminal.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 49b451ba..f857130b 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -270,7 +270,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._windowsMode.dispose(); this._windowsMode = undefined; } - this._renderService.dispose(); + if (this._renderService) { + this._renderService.dispose(); + } this._customKeyEventHandler = null; this.write = () => {}; if (this.element && this.element.parentNode) {