start work
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Terminal, IBufferLine, IDisposable, ITerminalAddon, ISelectionPosition } from 'xterm';
|
import { Terminal, IBufferLine, IDisposable, ITerminalAddon, ISelectionPosition, IDecoration } from 'xterm';
|
||||||
|
|
||||||
export interface ISearchOptions {
|
export interface ISearchOptions {
|
||||||
regex?: boolean;
|
regex?: boolean;
|
||||||
@@ -40,7 +40,7 @@ const LINES_CACHE_TIME_TO_LIVE = 15 * 1000; // 15 secs
|
|||||||
|
|
||||||
export class SearchAddon implements ITerminalAddon {
|
export class SearchAddon implements ITerminalAddon {
|
||||||
private _terminal: Terminal | undefined;
|
private _terminal: Terminal | undefined;
|
||||||
|
private _resultDecorations: IDecoration[] = [];
|
||||||
/**
|
/**
|
||||||
* translateBufferLineToStringWithWrap is a fairly expensive call.
|
* translateBufferLineToStringWithWrap is a fairly expensive call.
|
||||||
* We memoize the calls into an array that has a time based ttl.
|
* We memoize the calls into an array that has a time based ttl.
|
||||||
@@ -130,7 +130,7 @@ export class SearchAddon implements ITerminalAddon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set selection and scroll if a result was found
|
// Set selection and scroll if a result was found
|
||||||
return this._selectResult(result);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -149,69 +149,23 @@ export class SearchAddon implements ITerminalAddon {
|
|||||||
this._terminal.clearSelection();
|
this._terminal.clearSelection();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const results = [];
|
||||||
const isReverseSearch = true;
|
for (let i = this._terminal.buffer.active.viewportY; i < this._terminal.buffer.active.viewportY + this._terminal.rows; i++) {
|
||||||
let startRow = this._terminal.buffer.active.baseY + this._terminal.rows;
|
const result = this._findInLine(term, { startCol: 0, startRow: i }, searchOptions);
|
||||||
let startCol = this._terminal.cols;
|
if (result) {
|
||||||
let result: ISearchResult | undefined;
|
results.push(result);
|
||||||
const incremental = searchOptions ? searchOptions.incremental : false;
|
|
||||||
let currentSelection: ISelectionPosition | undefined;
|
|
||||||
if (this._terminal.hasSelection()) {
|
|
||||||
currentSelection = this._terminal.getSelectionPosition()!;
|
|
||||||
// Start from selection start if there is a selection
|
|
||||||
startRow = currentSelection.startRow;
|
|
||||||
startCol = currentSelection.startColumn;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._initLinesCache();
|
|
||||||
const searchPosition: ISearchPosition = {
|
|
||||||
startRow,
|
|
||||||
startCol
|
|
||||||
};
|
|
||||||
|
|
||||||
if (incremental) {
|
|
||||||
// Try to expand selection to right first.
|
|
||||||
result = this._findInLine(term, searchPosition, searchOptions, false);
|
|
||||||
const isOldResultHighlighted = result && result.row === startRow && result.col === startCol;
|
|
||||||
if (!isOldResultHighlighted) {
|
|
||||||
// If selection was not able to be expanded to the right, then try reverse search
|
|
||||||
if (currentSelection) {
|
|
||||||
searchPosition.startRow = currentSelection.endRow;
|
|
||||||
searchPosition.startCol = currentSelection.endColumn;
|
|
||||||
}
|
|
||||||
result = this._findInLine(term, searchPosition, searchOptions, true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search from startRow - 1 to top
|
|
||||||
if (!result) {
|
|
||||||
searchPosition.startCol = Math.max(searchPosition.startCol, this._terminal.cols);
|
|
||||||
for (let y = startRow - 1; y >= 0; y--) {
|
|
||||||
searchPosition.startRow = y;
|
|
||||||
result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);
|
|
||||||
if (result) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If we hit the top and didn't search from the very bottom wrap back down
|
for (const result of results.filter(r => !!r && r.term.length)) {
|
||||||
if (!result && startRow !== (this._terminal.buffer.active.baseY + this._terminal.rows)) {
|
const resultDecoration = this._showResultDecoration(result);
|
||||||
for (let y = (this._terminal.buffer.active.baseY + this._terminal.rows); y >= startRow; y--) {
|
if (resultDecoration) {
|
||||||
searchPosition.startRow = y;
|
// Add decoration
|
||||||
result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);
|
this._resultDecorations.push(resultDecoration);
|
||||||
if (result) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
console.log(results);
|
||||||
// If there is only one result, return true.
|
console.log(this._resultDecorations);
|
||||||
if (!result && currentSelection) return true;
|
return true;
|
||||||
|
|
||||||
// Set selection and scroll if a result was found
|
|
||||||
return this._selectResult(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -267,7 +221,7 @@ export class SearchAddon implements ITerminalAddon {
|
|||||||
* @param isReverseSearch Whether the search should start from the right side of the terminal and search to the left.
|
* @param isReverseSearch Whether the search should start from the right side of the terminal and search to the left.
|
||||||
* @return The search result if it was found.
|
* @return The search result if it was found.
|
||||||
*/
|
*/
|
||||||
protected _findInLine(term: string, searchPosition: ISearchPosition, searchOptions: ISearchOptions = {}, isReverseSearch: boolean = false): ISearchResult | undefined {
|
protected _findInLine(term: string, searchPosition: ISearchPosition, searchOptions: ISearchOptions = {}): ISearchResult | undefined {
|
||||||
const terminal = this._terminal!;
|
const terminal = this._terminal!;
|
||||||
const row = searchPosition.startRow;
|
const row = searchPosition.startRow;
|
||||||
const col = searchPosition.startCol;
|
const col = searchPosition.startCol;
|
||||||
@@ -275,10 +229,6 @@ export class SearchAddon implements ITerminalAddon {
|
|||||||
// Ignore wrapped lines, only consider on unwrapped line (first row of command string).
|
// Ignore wrapped lines, only consider on unwrapped line (first row of command string).
|
||||||
const firstLine = terminal.buffer.active.getLine(row);
|
const firstLine = terminal.buffer.active.getLine(row);
|
||||||
if (firstLine?.isWrapped) {
|
if (firstLine?.isWrapped) {
|
||||||
if (isReverseSearch) {
|
|
||||||
searchPosition.startCol += terminal.cols;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will iterate until we find the line start.
|
// This will iterate until we find the line start.
|
||||||
// When we find it, we will search using the calculated start column.
|
// When we find it, we will search using the calculated start column.
|
||||||
@@ -302,29 +252,13 @@ export class SearchAddon implements ITerminalAddon {
|
|||||||
let resultIndex = -1;
|
let resultIndex = -1;
|
||||||
if (searchOptions.regex) {
|
if (searchOptions.regex) {
|
||||||
const searchRegex = RegExp(searchTerm, 'g');
|
const searchRegex = RegExp(searchTerm, 'g');
|
||||||
let foundTerm: RegExpExecArray | null;
|
const foundTerm = searchRegex.exec(searchStringLine.slice(offset));
|
||||||
if (isReverseSearch) {
|
if (foundTerm && foundTerm[0].length > 0) {
|
||||||
// This loop will get the resultIndex of the _last_ regex match in the range 0..offset
|
resultIndex = offset + (searchRegex.lastIndex - foundTerm[0].length);
|
||||||
while (foundTerm = searchRegex.exec(searchStringLine.slice(0, offset))) {
|
term = foundTerm[0];
|
||||||
resultIndex = searchRegex.lastIndex - foundTerm[0].length;
|
|
||||||
term = foundTerm[0];
|
|
||||||
searchRegex.lastIndex -= (term.length - 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
foundTerm = searchRegex.exec(searchStringLine.slice(offset));
|
|
||||||
if (foundTerm && foundTerm[0].length > 0) {
|
|
||||||
resultIndex = offset + (searchRegex.lastIndex - foundTerm[0].length);
|
|
||||||
term = foundTerm[0];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isReverseSearch) {
|
resultIndex = searchStringLine.indexOf(searchTerm, offset);
|
||||||
if (offset - searchTerm.length >= 0) {
|
|
||||||
resultIndex = searchStringLine.lastIndexOf(searchTerm, offset - searchTerm.length);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resultIndex = searchStringLine.indexOf(searchTerm, offset);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resultIndex >= 0) {
|
if (resultIndex >= 0) {
|
||||||
@@ -448,19 +382,19 @@ export class SearchAddon implements ITerminalAddon {
|
|||||||
* @param result The result to select.
|
* @param result The result to select.
|
||||||
* @return Whethera result was selected.
|
* @return Whethera result was selected.
|
||||||
*/
|
*/
|
||||||
private _selectResult(result: ISearchResult | undefined): boolean {
|
private _showResultDecoration(result: ISearchResult | undefined): IDecoration | undefined {
|
||||||
const terminal = this._terminal!;
|
const terminal = this._terminal!;
|
||||||
if (!result) {
|
if (!result || result.row >= (terminal.buffer.active.viewportY + terminal.rows) || result.row < terminal.buffer.active.viewportY) {
|
||||||
terminal.clearSelection();
|
terminal.clearSelection();
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
terminal.select(result.col, result.row, result.size);
|
// TODO:
|
||||||
// If it is not in the viewport then we scroll else it just gets selected
|
const marker = terminal.registerMarker(undefined, result.col, result.row);
|
||||||
if (result.row >= (terminal.buffer.active.viewportY + terminal.rows) || result.row < terminal.buffer.active.viewportY) {
|
if (!marker) {
|
||||||
let scroll = result.row - terminal.buffer.active.viewportY;
|
return undefined;
|
||||||
scroll -= Math.floor(terminal.rows / 2);
|
|
||||||
terminal.scrollLines(scroll);
|
|
||||||
}
|
}
|
||||||
return true;
|
const findResultDecoration = terminal.registerDecoration({ marker, width: result.size });
|
||||||
|
findResultDecoration?.onRender((e) => console.log('rendered', e, result?.term, result?.col));
|
||||||
|
return findResultDecoration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-1
@@ -930,7 +930,7 @@ declare module 'xterm' {
|
|||||||
* @param cursorYOffset The y position offset of the marker from the cursor.
|
* @param cursorYOffset The y position offset of the marker from the cursor.
|
||||||
* @returns The new marker or undefined.
|
* @returns The new marker or undefined.
|
||||||
*/
|
*/
|
||||||
registerMarker(cursorYOffset?: number): IMarker | undefined;
|
registerMarker(cursorYOffset?: number, col?: number, row?: number): IMarker | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated use `registerMarker` instead.
|
* @deprecated use `registerMarker` instead.
|
||||||
|
|||||||
Reference in New Issue
Block a user