diff --git a/src/web/widgets/Console/History.js b/src/web/widgets/Console/History.js new file mode 100644 index 000000000..20947e059 --- /dev/null +++ b/src/web/widgets/Console/History.js @@ -0,0 +1,68 @@ +class History { + maxLength = Infinity; + history = []; // A circular history array if a maximum length is given + start = 0; // The start position for the history array + index = -1; // Current index of the history array + + constructor(maxLength) { + maxLength = Number(maxLength) || 0; + if (maxLength > 0) { + this.maxLength = maxLength; + } + } + current() { + if (this.history.length === 0) { + return undefined; + } + + const index = (this.start + this.index) % this.history.length; + return this.history[index]; + } + forward() { + if ((this.history.length === 0) || (this.index + 1 >= this.history.length)) { + return undefined; + } + + ++this.index; + const index = (this.start + this.index) % this.history.length; + return this.history[index]; + } + back() { + if ((this.history.length === 0) || (this.index - 1 < 0)) { + return undefined; + } + + --this.index; + const index = (this.start + this.index) % this.history.length; + return this.history[index]; + } + go(n) { + if (this.history.length === 0) { + return undefined; + } + n = Number(n) || 0; + this.index = Math.min(Math.max(0, this.index + n), this.history.length - 1); + const index = (this.start + this.index) % this.history.length; + return this.history[index]; + } + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + // S x x x x x x x x x >> x S x x x x x x x x + push(data) { + if (this.history.length < this.maxLength) { + if (this.index + 1 >= this.history.length) { + ++this.index; + } + this.history.push(data); + } else { + this.history[this.start] = data; + this.start = (this.start + 1) % this.history.length; + if (this.index > 0) { + --this.index; + } + } + + return data; + } +} + +export default History; diff --git a/src/web/widgets/Console/Terminal.jsx b/src/web/widgets/Console/Terminal.jsx index 74ade94bd..c990e9b34 100644 --- a/src/web/widgets/Console/Terminal.jsx +++ b/src/web/widgets/Console/Terminal.jsx @@ -1,11 +1,12 @@ import includes from 'lodash/includes'; import classNames from 'classnames'; -import { code } from 'keycode'; +//import { code } from 'keycode'; import PropTypes from 'prop-types'; import React, { PureComponent } from 'react'; import ReactDOM from 'react-dom'; import Xterm from 'xterm'; import fit from 'xterm/lib/addons/fit/fit'; +import History from './History'; import log from '../../lib/log'; import styles from './index.styl'; @@ -26,6 +27,8 @@ class Terminal extends PureComponent { tabStopWidth: 4, onData: () => {} }; + history = new History(1000); + historyCommand = ''; eventHandler = { onResize: (size) => { @@ -38,10 +41,13 @@ class Terminal extends PureComponent { return (key, event) => { const { onData } = this.props; const term = this.term; - const nonPrintable = (event.altKey || event.altGraphKey || event.ctrlKey || event.metaKey); + const nonPrintableKey = (event.altKey || event.altGraphKey || event.ctrlKey || event.metaKey); // Enter - if (event.keyCode === code.enter) { + if (event.key === 'Enter') { + if (buffer.length > 0) { + this.history.push(buffer); + } buffer += key; onData(buffer); buffer = ''; @@ -49,8 +55,8 @@ class Terminal extends PureComponent { return; } - // Delete - if (event.keyCode === code.backspace) { + // Backspace + if (event.key === 'Backspace') { let line = term.lines.get(term.ybase + term.y); let x = term.x; if (line && x > 0) { @@ -72,8 +78,8 @@ class Terminal extends PureComponent { return; } - // Non-printable character (e.g. ctrl-x) - if (nonPrintable) { + // Non-printable keys (e.g. ctrl-x) + if (nonPrintableKey) { onData(buffer); buffer = ''; @@ -81,8 +87,26 @@ class Terminal extends PureComponent { return; } - // Up, Right, Down, Left - if (includes([code.up, code.right, code.down, code.left], event.keyCode)) { + // Left, Right + if (includes(['ArrowLeft', 'ArrowRight'], event.key)) { + return; + } + + // Up, Down + if (includes(['ArrowUp', 'ArrowDown'], event.key)) { + if (event.key === 'ArrowUp') { + if (!this.historyCommand) { + this.historyCommand = this.history.current() || ''; + } else if (this.history.index > 0) { + this.historyCommand = this.history.back() || ''; + } + } else if (event.key === 'ArrowDown') { + this.historyCommand = this.history.forward() || ''; + } + + term.eraseLine(term.y); + term.x = 0; + term.write(this.historyCommand); return; }