pull apart and refactor findNext

This commit is contained in:
meganrogge
2022-03-18 19:07:39 -04:00
parent fef30bdba4
commit 61e5c10fd3
4 changed files with 63 additions and 55 deletions
+49 -47
View File
@@ -10,10 +10,9 @@ export interface ISearchOptions {
wholeWord?: boolean;
caseSensitive?: boolean;
incremental?: boolean;
highlightAllMatches?: boolean;
startRow?: number;
startCol?: number;
overviewRulerResultDecorationColor?: string;
overviewRulerSelectionDecorationColor?: string;
}
export interface ISearchPosition {
@@ -72,6 +71,7 @@ export class SearchAddon implements ITerminalAddon {
}
public clear(): void {
this._selectedDecoration?.dispose();
this._terminal?.clearSelection();
this._searchResults.clear();
this._resultDecorations.forEach(decorations => decorations.forEach(d=> d.dispose()));
@@ -81,17 +81,23 @@ export class SearchAddon implements ITerminalAddon {
}
/**
* Find all instances of the term, selecting the next one with each
* enter. If it doesn't exist, do nothing.
* Find the next instance of the term, then scroll to and select it. If it
* doesn't exist, do nothing.
* @param term The search term.
* @param searchOptions Search options.
* @return Whether a result was found.
*/
public find(term: string, searchOptions?: ISearchOptions): boolean {
public findNext(term: string, searchOptions?: ISearchOptions): boolean {
if (!this._terminal) {
throw new Error('Cannot use addon until it has been loaded');
}
return searchOptions?.highlightAllMatches ? this._highlightAllMatches(term, searchOptions) : this._findAndSelectNext(term, searchOptions);
}
private _highlightAllMatches(term: string, searchOptions: ISearchOptions): boolean {
if (!this._terminal) {
throw new Error('cannot find all matches with no terminal');
}
if (!term || term.length === 0) {
this.clear();
return false;
@@ -99,7 +105,7 @@ export class SearchAddon implements ITerminalAddon {
searchOptions = searchOptions || {};
if (term === this._cachedSearchTerm) {
if (!this._dataChanged) {
return this.findNext(term, searchOptions);
return this._findAndSelectNext(term, searchOptions);
}
// set start row and col to avoid redoing work
const key = Array.from(this._searchResults.keys()).pop()?.split('-');
@@ -113,51 +119,45 @@ export class SearchAddon implements ITerminalAddon {
this._resultDecorations.clear();
this._searchResults.clear();
}
if (!this._terminal.options.overviewRulerWidth) {
this._terminal.options.overviewRulerWidth = 10;
}
if (!this._terminal.options.findResultDecorationColor) {
this._terminal.options.findResultDecorationColor = '#555753';
}
if (!this._terminal.options.findResultSelectedDecorationColor) {
this._terminal.options.findResultSelectedDecorationColor = '#ef2929';
}
searchOptions.incremental = false;
let found = this.findNext(term, searchOptions);
let found = this._findAndSelectNext(term, searchOptions);
while (found && (!this._result || !this._searchResults.get(`${this._result.row}-${this._result.col}`))) {
if (this._result) {
this._searchResults.set(`${this._result.row}-${this._result.col}`, this._result);
}
found = this.findNext(term, searchOptions);
found = this._findAndSelectNext(term, searchOptions);
}
if (searchOptions.overviewRulerResultDecorationColor && searchOptions.overviewRulerSelectionDecorationColor) {
this._searchResults.forEach(result => {
const resultDecoration = this._createResultDecoration(result, searchOptions!.overviewRulerResultDecorationColor!);
if (resultDecoration) {
const decorationsForLine = this._resultDecorations.get(resultDecoration.marker.line) || [];
decorationsForLine.push(resultDecoration);
this._resultDecorations.set(resultDecoration.marker.line, decorationsForLine);
}
});
}
this._searchResults.forEach(result => {
const resultDecoration = this._createResultDecoration(result);
if (resultDecoration) {
const decorationsForLine = this._resultDecorations.get(resultDecoration.marker.line) || [];
decorationsForLine.push(resultDecoration);
this._resultDecorations.set(resultDecoration.marker.line, decorationsForLine);
}
});
if (this._dataChanged) {
this._dataChanged = false;
}
if (this._searchResults.size > 0) {
this._cachedSearchTerm = term;
}
return true;
return this._searchResults.size > 0;
}
/**
* Find the next instance of the term, then scroll to and select it. If it
* doesn't exist, do nothing.
* @param term The search term.
* @param searchOptions Search options.
* @return Whether a result was found.
*/
public findNext(term: string, searchOptions?: ISearchOptions): boolean {
if (!this._terminal) {
throw new Error('Cannot use addon until it has been loaded');
}
if (!term || term.length === 0) {
private _findAndSelectNext(term: string, searchOptions?: ISearchOptions): boolean {
if (!this._terminal || !term || term.length === 0) {
this._result = undefined;
this._terminal.clearSelection();
this._terminal?.clearSelection();
this.clear();
return false;
}
@@ -219,9 +219,8 @@ export class SearchAddon implements ITerminalAddon {
}
// Set selection and scroll if a result was found
return this._selectResult(this._result, searchOptions?.overviewRulerSelectionDecorationColor);
return this._selectResult(this._result, searchOptions?.highlightAllMatches);
}
/**
* Find the previous instance of the term, then scroll to and select it. If it
* doesn't exist, do nothing.
@@ -300,7 +299,7 @@ export class SearchAddon implements ITerminalAddon {
if (!result && currentSelection) return true;
// Set selection and scroll if a result was found
return this._selectResult(result, searchOptions?.overviewRulerSelectionDecorationColor);
return this._selectResult(result, searchOptions?.highlightAllMatches);
}
@@ -538,7 +537,7 @@ export class SearchAddon implements ITerminalAddon {
* @param result The result to select.
* @return Whether a result was selected.
*/
private _selectResult(result: ISearchResult | undefined, color?: string): boolean {
private _selectResult(result: ISearchResult | undefined, highlightAllMatches?: boolean): boolean {
const terminal = this._terminal!;
this._selectedDecoration?.dispose();
if (!result) {
@@ -546,11 +545,11 @@ export class SearchAddon implements ITerminalAddon {
return false;
}
terminal.select(result.col, result.row, result.size);
if (color) {
if (this._terminal?.options.findResultSelectedDecorationColor && highlightAllMatches) {
const marker = terminal.registerMarker(-terminal.buffer.active.baseY - terminal.buffer.active.cursorY + result.row);
if (marker) {
this._selectedDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color } });
this._selectedDecoration?.onRender((e) => this._applyStyles(e, color, result));
this._selectedDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: { color: this._terminal!.options.findResultSelectedDecorationColor } });
this._selectedDecoration?.onRender((e) => this._applyStyles(e, this._terminal!.options.findResultSelectedDecorationColor!, result));
}
}
@@ -589,14 +588,17 @@ export class SearchAddon implements ITerminalAddon {
* @param color the color to use for the decoration
* @returns the {@link IDecoration} or undefined if the marker has already been disposed of
*/
private _createResultDecoration(result: ISearchResult, color: string): IDecoration | undefined {
private _createResultDecoration(result: ISearchResult): IDecoration | undefined {
const terminal = this._terminal!;
const marker = terminal.registerMarker(-terminal.buffer.active.baseY - terminal.buffer.active.cursorY + result.row);
if (!marker) {
if (!marker || !this._terminal?.options.findResultDecorationColor) {
return undefined;
}
const findResultDecoration = terminal.registerDecoration({ marker, overviewRulerOptions: this._resultDecorations.get(marker.line) && !this._dataChanged ? undefined : { color, position: 'center' } });
findResultDecoration?.onRender((e) => this._applyStyles(e, color, result));
const findResultDecoration = terminal.registerDecoration(
{ marker,
overviewRulerOptions: this._resultDecorations.get(marker.line) && !this._dataChanged ? undefined : { color: this._terminal.options.findResultDecorationColor, position: 'center' }
});
findResultDecoration?.onRender((e) => this._applyStyles(e, this._terminal!.options.findResultDecorationColor!, result));
return findResultDecoration;
}
}
+1 -7
View File
@@ -109,8 +109,7 @@ function getSearchOptions(e: KeyboardEvent): ISearchOptions {
wholeWord: (document.getElementById('whole-word') as HTMLInputElement).checked,
caseSensitive: (document.getElementById('case-sensitive') as HTMLInputElement).checked,
incremental: e.key !== `Enter`,
overviewRulerResultDecorationColor: '#555753',
overviewRulerSelectionDecorationColor: '#ef2929'
highlightAllMatches: (document.getElementById('highlight-all-matches') as HTMLInputElement).checked,
};
}
@@ -203,11 +202,6 @@ function createTerminal(): void {
addDomListener(paddingElement, 'change', setPadding);
addDomListener(actionElements.find, 'keyup', (e) => {
term.options.overviewRulerWidth = 10;
addons.search.instance.find(actionElements.find.value, getSearchOptions(e));
});
addDomListener(actionElements.findNext, 'keyup', (e) => {
addons.search.instance.findNext(actionElements.findNext.value, getSearchOptions(e));
});
+1 -1
View File
@@ -38,12 +38,12 @@
<h3>Addons Control</h3>
<h4>SearchAddon</h4>
<div style= "display:flex; flex-direction:column;">
<label>Find <input id="find"/></label>
<label>Find next <input id="find-next"/></label>
<label>Find previous <input id="find-previous"/></label>
<label><input type="checkbox" id="regex"/>Use regex</label>
<label><input type="checkbox" id="case-sensitive"/>Case sensitive</label>
<label><input type="checkbox" id="whole-word"/>Whole word</label>
<label><input type="checkbox" id="highlight-all-matches"/>Highlight All Matches</label>
</div>
<h4>SerializeAddon</h4>
<div>
+12
View File
@@ -272,6 +272,18 @@ declare module 'xterm' {
* ruler will be hidden when not set.
*/
overviewRulerWidth?: number;
/**
* The color for all find result decorations
* in the overview ruler
*/
findResultDecorationColor?: string;
/**
* The color for the currently selected decoration
* when all matches are displayed
*/
findResultSelectedDecorationColor?: string;
}
/**