From b78cb4c8d4ae7d5e4bc9825a376101ab48b2337f Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 23 Mar 2022 11:16:01 -0400 Subject: [PATCH 01/27] add find count --- addons/xterm-addon-search/src/SearchAddon.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 3dd2992dac..958139128c 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -54,6 +54,7 @@ export class SearchAddon implements ITerminalAddon { private _onDataDisposable: IDisposable | undefined; private _lastSearchOptions: ISearchOptions | undefined; private _highlightTimeout: number | undefined; + private _searchResultIndex: number = 1; /** * translateBufferLineToStringWithWrap is a fairly expensive call. * We memoize the calls into an array that has a time based ttl. @@ -102,6 +103,10 @@ export class SearchAddon implements ITerminalAddon { this._resultDecorations.clear(); } + public getSearchResultCount(): string { + return `${this._searchResultIndex}%${this._searchResults?.size || 0}`; + } + /** * Find the next instance of the term, then scroll to and select it. If it * doesn't exist, do nothing. @@ -116,6 +121,7 @@ export class SearchAddon implements ITerminalAddon { this._lastSearchOptions = searchOptions; const findNextResult = this._findNextAndSelect(term, searchOptions); if (searchOptions?.decorations) { + this._searchResultIndex++; this._highlightAllMatches(term, searchOptions); } return findNextResult; @@ -271,6 +277,7 @@ export class SearchAddon implements ITerminalAddon { this._lastSearchOptions = searchOptions; const findPreviousResult = this._findAndSelectPrevious(term, searchOptions); if (searchOptions?.decorations) { + this._searchResultIndex--; this._highlightAllMatches(term, searchOptions); } return findPreviousResult; From c1c0df494fa689c01b6c00322c041768a1adfd2b Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 28 Mar 2022 15:38:50 -0400 Subject: [PATCH 02/27] remove index for now --- addons/xterm-addon-search/src/SearchAddon.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 958139128c..b2b0e6c978 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -54,7 +54,6 @@ export class SearchAddon implements ITerminalAddon { private _onDataDisposable: IDisposable | undefined; private _lastSearchOptions: ISearchOptions | undefined; private _highlightTimeout: number | undefined; - private _searchResultIndex: number = 1; /** * translateBufferLineToStringWithWrap is a fairly expensive call. * We memoize the calls into an array that has a time based ttl. @@ -103,8 +102,8 @@ export class SearchAddon implements ITerminalAddon { this._resultDecorations.clear(); } - public getSearchResultCount(): string { - return `${this._searchResultIndex}%${this._searchResults?.size || 0}`; + public getSearchResultCount(): number { + return this._searchResults?.size || 0; } /** @@ -121,7 +120,6 @@ export class SearchAddon implements ITerminalAddon { this._lastSearchOptions = searchOptions; const findNextResult = this._findNextAndSelect(term, searchOptions); if (searchOptions?.decorations) { - this._searchResultIndex++; this._highlightAllMatches(term, searchOptions); } return findNextResult; @@ -277,7 +275,6 @@ export class SearchAddon implements ITerminalAddon { this._lastSearchOptions = searchOptions; const findPreviousResult = this._findAndSelectPrevious(term, searchOptions); if (searchOptions?.decorations) { - this._searchResultIndex--; this._highlightAllMatches(term, searchOptions); } return findPreviousResult; From 6e7d1decc25d04c6807a5ac89ae1a7e8d0ae2068 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 28 Mar 2022 15:39:57 -0400 Subject: [PATCH 03/27] add jsdoc --- addons/xterm-addon-search/src/SearchAddon.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 32fb154d72..3218a77ffe 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -106,6 +106,9 @@ export class SearchAddon implements ITerminalAddon { this._resultDecorations.clear(); } + /** + * @returns the last search result count or 0 + */ public getSearchResultCount(): number { return this._searchResults?.size || 0; } From 6ff999b3b23eaedc3b1c98a2791725713cbe712d Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 28 Mar 2022 17:02:04 -0400 Subject: [PATCH 04/27] update jsdoc --- addons/xterm-addon-search/src/SearchAddon.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 3218a77ffe..d9e0a10646 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -108,6 +108,7 @@ export class SearchAddon implements ITerminalAddon { /** * @returns the last search result count or 0 + * if there are no results or decorations aren't enabled */ public getSearchResultCount(): number { return this._searchResults?.size || 0; From 463a0e9dddd3fc8c5ec70c4e691efa4b88430ed0 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 29 Mar 2022 12:51:24 -0400 Subject: [PATCH 05/27] property instead of method --- addons/xterm-addon-search/src/SearchAddon.ts | 6 +++--- addons/xterm-addon-search/typings/xterm-addon-search.d.ts | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index d9e0a10646..bd49d8ce5c 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -107,10 +107,10 @@ export class SearchAddon implements ITerminalAddon { } /** - * @returns the last search result count or 0 - * if there are no results or decorations aren't enabled + * @returns the last search result count or undefined + * if decorations aren't enabled */ - public getSearchResultCount(): number { + public get resultCount(): number { return this._searchResults?.size || 0; } diff --git a/addons/xterm-addon-search/typings/xterm-addon-search.d.ts b/addons/xterm-addon-search/typings/xterm-addon-search.d.ts index aedb6af9b9..f05bf7ff37 100644 --- a/addons/xterm-addon-search/typings/xterm-addon-search.d.ts +++ b/addons/xterm-addon-search/typings/xterm-addon-search.d.ts @@ -110,5 +110,11 @@ declare module 'xterm-addon-search' { * Clears the decorations and selection */ public clearDecorations(): void; + + /** + * @returns the last search result count or undefined + * if decorations aren't enabled + */ + public get resultCount(): number; } } From 49a170fc283b8a3d02b359254feba713034ea431 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 29 Mar 2022 12:57:01 -0400 Subject: [PATCH 06/27] return undefined when no search results --- addons/xterm-addon-search/src/SearchAddon.ts | 11 +++++++---- .../typings/xterm-addon-search.d.ts | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index bd49d8ce5c..6ea7acabf2 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -54,7 +54,7 @@ export class SearchAddon implements ITerminalAddon { private _cachedSearchTerm: string | undefined; private _selectedDecoration: IDecoration | undefined; private _resultDecorations: Map = new Map(); - private _searchResults: Map = new Map(); + private _searchResults: Map | undefined; private _onDataDisposable: IDisposable | undefined; private _lastSearchOptions: ISearchOptions | undefined; private _highlightTimeout: number | undefined; @@ -91,7 +91,7 @@ export class SearchAddon implements ITerminalAddon { public clearDecorations(): void { this._selectedDecoration?.dispose(); this._terminal?.clearSelection(); - this._searchResults.clear(); + this._searchResults?.clear(); this._disposeDecorations(); this._cachedSearchTerm = undefined; this._dataChanged = true; @@ -110,8 +110,8 @@ export class SearchAddon implements ITerminalAddon { * @returns the last search result count or undefined * if decorations aren't enabled */ - public get resultCount(): number { - return this._searchResults?.size || 0; + public get resultCount(): number | undefined { + return this._searchResults?.size || undefined; } /** @@ -137,6 +137,9 @@ export class SearchAddon implements ITerminalAddon { if (!this._terminal) { throw new Error('Cannot use addon until it has been loaded'); } + if (!this._searchResults) { + this._searchResults = new Map(); + } if (!term || term.length === 0) { this.clearDecorations(); return; diff --git a/addons/xterm-addon-search/typings/xterm-addon-search.d.ts b/addons/xterm-addon-search/typings/xterm-addon-search.d.ts index f05bf7ff37..a1d07f32cd 100644 --- a/addons/xterm-addon-search/typings/xterm-addon-search.d.ts +++ b/addons/xterm-addon-search/typings/xterm-addon-search.d.ts @@ -115,6 +115,6 @@ declare module 'xterm-addon-search' { * @returns the last search result count or undefined * if decorations aren't enabled */ - public get resultCount(): number; + public get resultCount(): number | undefined; } } From 691f3c94bb32cd167eb638d1ba500d5ee9ab9126 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 29 Mar 2022 12:59:25 -0400 Subject: [PATCH 07/27] initialize maps in highlightAllMatches --- addons/xterm-addon-search/src/SearchAddon.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 6ea7acabf2..e2aeb14142 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -53,7 +53,7 @@ export class SearchAddon implements ITerminalAddon { private _dataChanged: boolean = false; private _cachedSearchTerm: string | undefined; private _selectedDecoration: IDecoration | undefined; - private _resultDecorations: Map = new Map(); + private _resultDecorations: Map | undefined; private _searchResults: Map | undefined; private _onDataDisposable: IDisposable | undefined; private _lastSearchOptions: ISearchOptions | undefined; @@ -76,7 +76,7 @@ export class SearchAddon implements ITerminalAddon { window.clearTimeout(this._highlightTimeout); } this._highlightTimeout = setTimeout(() => { - if (this._lastSearchOptions?.decorations && this._cachedSearchTerm && this._resultDecorations.size > 0 && this._lastSearchOptions) { + if (this._lastSearchOptions?.decorations && this._cachedSearchTerm && this._resultDecorations?.size && this._resultDecorations.size > 0 && this._lastSearchOptions) { this._highlightAllMatches(this._cachedSearchTerm, this._lastSearchOptions); } }, 200); @@ -98,6 +98,9 @@ export class SearchAddon implements ITerminalAddon { } private _disposeDecorations(): void { + if (!this._resultDecorations) { + return; + } this._resultDecorations.forEach(decorations => { for (const d of decorations) { d.dispose(); @@ -138,7 +141,10 @@ export class SearchAddon implements ITerminalAddon { throw new Error('Cannot use addon until it has been loaded'); } if (!this._searchResults) { - this._searchResults = new Map(); + this._searchResults = new Map(); + } + if (!this._resultDecorations) { + this._resultDecorations = new Map(); } if (!term || term.length === 0) { this.clearDecorations(); @@ -164,9 +170,9 @@ export class SearchAddon implements ITerminalAddon { this._searchResults.forEach(result => { const resultDecoration = this._createResultDecoration(result, searchOptions.decorations!); if (resultDecoration) { - const decorationsForLine = this._resultDecorations.get(resultDecoration.marker.line) || []; + const decorationsForLine = this._resultDecorations!.get(resultDecoration.marker.line) || []; decorationsForLine.push(resultDecoration); - this._resultDecorations.set(resultDecoration.marker.line, decorationsForLine); + this._resultDecorations!.set(resultDecoration.marker.line, decorationsForLine); } }); if (this._dataChanged) { From 9a93d5c1738ba555ff1b5d52ef516c424832af9c Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 29 Mar 2022 14:14:31 -0400 Subject: [PATCH 08/27] ! -> ? --- addons/xterm-addon-search/src/SearchAddon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index e2aeb14142..db613a0fa6 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -686,7 +686,7 @@ export class SearchAddon implements ITerminalAddon { marker, x: result.col, width: result.size, - overviewRulerOptions: this._resultDecorations.get(marker.line) && !this._dataChanged ? undefined : { + overviewRulerOptions: this._resultDecorations?.get(marker.line) && !this._dataChanged ? undefined : { color: decorations.matchOverviewRuler, position: 'center' } }); From d7642810d6f18bdeb303b56a51144b796568d4ed Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 29 Mar 2022 15:29:46 -0400 Subject: [PATCH 09/27] add event to indicate when results have changed --- addons/xterm-addon-search/src/SearchAddon.ts | 71 +++++++++++++++++++ .../typings/xterm-addon-search.d.ts | 9 ++- 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index db613a0fa6..5ffde27bf5 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -4,6 +4,69 @@ */ import { Terminal, IDisposable, ITerminalAddon, ISelectionPosition, IDecoration } from 'xterm'; +interface IListener { + (arg1: T, arg2: U): void; +} + +export interface IEvent { + (listener: (arg1: T, arg2: U) => any): IDisposable; +} + +export interface IEventEmitter { + event: IEvent; + fire(arg1: T, arg2: U): void; + dispose(): void; +} + +export class EventEmitter implements IEventEmitter { + private _listeners: IListener[] = []; + private _event?: IEvent; + private _disposed: boolean = false; + + public get event(): IEvent { + if (!this._event) { + this._event = (listener: (arg1: T, arg2: U) => any) => { + this._listeners.push(listener); + const disposable = { + dispose: () => { + if (!this._disposed) { + for (let i = 0; i < this._listeners.length; i++) { + if (this._listeners[i] === listener) { + this._listeners.splice(i, 1); + return; + } + } + } + } + }; + return disposable; + }; + } + return this._event; + } + + public fire(arg1: T, arg2: U): void { + const queue: IListener[] = []; + for (let i = 0; i < this._listeners.length; i++) { + queue.push(this._listeners[i]); + } + for (let i = 0; i < queue.length; i++) { + queue[i].call(undefined, arg1, arg2); + } + } + + public dispose(): void { + if (this._listeners) { + this._listeners.length = 0; + } + this._disposed = true; + } +} + +export function forwardEvent(from: IEvent, to: IEventEmitter): IDisposable { + return from(e => to.fire(e)); +} + export interface ISearchOptions { regex?: boolean; @@ -68,6 +131,9 @@ export class SearchAddon implements ITerminalAddon { private _cursorMoveListener: IDisposable | undefined; private _resizeListener: IDisposable | undefined; + private readonly _onDidChangeResults = new EventEmitter(); + public readonly onDidChangeResults = this._onDidChangeResults.event; + public activate(terminal: Terminal): void { this._terminal = terminal; this._onDataDisposable = this._terminal.onData(() => { @@ -154,6 +220,8 @@ export class SearchAddon implements ITerminalAddon { if (term === this._cachedSearchTerm && !this._dataChanged) { return; } + const dataDrivenChange = term === this._cachedSearchTerm && this._dataChanged; + const lastSearchCount = this._searchResults.size; // new search, clear out the old decorations this._disposeDecorations(); this._searchResults.clear(); @@ -181,6 +249,9 @@ export class SearchAddon implements ITerminalAddon { if (this._searchResults.size > 0) { this._cachedSearchTerm = term; } + if (dataDrivenChange && lastSearchCount !== this._searchResults.size) { + this._onDidChangeResults.fire(); + } } private _find(term: string, startRow: number, startCol: number, searchOptions?: ISearchOptions): ISearchResult | undefined { diff --git a/addons/xterm-addon-search/typings/xterm-addon-search.d.ts b/addons/xterm-addon-search/typings/xterm-addon-search.d.ts index a1d07f32cd..c277d713fa 100644 --- a/addons/xterm-addon-search/typings/xterm-addon-search.d.ts +++ b/addons/xterm-addon-search/typings/xterm-addon-search.d.ts @@ -3,7 +3,8 @@ * @license MIT */ -import { Terminal, ILinkMatcherOptions, IDisposable, ITerminalAddon } from 'xterm'; +import { IEvent } from 'node-pty'; +import { Terminal, ITerminalAddon } from 'xterm'; declare module 'xterm-addon-search' { /** @@ -116,5 +117,11 @@ declare module 'xterm-addon-search' { * if decorations aren't enabled */ public get resultCount(): number | undefined; + + /** + * An event listener for when the search results change + * as a result of the buffer changing + */ + onDidChangeResults: IEvent; } } From 4b6198e6c6b75d872caaba9017c39572435d970c Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Tue, 29 Mar 2022 15:52:47 -0400 Subject: [PATCH 10/27] Update addons/xterm-addon-search/src/SearchAddon.ts Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com> --- addons/xterm-addon-search/src/SearchAddon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 5ffde27bf5..2483d1bcbf 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -180,7 +180,7 @@ export class SearchAddon implements ITerminalAddon { * if decorations aren't enabled */ public get resultCount(): number | undefined { - return this._searchResults?.size || undefined; + return this._searchResults?.size; } /** From f59db298f1e3c086a28761177ffc9a2f03d94d3c Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 29 Mar 2022 17:20:55 -0400 Subject: [PATCH 11/27] refactor --- addons/xterm-addon-search/src/SearchAddon.ts | 52 +++++++++++++------ .../typings/xterm-addon-search.d.ts | 14 +++-- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 5ffde27bf5..2fe7b3a218 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -131,6 +131,8 @@ export class SearchAddon implements ITerminalAddon { private _cursorMoveListener: IDisposable | undefined; private _resizeListener: IDisposable | undefined; + private _resultIndex: number | undefined; + private readonly _onDidChangeResults = new EventEmitter(); public readonly onDidChangeResults = this._onDidChangeResults.event; @@ -175,14 +177,6 @@ export class SearchAddon implements ITerminalAddon { this._resultDecorations.clear(); } - /** - * @returns the last search result count or undefined - * if decorations aren't enabled - */ - public get resultCount(): number | undefined { - return this._searchResults?.size || undefined; - } - /** * Find the next instance of the term, then scroll to and select it. If it * doesn't exist, do nothing. @@ -190,16 +184,20 @@ export class SearchAddon implements ITerminalAddon { * @param searchOptions Search options. * @return Whether a result was found. */ - public findNext(term: string, searchOptions?: ISearchOptions): boolean { + public findNext(term: string, searchOptions?: ISearchOptions): boolean | { resultIndex: number, resultCount: number } { if (!this._terminal) { throw new Error('Cannot use addon until it has been loaded'); } this._lastSearchOptions = searchOptions; - const findNextResult = this._findNextAndSelect(term, searchOptions); if (searchOptions?.decorations) { this._highlightAllMatches(term, searchOptions); } - return findNextResult; + // if (term !== this._cachedSearchTerm) { + // this._resultIndex = undefined; + // this.clearDecorations(); + // } + const findNextResult = this._findNextAndSelect(term, searchOptions); + return this._resultIndex ? { resultIndex: this._resultIndex!, resultCount: this._searchResults?.size || 0 } : findNextResult; } private _highlightAllMatches(term: string, searchOptions: ISearchOptions): void { @@ -298,6 +296,16 @@ export class SearchAddon implements ITerminalAddon { this.clearDecorations(); return false; } + if (this._searchResults) { + if (!this._resultIndex) { + this._resultIndex = 1; + } else { + this._resultIndex++; + if (this._resultIndex > this._searchResults.size) { + this._resultIndex = 1; + } + } + } let startCol = 0; let startRow = 0; @@ -352,7 +360,6 @@ export class SearchAddon implements ITerminalAddon { searchPosition.startCol = 0; result = this._findInLine(term, searchPosition, searchOptions); } - // Set selection and scroll if a result was found return this._selectResult(result, searchOptions?.decorations); } @@ -363,16 +370,20 @@ export class SearchAddon implements ITerminalAddon { * @param searchOptions Search options. * @return Whether a result was found. */ - public findPrevious(term: string, searchOptions?: ISearchOptions): boolean { + public findPrevious(term: string, searchOptions?: ISearchOptions): boolean | { resultIndex: number, resultCount: number } { if (!this._terminal) { throw new Error('Cannot use addon until it has been loaded'); } this._lastSearchOptions = searchOptions; - const findPreviousResult = this._findAndSelectPrevious(term, searchOptions); if (searchOptions?.decorations) { this._highlightAllMatches(term, searchOptions); } - return findPreviousResult; + // if (term !== this._cachedSearchTerm) { + // this._resultIndex = undefined; + // this.clearDecorations(); + // } + const findPreviousResult = this._findAndSelectPrevious(term, searchOptions); + return this._resultIndex ? { resultIndex: this._resultIndex, resultCount: this._searchResults?.size || 0 }: findPreviousResult; } private _findAndSelectPrevious(term: string, searchOptions?: ISearchOptions): boolean { @@ -387,6 +398,16 @@ export class SearchAddon implements ITerminalAddon { return false; } + if (this._searchResults) { + if (!this._resultIndex) { + this._resultIndex = this._searchResults?.size; + } else { + this._resultIndex--; + if (this._resultIndex === 0) { + this._resultIndex = this._searchResults?.size; + } + } + } let startRow = this._terminal.buffer.active.baseY + this._terminal.rows; let startCol = this._terminal.cols; const isReverseSearch = true; @@ -443,7 +464,6 @@ export class SearchAddon implements ITerminalAddon { } } } - // If there is only one result, return true. if (!result && currentSelection) return true; diff --git a/addons/xterm-addon-search/typings/xterm-addon-search.d.ts b/addons/xterm-addon-search/typings/xterm-addon-search.d.ts index c277d713fa..7cf8bdc312 100644 --- a/addons/xterm-addon-search/typings/xterm-addon-search.d.ts +++ b/addons/xterm-addon-search/typings/xterm-addon-search.d.ts @@ -96,28 +96,26 @@ declare module 'xterm-addon-search' { * options. * @param term The search term. * @param searchOptions The options for the search. + * @returns the result index and result count if decorations are provided or a boolean + * indicating if there was a match */ - public findNext(term: string, searchOptions?: ISearchOptions): boolean; + public findNext(term: string, searchOptions?: ISearchOptions): boolean | { resultIndex: number, resultCount: number }; /** * Search backwards for the previous result that matches the search term and * options. * @param term The search term. * @param searchOptions The options for the search. + * @returns the result index and result count if decorations are provided or a boolean + * indicating if there was a match */ - public findPrevious(term: string, searchOptions?: ISearchOptions): boolean; + public findPrevious(term: string, searchOptions?: ISearchOptions): boolean | { resultIndex: number, resultCount: number }; /** * Clears the decorations and selection */ public clearDecorations(): void; - /** - * @returns the last search result count or undefined - * if decorations aren't enabled - */ - public get resultCount(): number | undefined; - /** * An event listener for when the search results change * as a result of the buffer changing From 3df6be81c98ead7335b17db97a70eb9351219f7d Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 29 Mar 2022 21:21:55 -0400 Subject: [PATCH 12/27] clear decorations find previous --- addons/xterm-addon-search/src/SearchAddon.ts | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 2fe7b3a218..d8e9f59256 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -158,11 +158,12 @@ export class SearchAddon implements ITerminalAddon { public clearDecorations(): void { this._selectedDecoration?.dispose(); - this._terminal?.clearSelection(); + // this._terminal?.clearSelection(); this._searchResults?.clear(); this._disposeDecorations(); this._cachedSearchTerm = undefined; this._dataChanged = true; + this._resultIndex = undefined; } private _disposeDecorations(): void { @@ -192,10 +193,6 @@ export class SearchAddon implements ITerminalAddon { if (searchOptions?.decorations) { this._highlightAllMatches(term, searchOptions); } - // if (term !== this._cachedSearchTerm) { - // this._resultIndex = undefined; - // this.clearDecorations(); - // } const findNextResult = this._findNextAndSelect(term, searchOptions); return this._resultIndex ? { resultIndex: this._resultIndex!, resultCount: this._searchResults?.size || 0 } : findNextResult; } @@ -221,7 +218,7 @@ export class SearchAddon implements ITerminalAddon { const dataDrivenChange = term === this._cachedSearchTerm && this._dataChanged; const lastSearchCount = this._searchResults.size; // new search, clear out the old decorations - this._disposeDecorations(); + this.clearDecorations(); this._searchResults.clear(); let result = this._find(term, 0, 0, searchOptions); while (result && !this._searchResults.get(`${result.row}-${result.col}`)) { @@ -378,10 +375,6 @@ export class SearchAddon implements ITerminalAddon { if (searchOptions?.decorations) { this._highlightAllMatches(term, searchOptions); } - // if (term !== this._cachedSearchTerm) { - // this._resultIndex = undefined; - // this.clearDecorations(); - // } const findPreviousResult = this._findAndSelectPrevious(term, searchOptions); return this._resultIndex ? { resultIndex: this._resultIndex, resultCount: this._searchResults?.size || 0 }: findPreviousResult; } From e2541a18a21a029757244be9ef43c6eab99dc932 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 29 Mar 2022 21:37:40 -0400 Subject: [PATCH 13/27] import --- addons/xterm-addon-search/src/SearchAddon.ts | 64 +------------------ addons/xterm-addon-search/tsconfig.json | 4 +- .../typings/xterm-addon-search.d.ts | 3 +- 3 files changed, 5 insertions(+), 66 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index d8e9f59256..e3639ef1ea 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -4,69 +4,7 @@ */ import { Terminal, IDisposable, ITerminalAddon, ISelectionPosition, IDecoration } from 'xterm'; -interface IListener { - (arg1: T, arg2: U): void; -} - -export interface IEvent { - (listener: (arg1: T, arg2: U) => any): IDisposable; -} - -export interface IEventEmitter { - event: IEvent; - fire(arg1: T, arg2: U): void; - dispose(): void; -} - -export class EventEmitter implements IEventEmitter { - private _listeners: IListener[] = []; - private _event?: IEvent; - private _disposed: boolean = false; - - public get event(): IEvent { - if (!this._event) { - this._event = (listener: (arg1: T, arg2: U) => any) => { - this._listeners.push(listener); - const disposable = { - dispose: () => { - if (!this._disposed) { - for (let i = 0; i < this._listeners.length; i++) { - if (this._listeners[i] === listener) { - this._listeners.splice(i, 1); - return; - } - } - } - } - }; - return disposable; - }; - } - return this._event; - } - - public fire(arg1: T, arg2: U): void { - const queue: IListener[] = []; - for (let i = 0; i < this._listeners.length; i++) { - queue.push(this._listeners[i]); - } - for (let i = 0; i < queue.length; i++) { - queue[i].call(undefined, arg1, arg2); - } - } - - public dispose(): void { - if (this._listeners) { - this._listeners.length = 0; - } - this._disposed = true; - } -} - -export function forwardEvent(from: IEvent, to: IEventEmitter): IDisposable { - return from(e => to.fire(e)); -} - +import { EventEmitter } from 'common/EventEmitter'; export interface ISearchOptions { regex?: boolean; diff --git a/addons/xterm-addon-search/tsconfig.json b/addons/xterm-addon-search/tsconfig.json index 2d820dd1a6..f7710c49fe 100644 --- a/addons/xterm-addon-search/tsconfig.json +++ b/addons/xterm-addon-search/tsconfig.json @@ -3,6 +3,8 @@ "include": [], "references": [ { "path": "./src" }, - { "path": "./test" } + { "path": "./test" }, + { "path": "../../src/common" }, + { "path": "../../src/browser" } ] } diff --git a/addons/xterm-addon-search/typings/xterm-addon-search.d.ts b/addons/xterm-addon-search/typings/xterm-addon-search.d.ts index 7cf8bdc312..6eb8baee8b 100644 --- a/addons/xterm-addon-search/typings/xterm-addon-search.d.ts +++ b/addons/xterm-addon-search/typings/xterm-addon-search.d.ts @@ -3,8 +3,7 @@ * @license MIT */ -import { IEvent } from 'node-pty'; -import { Terminal, ITerminalAddon } from 'xterm'; +import { Terminal, ITerminalAddon, IEvent } from 'xterm'; declare module 'xterm-addon-search' { /** From 1f00d13db99196c0555cc07b9e63b2760666630b Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 29 Mar 2022 21:50:10 -0400 Subject: [PATCH 14/27] correct ts config --- addons/xterm-addon-search/src/tsconfig.json | 18 +++++++++++++++++- addons/xterm-addon-search/tsconfig.json | 2 -- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/addons/xterm-addon-search/src/tsconfig.json b/addons/xterm-addon-search/src/tsconfig.json index d264abe07b..f39261cdff 100644 --- a/addons/xterm-addon-search/src/tsconfig.json +++ b/addons/xterm-addon-search/src/tsconfig.json @@ -13,10 +13,26 @@ "strict": true, "types": [ "../../../node_modules/@types/mocha" - ] + ], + "paths": { + "common/*": [ + "../../../src/common/*" + ], + "browser/*": [ + "../../../src/browser/*" + ] + } }, "include": [ "./**/*", "../../../typings/xterm.d.ts" + ], + "references": [ + { + "path": "../../../src/common" + }, + { + "path": "../../../src/browser" + } ] } diff --git a/addons/xterm-addon-search/tsconfig.json b/addons/xterm-addon-search/tsconfig.json index f7710c49fe..332b9e1753 100644 --- a/addons/xterm-addon-search/tsconfig.json +++ b/addons/xterm-addon-search/tsconfig.json @@ -4,7 +4,5 @@ "references": [ { "path": "./src" }, { "path": "./test" }, - { "path": "../../src/common" }, - { "path": "../../src/browser" } ] } From c7d24bf7f764505c27656e1b7d4921905e4cc73b Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Tue, 29 Mar 2022 21:52:38 -0400 Subject: [PATCH 15/27] Update addons/xterm-addon-search/tsconfig.json --- addons/xterm-addon-search/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/xterm-addon-search/tsconfig.json b/addons/xterm-addon-search/tsconfig.json index 332b9e1753..2d820dd1a6 100644 --- a/addons/xterm-addon-search/tsconfig.json +++ b/addons/xterm-addon-search/tsconfig.json @@ -3,6 +3,6 @@ "include": [], "references": [ { "path": "./src" }, - { "path": "./test" }, + { "path": "./test" } ] } From f93e16669643da4c39db4cc711bb68efb1b27002 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Tue, 29 Mar 2022 21:53:26 -0400 Subject: [PATCH 16/27] Update addons/xterm-addon-search/src/SearchAddon.ts --- addons/xterm-addon-search/src/SearchAddon.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index e3639ef1ea..9689813daf 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -96,7 +96,6 @@ export class SearchAddon implements ITerminalAddon { public clearDecorations(): void { this._selectedDecoration?.dispose(); - // this._terminal?.clearSelection(); this._searchResults?.clear(); this._disposeDecorations(); this._cachedSearchTerm = undefined; From cd0658dac17230b1e86e7838f4b70dce59d6b595 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Tue, 29 Mar 2022 21:58:28 -0400 Subject: [PATCH 17/27] Update addons/xterm-addon-search/src/SearchAddon.ts --- addons/xterm-addon-search/src/SearchAddon.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 9689813daf..7521d0bd71 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -182,6 +182,7 @@ export class SearchAddon implements ITerminalAddon { this._cachedSearchTerm = term; } if (dataDrivenChange && lastSearchCount !== this._searchResults.size) { + // When data is added to the buffer that yields new search results, fire this so the match result count can be updated this._onDidChangeResults.fire(); } } From 56ab94df816c23b5ce364ff5ce1ce4603a204fb9 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 30 Mar 2022 11:48:04 -0400 Subject: [PATCH 18/27] refactor to fire event --- addons/xterm-addon-search/src/SearchAddon.ts | 40 +++++++++++-------- .../typings/xterm-addon-search.d.ts | 10 ++--- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index e3639ef1ea..95e55cc3b0 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -59,6 +59,7 @@ export class SearchAddon implements ITerminalAddon { private _onDataDisposable: IDisposable | undefined; private _lastSearchOptions: ISearchOptions | undefined; private _highlightTimeout: number | undefined; + private _previousWasLastCall: boolean = true; /** * translateBufferLineToStringWithWrap is a fairly expensive call. * We memoize the calls into an array that has a time based ttl. @@ -71,7 +72,7 @@ export class SearchAddon implements ITerminalAddon { private _resultIndex: number | undefined; - private readonly _onDidChangeResults = new EventEmitter(); + private readonly _onDidChangeResults = new EventEmitter<{resultIndex: number, resultCount: number} | undefined>(); public readonly onDidChangeResults = this._onDidChangeResults.event; public activate(terminal: Terminal): void { @@ -83,7 +84,7 @@ export class SearchAddon implements ITerminalAddon { } this._highlightTimeout = setTimeout(() => { if (this._lastSearchOptions?.decorations && this._cachedSearchTerm && this._resultDecorations?.size && this._resultDecorations.size > 0 && this._lastSearchOptions) { - this._highlightAllMatches(this._cachedSearchTerm, this._lastSearchOptions); + this.findPrevious(this._cachedSearchTerm, this._lastSearchOptions); } }, 200); }); @@ -96,7 +97,6 @@ export class SearchAddon implements ITerminalAddon { public clearDecorations(): void { this._selectedDecoration?.dispose(); - // this._terminal?.clearSelection(); this._searchResults?.clear(); this._disposeDecorations(); this._cachedSearchTerm = undefined; @@ -123,7 +123,7 @@ export class SearchAddon implements ITerminalAddon { * @param searchOptions Search options. * @return Whether a result was found. */ - public findNext(term: string, searchOptions?: ISearchOptions): boolean | { resultIndex: number, resultCount: number } { + public findNext(term: string, searchOptions?: ISearchOptions): boolean { if (!this._terminal) { throw new Error('Cannot use addon until it has been loaded'); } @@ -131,8 +131,14 @@ export class SearchAddon implements ITerminalAddon { if (searchOptions?.decorations) { this._highlightAllMatches(term, searchOptions); } - const findNextResult = this._findNextAndSelect(term, searchOptions); - return this._resultIndex ? { resultIndex: this._resultIndex!, resultCount: this._searchResults?.size || 0 } : findNextResult; + const next = this._findNextAndSelect(term, searchOptions); + if (next && this._resultIndex && this._searchResults?.size) { + this._onDidChangeResults.fire({ resultIndex: this._resultIndex, resultCount: this._searchResults?.size }); + } else if (searchOptions?.decorations) { + this._onDidChangeResults.fire(undefined); + } + this._previousWasLastCall = false; + return next; } private _highlightAllMatches(term: string, searchOptions: ISearchOptions): void { @@ -153,8 +159,7 @@ export class SearchAddon implements ITerminalAddon { if (term === this._cachedSearchTerm && !this._dataChanged) { return; } - const dataDrivenChange = term === this._cachedSearchTerm && this._dataChanged; - const lastSearchCount = this._searchResults.size; + // new search, clear out the old decorations this.clearDecorations(); this._searchResults.clear(); @@ -182,9 +187,6 @@ export class SearchAddon implements ITerminalAddon { if (this._searchResults.size > 0) { this._cachedSearchTerm = term; } - if (dataDrivenChange && lastSearchCount !== this._searchResults.size) { - this._onDidChangeResults.fire(); - } } private _find(term: string, startRow: number, startCol: number, searchOptions?: ISearchOptions): ISearchResult | undefined { @@ -246,7 +248,7 @@ export class SearchAddon implements ITerminalAddon { let startRow = 0; let currentSelection: ISelectionPosition | undefined; if (this._terminal.hasSelection()) { - const incremental = searchOptions ? searchOptions.incremental : false; + const incremental = searchOptions && !(searchOptions.decorations && this._previousWasLastCall) ? searchOptions.incremental : false; // Start from the selection end if there is a selection // For incremental search, use existing row currentSelection = this._terminal.getSelectionPosition()!; @@ -305,7 +307,7 @@ export class SearchAddon implements ITerminalAddon { * @param searchOptions Search options. * @return Whether a result was found. */ - public findPrevious(term: string, searchOptions?: ISearchOptions): boolean | { resultIndex: number, resultCount: number } { + public findPrevious(term: string, searchOptions?: ISearchOptions): boolean { if (!this._terminal) { throw new Error('Cannot use addon until it has been loaded'); } @@ -313,8 +315,14 @@ export class SearchAddon implements ITerminalAddon { if (searchOptions?.decorations) { this._highlightAllMatches(term, searchOptions); } - const findPreviousResult = this._findAndSelectPrevious(term, searchOptions); - return this._resultIndex ? { resultIndex: this._resultIndex, resultCount: this._searchResults?.size || 0 }: findPreviousResult; + const previous = this._findAndSelectPrevious(term, searchOptions); + if (previous && this._resultIndex && this._searchResults?.size) { + this._onDidChangeResults.fire({ resultIndex: this._resultIndex, resultCount: this._searchResults?.size }); + } else if (searchOptions?.decorations) { + this._onDidChangeResults.fire(undefined); + } + this._previousWasLastCall = true; + return previous; } private _findAndSelectPrevious(term: string, searchOptions?: ISearchOptions): boolean { @@ -343,7 +351,7 @@ export class SearchAddon implements ITerminalAddon { let startCol = this._terminal.cols; const isReverseSearch = true; - const incremental = searchOptions ? searchOptions.incremental : false; + const incremental = searchOptions && !(searchOptions.decorations && !this._previousWasLastCall) ? searchOptions.incremental : false; let currentSelection: ISelectionPosition | undefined; if (this._terminal.hasSelection()) { currentSelection = this._terminal.getSelectionPosition()!; diff --git a/addons/xterm-addon-search/typings/xterm-addon-search.d.ts b/addons/xterm-addon-search/typings/xterm-addon-search.d.ts index 6eb8baee8b..cdef4aa7d3 100644 --- a/addons/xterm-addon-search/typings/xterm-addon-search.d.ts +++ b/addons/xterm-addon-search/typings/xterm-addon-search.d.ts @@ -98,7 +98,7 @@ declare module 'xterm-addon-search' { * @returns the result index and result count if decorations are provided or a boolean * indicating if there was a match */ - public findNext(term: string, searchOptions?: ISearchOptions): boolean | { resultIndex: number, resultCount: number }; + public findNext(term: string, searchOptions?: ISearchOptions): boolean; /** * Search backwards for the previous result that matches the search term and @@ -108,7 +108,7 @@ declare module 'xterm-addon-search' { * @returns the result index and result count if decorations are provided or a boolean * indicating if there was a match */ - public findPrevious(term: string, searchOptions?: ISearchOptions): boolean | { resultIndex: number, resultCount: number }; + public findPrevious(term: string, searchOptions?: ISearchOptions): boolean; /** * Clears the decorations and selection @@ -116,9 +116,9 @@ declare module 'xterm-addon-search' { public clearDecorations(): void; /** - * An event listener for when the search results change - * as a result of the buffer changing + * Fired when the search results or the selected result changes. + * Provides undefined when there are no results. */ - onDidChangeResults: IEvent; + onDidChangeResults: IEvent< { resultIndex: number, resultCount: number } | undefined>; } } From 7099b03b97706392cf2cf547bb58d702c5e50798 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 30 Mar 2022 13:55:47 -0400 Subject: [PATCH 19/27] clean up --- addons/xterm-addon-search/src/SearchAddon.ts | 34 ++++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 95e55cc3b0..4e5b0dfa3c 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -88,6 +88,7 @@ export class SearchAddon implements ITerminalAddon { } }, 200); }); + this.onDidChangeResults((e) => console.log(e)); } public dispose(): void { @@ -98,22 +99,17 @@ export class SearchAddon implements ITerminalAddon { public clearDecorations(): void { this._selectedDecoration?.dispose(); this._searchResults?.clear(); - this._disposeDecorations(); - this._cachedSearchTerm = undefined; - this._dataChanged = true; - this._resultIndex = undefined; - } - - private _disposeDecorations(): void { - if (!this._resultDecorations) { - return; - } - this._resultDecorations.forEach(decorations => { + this._resultDecorations?.forEach(decorations => { for (const d of decorations) { d.dispose(); } }); - this._resultDecorations.clear(); + this._resultDecorations?.clear(); + this._cachedSearchTerm = undefined; + this._searchResults = undefined; + this._resultDecorations = undefined; + this._dataChanged = true; + this._resultIndex = undefined; } /** @@ -145,12 +141,6 @@ export class SearchAddon implements ITerminalAddon { if (!this._terminal) { throw new Error('Cannot use addon until it has been loaded'); } - if (!this._searchResults) { - this._searchResults = new Map(); - } - if (!this._resultDecorations) { - this._resultDecorations = new Map(); - } if (!term || term.length === 0) { this.clearDecorations(); return; @@ -162,7 +152,9 @@ export class SearchAddon implements ITerminalAddon { // new search, clear out the old decorations this.clearDecorations(); - this._searchResults.clear(); + this._searchResults = new Map(); + this._resultDecorations = new Map(); + const resultDecorations = this._resultDecorations; let result = this._find(term, 0, 0, searchOptions); while (result && !this._searchResults.get(`${result.row}-${result.col}`)) { this._searchResults.set(`${result.row}-${result.col}`, result); @@ -176,9 +168,9 @@ export class SearchAddon implements ITerminalAddon { this._searchResults.forEach(result => { const resultDecoration = this._createResultDecoration(result, searchOptions.decorations!); if (resultDecoration) { - const decorationsForLine = this._resultDecorations!.get(resultDecoration.marker.line) || []; + const decorationsForLine = resultDecorations.get(resultDecoration.marker.line) || []; decorationsForLine.push(resultDecoration); - this._resultDecorations!.set(resultDecoration.marker.line, decorationsForLine); + resultDecorations.set(resultDecoration.marker.line, decorationsForLine); } }); if (this._dataChanged) { From 39952832fe709c308bd7e4acdc5c21da38601a43 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 30 Mar 2022 14:07:29 -0400 Subject: [PATCH 20/27] revert changes to d.ts --- addons/xterm-addon-search/src/SearchAddon.ts | 10 +++++----- .../xterm-addon-search/typings/xterm-addon-search.d.ts | 4 ---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 4e5b0dfa3c..c15ad021f3 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -82,11 +82,11 @@ export class SearchAddon implements ITerminalAddon { if (this._highlightTimeout) { window.clearTimeout(this._highlightTimeout); } - this._highlightTimeout = setTimeout(() => { - if (this._lastSearchOptions?.decorations && this._cachedSearchTerm && this._resultDecorations?.size && this._resultDecorations.size > 0 && this._lastSearchOptions) { - this.findPrevious(this._cachedSearchTerm, this._lastSearchOptions); - } - }, 200); + if (this._cachedSearchTerm && this._lastSearchOptions?.decorations) { + this._highlightTimeout = setTimeout(() => { + this.findPrevious(this._cachedSearchTerm!, { ...this._lastSearchOptions, incremental: true }); + }, 200); + } }); this.onDidChangeResults((e) => console.log(e)); } diff --git a/addons/xterm-addon-search/typings/xterm-addon-search.d.ts b/addons/xterm-addon-search/typings/xterm-addon-search.d.ts index cdef4aa7d3..c2c8a6e7d4 100644 --- a/addons/xterm-addon-search/typings/xterm-addon-search.d.ts +++ b/addons/xterm-addon-search/typings/xterm-addon-search.d.ts @@ -95,8 +95,6 @@ declare module 'xterm-addon-search' { * options. * @param term The search term. * @param searchOptions The options for the search. - * @returns the result index and result count if decorations are provided or a boolean - * indicating if there was a match */ public findNext(term: string, searchOptions?: ISearchOptions): boolean; @@ -105,8 +103,6 @@ declare module 'xterm-addon-search' { * options. * @param term The search term. * @param searchOptions The options for the search. - * @returns the result index and result count if decorations are provided or a boolean - * indicating if there was a match */ public findPrevious(term: string, searchOptions?: ISearchOptions): boolean; From 2bce7f4afc20543699b27c04d4b5c5be2b952a34 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 30 Mar 2022 14:08:54 -0400 Subject: [PATCH 21/27] d.ts --- addons/xterm-addon-search/typings/xterm-addon-search.d.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/addons/xterm-addon-search/typings/xterm-addon-search.d.ts b/addons/xterm-addon-search/typings/xterm-addon-search.d.ts index c2c8a6e7d4..1cb9740e60 100644 --- a/addons/xterm-addon-search/typings/xterm-addon-search.d.ts +++ b/addons/xterm-addon-search/typings/xterm-addon-search.d.ts @@ -112,9 +112,10 @@ declare module 'xterm-addon-search' { public clearDecorations(): void; /** - * Fired when the search results or the selected result changes. - * Provides undefined when there are no results. + * When decorations are enabled, fires when + * the search results or the selected result changes, + * returning undefined if there are no matches. */ - onDidChangeResults: IEvent< { resultIndex: number, resultCount: number } | undefined>; + readonly onDidChangeResults: IEvent<{ resultIndex: number, resultCount: number } | undefined>; } } From c01aa9e276e10cc8163267ba73f8931ed9a01f4d Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 30 Mar 2022 14:25:05 -0400 Subject: [PATCH 22/27] fix remaining issues --- addons/xterm-addon-search/src/SearchAddon.ts | 81 +++++++++++--------- addons/xterm-addon-search/src/tsconfig.json | 6 -- 2 files changed, 45 insertions(+), 42 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index c15ad021f3..f7897aa00f 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -59,7 +59,6 @@ export class SearchAddon implements ITerminalAddon { private _onDataDisposable: IDisposable | undefined; private _lastSearchOptions: ISearchOptions | undefined; private _highlightTimeout: number | undefined; - private _previousWasLastCall: boolean = true; /** * translateBufferLineToStringWithWrap is a fairly expensive call. * We memoize the calls into an array that has a time based ttl. @@ -88,7 +87,6 @@ export class SearchAddon implements ITerminalAddon { }, 200); } }); - this.onDidChangeResults((e) => console.log(e)); } public dispose(): void { @@ -128,12 +126,13 @@ export class SearchAddon implements ITerminalAddon { this._highlightAllMatches(term, searchOptions); } const next = this._findNextAndSelect(term, searchOptions); - if (next && this._resultIndex && this._searchResults?.size) { - this._onDidChangeResults.fire({ resultIndex: this._resultIndex, resultCount: this._searchResults?.size }); - } else if (searchOptions?.decorations) { - this._onDidChangeResults.fire(undefined); + if (searchOptions?.decorations) { + if (next && this._resultIndex && this._searchResults?.size) { + this._onDidChangeResults.fire({ resultIndex: this._resultIndex, resultCount: this._searchResults.size }); + } else { + this._onDidChangeResults.fire(undefined); + } } - this._previousWasLastCall = false; return next; } @@ -225,22 +224,13 @@ export class SearchAddon implements ITerminalAddon { this.clearDecorations(); return false; } - if (this._searchResults) { - if (!this._resultIndex) { - this._resultIndex = 1; - } else { - this._resultIndex++; - if (this._resultIndex > this._searchResults.size) { - this._resultIndex = 1; - } - } - } + let startCol = 0; let startRow = 0; let currentSelection: ISelectionPosition | undefined; if (this._terminal.hasSelection()) { - const incremental = searchOptions && !(searchOptions.decorations && this._previousWasLastCall) ? searchOptions.incremental : false; + const incremental = searchOptions ? searchOptions.incremental : false; // Start from the selection end if there is a selection // For incremental search, use existing row currentSelection = this._terminal.getSelectionPosition()!; @@ -289,6 +279,20 @@ export class SearchAddon implements ITerminalAddon { searchPosition.startCol = 0; result = this._findInLine(term, searchPosition, searchOptions); } + + if (currentSelection?.startColumn !== result?.col || currentSelection?.startRow !== result?.row) { + if (this._searchResults) { + if (!this._resultIndex) { + this._resultIndex = 1; + } else { + this._resultIndex++; + if (this._resultIndex > this._searchResults.size) { + this._resultIndex = 1; + } + } + } + } + // Set selection and scroll if a result was found return this._selectResult(result, searchOptions?.decorations); } @@ -307,17 +311,18 @@ export class SearchAddon implements ITerminalAddon { if (searchOptions?.decorations) { this._highlightAllMatches(term, searchOptions); } - const previous = this._findAndSelectPrevious(term, searchOptions); - if (previous && this._resultIndex && this._searchResults?.size) { - this._onDidChangeResults.fire({ resultIndex: this._resultIndex, resultCount: this._searchResults?.size }); - } else if (searchOptions?.decorations) { - this._onDidChangeResults.fire(undefined); + const previous = this._findPreviousAndSelect(term, searchOptions); + if (searchOptions?.decorations) { + if (previous && this._resultIndex && this._searchResults?.size) { + this._onDidChangeResults.fire({ resultIndex: this._resultIndex, resultCount: this._searchResults.size }); + } else { + this._onDidChangeResults.fire(undefined); + } } - this._previousWasLastCall = true; return previous; } - private _findAndSelectPrevious(term: string, searchOptions?: ISearchOptions): boolean { + private _findPreviousAndSelect(term: string, searchOptions?: ISearchOptions): boolean { if (!this._terminal) { throw new Error('Cannot use addon until it has been loaded'); } @@ -329,21 +334,11 @@ export class SearchAddon implements ITerminalAddon { return false; } - if (this._searchResults) { - if (!this._resultIndex) { - this._resultIndex = this._searchResults?.size; - } else { - this._resultIndex--; - if (this._resultIndex === 0) { - this._resultIndex = this._searchResults?.size; - } - } - } let startRow = this._terminal.buffer.active.baseY + this._terminal.rows; let startCol = this._terminal.cols; const isReverseSearch = true; - const incremental = searchOptions && !(searchOptions.decorations && !this._previousWasLastCall) ? searchOptions.incremental : false; + const incremental = searchOptions ? searchOptions.incremental : false; let currentSelection: ISelectionPosition | undefined; if (this._terminal.hasSelection()) { currentSelection = this._terminal.getSelectionPosition()!; @@ -395,6 +390,20 @@ export class SearchAddon implements ITerminalAddon { } } } + + if (currentSelection?.startColumn !== result?.col || currentSelection?.startRow !== result?.row) { + if (this._searchResults) { + if (!this._resultIndex) { + this._resultIndex = this._searchResults?.size; + } else { + this._resultIndex--; + if (this._resultIndex === 0) { + this._resultIndex = this._searchResults?.size; + } + } + } + } + // If there is only one result, return true. if (!result && currentSelection) return true; diff --git a/addons/xterm-addon-search/src/tsconfig.json b/addons/xterm-addon-search/src/tsconfig.json index f39261cdff..5a5e671fcc 100644 --- a/addons/xterm-addon-search/src/tsconfig.json +++ b/addons/xterm-addon-search/src/tsconfig.json @@ -17,9 +17,6 @@ "paths": { "common/*": [ "../../../src/common/*" - ], - "browser/*": [ - "../../../src/browser/*" ] } }, @@ -30,9 +27,6 @@ "references": [ { "path": "../../../src/common" - }, - { - "path": "../../../src/browser" } ] } From c2df04a34a7c99aae6febf725a3f58f5a4dedfa0 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 30 Mar 2022 15:27:53 -0400 Subject: [PATCH 23/27] add webpack config change --- addons/xterm-addon-search/webpack.config.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/addons/xterm-addon-search/webpack.config.js b/addons/xterm-addon-search/webpack.config.js index 726dceb6f2..30526812e4 100644 --- a/addons/xterm-addon-search/webpack.config.js +++ b/addons/xterm-addon-search/webpack.config.js @@ -21,6 +21,13 @@ module.exports = { } ] }, + resolve: { + modules: ['./node_modules'], + extensions: [ '.js' ], + alias: { + common: path.resolve('../../out/common') + } + }, output: { filename: mainFile, path: path.resolve('./lib'), From 0c325c8692efc966758c505a17079cad2acaecc3 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 30 Mar 2022 16:03:54 -0400 Subject: [PATCH 24/27] fix check --- addons/xterm-addon-search/src/SearchAddon.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index f7897aa00f..5a039a52b1 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -280,7 +280,7 @@ export class SearchAddon implements ITerminalAddon { result = this._findInLine(term, searchPosition, searchOptions); } - if (currentSelection?.startColumn !== result?.col || currentSelection?.startRow !== result?.row) { + if (!currentSelection || currentSelection?.startColumn !== result?.col || currentSelection?.startRow !== result?.row) { if (this._searchResults) { if (!this._resultIndex) { this._resultIndex = 1; @@ -391,7 +391,7 @@ export class SearchAddon implements ITerminalAddon { } } - if (currentSelection?.startColumn !== result?.col || currentSelection?.startRow !== result?.row) { + if (!currentSelection || currentSelection?.startColumn !== result?.col || currentSelection?.startRow !== result?.row) { if (this._searchResults) { if (!this._resultIndex) { this._resultIndex = this._searchResults?.size; From 2bbcf8c02f8f286ef3e750fe2f2bcd4ffd541aea Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 30 Mar 2022 16:05:43 -0400 Subject: [PATCH 25/27] remove bad conditional --- addons/xterm-addon-search/src/SearchAddon.ts | 28 +++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 5a039a52b1..7236078704 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -280,15 +280,13 @@ export class SearchAddon implements ITerminalAddon { result = this._findInLine(term, searchPosition, searchOptions); } - if (!currentSelection || currentSelection?.startColumn !== result?.col || currentSelection?.startRow !== result?.row) { - if (this._searchResults) { - if (!this._resultIndex) { + if (this._searchResults) { + if (!this._resultIndex) { + this._resultIndex = 1; + } else { + this._resultIndex++; + if (this._resultIndex > this._searchResults.size) { this._resultIndex = 1; - } else { - this._resultIndex++; - if (this._resultIndex > this._searchResults.size) { - this._resultIndex = 1; - } } } } @@ -391,15 +389,13 @@ export class SearchAddon implements ITerminalAddon { } } - if (!currentSelection || currentSelection?.startColumn !== result?.col || currentSelection?.startRow !== result?.row) { - if (this._searchResults) { - if (!this._resultIndex) { + if (this._searchResults) { + if (!this._resultIndex) { + this._resultIndex = this._searchResults?.size; + } else { + this._resultIndex--; + if (this._resultIndex === 0) { this._resultIndex = this._searchResults?.size; - } else { - this._resultIndex--; - if (this._resultIndex === 0) { - this._resultIndex = this._searchResults?.size; - } } } } From e5fd206eb5f067a4a37787ec0537a855e3950470 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 30 Mar 2022 17:11:53 -0400 Subject: [PATCH 26/27] zero based indexing --- addons/xterm-addon-search/src/SearchAddon.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 7236078704..479749de6b 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -87,6 +87,7 @@ export class SearchAddon implements ITerminalAddon { }, 200); } }); + this.onDidChangeResults((results) => console.log(results)); } public dispose(): void { @@ -127,7 +128,7 @@ export class SearchAddon implements ITerminalAddon { } const next = this._findNextAndSelect(term, searchOptions); if (searchOptions?.decorations) { - if (next && this._resultIndex && this._searchResults?.size) { + if (next && this._resultIndex !== undefined && this._searchResults?.size) { this._onDidChangeResults.fire({ resultIndex: this._resultIndex, resultCount: this._searchResults.size }); } else { this._onDidChangeResults.fire(undefined); @@ -281,12 +282,12 @@ export class SearchAddon implements ITerminalAddon { } if (this._searchResults) { - if (!this._resultIndex) { - this._resultIndex = 1; + if (this._resultIndex === undefined) { + this._resultIndex = 0; } else { this._resultIndex++; - if (this._resultIndex > this._searchResults.size) { - this._resultIndex = 1; + if (this._resultIndex >= this._searchResults.size) { + this._resultIndex = 0; } } } @@ -390,12 +391,12 @@ export class SearchAddon implements ITerminalAddon { } if (this._searchResults) { - if (!this._resultIndex) { - this._resultIndex = this._searchResults?.size; + if (this._resultIndex === undefined) { + this._resultIndex = this._searchResults?.size - 1; } else { this._resultIndex--; - if (this._resultIndex === 0) { - this._resultIndex = this._searchResults?.size; + if (this._resultIndex === -1) { + this._resultIndex = this._searchResults?.size - 1; } } } From 0ea5e2bd90d125932831c4078860051ebef16b4a Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 30 Mar 2022 17:16:56 -0400 Subject: [PATCH 27/27] check not undefined --- addons/xterm-addon-search/src/SearchAddon.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index 479749de6b..d2ee05b4f6 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -87,7 +87,6 @@ export class SearchAddon implements ITerminalAddon { }, 200); } }); - this.onDidChangeResults((results) => console.log(results)); } public dispose(): void { @@ -312,7 +311,7 @@ export class SearchAddon implements ITerminalAddon { } const previous = this._findPreviousAndSelect(term, searchOptions); if (searchOptions?.decorations) { - if (previous && this._resultIndex && this._searchResults?.size) { + if (previous && this._resultIndex !== undefined && this._searchResults?.size) { this._onDidChangeResults.fire({ resultIndex: this._resultIndex, resultCount: this._searchResults.size }); } else { this._onDidChangeResults.fire(undefined);