Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
b78cb4c
add find count
meganrogge Mar 23, 2022
bb0f5d6
Merge branch 'xtermjs:master' into findCount
meganrogge Mar 28, 2022
c1c0df4
remove index for now
meganrogge Mar 28, 2022
7cc868b
Merge branch 'findCount' of https://github.com/meganrogge/xterm.js in…
meganrogge Mar 28, 2022
6e7d1de
add jsdoc
meganrogge Mar 28, 2022
6ff999b
update jsdoc
meganrogge Mar 28, 2022
463a0e9
property instead of method
meganrogge Mar 29, 2022
49a170f
return undefined when no search results
meganrogge Mar 29, 2022
691f3c9
initialize maps in highlightAllMatches
meganrogge Mar 29, 2022
9a93d5c
! -> ?
meganrogge Mar 29, 2022
d764281
add event to indicate when results have changed
meganrogge Mar 29, 2022
4b6198e
Update addons/xterm-addon-search/src/SearchAddon.ts
meganrogge Mar 29, 2022
f59db29
refactor
meganrogge Mar 29, 2022
dc490d1
Merge branch 'findCount' of https://github.com/meganrogge/xterm.js in…
meganrogge Mar 29, 2022
3df6be8
clear decorations find previous
meganrogge Mar 30, 2022
e2541a1
import
meganrogge Mar 30, 2022
1f00d13
correct ts config
meganrogge Mar 30, 2022
c7d24bf
Update addons/xterm-addon-search/tsconfig.json
meganrogge Mar 30, 2022
f93e166
Update addons/xterm-addon-search/src/SearchAddon.ts
meganrogge Mar 30, 2022
cd0658d
Update addons/xterm-addon-search/src/SearchAddon.ts
meganrogge Mar 30, 2022
f1a75b4
Merge branch 'master' into findCount
meganrogge Mar 30, 2022
56ab94d
refactor to fire event
meganrogge Mar 30, 2022
371f552
Merge branch 'findCount' of https://github.com/meganrogge/xterm.js in…
meganrogge Mar 30, 2022
7099b03
clean up
meganrogge Mar 30, 2022
3995283
revert changes to d.ts
meganrogge Mar 30, 2022
2bce7f4
d.ts
meganrogge Mar 30, 2022
c01aa9e
fix remaining issues
meganrogge Mar 30, 2022
c2df04a
add webpack config change
meganrogge Mar 30, 2022
0c325c8
fix check
meganrogge Mar 30, 2022
2bbcf8c
remove bad conditional
meganrogge Mar 30, 2022
e5fd206
zero based indexing
meganrogge Mar 30, 2022
0ea5e2b
check not undefined
meganrogge Mar 30, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 65 additions & 18 deletions addons/xterm-addon-search/src/SearchAddon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

import { Terminal, IDisposable, ITerminalAddon, ISelectionPosition, IDecoration } from 'xterm';
import { EventEmitter } from 'common/EventEmitter';

export interface ISearchOptions {
regex?: boolean;
Expand Down Expand Up @@ -53,11 +54,12 @@ export class SearchAddon implements ITerminalAddon {
private _dataChanged: boolean = false;
private _cachedSearchTerm: string | undefined;
private _selectedDecoration: IDecoration | undefined;
private _resultDecorations: Map<number, IDecoration[]> = new Map<number, IDecoration[]>();
private _searchResults: Map<string, ISearchResult> = new Map();
private _resultDecorations: Map<number, IDecoration[]> | undefined;
private _searchResults: Map<string, ISearchResult> | undefined;
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.
Expand All @@ -68,6 +70,11 @@ export class SearchAddon implements ITerminalAddon {
private _cursorMoveListener: IDisposable | undefined;
private _resizeListener: IDisposable | undefined;

private _resultIndex: number | undefined;

private readonly _onDidChangeResults = new EventEmitter<{resultIndex: number, resultCount: number} | undefined>();
public readonly onDidChangeResults = this._onDidChangeResults.event;

public activate(terminal: Terminal): void {
this._terminal = terminal;
this._onDataDisposable = this._terminal.onData(() => {
Expand All @@ -76,8 +83,8 @@ export class SearchAddon implements ITerminalAddon {
window.clearTimeout(this._highlightTimeout);
}
this._highlightTimeout = setTimeout(() => {
if (this._lastSearchOptions?.decorations && this._cachedSearchTerm && this._resultDecorations.size > 0 && this._lastSearchOptions) {
this._highlightAllMatches(this._cachedSearchTerm, this._lastSearchOptions);
if (this._lastSearchOptions?.decorations && this._cachedSearchTerm && this._resultDecorations?.size && this._resultDecorations.size > 0 && this._lastSearchOptions) {
this.findPrevious(this._cachedSearchTerm, this._lastSearchOptions);
}
}, 200);
});
Expand All @@ -90,14 +97,17 @@ 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;
this._resultIndex = undefined;
}

private _disposeDecorations(): void {
if (!this._resultDecorations) {
return;
}
this._resultDecorations.forEach(decorations => {
for (const d of decorations) {
d.dispose();
Expand All @@ -118,17 +128,29 @@ export class SearchAddon implements ITerminalAddon {
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;
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 {
if (!this._terminal) {
throw new Error('Cannot use addon until it has been loaded');
}
if (!this._searchResults) {
this._searchResults = new Map<string, ISearchResult>();
}
if (!this._resultDecorations) {
this._resultDecorations = new Map<number, IDecoration[]>();
}
if (!term || term.length === 0) {
this.clearDecorations();
return;
Expand All @@ -137,8 +159,9 @@ export class SearchAddon implements ITerminalAddon {
if (term === this._cachedSearchTerm && !this._dataChanged) {
return;
}

// 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}`)) {
Expand All @@ -153,9 +176,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) {
Expand Down Expand Up @@ -210,12 +233,22 @@ 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.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()!;
Expand Down Expand Up @@ -264,7 +297,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);
}
Expand All @@ -280,11 +312,17 @@ export class SearchAddon implements ITerminalAddon {
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;
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 {
Expand All @@ -299,11 +337,21 @@ 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.incremental : false;
const incremental = searchOptions && !(searchOptions.decorations && !this._previousWasLastCall) ? searchOptions.incremental : false;
let currentSelection: ISelectionPosition | undefined;
if (this._terminal.hasSelection()) {
currentSelection = this._terminal.getSelectionPosition()!;
Expand Down Expand Up @@ -355,7 +403,6 @@ export class SearchAddon implements ITerminalAddon {
}
}
}

// If there is only one result, return true.
if (!result && currentSelection) return true;

Expand Down Expand Up @@ -669,7 +716,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'
}
});
Expand Down
18 changes: 17 additions & 1 deletion addons/xterm-addon-search/src/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
]
}
12 changes: 11 additions & 1 deletion addons/xterm-addon-search/typings/xterm-addon-search.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* @license MIT
*/

import { Terminal, ILinkMatcherOptions, IDisposable, ITerminalAddon } from 'xterm';
import { Terminal, ITerminalAddon, IEvent } from 'xterm';

declare module 'xterm-addon-search' {
/**
Expand Down Expand Up @@ -95,6 +95,8 @@ 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;

Expand All @@ -103,12 +105,20 @@ 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;

/**
* Clears the decorations and selection
*/
public clearDecorations(): void;

/**
* Fired when the search results or the selected result changes.
* Provides undefined when there are no results.
*/
onDidChangeResults: IEvent< { resultIndex: number, resultCount: number } | undefined>;
}
}