]>
Commit | Line | Data |
---|---|---|
b9571307 DI |
1 | /** |
2 | * @license MIT | |
3 | */ | |
4 | ||
19381454 DI |
5 | // import { ITerminal } from '../../Interfaces'; |
6 | // import { translateBufferLineToString } from '../../utils/BufferLine'; | |
8d12881a | 7 | |
280b698a DI |
8 | interface ISearchResult { |
9 | term: string; | |
10 | col: number; | |
11 | row: number; | |
12 | } | |
13 | ||
4b0cf2ff DI |
14 | /** |
15 | * A class that knows how to search the terminal and how to display the results. | |
16 | */ | |
8d12881a | 17 | export class SearchHelper { |
19381454 | 18 | constructor(private _terminal: any, private _translateBufferLineToString: any) { |
7d8f6980 DI |
19 | // TODO: Search for multiple instances on 1 line |
20 | // TODO: Don't use the actual selection, instead use a "find selection" so multiple instances can be highlighted | |
21 | // TODO: Highlight other instances in the viewport | |
22 | // TODO: Support regex, case sensitivity, etc. | |
8d12881a DI |
23 | } |
24 | ||
25 | /** | |
26 | * Find the next instance of the term, then scroll to and select it. If it | |
27 | * doesn't exist, do nothing. | |
4b0cf2ff | 28 | * @param term Tne search term. |
280b698a | 29 | * @return Whether a result was found. |
8d12881a | 30 | */ |
280b698a | 31 | public findNext(term: string): boolean { |
8d12881a | 32 | if (!term || term.length === 0) { |
f88dcd16 | 33 | return false; |
8d12881a | 34 | } |
8d12881a | 35 | |
280b698a DI |
36 | let result: ISearchResult; |
37 | ||
f8e3fa5a | 38 | let startRow = this._terminal.buffer.ydisp; |
280b698a DI |
39 | if (this._terminal.selectionManager.selectionEnd) { |
40 | // Start from the selection end if there is a selection | |
41 | startRow = this._terminal.selectionManager.selectionEnd[1]; | |
42 | } | |
43 | ||
44 | // Search from ydisp + 1 to end | |
f8e3fa5a | 45 | for (let y = startRow + 1; y < this._terminal.buffer.ybase + this._terminal.rows; y++) { |
280b698a DI |
46 | result = this._findInLine(term, y); |
47 | if (result) { | |
8d12881a DI |
48 | break; |
49 | } | |
50 | } | |
280b698a DI |
51 | |
52 | // Search from the top to the current ydisp | |
53 | if (!result) { | |
54 | for (let y = 0; y < startRow; y++) { | |
55 | result = this._findInLine(term, y); | |
56 | if (result) { | |
57 | break; | |
58 | } | |
59 | } | |
60 | } | |
61 | ||
62 | // Set selection and scroll if a result was found | |
63 | return this._selectResult(result); | |
64 | } | |
65 | ||
7d8f6980 DI |
66 | /** |
67 | * Find the previous instance of the term, then scroll to and select it. If it | |
68 | * doesn't exist, do nothing. | |
4b0cf2ff | 69 | * @param term Tne search term. |
7d8f6980 DI |
70 | * @return Whether a result was found. |
71 | */ | |
72 | public findPrevious(term: string): boolean { | |
73 | if (!term || term.length === 0) { | |
f88dcd16 | 74 | return false; |
7d8f6980 DI |
75 | } |
76 | ||
77 | let result: ISearchResult; | |
78 | ||
f8e3fa5a | 79 | let startRow = this._terminal.buffer.ydisp; |
7d8f6980 DI |
80 | if (this._terminal.selectionManager.selectionStart) { |
81 | // Start from the selection end if there is a selection | |
82 | startRow = this._terminal.selectionManager.selectionStart[1]; | |
83 | } | |
84 | ||
85 | // Search from ydisp + 1 to end | |
86 | for (let y = startRow - 1; y >= 0; y--) { | |
87 | result = this._findInLine(term, y); | |
88 | if (result) { | |
89 | break; | |
90 | } | |
91 | } | |
92 | ||
93 | // Search from the top to the current ydisp | |
94 | if (!result) { | |
f8e3fa5a | 95 | for (let y = this._terminal.buffer.ybase + this._terminal.rows - 1; y > startRow; y--) { |
7d8f6980 DI |
96 | result = this._findInLine(term, y); |
97 | if (result) { | |
98 | break; | |
99 | } | |
100 | } | |
101 | } | |
102 | ||
103 | // Set selection and scroll if a result was found | |
104 | return this._selectResult(result); | |
105 | } | |
106 | ||
4b0cf2ff DI |
107 | /** |
108 | * Searches a line for a search term. | |
109 | * @param term Tne search term. | |
110 | * @param y The line to search. | |
111 | * @return The search result if it was found. | |
112 | */ | |
280b698a | 113 | private _findInLine(term: string, y: number): ISearchResult { |
f8e3fa5a | 114 | const bufferLine = this._terminal.buffer.lines.get(y); |
19381454 | 115 | const lowerStringLine = this._translateBufferLineToString(bufferLine, true).toLowerCase(); |
6854def1 DI |
116 | const lowerTerm = term.toLowerCase(); |
117 | const searchIndex = lowerStringLine.indexOf(lowerTerm); | |
280b698a DI |
118 | if (searchIndex >= 0) { |
119 | return { | |
120 | term, | |
121 | col: searchIndex, | |
122 | row: y | |
123 | }; | |
124 | } | |
125 | } | |
126 | ||
4b0cf2ff DI |
127 | /** |
128 | * Selects and scrolls to a result. | |
129 | * @param result The result to select. | |
130 | * @return Whethera result was selected. | |
131 | */ | |
280b698a DI |
132 | private _selectResult(result: ISearchResult): boolean { |
133 | if (!result) { | |
f88dcd16 | 134 | return false; |
280b698a DI |
135 | } |
136 | this._terminal.selectionManager.setSelection(result.col, result.row, result.term.length); | |
f8e3fa5a | 137 | this._terminal.scrollDisp(result.row - this._terminal.buffer.ydisp, false); |
f88dcd16 | 138 | return true; |
8d12881a | 139 | } |
8d12881a | 140 | } |