UNPKG

stmux

Version:

Simple Terminal Multiplexing for Node Environments

176 lines (161 loc) 6.68 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _nodeNotifier = _interopRequireDefault(require("node-notifier")); var _stripAnsi = _interopRequireDefault(require("strip-ansi")); /* ** stmux -- Simple Terminal Multiplexing for Node Environments ** Copyright (c) 2017-2024 Dr. Ralf S. Engelschall <rse@engelschall.com> ** ** Permission is hereby granted, free of charge, to any person obtaining ** a copy of this software and associated documentation files (the ** "Software"), to deal in the Software without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Software, and to ** permit persons to whom the Software is furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be included ** in all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ class stmuxErrors { handleErrors() { /* determine error patterns */ const parseErrorPatterns = patterns => { const result = []; while (patterns) { let [, pattern, rest] = patterns.match(/^((?:\\,|.)+?)(?:,(.+))?$/); const m = pattern.match(/^!(.+)$/); let negate = false; if (m) { negate = true; pattern = m[1]; } result.push({ negate, regexp: new RegExp(pattern) }); patterns = rest; } return result; }; const globalErrorPatterns = parseErrorPatterns(this.argv.error); this.terms.forEach(term => { const patterns = term.node.get("error"); term.stmuxErrorPatterns = patterns ? parseErrorPatterns(patterns) : []; term.stmuxError = false; }); /* handle error detection */ let notifyLocked = false; const notifyStateOld = []; const notifyStateNew = []; this.terms.forEach(term => { notifyStateOld[term.stmuxNumber - 1] = ""; notifyStateNew[term.stmuxNumber - 1] = ""; }); setInterval(() => { let dirty = false; const notify = []; this.terms.forEach(term => { /* act only if an update exists */ if (!term.stmuxUpdate) return; term.stmuxUpdate = false; /* short-circuit processing */ if (globalErrorPatterns.length === 0 && term.stmuxErrorPatterns.length === 0) return; /* take screenshot */ let screenshot = term.screenshot(); screenshot = (0, _stripAnsi.default)(screenshot); const lines = screenshot.split(/\r?\n/); /* match errors in screenshot */ const matches = (string, patterns) => { for (let i = 0; i < patterns.length; i++) { const matched = patterns[i].regexp.test(string); if (!(matched && !patterns[i].negate || !matched && patterns[i].negate)) return false; } return true; }; term.stmuxError = false; for (let i = 0; i < lines.length; i++) { if (globalErrorPatterns.length > 0 && matches(lines[i], globalErrorPatterns) || term.stmuxErrorPatterns.length > 0 && matches(lines[i], term.stmuxErrorPatterns)) { term.stmuxError = true; break; } } /* record notification state */ if (term.stmuxError) { notifyStateNew[term.stmuxNumber - 1] = screenshot; notify.push(term); } else notifyStateNew[term.stmuxNumber - 1] = ""; /* determine and record screen updates */ if (term.stmuxError && term.style.border.fg === "default") { term.style.border.fg = "red"; dirty = true; } else if (!term.stmuxError && term.style.border.fg === "red") { term.style.border.fg = "default"; dirty = true; } if (term.stmuxError && term.style.focus.border.fg === "green") { term.style.border.fg = "red"; dirty = true; } else if (!term.stmuxError && term.style.focus.border.fg === "red") { term.style.border.fg = "green"; dirty = true; } if (term.stmuxError && !term.stmuxTitle.match(/-\[ERROR\]/)) { this.setTerminalTitle(term); dirty = true; } else if (!term.stmuxError && term.stmuxTitle.match(/-\[ERROR\]/)) { this.setTerminalTitle(term); dirty = true; } }); /* update screen */ if (dirty) this.screen.render(); /* raise notification */ if (this.argv.method !== "" && notify.length > 0 && !notifyLocked) { const stateOld = notifyStateOld.reduce((a, v) => a + v, ""); const stateNew = notifyStateNew.reduce((a, v) => a + v, ""); if (stateOld !== stateNew) { /* determine message */ const notifyMsg = `${this.my.name}: ERROR situation${notify.length > 1 ? "s" : ""} ` + `detected in terminal${notify.length > 1 ? "s" : ""} ` + notify.map(term => "#" + term.stmuxNumber).join(", "); /* determine method(s) */ const methods = {}; this.argv.method.split(",").forEach(method => { methods[method] = true; }); /* send notification(s) */ if (methods.beep) this.screen.program.output.write("\x07"); if (methods.system) { _nodeNotifier.default.notify({ title: `${this.my.name}: Detected new ERROR situation${notify.length > 1 ? "s" : ""}`, message: notifyMsg, wait: false }); } /* swap notification state */ this.terms.forEach(term => { notifyStateOld[term.stmuxNumber - 1] = notifyStateNew[term.stmuxNumber - 1]; notifyStateNew[term.stmuxNumber - 1] = ""; }); /* lock notification for some time to not bug the user too much */ notifyLocked = true; setTimeout(() => { notifyLocked = false; }, 5 * 1000); } } }, 500); } } exports.default = stmuxErrors;