UNPKG

react-terminal-viewer

Version:

<h1 align="center"> react-terminal-viewer </h1>

792 lines (771 loc) 37.8 kB
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _iterableToArrayLimit(arr, i) { var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"]; if (null != _i) { var _s, _e, _x, _r, _arr = [], _n = !0, _d = !1; try { if (_x = (_i = _i.call(arr)).next, 0 === i) { if (Object(_i) !== _i) return; _n = !1; } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0); } catch (err) { _d = !0, _e = err; } finally { try { if (!_n && null != _i.return && (_r = _i.return(), Object(_r) !== _r)) return; } finally { if (_d) throw _e; } } return _arr; } } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); } function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } /* eslint-disable no-restricted-syntax */ /* eslint-disable prefer-destructuring */ /* eslint-disable no-cond-assign */ /* eslint-disable consistent-return */ /* eslint-disable no-plusplus */ /* eslint-disable max-len */ /* eslint-disable no-param-reassign */ /* eslint-disable class-methods-use-this */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable no-underscore-dangle */ /** * 从 https://github.com/xtermjs/xterm.js/blob/master/addons/xterm-addon-search/src/SearchAddon.ts 复制以解决部分问题 */ import EventEmitter from "./EventEmiter"; var NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\\;:"\',./<>?'; var LINES_CACHE_TIME_TO_LIVE = 15 * 1000; // 15 secs export var SearchAddon = /*#__PURE__*/function () { function SearchAddon() { _classCallCheck(this, SearchAddon); _defineProperty(this, "_terminal", void 0); _defineProperty(this, "_cachedSearchTerm", void 0); _defineProperty(this, "_selectedDecoration", void 0); _defineProperty(this, "_resultDecorations", void 0); _defineProperty(this, "_searchResults", void 0); _defineProperty(this, "_onDataDisposable", void 0); _defineProperty(this, "_onResizeDisposable", void 0); _defineProperty(this, "_lastSearchOptions", void 0); _defineProperty(this, "_highlightTimeout", void 0); _defineProperty(this, "_linesCache", void 0); _defineProperty(this, "_linesCacheTimeoutId", 0); _defineProperty(this, "_cursorMoveListener", void 0); _defineProperty(this, "_resizeListener", void 0); _defineProperty(this, "_resultIndex", void 0); _defineProperty(this, "_onDidChangeResults", new EventEmitter()); _defineProperty(this, "onDidChangeResults", this._onDidChangeResults.event); } _createClass(SearchAddon, [{ key: "activate", value: function activate(terminal) { var _this = this; this._terminal = terminal; this._onDataDisposable = this._terminal.onWriteParsed(function () { return _this._updateMatches(); }); this._onResizeDisposable = this._terminal.onResize(function () { return _this._updateMatches(); }); } }, { key: "_updateMatches", value: function _updateMatches() { var _this$_lastSearchOpti, _this2 = this; if (this._highlightTimeout) { window.clearTimeout(this._highlightTimeout); } if (this._cachedSearchTerm && (_this$_lastSearchOpti = this._lastSearchOptions) !== null && _this$_lastSearchOpti !== void 0 && _this$_lastSearchOpti.decorations) { this._highlightTimeout = setTimeout(function () { var _this2$_searchResults, _this2$_searchResults2; _this2.findPrevious(_this2._cachedSearchTerm, _objectSpread(_objectSpread({}, _this2._lastSearchOptions), {}, { incremental: true, noScroll: true })); _this2._resultIndex = _this2._searchResults ? _this2._searchResults.size - 1 : -1; _this2._onDidChangeResults.fire({ resultIndex: _this2._resultIndex, resultCount: (_this2$_searchResults = (_this2$_searchResults2 = _this2._searchResults) === null || _this2$_searchResults2 === void 0 ? void 0 : _this2$_searchResults2.size) !== null && _this2$_searchResults !== void 0 ? _this2$_searchResults : -1 }); }, 200); } } }, { key: "dispose", value: function dispose() { var _this$_onDataDisposab, _this$_onResizeDispos; this.clearDecorations(); (_this$_onDataDisposab = this._onDataDisposable) === null || _this$_onDataDisposab === void 0 ? void 0 : _this$_onDataDisposab.dispose(); (_this$_onResizeDispos = this._onResizeDisposable) === null || _this$_onResizeDispos === void 0 ? void 0 : _this$_onResizeDispos.dispose(); } }, { key: "clearDecorations", value: function clearDecorations(retainCachedSearchTerm) { var _this$_selectedDecora, _this$_searchResults, _this$_resultDecorati, _this$_resultDecorati2; (_this$_selectedDecora = this._selectedDecoration) === null || _this$_selectedDecora === void 0 ? void 0 : _this$_selectedDecora.dispose(); (_this$_searchResults = this._searchResults) === null || _this$_searchResults === void 0 ? void 0 : _this$_searchResults.clear(); (_this$_resultDecorati = this._resultDecorations) === null || _this$_resultDecorati === void 0 ? void 0 : _this$_resultDecorati.forEach(function (decorations) { var _iterator = _createForOfIteratorHelper(decorations), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var d = _step.value; d.dispose(); } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } }); (_this$_resultDecorati2 = this._resultDecorations) === null || _this$_resultDecorati2 === void 0 ? void 0 : _this$_resultDecorati2.clear(); this._searchResults = undefined; this._resultDecorations = undefined; if (!retainCachedSearchTerm) { this._cachedSearchTerm = undefined; } } }, { key: "clearActiveDecoration", value: function clearActiveDecoration() { var _this$_selectedDecora2; (_this$_selectedDecora2 = this._selectedDecoration) === null || _this$_selectedDecora2 === void 0 ? void 0 : _this$_selectedDecora2.dispose(); this._selectedDecoration = undefined; } /** * Find the next instance of the term, then scroll to and select it. If it * doesn't exist, do nothing. * @param term The search term. * @param searchOptions Search options. * @return Whether a result was found. */ }, { key: "findNext", value: function findNext(term, searchOptions) { if (!this._terminal) { throw new Error('Cannot use addon until it has been loaded'); } this._lastSearchOptions = searchOptions; if (searchOptions !== null && searchOptions !== void 0 && searchOptions.decorations) { if (this._resultIndex !== undefined || this._cachedSearchTerm === undefined || term !== this._cachedSearchTerm) { this._highlightAllMatches(term, searchOptions); } } return this._fireResults(term, this._findNextAndSelect(term, searchOptions), searchOptions); } }, { key: "_highlightAllMatches", value: function _highlightAllMatches(term, searchOptions) { var _this3 = this; if (!this._terminal) { throw new Error('Cannot use addon until it has been loaded'); } if (!term || term.length === 0) { this.clearDecorations(); return; } searchOptions = searchOptions || {}; // new search, clear out the old decorations this.clearDecorations(true); this._searchResults = new Map(); this._resultDecorations = new Map(); var resultDecorations = this._resultDecorations; var result = this._find(term, 0, 0, searchOptions); while (result && !this._searchResults.get("".concat(result.row, "-").concat(result.col))) { this._searchResults.set("".concat(result.row, "-").concat(result.col), result); result = this._find(term, result.col + result.term.length >= this._terminal.cols ? result.row + 1 : result.row, result.col + result.term.length >= this._terminal.cols ? 0 : result.col + 1, searchOptions); if (this._searchResults.size >= 1000) { // this.clearDecorations(); // this._resultIndex = undefined; return; } } this._searchResults.forEach(function (r) { var resultDecoration = _this3._createResultDecoration(r, searchOptions.decorations); if (resultDecoration) { var decorationsForLine = resultDecorations.get(resultDecoration.marker.line) || []; decorationsForLine.push(resultDecoration); resultDecorations.set(resultDecoration.marker.line, decorationsForLine); } }); } }, { key: "_find", value: function _find(term, startRow, startCol, searchOptions) { if (!this._terminal || !term || term.length === 0) { var _this$_terminal; (_this$_terminal = this._terminal) === null || _this$_terminal === void 0 ? void 0 : _this$_terminal.clearSelection(); this.clearDecorations(); return undefined; } if (startCol > this._terminal.cols) { throw new Error("Invalid col: ".concat(startCol, " to search in terminal of ").concat(this._terminal.cols, " cols")); } var result; this._initLinesCache(); var searchPosition = { startRow: startRow, startCol: startCol }; // Search startRow result = this._findInLine(term, searchPosition, searchOptions); // Search from startRow + 1 to end if (!result) { for (var y = startRow + 1; y < this._terminal.buffer.active.baseY + this._terminal.rows; y++) { searchPosition.startRow = y; searchPosition.startCol = 0; // If the current line is wrapped line, increase index of column to ignore the previous scan // Otherwise, reset beginning column index to zero with set new unwrapped line index result = this._findInLine(term, searchPosition, searchOptions); if (result) { break; } } } return result; } }, { key: "_findNextAndSelect", value: function _findNextAndSelect(term, searchOptions) { if (!this._terminal || !term || term.length === 0) { var _this$_terminal2; (_this$_terminal2 = this._terminal) === null || _this$_terminal2 === void 0 ? void 0 : _this$_terminal2.clearSelection(); this.clearDecorations(); this._cachedSearchTerm = undefined; this._resultIndex = -1; return false; } if (this._cachedSearchTerm !== term) { this._resultIndex = undefined; this._terminal.clearSelection(); } var startCol = 0; var startRow = 0; var currentSelection; if (this._terminal.hasSelection()) { var incremental = searchOptions ? searchOptions.incremental : false; // Start from the selection end if there is a selection // For incremental search, use existing row currentSelection = this._terminal.getSelectionPosition(); startRow = incremental ? currentSelection.start.y : currentSelection.end.y; startCol = incremental ? currentSelection.start.x : currentSelection.end.x; } this._initLinesCache(); var searchPosition = { startRow: startRow, startCol: startCol }; // Search startRow var result = this._findInLine(term, searchPosition, searchOptions); // Search from startRow + 1 to end if (!result) { for (var y = startRow + 1; y < this._terminal.buffer.active.baseY + this._terminal.rows; y++) { searchPosition.startRow = y; searchPosition.startCol = 0; // If the current line is wrapped line, increase index of column to ignore the previous scan // Otherwise, reset beginning column index to zero with set new unwrapped line index result = this._findInLine(term, searchPosition, searchOptions); if (result) { break; } } } // If we hit the bottom and didn't search from the very top wrap back up if (!result && startRow !== 0) { for (var _y = 0; _y < startRow; _y++) { searchPosition.startRow = _y; searchPosition.startCol = 0; result = this._findInLine(term, searchPosition, searchOptions); if (result) { break; } } } // If there is only one result, wrap back and return selection if it exists. if (!result && currentSelection) { searchPosition.startRow = currentSelection.start.y; searchPosition.startCol = 0; result = this._findInLine(term, searchPosition, searchOptions); } if (this._searchResults) { if (this._searchResults.size === 0) { this._resultIndex = -1; } else if (this._resultIndex === undefined) { this._resultIndex = 0; } else { this._resultIndex++; if (this._resultIndex >= this._searchResults.size) { this._resultIndex = 0; } } } // Set selection and scroll if a result was found return this._selectResult(result, searchOptions === null || searchOptions === void 0 ? void 0 : searchOptions.decorations, searchOptions === null || searchOptions === void 0 ? void 0 : searchOptions.noScroll); } /** * Find the previous instance of the term, then scroll to and select it. If it * doesn't exist, do nothing. * @param term The search term. * @param searchOptions Search options. * @return Whether a result was found. */ }, { key: "findPrevious", value: function findPrevious(term, searchOptions) { if (!this._terminal) { throw new Error('Cannot use addon until it has been loaded'); } this._lastSearchOptions = searchOptions; if (searchOptions !== null && searchOptions !== void 0 && searchOptions.decorations) { if (this._resultIndex !== undefined || this._cachedSearchTerm === undefined || term !== this._cachedSearchTerm) { this._highlightAllMatches(term, searchOptions); } } return this._fireResults(term, this._findPreviousAndSelect(term, searchOptions), searchOptions); } }, { key: "_fireResults", value: function _fireResults(term, found, searchOptions) { if (searchOptions !== null && searchOptions !== void 0 && searchOptions.decorations) { var _this$_searchResults2; if (this._resultIndex !== undefined && ((_this$_searchResults2 = this._searchResults) === null || _this$_searchResults2 === void 0 ? void 0 : _this$_searchResults2.size) !== undefined) { this._onDidChangeResults.fire({ resultIndex: this._resultIndex, resultCount: this._searchResults.size }); } else { this._onDidChangeResults.fire(undefined); } } this._cachedSearchTerm = term; return found; } }, { key: "_findPreviousAndSelect", value: function _findPreviousAndSelect(term, searchOptions) { if (!this._terminal) { throw new Error('Cannot use addon until it has been loaded'); } var result; if (!this._terminal || !term || term.length === 0) { var _this$_terminal3; result = undefined; (_this$_terminal3 = this._terminal) === null || _this$_terminal3 === void 0 ? void 0 : _this$_terminal3.clearSelection(); this.clearDecorations(); this._resultIndex = -1; return false; } if (this._cachedSearchTerm !== term) { this._resultIndex = undefined; this._terminal.clearSelection(); } var startRow = this._terminal.buffer.active.baseY + this._terminal.rows; var startCol = this._terminal.cols; var isReverseSearch = true; var incremental = searchOptions ? searchOptions.incremental : false; var currentSelection; if (this._terminal.hasSelection()) { currentSelection = this._terminal.getSelectionPosition(); // Start from selection start if there is a selection startRow = currentSelection.start.y; startCol = currentSelection.start.x; } this._initLinesCache(); var searchPosition = { startRow: startRow, startCol: startCol }; if (incremental) { // Try to expand selection to right first. result = this._findInLine(term, searchPosition, searchOptions, false); var 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.end.y; searchPosition.startCol = currentSelection.end.x; } 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 (var 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 if (!result && startRow !== this._terminal.buffer.active.baseY + this._terminal.rows) { for (var _y2 = this._terminal.buffer.active.baseY + this._terminal.rows; _y2 >= startRow; _y2--) { searchPosition.startRow = _y2; result = this._findInLine(term, searchPosition, searchOptions, isReverseSearch); if (result) { break; } } } if (this._searchResults) { if (this._searchResults.size === 0) { this._resultIndex = -1; } else if (this._resultIndex === undefined || this._resultIndex < 0) { this._resultIndex = this._searchResults.size - 1; } else { this._resultIndex--; if (this._resultIndex === -1) { this._resultIndex = this._searchResults.size - 1; } } } // If there is only one result, return true. if (!result && currentSelection) return true; // Set selection and scroll if a result was found return this._selectResult(result, searchOptions === null || searchOptions === void 0 ? void 0 : searchOptions.decorations, searchOptions === null || searchOptions === void 0 ? void 0 : searchOptions.noScroll); } /** * Sets up a line cache with a ttl */ }, { key: "_initLinesCache", value: function _initLinesCache() { var _this4 = this; var terminal = this._terminal; if (!this._linesCache) { this._linesCache = new Array(terminal.buffer.active.length); this._cursorMoveListener = terminal.onCursorMove(function () { return _this4._destroyLinesCache(); }); this._resizeListener = terminal.onResize(function () { return _this4._destroyLinesCache(); }); } window.clearTimeout(this._linesCacheTimeoutId); this._linesCacheTimeoutId = window.setTimeout(function () { return _this4._destroyLinesCache(); }, LINES_CACHE_TIME_TO_LIVE); } }, { key: "_destroyLinesCache", value: function _destroyLinesCache() { this._linesCache = undefined; if (this._cursorMoveListener) { this._cursorMoveListener.dispose(); this._cursorMoveListener = undefined; } if (this._resizeListener) { this._resizeListener.dispose(); this._resizeListener = undefined; } if (this._linesCacheTimeoutId) { window.clearTimeout(this._linesCacheTimeoutId); this._linesCacheTimeoutId = 0; } } /** * A found substring is a whole word if it doesn't have an alphanumeric character directly adjacent to it. * @param searchIndex starting indext of the potential whole word substring * @param line entire string in which the potential whole word was found * @param term the substring that starts at searchIndex */ }, { key: "_isWholeWord", value: function _isWholeWord(searchIndex, line, term) { return (searchIndex === 0 || NON_WORD_CHARACTERS.includes(line[searchIndex - 1])) && (searchIndex + term.length === line.length || NON_WORD_CHARACTERS.includes(line[searchIndex + term.length])); } /** * Searches a line for a search term. Takes the provided terminal line and searches the text line, which may contain * subsequent terminal lines if the text is wrapped. If the provided line number is part of a wrapped text line that * started on an earlier line then it is skipped since it will be properly searched when the terminal line that the * text starts on is searched. * @param term The search term. * @param position The position to start the search. * @param searchOptions Search options. * @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. */ }, { key: "_findInLine", value: function _findInLine(term, searchPosition) { var _this$_linesCache; var searchOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var isReverseSearch = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; var terminal = this._terminal; var row = searchPosition.startRow; var col = searchPosition.startCol; // Ignore wrapped lines, only consider on unwrapped line (first row of command string). var firstLine = terminal.buffer.active.getLine(row); if (firstLine !== null && firstLine !== void 0 && firstLine.isWrapped) { if (isReverseSearch) { searchPosition.startCol += terminal.cols; return; } // This will iterate until we find the line start. // When we find it, we will search using the calculated start column. searchPosition.startRow--; searchPosition.startCol += terminal.cols; return this._findInLine(term, searchPosition, searchOptions); } var cache = (_this$_linesCache = this._linesCache) === null || _this$_linesCache === void 0 ? void 0 : _this$_linesCache[row]; if (!cache) { cache = this._translateBufferLineToStringWithWrap(row, true); if (this._linesCache) { this._linesCache[row] = cache; } } var _cache = cache, _cache2 = _slicedToArray(_cache, 2), stringLine = _cache2[0], offsets = _cache2[1]; var offset = this._bufferColsToStringOffset(row, col); var searchTerm = searchOptions.caseSensitive ? term : term.toLowerCase(); var searchStringLine = searchOptions.caseSensitive ? stringLine : stringLine.toLowerCase(); var resultIndex = -1; if (searchOptions.regex) { var searchRegex = RegExp(searchTerm, 'g'); var foundTerm; if (isReverseSearch) { // This loop will get the resultIndex of the _last_ regex match in the range 0..offset while (foundTerm = searchRegex.exec(searchStringLine.slice(0, offset))) { 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 if (isReverseSearch) { if (offset - searchTerm.length >= 0) { resultIndex = searchStringLine.lastIndexOf(searchTerm, offset - searchTerm.length); } } else { resultIndex = searchStringLine.indexOf(searchTerm, offset); } if (resultIndex >= 0) { if (searchOptions.wholeWord && !this._isWholeWord(resultIndex, searchStringLine, term)) { return; } // Adjust the row number and search index if needed since a "line" of text can span multiple rows var startRowOffset = 0; while (startRowOffset < offsets.length - 1 && resultIndex >= offsets[startRowOffset + 1]) { startRowOffset++; } var endRowOffset = startRowOffset; while (endRowOffset < offsets.length - 1 && resultIndex + term.length >= offsets[endRowOffset + 1]) { endRowOffset++; } var startColOffset = resultIndex - offsets[startRowOffset]; var endColOffset = resultIndex + term.length - offsets[endRowOffset]; var startColIndex = this._stringLengthToBufferSize(row + startRowOffset, startColOffset); var endColIndex = this._stringLengthToBufferSize(row + endRowOffset, endColOffset); var size = endColIndex - startColIndex + terminal.cols * (endRowOffset - startRowOffset); return { term: term, col: startColIndex, row: row + startRowOffset, size: size }; } } }, { key: "_stringLengthToBufferSize", value: function _stringLengthToBufferSize(row, offset) { var line = this._terminal.buffer.active.getLine(row); if (!line) { return 0; } for (var i = 0; i < offset; i++) { var cell = line.getCell(i); if (!cell) { break; } // Adjust the searchIndex to normalize emoji into single chars var char = cell.getChars(); if (char.length > 1) { offset -= char.length - 1; } // Adjust the searchIndex for empty characters following wide unicode // chars (eg. CJK) var nextCell = line.getCell(i + 1); if (nextCell && nextCell.getWidth() === 0) { offset++; } } return offset; } }, { key: "_bufferColsToStringOffset", value: function _bufferColsToStringOffset(startRow, cols) { var terminal = this._terminal; var lineIndex = startRow; var offset = 0; var line = terminal.buffer.active.getLine(lineIndex); while (cols > 0 && line) { for (var i = 0; i < cols && i < terminal.cols; i++) { var cell = line.getCell(i); if (!cell) { break; } if (cell.getWidth()) { // Treat null characters as whitespace to align with the translateToString API offset += cell.getCode() === 0 ? 1 : cell.getChars().length; } } lineIndex++; line = terminal.buffer.active.getLine(lineIndex); if (line && !line.isWrapped) { break; } cols -= terminal.cols; } return offset; } /** * Translates a buffer line to a string, including subsequent lines if they are wraps. * Wide characters will count as two columns in the resulting string. This * function is useful for getting the actual text underneath the raw selection * position. * @param line The line being translated. * @param trimRight Whether to trim whitespace to the right. */ }, { key: "_translateBufferLineToStringWithWrap", value: function _translateBufferLineToStringWithWrap(lineIndex, trimRight) { var terminal = this._terminal; var strings = []; var lineOffsets = [0]; var line = terminal.buffer.active.getLine(lineIndex); while (line) { var nextLine = terminal.buffer.active.getLine(lineIndex + 1); var lineWrapsToNext = nextLine ? nextLine.isWrapped : false; var string = line.translateToString(!lineWrapsToNext && trimRight); if (lineWrapsToNext && nextLine) { var _nextLine$getCell; var lastCell = line.getCell(line.length - 1); var lastCellIsNull = lastCell && lastCell.getCode() === 0 && lastCell.getWidth() === 1; // a wide character wrapped to the next line if (lastCellIsNull && ((_nextLine$getCell = nextLine.getCell(0)) === null || _nextLine$getCell === void 0 ? void 0 : _nextLine$getCell.getWidth()) === 2) { string = string.slice(0, -1); } } strings.push(string); if (lineWrapsToNext) { lineOffsets.push(lineOffsets[lineOffsets.length - 1] + string.length); } else { break; } lineIndex++; line = nextLine; } return [strings.join(''), lineOffsets]; } /** * Selects and scrolls to a result. * @param result The result to select. * @return Whether a result was selected. */ }, { key: "_selectResult", value: function _selectResult(result, options, noScroll) { var terminal = this._terminal; this.clearActiveDecoration(); if (!result) { terminal.clearSelection(); return false; } terminal.select(result.col, result.row, result.size); if (options) { var marker = terminal.registerMarker(-terminal.buffer.active.baseY - terminal.buffer.active.cursorY + result.row); if (marker) { var _this$_selectedDecora3; this._selectedDecoration = terminal.registerDecoration({ marker: marker, x: result.col, width: result.size, backgroundColor: options.activeMatchBackground, foregroundColor: options === null || options === void 0 ? void 0 : options.matchForegroundColor, layer: 'top', overviewRulerOptions: { color: options.activeMatchColorOverviewRuler } }); // this._selectedDecoration?.onRender((e) => { // this._applyStyles(e, options.activeMatchBorder, true); // }); (_this$_selectedDecora3 = this._selectedDecoration) === null || _this$_selectedDecora3 === void 0 ? void 0 : _this$_selectedDecora3.onDispose(function () { marker.dispose(); }); } } if (!noScroll) { // If it is not in the viewport then we scroll else it just gets selected if (result.row >= terminal.buffer.active.viewportY + terminal.rows || result.row < terminal.buffer.active.viewportY) { var scroll = result.row - terminal.buffer.active.viewportY; scroll -= Math.floor(terminal.rows / 2); terminal.scrollLines(scroll); } } return true; } /** * Applies styles to the decoration when it is rendered * @param element the decoration's element * @param backgroundColor the background color to apply * @param borderColor the border color to apply * @returns */ }, { key: "_applyStyles", value: function _applyStyles(element, borderColor, isActiveResult) { if (element.clientWidth <= 0) { return; } if (!element.classList.contains('xterm-find-result-decoration')) { element.classList.add('xterm-find-result-decoration'); if (borderColor) { element.style.outline = "1px solid ".concat(borderColor); } } if (isActiveResult) { element.classList.add('xterm-find-active-result-decoration'); } } /** * Creates a decoration for the result and applies styles * @param result the search result for which to create the decoration * @param options the options for the decoration * @returns the {@link IDecoration} or undefined if the marker has already been disposed of */ }, { key: "_createResultDecoration", value: function _createResultDecoration(result, options) { var _this$_resultDecorati3; var terminal = this._terminal; var marker = terminal.registerMarker(-terminal.buffer.active.baseY - terminal.buffer.active.cursorY + result.row); if (!marker) { return undefined; } var findResultDecoration = terminal.registerDecoration({ marker: marker, x: result.col, width: result.size, foregroundColor: options.matchForegroundColor, backgroundColor: options.matchBackground, overviewRulerOptions: (_this$_resultDecorati3 = this._resultDecorations) !== null && _this$_resultDecorati3 !== void 0 && _this$_resultDecorati3.get(marker.line) ? undefined : { color: options.matchOverviewRuler, position: 'center' } }); // findResultDecoration?.onRender((e) => this._applyStyles(e, options.matchBorder, false)); findResultDecoration === null || findResultDecoration === void 0 ? void 0 : findResultDecoration.onDispose(function () { return marker.dispose(); }); return findResultDecoration; } }]); return SearchAddon; }();