diff --git a/src/Interfaces.ts b/src/Interfaces.ts index 5016b4f6f..4de2f2858 100644 --- a/src/Interfaces.ts +++ b/src/Interfaces.ts @@ -52,6 +52,8 @@ export interface ITerminal { export interface ISelectionManager { selectionText: string; + selectionStart: [number, number]; + selectionEnd: [number, number]; setSelection(row: number, col: number, length: number); } diff --git a/src/SearchHelper.ts b/src/SearchHelper.ts index f5021076e..9b2f07b67 100644 --- a/src/SearchHelper.ts +++ b/src/SearchHelper.ts @@ -1,32 +1,77 @@ import { ITerminal } from './Interfaces'; +interface ISearchResult { + term: string; + col: number; + row: number; +} + export class SearchHelper { constructor(private _terminal: ITerminal) { - } /** * Find the next instance of the term, then scroll to and select it. If it * doesn't exist, do nothing. * @param term The term to search for. + * @return Whether a result was found. */ - public findNext(term: string): void { + public findNext(term: string): boolean { if (!term || term.length === 0) { return; } // TODO: Return number of results? - for (let currentLine = this._terminal.ydisp; currentLine < this._terminal.ybase + this._terminal.rows; currentLine++) { - const bufferLine = this._terminal.lines.get(currentLine); - const stringLine = this._translateBufferLineToString(bufferLine, true); - const searchIndex = stringLine.indexOf(term); - if (searchIndex >= 0) { - console.log('found term on line: ' + currentLine); - console.log(stringLine); - this._terminal.selectionManager.setSelection(searchIndex, currentLine, term.length); + let result: ISearchResult; + + let startRow = this._terminal.ydisp; + if (this._terminal.selectionManager.selectionEnd) { + // Start from the selection end if there is a selection + startRow = this._terminal.selectionManager.selectionEnd[1]; + } + + // Search from ydisp + 1 to end + for (let y = startRow + 1; y < this._terminal.ybase + this._terminal.rows; y++) { + result = this._findInLine(term, y); + if (result) { break; } } + + // Search from the top to the current ydisp + if (!result) { + for (let y = 0; y < startRow; y++) { + result = this._findInLine(term, y); + if (result) { + break; + } + } + } + + // Set selection and scroll if a result was found + return this._selectResult(result); + } + + private _findInLine(term: string, y: number): ISearchResult { + const bufferLine = this._terminal.lines.get(y); + const stringLine = this._translateBufferLineToString(bufferLine, true); + const searchIndex = stringLine.indexOf(term); + if (searchIndex >= 0) { + return { + term, + col: searchIndex, + row: y + }; + } + } + + private _selectResult(result: ISearchResult): boolean { + if (!result) { + return; + } + this._terminal.selectionManager.setSelection(result.col, result.row, result.term.length); + const scrollAmount = Math.max(Math.min(result.row - this._terminal.ydisp, this._terminal.ybase), 0); + this._terminal.scrollDisp(scrollAmount, false); } // TODO: Consolidate with SelectionManager function diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index 3c3b839fb..14a0f5a34 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -156,6 +156,9 @@ export class SelectionManager extends EventEmitter { this._buffer = buffer; } + public get selectionStart(): [number, number] { return this._model.finalSelectionStart; } + public get selectionEnd(): [number, number] { return this._model.finalSelectionEnd; } + /** * Gets whether there is an active text selection. */