@kui-shell/core
Version:
An Electron-based shell for cloud-native development
235 lines (233 loc) • 7.55 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.HistoryModel = void 0;
exports.getHistoryForTab = getHistoryForTab;
var _debug = _interopRequireDefault(require("debug"));
var _store = _interopRequireDefault(require("./store"));
var _capabilities = require("../core/capabilities");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/*
* Copyright 2017 The Kubernetes Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var __awaiter = void 0 && (void 0).__awaiter || function (thisArg, _arguments, P, generator) {
function adopt(value) {
return value instanceof P ? value : new P(function (resolve) {
resolve(value);
});
}
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
}
function rejected(value) {
try {
step(generator["throw"](value));
} catch (e) {
reject(e);
}
}
function step(result) {
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
}
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
/** Legacy localStorage key for long-time Kui users */
const legacyKey = 'openwhisk.history';
/** localStorage key */
const Key = 'kui-shell.history';
/** We will persist no more than this number of history entries per Tab */
const MAX_HISTORY = 250;
const debug = (0, _debug.default)('core/history');
/** A tuple of History entries, one per Tab (as specified by its given uuid) */
class HistoryModel {
// re: eslint-disable; we don't want to publicize this beyond the
// core, but still want it accessible within this file; eslint
// doesn't seem to allow for this, by default.
// eslint-disable-next-line @typescript-eslint/explicit-member-accessibility
constructor(uuid) {
this.uuid = uuid;
let lines = typeof window !== 'undefined' && !(0, _capabilities.isHeadless)() ? this.restore() : undefined;
if (!lines && typeof window !== 'undefined') {
const raw = (0, _store.default)().getItem(legacyKey);
lines = JSON.parse(raw);
(0, _store.default)().setItem(this.key(), raw);
const backupKey = `${legacyKey}.bak`;
(0, _store.default)().setItem(backupKey, raw);
(0, _store.default)().removeItem(legacyKey);
debug('legacy history copied over, backed up in this key', backupKey);
}
this._lines = lines || [];
if (!HistoryModel.masterUUID) {
debug('setting master', uuid);
HistoryModel.masterUUID = uuid;
} else if (!lines || lines.length === 0) {
this._lines = this.restore(HistoryModel.masterUUID) || [];
debug('copying history from master', HistoryModel.masterUUID, this._lines);
this.save();
}
if (this._lines.length > MAX_HISTORY) {
debug('cropping history', this._lines.length);
this._lines = this._lines.slice(-MAX_HISTORY);
this.save();
}
this._cursor = this._lines.length; // pointer to historic line
}
/** The persistence key for this tab */
key(uuid = this.uuid) {
return `${Key}.${uuid}`;
}
/** return the given line of history */
line(idx) {
return this._lines[idx];
}
slice(start, end) {
return this._lines.slice(start, end);
}
get cursor() {
return this._cursor;
}
/** change the cursor, protecting against under- and overflow */
guardedChange(incr) {
const newCursor = this._cursor + incr;
if (newCursor < 0) {
this._cursor = 0;
} else if (newCursor > this._lines.length) {
this._cursor = this._lines.length;
} else {
this._cursor = newCursor;
}
// console.log('history::newCursor', cursor, lines.length, lines[cursor])
return this._cursor;
}
/** Low-level save to persistent storage */
save(lines = this._lines, uuid = this.uuid) {
(0, _store.default)().setItem(this.key(uuid), JSON.stringify(lines));
}
/** Low-level restore from persistent storage */
restore(uuid = this.uuid) {
try {
const model = JSON.parse((0, _store.default)().getItem(this.key(uuid)));
debug('restoring', uuid, model);
return model;
} catch (err) {
debug('corrupt history', uuid, err);
this.save([], uuid); // invalidate the corrupt state
return [];
}
}
/**
* Clear out all history
*
*/
wipe() {
this._lines = [];
this.save();
return true;
}
/** add a line of repl history */
add(line) {
if (this._lines.length === 0 || this._lines[this._lines.length - 1].raw !== line.raw) {
// don't add sequential duplicates
this._lines.push(line);
this.save();
// console.log('history::add', cursor)
}
this._cursor = this._lines.length;
return this._cursor - 1;
}
/** update a line of repl history -- for async operations */
update(cursor, updateFn) {
return __awaiter(this, void 0, void 0, function* () {
// console.log('history::update', cursor)
yield updateFn(this._lines[cursor]);
this.save();
});
}
lineByIncr(incr) {
return this.line(this.guardedChange(incr));
}
/** go back one entry */
previous() {
return this.lineByIncr(-1);
}
/** go forward one entry */
next() {
return this.lineByIncr(+1);
}
/** return to the oldest entry */
first() {
this._cursor = 0;
return this.line(this._cursor);
}
/** return to the newest entry */
last() {
this._cursor = this._lines.length - 1;
return this.line(this.cursor);
}
/**
* Search the history model
*
* @param filter a search string, search regexp, or search function
* @param startIdx if undefined or negative, start from the end, otherwise,
* search backwards from the given index
*
*/
findIndex(filter, startIdx) {
let filterFn;
if (typeof filter === 'string') {
const regexp = new RegExp(filter.replace(/([$.])/g, '\\$1'));
filterFn = line => regexp.test(line.raw);
} else if (filter instanceof RegExp) {
filterFn = line => filter.test(line.raw);
} else {
filterFn = filter;
}
for (let idx = startIdx !== undefined && startIdx >= 0 ? startIdx : this._lines.length - 1; idx >= 0; idx--) {
if (filterFn(this._lines[idx])) {
return idx;
}
}
return -1;
}
/**
* Search the history model
*
* @param filter a search string, search regexp, or search function
*
*/
find(filter) {
const idx = this.findIndex(filter);
return idx !== undefined && this._lines[idx];
}
}
/** Here, we cache the deserialized form, indexed by Tab.uuid */
exports.HistoryModel = HistoryModel;
const cache = {};
/** @return the HistoryModel for the given Tab, as identified by `uuid` */
function getHistoryForTab(uuid) {
if (!cache[uuid]) {
cache[uuid] = new HistoryModel(uuid);
}
return cache[uuid];
}
var _default = HistoryModel;
exports.default = _default;