diff --git a/src/ansi-shell/readline/history.js b/src/ansi-shell/readline/history.js
new file mode 100644
index 0000000..f04b3a3
--- /dev/null
+++ b/src/ansi-shell/readline/history.js
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2024 Puter Technologies Inc.
+ *
+ * This file is part of Phoenix Shell.
+ *
+ * Phoenix Shell is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+export class HistoryManager {
+ constructor({ enableLogging = false } = {}) {
+ this.items = [];
+ this.index_ = 0;
+ this.listeners_ = {};
+ this.enableLogging_ = enableLogging;
+ }
+
+ log(...a) {
+ // TODO: Command line option for configuring logging
+ if ( this.enableLogging_ ) {
+ console.log('[HistoryManager]', ...a);
+ }
+ }
+
+ get index() {
+ return this.index_;
+ }
+
+ set index(v) {
+ this.log('setting index', v);
+ this.index_ = v;
+ }
+
+ get() {
+ return this.items[this.index];
+ }
+
+ // Save, overwriting the current history item
+ save(data, { opt_debug } = {}) {
+ this.log('saving', data, 'at', this.index,
+ ...(opt_debug ? [ 'from', opt_debug ] : []));
+ this.items[this.index] = data;
+
+ if (this.listeners_.hasOwnProperty('add')) {
+ for (const listener of this.listeners_.add) {
+ listener(data);
+ }
+ }
+ }
+
+ append(data) {
+ if (
+ this.items.length !== 0 &&
+ this.index !== this.items.length
+ ) {
+ this.log('POP');
+ // remove last item
+ this.items.pop();
+ }
+ this.index = this.items.length;
+ this.save(data, { opt_debug: 'append' });
+ this.index++;
+ }
+
+ on(topic, listener) {
+ if (!this.listeners_.hasOwnProperty(topic)) {
+ this.listeners_[topic] = [];
+ }
+ this.listeners_[topic].push(listener);
+ }
+}
\ No newline at end of file
diff --git a/src/ansi-shell/readline/readline.js b/src/ansi-shell/readline/readline.js
index a4fe39d..2ac42c7 100644
--- a/src/ansi-shell/readline/readline.js
+++ b/src/ansi-shell/readline/readline.js
@@ -16,15 +16,16 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
-import { Context } from "../../context/context.js";
-import { CommandCompleter } from "../../puter-shell/completers/command_completer.js";
-import { FileCompleter } from "../../puter-shell/completers/file_completer.js";
+import { Context } from '../../context/context.js';
+import { CommandCompleter } from '../../puter-shell/completers/command_completer.js';
+import { FileCompleter } from '../../puter-shell/completers/file_completer.js';
import { OptionCompleter } from '../../puter-shell/completers/option_completer.js';
-import { Uint8List } from "../../util/bytes.js";
-import { StatefulProcessorBuilder } from "../../util/statemachine.js";
-import { ANSIContext } from "../ANSIContext.js";
-import { readline_comprehend } from "./rl_comprehend.js";
-import { CSI_HANDLERS } from "./rl_csi_handlers.js";
+import { Uint8List } from '../../util/bytes.js';
+import { StatefulProcessorBuilder } from '../../util/statemachine.js';
+import { ANSIContext } from '../ANSIContext.js';
+import { readline_comprehend } from './rl_comprehend.js';
+import { CSI_HANDLERS } from './rl_csi_handlers.js';
+import { HistoryManager } from './history.js';
const decoder = new TextDecoder();
@@ -71,6 +72,13 @@ const ReadlineProcessorBuilder = builder => builder
if ( locals.byte === consts.CHAR_ETX ) {
externs.out.write('^C\n');
+ // Exit if input line is empty
+ // FIXME: Check for 'process' is so we only do this on Node. How should we handle exiting in Puter terminal?
+ if ( process && ctx.vars.result.length === 0 ) {
+ process.exit(1);
+ return;
+ }
+ // Otherwise clear it
ctx.vars.result = '';
ctx.setState('end');
return;
@@ -315,51 +323,6 @@ const ReadlineProcessor = ReadlineProcessorBuilder(
new StatefulProcessorBuilder()
);
-class HistoryManager {
- constructor () {
- this.items = [];
- this.index_ = 0;
- this.listeners_ = {};
- }
-
- log (...a) {
- // TODO: proper logging and verbosity config
- // console.log('[HistoryManager]', ...a);
- }
-
- get index () {
- return this.index_;
- }
-
- set index (v) {
- this.log('setting index', v);
- this.index_ = v;
- }
-
- get () {
- return this.items[this.index];
- }
-
- save (data, { opt_debug } = {}) {
- this.log('saving', data, 'at', this.index,
- ...(opt_debug ? ['from', opt_debug] : []));
- this.items[this.index] = data;
-
- if ( this.listeners_.hasOwnProperty('add') ) {
- for ( const listener of this.listeners_.add ) {
- listener(data);
- }
- }
- }
-
- on (topic, listener) {
- if ( ! this.listeners_.hasOwnProperty(topic) ) {
- this.listeners_[topic] = [];
- }
- this.listeners_[topic].push(listener);
- }
-}
-
class Readline {
constructor (params) {
this.internal_ = {};
@@ -383,21 +346,8 @@ class Readline {
commandCtx,
});
- // TODO: this condition, redundant to the one in ANSIShell,
- // is an indication that HistoryManager
if ( result.trim() !== '' ) {
- // console.log('[HistoryManager] len?', this.history.items.length);
- if (
- this.history.items.length !== 0 &&
- this.history.index !== this.history.items.length
- ) {
- // console.log('[HistoryManager] POP');
- // remove last item
- this.history.items.pop();
- }
- this.history.index = this.history.items.length;
- this.history.save(result, { opt_debug: 'post-readline' });
- this.history.index++;
+ this.history.append(result);
}
return result;
diff --git a/src/pty/NodeStdioPTT.js b/src/pty/NodeStdioPTT.js
index e0f5855..58cd947 100644
--- a/src/pty/NodeStdioPTT.js
+++ b/src/pty/NodeStdioPTT.js
@@ -29,10 +29,6 @@ export class NodeStdioPTT {
process.stdin.on('data', chunk => {
const input = new Uint8Array(chunk);
readController.enqueue(input);
- if (input.length === 1 && (input[0] === signals.SIGINT || input[0] === signals.SIGQUIT)) {
- process.exit(1);
- return;
- }
});
this.out = writestream_node_to_web(process.stdout);