@xterm/addon-search 0.14.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2017, The xterm.js authors (https://github.com/xtermjs/xterm.js)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,23 @@
1
+ ## @xterm/addon-search
2
+
3
+ An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables searching the buffer. This addon requires xterm.js v4+.
4
+
5
+ ### Install
6
+
7
+ ```bash
8
+ npm install --save @xterm/addon-search
9
+ ```
10
+
11
+ ### Usage
12
+
13
+ ```ts
14
+ import { Terminal } from '@xterm/xterm';
15
+ import { SearchAddon } from '@xterm/addon-search';
16
+
17
+ const terminal = new Terminal();
18
+ const searchAddon = new SearchAddon();
19
+ terminal.loadAddon(searchAddon);
20
+ searchAddon.findNext('foo');
21
+ ```
22
+
23
+ See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/addon-search/typings/addon-search.d.ts) for more advanced usage.
@@ -0,0 +1,2 @@
1
+ !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.SearchAddon=t():e.SearchAddon=t()}(self,(()=>(()=>{"use strict";var e={345:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.runAndSubscribe=t.forwardEvent=t.EventEmitter=void 0,t.EventEmitter=class{constructor(){this._listeners=[],this._disposed=!1}get event(){return this._event||(this._event=e=>(this._listeners.push(e),{dispose:()=>{if(!this._disposed)for(let t=0;t<this._listeners.length;t++)if(this._listeners[t]===e)return void this._listeners.splice(t,1)}})),this._event}fire(e,t){const i=[];for(let e=0;e<this._listeners.length;e++)i.push(this._listeners[e]);for(let s=0;s<i.length;s++)i[s].call(void 0,e,t)}dispose(){this.clearListeners(),this._disposed=!0}clearListeners(){this._listeners&&(this._listeners.length=0)}},t.forwardEvent=function(e,t){return e((e=>t.fire(e)))},t.runAndSubscribe=function(e,t){return t(void 0),e((e=>t(e)))}},859:(e,t)=>{function i(e){for(const t of e)t.dispose();e.length=0}Object.defineProperty(t,"__esModule",{value:!0}),t.getDisposeArrayDisposable=t.disposeArray=t.toDisposable=t.MutableDisposable=t.Disposable=void 0,t.Disposable=class{constructor(){this._disposables=[],this._isDisposed=!1}dispose(){this._isDisposed=!0;for(const e of this._disposables)e.dispose();this._disposables.length=0}register(e){return this._disposables.push(e),e}unregister(e){const t=this._disposables.indexOf(e);-1!==t&&this._disposables.splice(t,1)}},t.MutableDisposable=class{constructor(){this._isDisposed=!1}get value(){return this._isDisposed?void 0:this._value}set value(e){this._isDisposed||e===this._value||(this._value?.dispose(),this._value=e)}clear(){this.value=void 0}dispose(){this._isDisposed=!0,this._value?.dispose(),this._value=void 0}},t.toDisposable=function(e){return{dispose:e}},t.disposeArray=i,t.getDisposeArrayDisposable=function(e){return{dispose:()=>i(e)}}}},t={};function i(s){var r=t[s];if(void 0!==r)return r.exports;var o=t[s]={exports:{}};return e[s](o,o.exports,i),o.exports}var s={};return(()=>{var e=s;Object.defineProperty(e,"__esModule",{value:!0}),e.SearchAddon=void 0;const t=i(345),r=i(859),o=" ~!@#$%^&*()+`-=[]{}|\\;:\"',./<>?";class n extends r.Disposable{constructor(e){super(),this._highlightedLines=new Set,this._highlightDecorations=[],this._selectedDecoration=this.register(new r.MutableDisposable),this._linesCacheTimeoutId=0,this._onDidChangeResults=this.register(new t.EventEmitter),this.onDidChangeResults=this._onDidChangeResults.event,this._highlightLimit=e?.highlightLimit??1e3}activate(e){this._terminal=e,this.register(this._terminal.onWriteParsed((()=>this._updateMatches()))),this.register(this._terminal.onResize((()=>this._updateMatches()))),this.register((0,r.toDisposable)((()=>this.clearDecorations())))}_updateMatches(){this._highlightTimeout&&window.clearTimeout(this._highlightTimeout),this._cachedSearchTerm&&this._lastSearchOptions?.decorations&&(this._highlightTimeout=setTimeout((()=>{const e=this._cachedSearchTerm;this._cachedSearchTerm=void 0,this.findPrevious(e,{...this._lastSearchOptions,incremental:!0,noScroll:!0})}),200))}clearDecorations(e){this._selectedDecoration.clear(),(0,r.disposeArray)(this._highlightDecorations),this._highlightDecorations=[],this._highlightedLines.clear(),e||(this._cachedSearchTerm=void 0)}clearActiveDecoration(){this._selectedDecoration.clear()}findNext(e,t){if(!this._terminal)throw new Error("Cannot use addon until it has been loaded");const i=!this._lastSearchOptions||this._didOptionsChange(this._lastSearchOptions,t);this._lastSearchOptions=t,t?.decorations&&(void 0===this._cachedSearchTerm||e!==this._cachedSearchTerm||i)&&this._highlightAllMatches(e,t);const s=this._findNextAndSelect(e,t);return this._fireResults(t),this._cachedSearchTerm=e,s}_highlightAllMatches(e,t){if(!this._terminal)throw new Error("Cannot use addon until it has been loaded");if(!e||0===e.length)return void this.clearDecorations();t=t||{},this.clearDecorations(!0);const i=[];let s,r=this._find(e,0,0,t);for(;r&&(s?.row!==r.row||s?.col!==r.col)&&!(i.length>=this._highlightLimit);)s=r,i.push(s),r=this._find(e,s.col+s.term.length>=this._terminal.cols?s.row+1:s.row,s.col+s.term.length>=this._terminal.cols?0:s.col+1,t);for(const e of i){const i=this._createResultDecoration(e,t.decorations);i&&(this._highlightedLines.add(i.marker.line),this._highlightDecorations.push({decoration:i,match:e,dispose(){i.dispose()}}))}}_find(e,t,i,s){if(!this._terminal||!e||0===e.length)return this._terminal?.clearSelection(),void this.clearDecorations();if(i>this._terminal.cols)throw new Error(`Invalid col: ${i} to search in terminal of ${this._terminal.cols} cols`);let r;this._initLinesCache();const o={startRow:t,startCol:i};if(r=this._findInLine(e,o,s),!r)for(let i=t+1;i<this._terminal.buffer.active.baseY+this._terminal.rows&&(o.startRow=i,o.startCol=0,r=this._findInLine(e,o,s),!r);i++);return r}_findNextAndSelect(e,t){if(!this._terminal||!e||0===e.length)return this._terminal?.clearSelection(),this.clearDecorations(),!1;const i=this._terminal.getSelectionPosition();this._terminal.clearSelection();let s=0,r=0;i&&(this._cachedSearchTerm===e?(s=i.end.x,r=i.end.y):(s=i.start.x,r=i.start.y)),this._initLinesCache();const o={startRow:r,startCol:s};let n=this._findInLine(e,o,t);if(!n)for(let i=r+1;i<this._terminal.buffer.active.baseY+this._terminal.rows&&(o.startRow=i,o.startCol=0,n=this._findInLine(e,o,t),!n);i++);if(!n&&0!==r)for(let i=0;i<r&&(o.startRow=i,o.startCol=0,n=this._findInLine(e,o,t),!n);i++);return!n&&i&&(o.startRow=i.start.y,o.startCol=0,n=this._findInLine(e,o,t)),this._selectResult(n,t?.decorations,t?.noScroll)}findPrevious(e,t){if(!this._terminal)throw new Error("Cannot use addon until it has been loaded");const i=!this._lastSearchOptions||this._didOptionsChange(this._lastSearchOptions,t);this._lastSearchOptions=t,t?.decorations&&(void 0===this._cachedSearchTerm||e!==this._cachedSearchTerm||i)&&this._highlightAllMatches(e,t);const s=this._findPreviousAndSelect(e,t);return this._fireResults(t),this._cachedSearchTerm=e,s}_didOptionsChange(e,t){return!!t&&(e.caseSensitive!==t.caseSensitive||e.regex!==t.regex||e.wholeWord!==t.wholeWord)}_fireResults(e){if(e?.decorations){let e=-1;if(this._selectedDecoration.value){const t=this._selectedDecoration.value.match;for(let i=0;i<this._highlightDecorations.length;i++){const s=this._highlightDecorations[i].match;if(s.row===t.row&&s.col===t.col&&s.size===t.size){e=i;break}}}this._onDidChangeResults.fire({resultIndex:e,resultCount:this._highlightDecorations.length})}}_findPreviousAndSelect(e,t){if(!this._terminal)throw new Error("Cannot use addon until it has been loaded");if(!this._terminal||!e||0===e.length)return this._terminal?.clearSelection(),this.clearDecorations(),!1;const i=this._terminal.getSelectionPosition();this._terminal.clearSelection();let s=this._terminal.buffer.active.baseY+this._terminal.rows-1,r=this._terminal.cols;const o=!0;this._initLinesCache();const n={startRow:s,startCol:r};let h;if(i&&(n.startRow=s=i.start.y,n.startCol=r=i.start.x,this._cachedSearchTerm!==e&&(h=this._findInLine(e,n,t,!1),h||(n.startRow=s=i.end.y,n.startCol=r=i.end.x))),h||(h=this._findInLine(e,n,t,o)),!h){n.startCol=Math.max(n.startCol,this._terminal.cols);for(let i=s-1;i>=0&&(n.startRow=i,h=this._findInLine(e,n,t,o),!h);i--);}if(!h&&s!==this._terminal.buffer.active.baseY+this._terminal.rows-1)for(let i=this._terminal.buffer.active.baseY+this._terminal.rows-1;i>=s&&(n.startRow=i,h=this._findInLine(e,n,t,o),!h);i--);return this._selectResult(h,t?.decorations,t?.noScroll)}_initLinesCache(){const e=this._terminal;this._linesCache||(this._linesCache=new Array(e.buffer.active.length),this._cursorMoveListener=e.onCursorMove((()=>this._destroyLinesCache())),this._resizeListener=e.onResize((()=>this._destroyLinesCache()))),window.clearTimeout(this._linesCacheTimeoutId),this._linesCacheTimeoutId=window.setTimeout((()=>this._destroyLinesCache()),15e3)}_destroyLinesCache(){this._linesCache=void 0,this._cursorMoveListener&&(this._cursorMoveListener.dispose(),this._cursorMoveListener=void 0),this._resizeListener&&(this._resizeListener.dispose(),this._resizeListener=void 0),this._linesCacheTimeoutId&&(window.clearTimeout(this._linesCacheTimeoutId),this._linesCacheTimeoutId=0)}_isWholeWord(e,t,i){return(0===e||o.includes(t[e-1]))&&(e+i.length===t.length||o.includes(t[e+i.length]))}_findInLine(e,t,i={},s=!1){const r=this._terminal,o=t.startRow,n=t.startCol,h=r.buffer.active.getLine(o);if(h?.isWrapped)return s?void(t.startCol+=r.cols):(t.startRow--,t.startCol+=r.cols,this._findInLine(e,t,i));let a=this._linesCache?.[o];a||(a=this._translateBufferLineToStringWithWrap(o,!0),this._linesCache&&(this._linesCache[o]=a));const[l,c]=a,d=this._bufferColsToStringOffset(o,n),_=i.caseSensitive?e:e.toLowerCase(),u=i.caseSensitive?l:l.toLowerCase();let f=-1;if(i.regex){const t=RegExp(_,"g");let i;if(s)for(;i=t.exec(u.slice(0,d));)f=t.lastIndex-i[0].length,e=i[0],t.lastIndex-=e.length-1;else i=t.exec(u.slice(d)),i&&i[0].length>0&&(f=d+(t.lastIndex-i[0].length),e=i[0])}else s?d-_.length>=0&&(f=u.lastIndexOf(_,d-_.length)):f=u.indexOf(_,d);if(f>=0){if(i.wholeWord&&!this._isWholeWord(f,u,e))return;let t=0;for(;t<c.length-1&&f>=c[t+1];)t++;let s=t;for(;s<c.length-1&&f+e.length>=c[s+1];)s++;const n=f-c[t],h=f+e.length-c[s],a=this._stringLengthToBufferSize(o+t,n);return{term:e,col:a,row:o+t,size:this._stringLengthToBufferSize(o+s,h)-a+r.cols*(s-t)}}}_stringLengthToBufferSize(e,t){const i=this._terminal.buffer.active.getLine(e);if(!i)return 0;for(let e=0;e<t;e++){const s=i.getCell(e);if(!s)break;const r=s.getChars();r.length>1&&(t-=r.length-1);const o=i.getCell(e+1);o&&0===o.getWidth()&&t++}return t}_bufferColsToStringOffset(e,t){const i=this._terminal;let s=e,r=0,o=i.buffer.active.getLine(s);for(;t>0&&o;){for(let e=0;e<t&&e<i.cols;e++){const t=o.getCell(e);if(!t)break;t.getWidth()&&(r+=0===t.getCode()?1:t.getChars().length)}if(s++,o=i.buffer.active.getLine(s),o&&!o.isWrapped)break;t-=i.cols}return r}_translateBufferLineToStringWithWrap(e,t){const i=this._terminal,s=[],r=[0];let o=i.buffer.active.getLine(e);for(;o;){const n=i.buffer.active.getLine(e+1),h=!!n&&n.isWrapped;let a=o.translateToString(!h&&t);if(h&&n){const e=o.getCell(o.length-1);e&&0===e.getCode()&&1===e.getWidth()&&2===n.getCell(0)?.getWidth()&&(a=a.slice(0,-1))}if(s.push(a),!h)break;r.push(r[r.length-1]+a.length),e++,o=n}return[s.join(""),r]}_selectResult(e,t,i){const s=this._terminal;if(this._selectedDecoration.clear(),!e)return s.clearSelection(),!1;if(s.select(e.col,e.row,e.size),t){const i=s.registerMarker(-s.buffer.active.baseY-s.buffer.active.cursorY+e.row);if(i){const o=s.registerDecoration({marker:i,x:e.col,width:e.size,backgroundColor:t.activeMatchBackground,layer:"top",overviewRulerOptions:{color:t.activeMatchColorOverviewRuler}});if(o){const s=[];s.push(i),s.push(o.onRender((e=>this._applyStyles(e,t.activeMatchBorder,!0)))),s.push(o.onDispose((()=>(0,r.disposeArray)(s)))),this._selectedDecoration.value={decoration:o,match:e,dispose(){o.dispose()}}}}}if(!i&&(e.row>=s.buffer.active.viewportY+s.rows||e.row<s.buffer.active.viewportY)){let t=e.row-s.buffer.active.viewportY;t-=Math.floor(s.rows/2),s.scrollLines(t)}return!0}_applyStyles(e,t,i){e.classList.contains("xterm-find-result-decoration")||(e.classList.add("xterm-find-result-decoration"),t&&(e.style.outline=`1px solid ${t}`)),i&&e.classList.add("xterm-find-active-result-decoration")}_createResultDecoration(e,t){const i=this._terminal,s=i.registerMarker(-i.buffer.active.baseY-i.buffer.active.cursorY+e.row);if(!s)return;const o=i.registerDecoration({marker:s,x:e.col,width:e.size,backgroundColor:t.matchBackground,overviewRulerOptions:this._highlightedLines.has(s.line)?void 0:{color:t.matchOverviewRuler,position:"center"}});if(o){const e=[];e.push(s),e.push(o.onRender((e=>this._applyStyles(e,t.matchBorder,!1)))),e.push(o.onDispose((()=>(0,r.disposeArray)(e))))}return o}}e.SearchAddon=n})(),s})()));
2
+ //# sourceMappingURL=addon-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"addon-search.js","mappings":"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","sources":["webpack://SearchAddon/webpack/universalModuleDefinition","webpack://SearchAddon/../../src/common/EventEmitter.ts","webpack://SearchAddon/../../src/common/Lifecycle.ts","webpack://SearchAddon/webpack/bootstrap","webpack://SearchAddon/./src/SearchAddon.ts"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"SearchAddon\"] = factory();\n\telse\n\t\troot[\"SearchAddon\"] = factory();\n})(self, () => {\nreturn ","/**\n * Copyright (c) 2019 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IDisposable } from 'common/Types';\n\ninterface IListener<T, U = void> {\n (arg1: T, arg2: U): void;\n}\n\nexport interface IEvent<T, U = void> {\n (listener: (arg1: T, arg2: U) => any): IDisposable;\n}\n\nexport interface IEventEmitter<T, U = void> {\n event: IEvent<T, U>;\n fire(arg1: T, arg2: U): void;\n dispose(): void;\n}\n\nexport class EventEmitter<T, U = void> implements IEventEmitter<T, U> {\n private _listeners: IListener<T, U>[] = [];\n private _event?: IEvent<T, U>;\n private _disposed: boolean = false;\n\n public get event(): IEvent<T, U> {\n if (!this._event) {\n this._event = (listener: (arg1: T, arg2: U) => any) => {\n this._listeners.push(listener);\n const disposable = {\n dispose: () => {\n if (!this._disposed) {\n for (let i = 0; i < this._listeners.length; i++) {\n if (this._listeners[i] === listener) {\n this._listeners.splice(i, 1);\n return;\n }\n }\n }\n }\n };\n return disposable;\n };\n }\n return this._event;\n }\n\n public fire(arg1: T, arg2: U): void {\n const queue: IListener<T, U>[] = [];\n for (let i = 0; i < this._listeners.length; i++) {\n queue.push(this._listeners[i]);\n }\n for (let i = 0; i < queue.length; i++) {\n queue[i].call(undefined, arg1, arg2);\n }\n }\n\n public dispose(): void {\n this.clearListeners();\n this._disposed = true;\n }\n\n public clearListeners(): void {\n if (this._listeners) {\n this._listeners.length = 0;\n }\n }\n}\n\nexport function forwardEvent<T>(from: IEvent<T>, to: IEventEmitter<T>): IDisposable {\n return from(e => to.fire(e));\n}\n\nexport function runAndSubscribe<T>(event: IEvent<T>, handler: (e: T | undefined) => any): IDisposable {\n handler(undefined);\n return event(e => handler(e));\n}\n","/**\n * Copyright (c) 2018 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { IDisposable } from 'common/Types';\n\n/**\n * A base class that can be extended to provide convenience methods for managing the lifecycle of an\n * object and its components.\n */\nexport abstract class Disposable implements IDisposable {\n protected _disposables: IDisposable[] = [];\n protected _isDisposed: boolean = false;\n\n /**\n * Disposes the object, triggering the `dispose` method on all registered IDisposables.\n */\n public dispose(): void {\n this._isDisposed = true;\n for (const d of this._disposables) {\n d.dispose();\n }\n this._disposables.length = 0;\n }\n\n /**\n * Registers a disposable object.\n * @param d The disposable to register.\n * @returns The disposable.\n */\n public register<T extends IDisposable>(d: T): T {\n this._disposables.push(d);\n return d;\n }\n\n /**\n * Unregisters a disposable object if it has been registered, if not do\n * nothing.\n * @param d The disposable to unregister.\n */\n public unregister<T extends IDisposable>(d: T): void {\n const index = this._disposables.indexOf(d);\n if (index !== -1) {\n this._disposables.splice(index, 1);\n }\n }\n}\n\nexport class MutableDisposable<T extends IDisposable> implements IDisposable {\n private _value?: T;\n private _isDisposed = false;\n\n /**\n * Gets the value if it exists.\n */\n public get value(): T | undefined {\n return this._isDisposed ? undefined : this._value;\n }\n\n /**\n * Sets the value, disposing of the old value if it exists.\n */\n public set value(value: T | undefined) {\n if (this._isDisposed || value === this._value) {\n return;\n }\n this._value?.dispose();\n this._value = value;\n }\n\n /**\n * Resets the stored value and disposes of the previously stored value.\n */\n public clear(): void {\n this.value = undefined;\n }\n\n public dispose(): void {\n this._isDisposed = true;\n this._value?.dispose();\n this._value = undefined;\n }\n}\n\n/**\n * Wrap a function in a disposable.\n */\nexport function toDisposable(f: () => void): IDisposable {\n return { dispose: f };\n}\n\n/**\n * Dispose of all disposables in an array and set its length to 0.\n */\nexport function disposeArray(disposables: IDisposable[]): void {\n for (const d of disposables) {\n d.dispose();\n }\n disposables.length = 0;\n}\n\n/**\n * Creates a disposable that will dispose of an array of disposables when disposed.\n */\nexport function getDisposeArrayDisposable(array: IDisposable[]): IDisposable {\n return { dispose: () => disposeArray(array) };\n}\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { Terminal, IDisposable, ITerminalAddon, IDecoration } from '@xterm/xterm';\nimport { EventEmitter } from 'common/EventEmitter';\nimport { Disposable, toDisposable, disposeArray, MutableDisposable } from 'common/Lifecycle';\n\nexport interface ISearchOptions {\n regex?: boolean;\n wholeWord?: boolean;\n caseSensitive?: boolean;\n incremental?: boolean;\n decorations?: ISearchDecorationOptions;\n noScroll?: boolean;\n}\n\ninterface ISearchDecorationOptions {\n matchBackground?: string;\n matchBorder?: string;\n matchOverviewRuler: string;\n activeMatchBackground?: string;\n activeMatchBorder?: string;\n activeMatchColorOverviewRuler: string;\n}\n\nexport interface ISearchPosition {\n startCol: number;\n startRow: number;\n}\n\nexport interface ISearchAddonOptions {\n highlightLimit: number;\n}\n\nexport interface ISearchResult {\n term: string;\n col: number;\n row: number;\n size: number;\n}\n\ntype LineCacheEntry = [\n /**\n * The string representation of a line (as opposed to the buffer cell representation).\n */\n lineAsString: string,\n /**\n * The offsets where each line starts when the entry describes a wrapped line.\n */\n lineOffsets: number[]\n];\n\ninterface IHighlight extends IDisposable {\n decoration: IDecoration;\n match: ISearchResult;\n}\n\nconst NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\\\\;:\"\\',./<>?';\nconst LINES_CACHE_TIME_TO_LIVE = 15 * 1000; // 15 secs\nconst DEFAULT_HIGHLIGHT_LIMIT = 1000;\n\nexport class SearchAddon extends Disposable implements ITerminalAddon {\n private _terminal: Terminal | undefined;\n private _cachedSearchTerm: string | undefined;\n private _highlightedLines: Set<number> = new Set();\n private _highlightDecorations: IHighlight[] = [];\n private _selectedDecoration: MutableDisposable<IHighlight> = this.register(new MutableDisposable());\n private _highlightLimit: number;\n private _lastSearchOptions: ISearchOptions | undefined;\n private _highlightTimeout: number | undefined;\n /**\n * translateBufferLineToStringWithWrap is a fairly expensive call.\n * We memoize the calls into an array that has a time based ttl.\n * _linesCache is also invalidated when the terminal cursor moves.\n */\n private _linesCache: LineCacheEntry[] | undefined;\n private _linesCacheTimeoutId = 0;\n private _cursorMoveListener: IDisposable | undefined;\n private _resizeListener: IDisposable | undefined;\n\n private readonly _onDidChangeResults = this.register(new EventEmitter<{ resultIndex: number, resultCount: number }>());\n public readonly onDidChangeResults = this._onDidChangeResults.event;\n\n constructor(options?: Partial<ISearchAddonOptions>) {\n super();\n\n this._highlightLimit = options?.highlightLimit ?? DEFAULT_HIGHLIGHT_LIMIT;\n }\n\n public activate(terminal: Terminal): void {\n this._terminal = terminal;\n this.register(this._terminal.onWriteParsed(() => this._updateMatches()));\n this.register(this._terminal.onResize(() => this._updateMatches()));\n this.register(toDisposable(() => this.clearDecorations()));\n }\n\n private _updateMatches(): void {\n if (this._highlightTimeout) {\n window.clearTimeout(this._highlightTimeout);\n }\n if (this._cachedSearchTerm && this._lastSearchOptions?.decorations) {\n this._highlightTimeout = setTimeout(() => {\n const term = this._cachedSearchTerm;\n this._cachedSearchTerm = undefined;\n this.findPrevious(term!, { ...this._lastSearchOptions, incremental: true, noScroll: true });\n }, 200);\n }\n }\n\n public clearDecorations(retainCachedSearchTerm?: boolean): void {\n this._selectedDecoration.clear();\n disposeArray(this._highlightDecorations);\n this._highlightDecorations = [];\n this._highlightedLines.clear();\n if (!retainCachedSearchTerm) {\n this._cachedSearchTerm = undefined;\n }\n }\n\n public clearActiveDecoration(): void {\n this._selectedDecoration.clear();\n }\n\n /**\n * Find the next instance of the term, then scroll to and select it. If it\n * doesn't exist, do nothing.\n * @param term The search term.\n * @param searchOptions Search options.\n * @returns Whether a result was found.\n */\n public findNext(term: string, searchOptions?: ISearchOptions): boolean {\n if (!this._terminal) {\n throw new Error('Cannot use addon until it has been loaded');\n }\n const didOptionsChanged = this._lastSearchOptions ? this._didOptionsChange(this._lastSearchOptions, searchOptions) : true;\n this._lastSearchOptions = searchOptions;\n if (searchOptions?.decorations) {\n if (this._cachedSearchTerm === undefined || term !== this._cachedSearchTerm || didOptionsChanged) {\n this._highlightAllMatches(term, searchOptions);\n }\n }\n\n const found = this._findNextAndSelect(term, searchOptions);\n this._fireResults(searchOptions);\n this._cachedSearchTerm = term;\n\n return found;\n }\n\n private _highlightAllMatches(term: string, searchOptions: ISearchOptions): void {\n if (!this._terminal) {\n throw new Error('Cannot use addon until it has been loaded');\n }\n if (!term || term.length === 0) {\n this.clearDecorations();\n return;\n }\n searchOptions = searchOptions || {};\n\n // new search, clear out the old decorations\n this.clearDecorations(true);\n\n const searchResultsWithHighlight: ISearchResult[] = [];\n let prevResult: ISearchResult | undefined = undefined;\n let result = this._find(term, 0, 0, searchOptions);\n while (result && (prevResult?.row !== result.row || prevResult?.col !== result.col)) {\n if (searchResultsWithHighlight.length >= this._highlightLimit) {\n break;\n }\n prevResult = result;\n searchResultsWithHighlight.push(prevResult);\n result = this._find(\n term,\n prevResult.col + prevResult.term.length >= this._terminal.cols ? prevResult.row + 1 : prevResult.row,\n prevResult.col + prevResult.term.length >= this._terminal.cols ? 0 : prevResult.col + 1,\n searchOptions\n );\n }\n for (const match of searchResultsWithHighlight) {\n const decoration = this._createResultDecoration(match, searchOptions.decorations!);\n if (decoration) {\n this._highlightedLines.add(decoration.marker.line);\n this._highlightDecorations.push({ decoration, match, dispose() { decoration.dispose(); } });\n }\n }\n }\n\n private _find(term: string, startRow: number, startCol: number, searchOptions?: ISearchOptions): ISearchResult | undefined {\n if (!this._terminal || !term || term.length === 0) {\n this._terminal?.clearSelection();\n this.clearDecorations();\n return undefined;\n }\n if (startCol > this._terminal.cols) {\n throw new Error(`Invalid col: ${startCol} to search in terminal of ${this._terminal.cols} cols`);\n }\n\n let result: ISearchResult | undefined = undefined;\n\n this._initLinesCache();\n\n const searchPosition: ISearchPosition = {\n startRow,\n startCol\n };\n\n // Search startRow\n result = this._findInLine(term, searchPosition, searchOptions);\n // Search from startRow + 1 to end\n if (!result) {\n\n for (let y = startRow + 1; y < this._terminal.buffer.active.baseY + this._terminal.rows; y++) {\n searchPosition.startRow = y;\n searchPosition.startCol = 0;\n // If the current line is wrapped line, increase index of column to ignore the previous scan\n // Otherwise, reset beginning column index to zero with set new unwrapped line index\n result = this._findInLine(term, searchPosition, searchOptions);\n if (result) {\n break;\n }\n }\n }\n return result;\n }\n\n private _findNextAndSelect(term: string, searchOptions?: ISearchOptions): boolean {\n if (!this._terminal || !term || term.length === 0) {\n this._terminal?.clearSelection();\n this.clearDecorations();\n return false;\n }\n\n const prevSelectedPos = this._terminal.getSelectionPosition();\n this._terminal.clearSelection();\n\n let startCol = 0;\n let startRow = 0;\n if (prevSelectedPos) {\n if (this._cachedSearchTerm === term) {\n startCol = prevSelectedPos.end.x;\n startRow = prevSelectedPos.end.y;\n } else {\n startCol = prevSelectedPos.start.x;\n startRow = prevSelectedPos.start.y;\n }\n }\n\n this._initLinesCache();\n\n const searchPosition: ISearchPosition = {\n startRow,\n startCol\n };\n\n // Search startRow\n let result = this._findInLine(term, searchPosition, searchOptions);\n // Search from startRow + 1 to end\n if (!result) {\n\n for (let y = startRow + 1; y < this._terminal.buffer.active.baseY + this._terminal.rows; y++) {\n searchPosition.startRow = y;\n searchPosition.startCol = 0;\n // If the current line is wrapped line, increase index of column to ignore the previous scan\n // Otherwise, reset beginning column index to zero with set new unwrapped line index\n result = this._findInLine(term, searchPosition, searchOptions);\n if (result) {\n break;\n }\n }\n }\n // If we hit the bottom and didn't search from the very top wrap back up\n if (!result && startRow !== 0) {\n for (let y = 0; y < startRow; y++) {\n searchPosition.startRow = y;\n searchPosition.startCol = 0;\n result = this._findInLine(term, searchPosition, searchOptions);\n if (result) {\n break;\n }\n }\n }\n\n // If there is only one result, wrap back and return selection if it exists.\n if (!result && prevSelectedPos) {\n searchPosition.startRow = prevSelectedPos.start.y;\n searchPosition.startCol = 0;\n result = this._findInLine(term, searchPosition, searchOptions);\n }\n\n // Set selection and scroll if a result was found\n return this._selectResult(result, searchOptions?.decorations, searchOptions?.noScroll);\n }\n /**\n * Find the previous instance of the term, then scroll to and select it. If it\n * doesn't exist, do nothing.\n * @param term The search term.\n * @param searchOptions Search options.\n * @returns Whether a result was found.\n */\n public findPrevious(term: string, searchOptions?: ISearchOptions): boolean {\n if (!this._terminal) {\n throw new Error('Cannot use addon until it has been loaded');\n }\n const didOptionsChanged = this._lastSearchOptions ? this._didOptionsChange(this._lastSearchOptions, searchOptions) : true;\n this._lastSearchOptions = searchOptions;\n if (searchOptions?.decorations) {\n if (this._cachedSearchTerm === undefined || term !== this._cachedSearchTerm || didOptionsChanged) {\n this._highlightAllMatches(term, searchOptions);\n }\n }\n\n const found = this._findPreviousAndSelect(term, searchOptions);\n this._fireResults(searchOptions);\n this._cachedSearchTerm = term;\n\n return found;\n }\n\n private _didOptionsChange(lastSearchOptions: ISearchOptions, searchOptions?: ISearchOptions): boolean {\n if (!searchOptions) {\n return false;\n }\n if (lastSearchOptions.caseSensitive !== searchOptions.caseSensitive) {\n return true;\n }\n if (lastSearchOptions.regex !== searchOptions.regex) {\n return true;\n }\n if (lastSearchOptions.wholeWord !== searchOptions.wholeWord) {\n return true;\n }\n return false;\n }\n\n private _fireResults(searchOptions?: ISearchOptions): void {\n if (searchOptions?.decorations) {\n let resultIndex = -1;\n if (this._selectedDecoration.value) {\n const selectedMatch = this._selectedDecoration.value.match;\n for (let i = 0; i < this._highlightDecorations.length; i++) {\n const match = this._highlightDecorations[i].match;\n if (match.row === selectedMatch.row && match.col === selectedMatch.col && match.size === selectedMatch.size) {\n resultIndex = i;\n break;\n }\n }\n }\n this._onDidChangeResults.fire({ resultIndex, resultCount: this._highlightDecorations.length });\n }\n }\n\n private _findPreviousAndSelect(term: string, searchOptions?: ISearchOptions): boolean {\n if (!this._terminal) {\n throw new Error('Cannot use addon until it has been loaded');\n }\n if (!this._terminal || !term || term.length === 0) {\n this._terminal?.clearSelection();\n this.clearDecorations();\n return false;\n }\n\n const prevSelectedPos = this._terminal.getSelectionPosition();\n this._terminal.clearSelection();\n\n let startRow = this._terminal.buffer.active.baseY + this._terminal.rows - 1;\n let startCol = this._terminal.cols;\n const isReverseSearch = true;\n\n this._initLinesCache();\n const searchPosition: ISearchPosition = {\n startRow,\n startCol\n };\n\n let result: ISearchResult | undefined;\n if (prevSelectedPos) {\n searchPosition.startRow = startRow = prevSelectedPos.start.y;\n searchPosition.startCol = startCol = prevSelectedPos.start.x;\n if (this._cachedSearchTerm !== term) {\n // Try to expand selection to right first.\n result = this._findInLine(term, searchPosition, searchOptions, false);\n if (!result) {\n // If selection was not able to be expanded to the right, then try reverse search\n searchPosition.startRow = startRow = prevSelectedPos.end.y;\n searchPosition.startCol = startCol = prevSelectedPos.end.x;\n }\n }\n }\n\n if (!result) {\n result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);\n }\n\n // Search from startRow - 1 to top\n if (!result) {\n searchPosition.startCol = Math.max(searchPosition.startCol, this._terminal.cols);\n for (let y = startRow - 1; y >= 0; y--) {\n searchPosition.startRow = y;\n result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);\n if (result) {\n break;\n }\n }\n }\n // If we hit the top and didn't search from the very bottom wrap back down\n if (!result && startRow !== (this._terminal.buffer.active.baseY + this._terminal.rows - 1)) {\n for (let y = (this._terminal.buffer.active.baseY + this._terminal.rows - 1); y >= startRow; y--) {\n searchPosition.startRow = y;\n result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);\n if (result) {\n break;\n }\n }\n }\n\n // Set selection and scroll if a result was found\n return this._selectResult(result, searchOptions?.decorations, searchOptions?.noScroll);\n }\n\n /**\n * Sets up a line cache with a ttl\n */\n private _initLinesCache(): void {\n const terminal = this._terminal!;\n if (!this._linesCache) {\n this._linesCache = new Array(terminal.buffer.active.length);\n this._cursorMoveListener = terminal.onCursorMove(() => this._destroyLinesCache());\n this._resizeListener = terminal.onResize(() => this._destroyLinesCache());\n }\n\n window.clearTimeout(this._linesCacheTimeoutId);\n this._linesCacheTimeoutId = window.setTimeout(() => this._destroyLinesCache(), LINES_CACHE_TIME_TO_LIVE);\n }\n\n private _destroyLinesCache(): void {\n this._linesCache = undefined;\n if (this._cursorMoveListener) {\n this._cursorMoveListener.dispose();\n this._cursorMoveListener = undefined;\n }\n if (this._resizeListener) {\n this._resizeListener.dispose();\n this._resizeListener = undefined;\n }\n if (this._linesCacheTimeoutId) {\n window.clearTimeout(this._linesCacheTimeoutId);\n this._linesCacheTimeoutId = 0;\n }\n }\n\n /**\n * A found substring is a whole word if it doesn't have an alphanumeric character directly\n * adjacent to it.\n * @param searchIndex starting indext of the potential whole word substring\n * @param line entire string in which the potential whole word was found\n * @param term the substring that starts at searchIndex\n */\n private _isWholeWord(searchIndex: number, line: string, term: string): boolean {\n return ((searchIndex === 0) || (NON_WORD_CHARACTERS.includes(line[searchIndex - 1]))) &&\n (((searchIndex + term.length) === line.length) || (NON_WORD_CHARACTERS.includes(line[searchIndex + term.length])));\n }\n\n /**\n * Searches a line for a search term. Takes the provided terminal line and searches the text line,\n * which may contain subsequent terminal lines if the text is wrapped. If the provided line number\n * is part of a wrapped text line that started on an earlier line then it is skipped since it will\n * be properly searched when the terminal line that the text starts on is searched.\n * @param term The search term.\n * @param searchPosition The position to start the search.\n * @param searchOptions Search options.\n * @param isReverseSearch Whether the search should start from the right side of the terminal and\n * search to the left.\n * @returns The search result if it was found.\n */\n protected _findInLine(term: string, searchPosition: ISearchPosition, searchOptions: ISearchOptions = {}, isReverseSearch: boolean = false): ISearchResult | undefined {\n const terminal = this._terminal!;\n const row = searchPosition.startRow;\n const col = searchPosition.startCol;\n\n // Ignore wrapped lines, only consider on unwrapped line (first row of command string).\n const firstLine = terminal.buffer.active.getLine(row);\n if (firstLine?.isWrapped) {\n if (isReverseSearch) {\n searchPosition.startCol += terminal.cols;\n return;\n }\n\n // This will iterate until we find the line start.\n // When we find it, we will search using the calculated start column.\n searchPosition.startRow--;\n searchPosition.startCol += terminal.cols;\n return this._findInLine(term, searchPosition, searchOptions);\n }\n let cache = this._linesCache?.[row];\n if (!cache) {\n cache = this._translateBufferLineToStringWithWrap(row, true);\n if (this._linesCache) {\n this._linesCache[row] = cache;\n }\n }\n const [stringLine, offsets] = cache;\n\n const offset = this._bufferColsToStringOffset(row, col);\n const searchTerm = searchOptions.caseSensitive ? term : term.toLowerCase();\n const searchStringLine = searchOptions.caseSensitive ? stringLine : stringLine.toLowerCase();\n\n let resultIndex = -1;\n if (searchOptions.regex) {\n const searchRegex = RegExp(searchTerm, 'g');\n let foundTerm: RegExpExecArray | null;\n if (isReverseSearch) {\n // This loop will get the resultIndex of the _last_ regex match in the range 0..offset\n while (foundTerm = searchRegex.exec(searchStringLine.slice(0, offset))) {\n resultIndex = searchRegex.lastIndex - foundTerm[0].length;\n term = foundTerm[0];\n searchRegex.lastIndex -= (term.length - 1);\n }\n } else {\n foundTerm = searchRegex.exec(searchStringLine.slice(offset));\n if (foundTerm && foundTerm[0].length > 0) {\n resultIndex = offset + (searchRegex.lastIndex - foundTerm[0].length);\n term = foundTerm[0];\n }\n }\n } else {\n if (isReverseSearch) {\n if (offset - searchTerm.length >= 0) {\n resultIndex = searchStringLine.lastIndexOf(searchTerm, offset - searchTerm.length);\n }\n } else {\n resultIndex = searchStringLine.indexOf(searchTerm, offset);\n }\n }\n\n if (resultIndex >= 0) {\n if (searchOptions.wholeWord && !this._isWholeWord(resultIndex, searchStringLine, term)) {\n return;\n }\n\n // Adjust the row number and search index if needed since a \"line\" of text can span multiple\n // rows\n let startRowOffset = 0;\n while (startRowOffset < offsets.length - 1 && resultIndex >= offsets[startRowOffset + 1]) {\n startRowOffset++;\n }\n let endRowOffset = startRowOffset;\n while (endRowOffset < offsets.length - 1 && resultIndex + term.length >= offsets[endRowOffset + 1]) {\n endRowOffset++;\n }\n const startColOffset = resultIndex - offsets[startRowOffset];\n const endColOffset = resultIndex + term.length - offsets[endRowOffset];\n const startColIndex = this._stringLengthToBufferSize(row + startRowOffset, startColOffset);\n const endColIndex = this._stringLengthToBufferSize(row + endRowOffset, endColOffset);\n const size = endColIndex - startColIndex + terminal.cols * (endRowOffset - startRowOffset);\n\n return {\n term,\n col: startColIndex,\n row: row + startRowOffset,\n size\n };\n }\n }\n\n private _stringLengthToBufferSize(row: number, offset: number): number {\n const line = this._terminal!.buffer.active.getLine(row);\n if (!line) {\n return 0;\n }\n for (let i = 0; i < offset; i++) {\n const cell = line.getCell(i);\n if (!cell) {\n break;\n }\n // Adjust the searchIndex to normalize emoji into single chars\n const char = cell.getChars();\n if (char.length > 1) {\n offset -= char.length - 1;\n }\n // Adjust the searchIndex for empty characters following wide unicode\n // chars (eg. CJK)\n const nextCell = line.getCell(i + 1);\n if (nextCell && nextCell.getWidth() === 0) {\n offset++;\n }\n }\n return offset;\n }\n\n private _bufferColsToStringOffset(startRow: number, cols: number): number {\n const terminal = this._terminal!;\n let lineIndex = startRow;\n let offset = 0;\n let line = terminal.buffer.active.getLine(lineIndex);\n while (cols > 0 && line) {\n for (let i = 0; i < cols && i < terminal.cols; i++) {\n const cell = line.getCell(i);\n if (!cell) {\n break;\n }\n if (cell.getWidth()) {\n // Treat null characters as whitespace to align with the translateToString API\n offset += cell.getCode() === 0 ? 1 : cell.getChars().length;\n }\n }\n lineIndex++;\n line = terminal.buffer.active.getLine(lineIndex);\n if (line && !line.isWrapped) {\n break;\n }\n cols -= terminal.cols;\n }\n return offset;\n }\n\n /**\n * Translates a buffer line to a string, including subsequent lines if they are wraps.\n * Wide characters will count as two columns in the resulting string. This\n * function is useful for getting the actual text underneath the raw selection\n * position.\n * @param lineIndex The index of the line being translated.\n * @param trimRight Whether to trim whitespace to the right.\n */\n private _translateBufferLineToStringWithWrap(lineIndex: number, trimRight: boolean): LineCacheEntry {\n const terminal = this._terminal!;\n const strings = [];\n const lineOffsets = [0];\n let line = terminal.buffer.active.getLine(lineIndex);\n while (line) {\n const nextLine = terminal.buffer.active.getLine(lineIndex + 1);\n const lineWrapsToNext = nextLine ? nextLine.isWrapped : false;\n let string = line.translateToString(!lineWrapsToNext && trimRight);\n if (lineWrapsToNext && nextLine) {\n const lastCell = line.getCell(line.length - 1);\n const lastCellIsNull = lastCell && lastCell.getCode() === 0 && lastCell.getWidth() === 1;\n // a wide character wrapped to the next line\n if (lastCellIsNull && nextLine.getCell(0)?.getWidth() === 2) {\n string = string.slice(0, -1);\n }\n }\n strings.push(string);\n if (lineWrapsToNext) {\n lineOffsets.push(lineOffsets[lineOffsets.length - 1] + string.length);\n } else {\n break;\n }\n lineIndex++;\n line = nextLine;\n }\n return [strings.join(''), lineOffsets];\n }\n\n /**\n * Selects and scrolls to a result.\n * @param result The result to select.\n * @returns Whether a result was selected.\n */\n private _selectResult(result: ISearchResult | undefined, options?: ISearchDecorationOptions, noScroll?: boolean): boolean {\n const terminal = this._terminal!;\n this._selectedDecoration.clear();\n if (!result) {\n terminal.clearSelection();\n return false;\n }\n terminal.select(result.col, result.row, result.size);\n if (options) {\n const marker = terminal.registerMarker(-terminal.buffer.active.baseY - terminal.buffer.active.cursorY + result.row);\n if (marker) {\n const decoration = terminal.registerDecoration({\n marker,\n x: result.col,\n width: result.size,\n backgroundColor: options.activeMatchBackground,\n layer: 'top',\n overviewRulerOptions: {\n color: options.activeMatchColorOverviewRuler\n }\n });\n if (decoration) {\n const disposables: IDisposable[] = [];\n disposables.push(marker);\n disposables.push(decoration.onRender((e) => this._applyStyles(e, options.activeMatchBorder, true)));\n disposables.push(decoration.onDispose(() => disposeArray(disposables)));\n this._selectedDecoration.value = { decoration, match: result, dispose() { decoration.dispose(); } };\n }\n }\n }\n\n if (!noScroll) {\n // If it is not in the viewport then we scroll else it just gets selected\n if (result.row >= (terminal.buffer.active.viewportY + terminal.rows) || result.row < terminal.buffer.active.viewportY) {\n let scroll = result.row - terminal.buffer.active.viewportY;\n scroll -= Math.floor(terminal.rows / 2);\n terminal.scrollLines(scroll);\n }\n }\n return true;\n }\n\n /**\n * Applies styles to the decoration when it is rendered.\n * @param element The decoration's element.\n * @param borderColor The border color to apply.\n * @param isActiveResult Whether the element is part of the active search result.\n * @returns\n */\n private _applyStyles(element: HTMLElement, borderColor: string | undefined, isActiveResult: boolean): void {\n if (!element.classList.contains('xterm-find-result-decoration')) {\n element.classList.add('xterm-find-result-decoration');\n if (borderColor) {\n element.style.outline = `1px solid ${borderColor}`;\n }\n }\n if (isActiveResult) {\n element.classList.add('xterm-find-active-result-decoration');\n }\n }\n\n /**\n * Creates a decoration for the result and applies styles\n * @param result the search result for which to create the decoration\n * @param options the options for the decoration\n * @returns the {@link IDecoration} or undefined if the marker has already been disposed of\n */\n private _createResultDecoration(result: ISearchResult, options: ISearchDecorationOptions): IDecoration | undefined {\n const terminal = this._terminal!;\n const marker = terminal.registerMarker(-terminal.buffer.active.baseY - terminal.buffer.active.cursorY + result.row);\n if (!marker) {\n return undefined;\n }\n const findResultDecoration = terminal.registerDecoration({\n marker,\n x: result.col,\n width: result.size,\n backgroundColor: options.matchBackground,\n overviewRulerOptions: this._highlightedLines.has(marker.line) ? undefined : {\n color: options.matchOverviewRuler,\n position: 'center'\n }\n });\n if (findResultDecoration) {\n const disposables: IDisposable[] = [];\n disposables.push(marker);\n disposables.push(findResultDecoration.onRender((e) => this._applyStyles(e, options.matchBorder, false)));\n disposables.push(findResultDecoration.onDispose(() => disposeArray(disposables)));\n }\n return findResultDecoration;\n }\n}\n"],"names":["root","factory","exports","module","define","amd","self","_listeners","_disposed","event","this","_event","listener","push","dispose","i","length","splice","fire","arg1","arg2","queue","call","undefined","clearListeners","from","to","e","handler","disposeArray","disposables","d","_disposables","_isDisposed","register","unregister","index","indexOf","value","_value","clear","f","array","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__","NON_WORD_CHARACTERS","SearchAddon","Disposable","constructor","options","super","_highlightedLines","Set","_highlightDecorations","_selectedDecoration","MutableDisposable","_linesCacheTimeoutId","_onDidChangeResults","EventEmitter","onDidChangeResults","_highlightLimit","highlightLimit","activate","terminal","_terminal","onWriteParsed","_updateMatches","onResize","toDisposable","clearDecorations","_highlightTimeout","window","clearTimeout","_cachedSearchTerm","_lastSearchOptions","decorations","setTimeout","term","findPrevious","incremental","noScroll","retainCachedSearchTerm","clearActiveDecoration","findNext","searchOptions","Error","didOptionsChanged","_didOptionsChange","_highlightAllMatches","found","_findNextAndSelect","_fireResults","searchResultsWithHighlight","prevResult","result","_find","row","col","cols","match","decoration","_createResultDecoration","add","marker","line","startRow","startCol","clearSelection","_initLinesCache","searchPosition","_findInLine","y","buffer","active","baseY","rows","prevSelectedPos","getSelectionPosition","end","x","start","_selectResult","_findPreviousAndSelect","lastSearchOptions","caseSensitive","regex","wholeWord","resultIndex","selectedMatch","size","resultCount","isReverseSearch","Math","max","_linesCache","Array","_cursorMoveListener","onCursorMove","_destroyLinesCache","_resizeListener","_isWholeWord","searchIndex","includes","firstLine","getLine","isWrapped","cache","_translateBufferLineToStringWithWrap","stringLine","offsets","offset","_bufferColsToStringOffset","searchTerm","toLowerCase","searchStringLine","searchRegex","RegExp","foundTerm","exec","slice","lastIndex","lastIndexOf","startRowOffset","endRowOffset","startColOffset","endColOffset","startColIndex","_stringLengthToBufferSize","cell","getCell","char","getChars","nextCell","getWidth","lineIndex","getCode","trimRight","strings","lineOffsets","nextLine","lineWrapsToNext","string","translateToString","lastCell","join","select","registerMarker","cursorY","registerDecoration","width","backgroundColor","activeMatchBackground","layer","overviewRulerOptions","color","activeMatchColorOverviewRuler","onRender","_applyStyles","activeMatchBorder","onDispose","viewportY","scroll","floor","scrollLines","element","borderColor","isActiveResult","classList","contains","style","outline","findResultDecoration","matchBackground","has","matchOverviewRuler","position","matchBorder"],"sourceRoot":""}
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@xterm/addon-search",
3
+ "version": "0.14.0-beta.1",
4
+ "author": {
5
+ "name": "The xterm.js authors",
6
+ "url": "https://xtermjs.org/"
7
+ },
8
+ "main": "lib/addon-search.js",
9
+ "types": "typings/addon-search.d.ts",
10
+ "repository": "https://github.com/xtermjs/xterm.js/tree/master/addons/addon-search",
11
+ "license": "MIT",
12
+ "keywords": [
13
+ "terminal",
14
+ "xterm",
15
+ "xterm.js"
16
+ ],
17
+ "scripts": {
18
+ "build": "../../node_modules/.bin/tsc -p .",
19
+ "prepackage": "npm run build",
20
+ "package": "../../node_modules/.bin/webpack",
21
+ "prepublishOnly": "npm run package"
22
+ },
23
+ "peerDependencies": {
24
+ "xterm": "^5.0.0"
25
+ }
26
+ }
@@ -0,0 +1,751 @@
1
+ /**
2
+ * Copyright (c) 2017 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ import { Terminal, IDisposable, ITerminalAddon, IDecoration } from '@xterm/xterm';
7
+ import { EventEmitter } from 'common/EventEmitter';
8
+ import { Disposable, toDisposable, disposeArray, MutableDisposable } from 'common/Lifecycle';
9
+
10
+ export interface ISearchOptions {
11
+ regex?: boolean;
12
+ wholeWord?: boolean;
13
+ caseSensitive?: boolean;
14
+ incremental?: boolean;
15
+ decorations?: ISearchDecorationOptions;
16
+ noScroll?: boolean;
17
+ }
18
+
19
+ interface ISearchDecorationOptions {
20
+ matchBackground?: string;
21
+ matchBorder?: string;
22
+ matchOverviewRuler: string;
23
+ activeMatchBackground?: string;
24
+ activeMatchBorder?: string;
25
+ activeMatchColorOverviewRuler: string;
26
+ }
27
+
28
+ export interface ISearchPosition {
29
+ startCol: number;
30
+ startRow: number;
31
+ }
32
+
33
+ export interface ISearchAddonOptions {
34
+ highlightLimit: number;
35
+ }
36
+
37
+ export interface ISearchResult {
38
+ term: string;
39
+ col: number;
40
+ row: number;
41
+ size: number;
42
+ }
43
+
44
+ type LineCacheEntry = [
45
+ /**
46
+ * The string representation of a line (as opposed to the buffer cell representation).
47
+ */
48
+ lineAsString: string,
49
+ /**
50
+ * The offsets where each line starts when the entry describes a wrapped line.
51
+ */
52
+ lineOffsets: number[]
53
+ ];
54
+
55
+ interface IHighlight extends IDisposable {
56
+ decoration: IDecoration;
57
+ match: ISearchResult;
58
+ }
59
+
60
+ const NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\\;:"\',./<>?';
61
+ const LINES_CACHE_TIME_TO_LIVE = 15 * 1000; // 15 secs
62
+ const DEFAULT_HIGHLIGHT_LIMIT = 1000;
63
+
64
+ export class SearchAddon extends Disposable implements ITerminalAddon {
65
+ private _terminal: Terminal | undefined;
66
+ private _cachedSearchTerm: string | undefined;
67
+ private _highlightedLines: Set<number> = new Set();
68
+ private _highlightDecorations: IHighlight[] = [];
69
+ private _selectedDecoration: MutableDisposable<IHighlight> = this.register(new MutableDisposable());
70
+ private _highlightLimit: number;
71
+ private _lastSearchOptions: ISearchOptions | undefined;
72
+ private _highlightTimeout: number | undefined;
73
+ /**
74
+ * translateBufferLineToStringWithWrap is a fairly expensive call.
75
+ * We memoize the calls into an array that has a time based ttl.
76
+ * _linesCache is also invalidated when the terminal cursor moves.
77
+ */
78
+ private _linesCache: LineCacheEntry[] | undefined;
79
+ private _linesCacheTimeoutId = 0;
80
+ private _cursorMoveListener: IDisposable | undefined;
81
+ private _resizeListener: IDisposable | undefined;
82
+
83
+ private readonly _onDidChangeResults = this.register(new EventEmitter<{ resultIndex: number, resultCount: number }>());
84
+ public readonly onDidChangeResults = this._onDidChangeResults.event;
85
+
86
+ constructor(options?: Partial<ISearchAddonOptions>) {
87
+ super();
88
+
89
+ this._highlightLimit = options?.highlightLimit ?? DEFAULT_HIGHLIGHT_LIMIT;
90
+ }
91
+
92
+ public activate(terminal: Terminal): void {
93
+ this._terminal = terminal;
94
+ this.register(this._terminal.onWriteParsed(() => this._updateMatches()));
95
+ this.register(this._terminal.onResize(() => this._updateMatches()));
96
+ this.register(toDisposable(() => this.clearDecorations()));
97
+ }
98
+
99
+ private _updateMatches(): void {
100
+ if (this._highlightTimeout) {
101
+ window.clearTimeout(this._highlightTimeout);
102
+ }
103
+ if (this._cachedSearchTerm && this._lastSearchOptions?.decorations) {
104
+ this._highlightTimeout = setTimeout(() => {
105
+ const term = this._cachedSearchTerm;
106
+ this._cachedSearchTerm = undefined;
107
+ this.findPrevious(term!, { ...this._lastSearchOptions, incremental: true, noScroll: true });
108
+ }, 200);
109
+ }
110
+ }
111
+
112
+ public clearDecorations(retainCachedSearchTerm?: boolean): void {
113
+ this._selectedDecoration.clear();
114
+ disposeArray(this._highlightDecorations);
115
+ this._highlightDecorations = [];
116
+ this._highlightedLines.clear();
117
+ if (!retainCachedSearchTerm) {
118
+ this._cachedSearchTerm = undefined;
119
+ }
120
+ }
121
+
122
+ public clearActiveDecoration(): void {
123
+ this._selectedDecoration.clear();
124
+ }
125
+
126
+ /**
127
+ * Find the next instance of the term, then scroll to and select it. If it
128
+ * doesn't exist, do nothing.
129
+ * @param term The search term.
130
+ * @param searchOptions Search options.
131
+ * @returns Whether a result was found.
132
+ */
133
+ public findNext(term: string, searchOptions?: ISearchOptions): boolean {
134
+ if (!this._terminal) {
135
+ throw new Error('Cannot use addon until it has been loaded');
136
+ }
137
+ const didOptionsChanged = this._lastSearchOptions ? this._didOptionsChange(this._lastSearchOptions, searchOptions) : true;
138
+ this._lastSearchOptions = searchOptions;
139
+ if (searchOptions?.decorations) {
140
+ if (this._cachedSearchTerm === undefined || term !== this._cachedSearchTerm || didOptionsChanged) {
141
+ this._highlightAllMatches(term, searchOptions);
142
+ }
143
+ }
144
+
145
+ const found = this._findNextAndSelect(term, searchOptions);
146
+ this._fireResults(searchOptions);
147
+ this._cachedSearchTerm = term;
148
+
149
+ return found;
150
+ }
151
+
152
+ private _highlightAllMatches(term: string, searchOptions: ISearchOptions): void {
153
+ if (!this._terminal) {
154
+ throw new Error('Cannot use addon until it has been loaded');
155
+ }
156
+ if (!term || term.length === 0) {
157
+ this.clearDecorations();
158
+ return;
159
+ }
160
+ searchOptions = searchOptions || {};
161
+
162
+ // new search, clear out the old decorations
163
+ this.clearDecorations(true);
164
+
165
+ const searchResultsWithHighlight: ISearchResult[] = [];
166
+ let prevResult: ISearchResult | undefined = undefined;
167
+ let result = this._find(term, 0, 0, searchOptions);
168
+ while (result && (prevResult?.row !== result.row || prevResult?.col !== result.col)) {
169
+ if (searchResultsWithHighlight.length >= this._highlightLimit) {
170
+ break;
171
+ }
172
+ prevResult = result;
173
+ searchResultsWithHighlight.push(prevResult);
174
+ result = this._find(
175
+ term,
176
+ prevResult.col + prevResult.term.length >= this._terminal.cols ? prevResult.row + 1 : prevResult.row,
177
+ prevResult.col + prevResult.term.length >= this._terminal.cols ? 0 : prevResult.col + 1,
178
+ searchOptions
179
+ );
180
+ }
181
+ for (const match of searchResultsWithHighlight) {
182
+ const decoration = this._createResultDecoration(match, searchOptions.decorations!);
183
+ if (decoration) {
184
+ this._highlightedLines.add(decoration.marker.line);
185
+ this._highlightDecorations.push({ decoration, match, dispose() { decoration.dispose(); } });
186
+ }
187
+ }
188
+ }
189
+
190
+ private _find(term: string, startRow: number, startCol: number, searchOptions?: ISearchOptions): ISearchResult | undefined {
191
+ if (!this._terminal || !term || term.length === 0) {
192
+ this._terminal?.clearSelection();
193
+ this.clearDecorations();
194
+ return undefined;
195
+ }
196
+ if (startCol > this._terminal.cols) {
197
+ throw new Error(`Invalid col: ${startCol} to search in terminal of ${this._terminal.cols} cols`);
198
+ }
199
+
200
+ let result: ISearchResult | undefined = undefined;
201
+
202
+ this._initLinesCache();
203
+
204
+ const searchPosition: ISearchPosition = {
205
+ startRow,
206
+ startCol
207
+ };
208
+
209
+ // Search startRow
210
+ result = this._findInLine(term, searchPosition, searchOptions);
211
+ // Search from startRow + 1 to end
212
+ if (!result) {
213
+
214
+ for (let y = startRow + 1; y < this._terminal.buffer.active.baseY + this._terminal.rows; y++) {
215
+ searchPosition.startRow = y;
216
+ searchPosition.startCol = 0;
217
+ // If the current line is wrapped line, increase index of column to ignore the previous scan
218
+ // Otherwise, reset beginning column index to zero with set new unwrapped line index
219
+ result = this._findInLine(term, searchPosition, searchOptions);
220
+ if (result) {
221
+ break;
222
+ }
223
+ }
224
+ }
225
+ return result;
226
+ }
227
+
228
+ private _findNextAndSelect(term: string, searchOptions?: ISearchOptions): boolean {
229
+ if (!this._terminal || !term || term.length === 0) {
230
+ this._terminal?.clearSelection();
231
+ this.clearDecorations();
232
+ return false;
233
+ }
234
+
235
+ const prevSelectedPos = this._terminal.getSelectionPosition();
236
+ this._terminal.clearSelection();
237
+
238
+ let startCol = 0;
239
+ let startRow = 0;
240
+ if (prevSelectedPos) {
241
+ if (this._cachedSearchTerm === term) {
242
+ startCol = prevSelectedPos.end.x;
243
+ startRow = prevSelectedPos.end.y;
244
+ } else {
245
+ startCol = prevSelectedPos.start.x;
246
+ startRow = prevSelectedPos.start.y;
247
+ }
248
+ }
249
+
250
+ this._initLinesCache();
251
+
252
+ const searchPosition: ISearchPosition = {
253
+ startRow,
254
+ startCol
255
+ };
256
+
257
+ // Search startRow
258
+ let result = this._findInLine(term, searchPosition, searchOptions);
259
+ // Search from startRow + 1 to end
260
+ if (!result) {
261
+
262
+ for (let y = startRow + 1; y < this._terminal.buffer.active.baseY + this._terminal.rows; y++) {
263
+ searchPosition.startRow = y;
264
+ searchPosition.startCol = 0;
265
+ // If the current line is wrapped line, increase index of column to ignore the previous scan
266
+ // Otherwise, reset beginning column index to zero with set new unwrapped line index
267
+ result = this._findInLine(term, searchPosition, searchOptions);
268
+ if (result) {
269
+ break;
270
+ }
271
+ }
272
+ }
273
+ // If we hit the bottom and didn't search from the very top wrap back up
274
+ if (!result && startRow !== 0) {
275
+ for (let y = 0; y < startRow; y++) {
276
+ searchPosition.startRow = y;
277
+ searchPosition.startCol = 0;
278
+ result = this._findInLine(term, searchPosition, searchOptions);
279
+ if (result) {
280
+ break;
281
+ }
282
+ }
283
+ }
284
+
285
+ // If there is only one result, wrap back and return selection if it exists.
286
+ if (!result && prevSelectedPos) {
287
+ searchPosition.startRow = prevSelectedPos.start.y;
288
+ searchPosition.startCol = 0;
289
+ result = this._findInLine(term, searchPosition, searchOptions);
290
+ }
291
+
292
+ // Set selection and scroll if a result was found
293
+ return this._selectResult(result, searchOptions?.decorations, searchOptions?.noScroll);
294
+ }
295
+ /**
296
+ * Find the previous instance of the term, then scroll to and select it. If it
297
+ * doesn't exist, do nothing.
298
+ * @param term The search term.
299
+ * @param searchOptions Search options.
300
+ * @returns Whether a result was found.
301
+ */
302
+ public findPrevious(term: string, searchOptions?: ISearchOptions): boolean {
303
+ if (!this._terminal) {
304
+ throw new Error('Cannot use addon until it has been loaded');
305
+ }
306
+ const didOptionsChanged = this._lastSearchOptions ? this._didOptionsChange(this._lastSearchOptions, searchOptions) : true;
307
+ this._lastSearchOptions = searchOptions;
308
+ if (searchOptions?.decorations) {
309
+ if (this._cachedSearchTerm === undefined || term !== this._cachedSearchTerm || didOptionsChanged) {
310
+ this._highlightAllMatches(term, searchOptions);
311
+ }
312
+ }
313
+
314
+ const found = this._findPreviousAndSelect(term, searchOptions);
315
+ this._fireResults(searchOptions);
316
+ this._cachedSearchTerm = term;
317
+
318
+ return found;
319
+ }
320
+
321
+ private _didOptionsChange(lastSearchOptions: ISearchOptions, searchOptions?: ISearchOptions): boolean {
322
+ if (!searchOptions) {
323
+ return false;
324
+ }
325
+ if (lastSearchOptions.caseSensitive !== searchOptions.caseSensitive) {
326
+ return true;
327
+ }
328
+ if (lastSearchOptions.regex !== searchOptions.regex) {
329
+ return true;
330
+ }
331
+ if (lastSearchOptions.wholeWord !== searchOptions.wholeWord) {
332
+ return true;
333
+ }
334
+ return false;
335
+ }
336
+
337
+ private _fireResults(searchOptions?: ISearchOptions): void {
338
+ if (searchOptions?.decorations) {
339
+ let resultIndex = -1;
340
+ if (this._selectedDecoration.value) {
341
+ const selectedMatch = this._selectedDecoration.value.match;
342
+ for (let i = 0; i < this._highlightDecorations.length; i++) {
343
+ const match = this._highlightDecorations[i].match;
344
+ if (match.row === selectedMatch.row && match.col === selectedMatch.col && match.size === selectedMatch.size) {
345
+ resultIndex = i;
346
+ break;
347
+ }
348
+ }
349
+ }
350
+ this._onDidChangeResults.fire({ resultIndex, resultCount: this._highlightDecorations.length });
351
+ }
352
+ }
353
+
354
+ private _findPreviousAndSelect(term: string, searchOptions?: ISearchOptions): boolean {
355
+ if (!this._terminal) {
356
+ throw new Error('Cannot use addon until it has been loaded');
357
+ }
358
+ if (!this._terminal || !term || term.length === 0) {
359
+ this._terminal?.clearSelection();
360
+ this.clearDecorations();
361
+ return false;
362
+ }
363
+
364
+ const prevSelectedPos = this._terminal.getSelectionPosition();
365
+ this._terminal.clearSelection();
366
+
367
+ let startRow = this._terminal.buffer.active.baseY + this._terminal.rows - 1;
368
+ let startCol = this._terminal.cols;
369
+ const isReverseSearch = true;
370
+
371
+ this._initLinesCache();
372
+ const searchPosition: ISearchPosition = {
373
+ startRow,
374
+ startCol
375
+ };
376
+
377
+ let result: ISearchResult | undefined;
378
+ if (prevSelectedPos) {
379
+ searchPosition.startRow = startRow = prevSelectedPos.start.y;
380
+ searchPosition.startCol = startCol = prevSelectedPos.start.x;
381
+ if (this._cachedSearchTerm !== term) {
382
+ // Try to expand selection to right first.
383
+ result = this._findInLine(term, searchPosition, searchOptions, false);
384
+ if (!result) {
385
+ // If selection was not able to be expanded to the right, then try reverse search
386
+ searchPosition.startRow = startRow = prevSelectedPos.end.y;
387
+ searchPosition.startCol = startCol = prevSelectedPos.end.x;
388
+ }
389
+ }
390
+ }
391
+
392
+ if (!result) {
393
+ result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);
394
+ }
395
+
396
+ // Search from startRow - 1 to top
397
+ if (!result) {
398
+ searchPosition.startCol = Math.max(searchPosition.startCol, this._terminal.cols);
399
+ for (let y = startRow - 1; y >= 0; y--) {
400
+ searchPosition.startRow = y;
401
+ result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);
402
+ if (result) {
403
+ break;
404
+ }
405
+ }
406
+ }
407
+ // If we hit the top and didn't search from the very bottom wrap back down
408
+ if (!result && startRow !== (this._terminal.buffer.active.baseY + this._terminal.rows - 1)) {
409
+ for (let y = (this._terminal.buffer.active.baseY + this._terminal.rows - 1); y >= startRow; y--) {
410
+ searchPosition.startRow = y;
411
+ result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch);
412
+ if (result) {
413
+ break;
414
+ }
415
+ }
416
+ }
417
+
418
+ // Set selection and scroll if a result was found
419
+ return this._selectResult(result, searchOptions?.decorations, searchOptions?.noScroll);
420
+ }
421
+
422
+ /**
423
+ * Sets up a line cache with a ttl
424
+ */
425
+ private _initLinesCache(): void {
426
+ const terminal = this._terminal!;
427
+ if (!this._linesCache) {
428
+ this._linesCache = new Array(terminal.buffer.active.length);
429
+ this._cursorMoveListener = terminal.onCursorMove(() => this._destroyLinesCache());
430
+ this._resizeListener = terminal.onResize(() => this._destroyLinesCache());
431
+ }
432
+
433
+ window.clearTimeout(this._linesCacheTimeoutId);
434
+ this._linesCacheTimeoutId = window.setTimeout(() => this._destroyLinesCache(), LINES_CACHE_TIME_TO_LIVE);
435
+ }
436
+
437
+ private _destroyLinesCache(): void {
438
+ this._linesCache = undefined;
439
+ if (this._cursorMoveListener) {
440
+ this._cursorMoveListener.dispose();
441
+ this._cursorMoveListener = undefined;
442
+ }
443
+ if (this._resizeListener) {
444
+ this._resizeListener.dispose();
445
+ this._resizeListener = undefined;
446
+ }
447
+ if (this._linesCacheTimeoutId) {
448
+ window.clearTimeout(this._linesCacheTimeoutId);
449
+ this._linesCacheTimeoutId = 0;
450
+ }
451
+ }
452
+
453
+ /**
454
+ * A found substring is a whole word if it doesn't have an alphanumeric character directly
455
+ * adjacent to it.
456
+ * @param searchIndex starting indext of the potential whole word substring
457
+ * @param line entire string in which the potential whole word was found
458
+ * @param term the substring that starts at searchIndex
459
+ */
460
+ private _isWholeWord(searchIndex: number, line: string, term: string): boolean {
461
+ return ((searchIndex === 0) || (NON_WORD_CHARACTERS.includes(line[searchIndex - 1]))) &&
462
+ (((searchIndex + term.length) === line.length) || (NON_WORD_CHARACTERS.includes(line[searchIndex + term.length])));
463
+ }
464
+
465
+ /**
466
+ * Searches a line for a search term. Takes the provided terminal line and searches the text line,
467
+ * which may contain subsequent terminal lines if the text is wrapped. If the provided line number
468
+ * is part of a wrapped text line that started on an earlier line then it is skipped since it will
469
+ * be properly searched when the terminal line that the text starts on is searched.
470
+ * @param term The search term.
471
+ * @param searchPosition The position to start the search.
472
+ * @param searchOptions Search options.
473
+ * @param isReverseSearch Whether the search should start from the right side of the terminal and
474
+ * search to the left.
475
+ * @returns The search result if it was found.
476
+ */
477
+ protected _findInLine(term: string, searchPosition: ISearchPosition, searchOptions: ISearchOptions = {}, isReverseSearch: boolean = false): ISearchResult | undefined {
478
+ const terminal = this._terminal!;
479
+ const row = searchPosition.startRow;
480
+ const col = searchPosition.startCol;
481
+
482
+ // Ignore wrapped lines, only consider on unwrapped line (first row of command string).
483
+ const firstLine = terminal.buffer.active.getLine(row);
484
+ if (firstLine?.isWrapped) {
485
+ if (isReverseSearch) {
486
+ searchPosition.startCol += terminal.cols;
487
+ return;
488
+ }
489
+
490
+ // This will iterate until we find the line start.
491
+ // When we find it, we will search using the calculated start column.
492
+ searchPosition.startRow--;
493
+ searchPosition.startCol += terminal.cols;
494
+ return this._findInLine(term, searchPosition, searchOptions);
495
+ }
496
+ let cache = this._linesCache?.[row];
497
+ if (!cache) {
498
+ cache = this._translateBufferLineToStringWithWrap(row, true);
499
+ if (this._linesCache) {
500
+ this._linesCache[row] = cache;
501
+ }
502
+ }
503
+ const [stringLine, offsets] = cache;
504
+
505
+ const offset = this._bufferColsToStringOffset(row, col);
506
+ const searchTerm = searchOptions.caseSensitive ? term : term.toLowerCase();
507
+ const searchStringLine = searchOptions.caseSensitive ? stringLine : stringLine.toLowerCase();
508
+
509
+ let resultIndex = -1;
510
+ if (searchOptions.regex) {
511
+ const searchRegex = RegExp(searchTerm, 'g');
512
+ let foundTerm: RegExpExecArray | null;
513
+ if (isReverseSearch) {
514
+ // This loop will get the resultIndex of the _last_ regex match in the range 0..offset
515
+ while (foundTerm = searchRegex.exec(searchStringLine.slice(0, offset))) {
516
+ resultIndex = searchRegex.lastIndex - foundTerm[0].length;
517
+ term = foundTerm[0];
518
+ searchRegex.lastIndex -= (term.length - 1);
519
+ }
520
+ } else {
521
+ foundTerm = searchRegex.exec(searchStringLine.slice(offset));
522
+ if (foundTerm && foundTerm[0].length > 0) {
523
+ resultIndex = offset + (searchRegex.lastIndex - foundTerm[0].length);
524
+ term = foundTerm[0];
525
+ }
526
+ }
527
+ } else {
528
+ if (isReverseSearch) {
529
+ if (offset - searchTerm.length >= 0) {
530
+ resultIndex = searchStringLine.lastIndexOf(searchTerm, offset - searchTerm.length);
531
+ }
532
+ } else {
533
+ resultIndex = searchStringLine.indexOf(searchTerm, offset);
534
+ }
535
+ }
536
+
537
+ if (resultIndex >= 0) {
538
+ if (searchOptions.wholeWord && !this._isWholeWord(resultIndex, searchStringLine, term)) {
539
+ return;
540
+ }
541
+
542
+ // Adjust the row number and search index if needed since a "line" of text can span multiple
543
+ // rows
544
+ let startRowOffset = 0;
545
+ while (startRowOffset < offsets.length - 1 && resultIndex >= offsets[startRowOffset + 1]) {
546
+ startRowOffset++;
547
+ }
548
+ let endRowOffset = startRowOffset;
549
+ while (endRowOffset < offsets.length - 1 && resultIndex + term.length >= offsets[endRowOffset + 1]) {
550
+ endRowOffset++;
551
+ }
552
+ const startColOffset = resultIndex - offsets[startRowOffset];
553
+ const endColOffset = resultIndex + term.length - offsets[endRowOffset];
554
+ const startColIndex = this._stringLengthToBufferSize(row + startRowOffset, startColOffset);
555
+ const endColIndex = this._stringLengthToBufferSize(row + endRowOffset, endColOffset);
556
+ const size = endColIndex - startColIndex + terminal.cols * (endRowOffset - startRowOffset);
557
+
558
+ return {
559
+ term,
560
+ col: startColIndex,
561
+ row: row + startRowOffset,
562
+ size
563
+ };
564
+ }
565
+ }
566
+
567
+ private _stringLengthToBufferSize(row: number, offset: number): number {
568
+ const line = this._terminal!.buffer.active.getLine(row);
569
+ if (!line) {
570
+ return 0;
571
+ }
572
+ for (let i = 0; i < offset; i++) {
573
+ const cell = line.getCell(i);
574
+ if (!cell) {
575
+ break;
576
+ }
577
+ // Adjust the searchIndex to normalize emoji into single chars
578
+ const char = cell.getChars();
579
+ if (char.length > 1) {
580
+ offset -= char.length - 1;
581
+ }
582
+ // Adjust the searchIndex for empty characters following wide unicode
583
+ // chars (eg. CJK)
584
+ const nextCell = line.getCell(i + 1);
585
+ if (nextCell && nextCell.getWidth() === 0) {
586
+ offset++;
587
+ }
588
+ }
589
+ return offset;
590
+ }
591
+
592
+ private _bufferColsToStringOffset(startRow: number, cols: number): number {
593
+ const terminal = this._terminal!;
594
+ let lineIndex = startRow;
595
+ let offset = 0;
596
+ let line = terminal.buffer.active.getLine(lineIndex);
597
+ while (cols > 0 && line) {
598
+ for (let i = 0; i < cols && i < terminal.cols; i++) {
599
+ const cell = line.getCell(i);
600
+ if (!cell) {
601
+ break;
602
+ }
603
+ if (cell.getWidth()) {
604
+ // Treat null characters as whitespace to align with the translateToString API
605
+ offset += cell.getCode() === 0 ? 1 : cell.getChars().length;
606
+ }
607
+ }
608
+ lineIndex++;
609
+ line = terminal.buffer.active.getLine(lineIndex);
610
+ if (line && !line.isWrapped) {
611
+ break;
612
+ }
613
+ cols -= terminal.cols;
614
+ }
615
+ return offset;
616
+ }
617
+
618
+ /**
619
+ * Translates a buffer line to a string, including subsequent lines if they are wraps.
620
+ * Wide characters will count as two columns in the resulting string. This
621
+ * function is useful for getting the actual text underneath the raw selection
622
+ * position.
623
+ * @param lineIndex The index of the line being translated.
624
+ * @param trimRight Whether to trim whitespace to the right.
625
+ */
626
+ private _translateBufferLineToStringWithWrap(lineIndex: number, trimRight: boolean): LineCacheEntry {
627
+ const terminal = this._terminal!;
628
+ const strings = [];
629
+ const lineOffsets = [0];
630
+ let line = terminal.buffer.active.getLine(lineIndex);
631
+ while (line) {
632
+ const nextLine = terminal.buffer.active.getLine(lineIndex + 1);
633
+ const lineWrapsToNext = nextLine ? nextLine.isWrapped : false;
634
+ let string = line.translateToString(!lineWrapsToNext && trimRight);
635
+ if (lineWrapsToNext && nextLine) {
636
+ const lastCell = line.getCell(line.length - 1);
637
+ const lastCellIsNull = lastCell && lastCell.getCode() === 0 && lastCell.getWidth() === 1;
638
+ // a wide character wrapped to the next line
639
+ if (lastCellIsNull && nextLine.getCell(0)?.getWidth() === 2) {
640
+ string = string.slice(0, -1);
641
+ }
642
+ }
643
+ strings.push(string);
644
+ if (lineWrapsToNext) {
645
+ lineOffsets.push(lineOffsets[lineOffsets.length - 1] + string.length);
646
+ } else {
647
+ break;
648
+ }
649
+ lineIndex++;
650
+ line = nextLine;
651
+ }
652
+ return [strings.join(''), lineOffsets];
653
+ }
654
+
655
+ /**
656
+ * Selects and scrolls to a result.
657
+ * @param result The result to select.
658
+ * @returns Whether a result was selected.
659
+ */
660
+ private _selectResult(result: ISearchResult | undefined, options?: ISearchDecorationOptions, noScroll?: boolean): boolean {
661
+ const terminal = this._terminal!;
662
+ this._selectedDecoration.clear();
663
+ if (!result) {
664
+ terminal.clearSelection();
665
+ return false;
666
+ }
667
+ terminal.select(result.col, result.row, result.size);
668
+ if (options) {
669
+ const marker = terminal.registerMarker(-terminal.buffer.active.baseY - terminal.buffer.active.cursorY + result.row);
670
+ if (marker) {
671
+ const decoration = terminal.registerDecoration({
672
+ marker,
673
+ x: result.col,
674
+ width: result.size,
675
+ backgroundColor: options.activeMatchBackground,
676
+ layer: 'top',
677
+ overviewRulerOptions: {
678
+ color: options.activeMatchColorOverviewRuler
679
+ }
680
+ });
681
+ if (decoration) {
682
+ const disposables: IDisposable[] = [];
683
+ disposables.push(marker);
684
+ disposables.push(decoration.onRender((e) => this._applyStyles(e, options.activeMatchBorder, true)));
685
+ disposables.push(decoration.onDispose(() => disposeArray(disposables)));
686
+ this._selectedDecoration.value = { decoration, match: result, dispose() { decoration.dispose(); } };
687
+ }
688
+ }
689
+ }
690
+
691
+ if (!noScroll) {
692
+ // If it is not in the viewport then we scroll else it just gets selected
693
+ if (result.row >= (terminal.buffer.active.viewportY + terminal.rows) || result.row < terminal.buffer.active.viewportY) {
694
+ let scroll = result.row - terminal.buffer.active.viewportY;
695
+ scroll -= Math.floor(terminal.rows / 2);
696
+ terminal.scrollLines(scroll);
697
+ }
698
+ }
699
+ return true;
700
+ }
701
+
702
+ /**
703
+ * Applies styles to the decoration when it is rendered.
704
+ * @param element The decoration's element.
705
+ * @param borderColor The border color to apply.
706
+ * @param isActiveResult Whether the element is part of the active search result.
707
+ * @returns
708
+ */
709
+ private _applyStyles(element: HTMLElement, borderColor: string | undefined, isActiveResult: boolean): void {
710
+ if (!element.classList.contains('xterm-find-result-decoration')) {
711
+ element.classList.add('xterm-find-result-decoration');
712
+ if (borderColor) {
713
+ element.style.outline = `1px solid ${borderColor}`;
714
+ }
715
+ }
716
+ if (isActiveResult) {
717
+ element.classList.add('xterm-find-active-result-decoration');
718
+ }
719
+ }
720
+
721
+ /**
722
+ * Creates a decoration for the result and applies styles
723
+ * @param result the search result for which to create the decoration
724
+ * @param options the options for the decoration
725
+ * @returns the {@link IDecoration} or undefined if the marker has already been disposed of
726
+ */
727
+ private _createResultDecoration(result: ISearchResult, options: ISearchDecorationOptions): IDecoration | undefined {
728
+ const terminal = this._terminal!;
729
+ const marker = terminal.registerMarker(-terminal.buffer.active.baseY - terminal.buffer.active.cursorY + result.row);
730
+ if (!marker) {
731
+ return undefined;
732
+ }
733
+ const findResultDecoration = terminal.registerDecoration({
734
+ marker,
735
+ x: result.col,
736
+ width: result.size,
737
+ backgroundColor: options.matchBackground,
738
+ overviewRulerOptions: this._highlightedLines.has(marker.line) ? undefined : {
739
+ color: options.matchOverviewRuler,
740
+ position: 'center'
741
+ }
742
+ });
743
+ if (findResultDecoration) {
744
+ const disposables: IDisposable[] = [];
745
+ disposables.push(marker);
746
+ disposables.push(findResultDecoration.onRender((e) => this._applyStyles(e, options.matchBorder, false)));
747
+ disposables.push(findResultDecoration.onDispose(() => disposeArray(disposables)));
748
+ }
749
+ return findResultDecoration;
750
+ }
751
+ }
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Copyright (c) 2017 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ import { Terminal, ITerminalAddon, IEvent } from '@xterm/xterm';
7
+
8
+ declare module '@xterm/addon-search' {
9
+ /**
10
+ * Options for a search.
11
+ */
12
+ export interface ISearchOptions {
13
+ /**
14
+ * Whether the search term is a regex.
15
+ */
16
+ regex?: boolean;
17
+
18
+ /**
19
+ * Whether to search for a whole word, the result is only valid if it's
20
+ * surrounded in "non-word" characters such as `_`, `(`, `)` or space.
21
+ */
22
+ wholeWord?: boolean;
23
+
24
+ /**
25
+ * Whether the search is case sensitive.
26
+ */
27
+ caseSensitive?: boolean;
28
+
29
+ /**
30
+ * Whether to do an incremental search, this will expand the selection if it
31
+ * still matches the term the user typed. Note that this only affects
32
+ * `findNext`, not `findPrevious`.
33
+ */
34
+ incremental?: boolean;
35
+
36
+ /**
37
+ * When set, will highlight all instances of the word on search and show
38
+ * them in the overview ruler if it's enabled.
39
+ */
40
+ decorations?: ISearchDecorationOptions;
41
+ }
42
+
43
+ /**
44
+ * Options for showing decorations when searching.
45
+ */
46
+ interface ISearchDecorationOptions {
47
+ /**
48
+ * The background color of a match, this must use #RRGGBB format.
49
+ */
50
+ matchBackground?: string;
51
+
52
+ /**
53
+ * The border color of a match.
54
+ */
55
+ matchBorder?: string;
56
+
57
+ /**
58
+ * The overview ruler color of a match.
59
+ */
60
+ matchOverviewRuler: string;
61
+
62
+ /**
63
+ * The background color for the currently active match, this must use #RRGGBB format.
64
+ */
65
+ activeMatchBackground?: string;
66
+
67
+ /**
68
+ * The border color of the currently active match.
69
+ */
70
+ activeMatchBorder?: string;
71
+
72
+ /**
73
+ * The overview ruler color of the currently active match.
74
+ */
75
+ activeMatchColorOverviewRuler: string;
76
+ }
77
+
78
+ /**
79
+ * Options for the search addon.
80
+ */
81
+ export interface ISearchAddonOptions {
82
+ /**
83
+ * Max number of matches highlighted when decorations are enabled.
84
+ * Defaults to 1000 highlighted matches
85
+ */
86
+ highlightLimit: number
87
+ }
88
+
89
+ /**
90
+ * An xterm.js addon that provides search functionality.
91
+ */
92
+ export class SearchAddon implements ITerminalAddon {
93
+
94
+ /**
95
+ * Creates a new search addon.
96
+ * @param options Options for the search addon.
97
+ */
98
+ constructor(options?: Partial<ISearchAddonOptions>);
99
+
100
+ /**
101
+ * Activates the addon
102
+ * @param terminal The terminal the addon is being loaded in.
103
+ */
104
+ public activate(terminal: Terminal): void;
105
+
106
+ /**
107
+ * Disposes the addon.
108
+ */
109
+ public dispose(): void;
110
+
111
+ /**
112
+ * Search forwards for the next result that matches the search term and
113
+ * options.
114
+ * @param term The search term.
115
+ * @param searchOptions The options for the search.
116
+ */
117
+ public findNext(term: string, searchOptions?: ISearchOptions): boolean;
118
+
119
+ /**
120
+ * Search backwards for the previous result that matches the search term and
121
+ * options.
122
+ * @param term The search term.
123
+ * @param searchOptions The options for the search.
124
+ */
125
+ public findPrevious(term: string, searchOptions?: ISearchOptions): boolean;
126
+
127
+ /**
128
+ * Clears the decorations and selection
129
+ */
130
+ public clearDecorations(): void;
131
+
132
+ /**
133
+ * Clears the active result decoration, this decoration is applied on top of the selection so
134
+ * removing it will reveal the selection underneath. This is intended to be called on the search
135
+ * textarea's `blur` event.
136
+ */
137
+ public clearActiveDecoration(): void;
138
+
139
+ /**
140
+ * When decorations are enabled, fires when
141
+ * the search results change.
142
+ * @returns -1 for resultIndex when the threshold of matches is exceeded.
143
+ */
144
+ readonly onDidChangeResults: IEvent<{ resultIndex: number, resultCount: number }>;
145
+ }
146
+ }