mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
pull apart and refactor findNext
This commit is contained in:
@@ -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
@@ -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
@@ -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>
|
||||
|
||||
Vendored
+12
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user