react-terminal-viewer
Version:
<h1 align="center"> react-terminal-viewer </h1>
792 lines (771 loc) • 37.8 kB
JavaScript
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;
}();