[gjs/ewlsh/nova-repl] WIP - Add multiline input



commit ad0fa71cc1c5a1cfbc0b2b0217e8f4dca97da775
Author: Evan Welsh <contact evanwelsh com>
Date:   Wed Sep 1 00:41:52 2021 -0700

    WIP - Add multiline input

 modules/console.cpp | 26 +++++++++++++++++++++++++-
 modules/esm/repl.js | 51 ++++++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 65 insertions(+), 12 deletions(-)
---
diff --git a/modules/console.cpp b/modules/console.cpp
index 46da5cfb..228055f3 100644
--- a/modules/console.cpp
+++ b/modules/console.cpp
@@ -320,6 +320,27 @@ static bool gjs_console_eval_js(JSContext* cx, unsigned argc, JS::Value* vp) {
     return ok;
 }
 
+GJS_JSAPI_RETURN_CONVENTION
+static bool gjs_console_is_valid_js(JSContext* cx, unsigned argc,
+                                    JS::Value* vp) {
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+    JS::RootedString str(cx);
+
+    if (!gjs_parse_call_args(cx, "isValid", args, "S", "code", &str))
+        return false;
+
+    JS::UniqueChars code;
+    size_t code_len;
+    if (!gjs_string_to_utf8_n(cx, str, &code, &code_len))
+        return false;
+
+    JS::RootedObject global(cx, gjs_get_import_global(cx));
+
+    args.rval().setBoolean(
+        JS_Utf8BufferIsCompilableUnit(cx, global, code.get(), code_len));
+    return true;
+}
+
 bool gjs_console_get_terminal_size(JSContext* cx, unsigned argc,
                                    JS::Value* vp) {
     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
@@ -362,7 +383,10 @@ bool gjs_define_console_private_stuff(JSContext* context,
     return JS_DefineFunction(context, module, "enable_raw_mode",
                              gjs_console_enable_raw_mode, 0,
                              GJS_MODULE_PROP_FLAGS) &&
-           JS_DefineFunction(context, module, "eval", gjs_console_eval_js, 0,
+           JS_DefineFunction(context, module, "eval", gjs_console_eval_js, 2,
+                             GJS_MODULE_PROP_FLAGS) &&
+           JS_DefineFunction(context, module, "isValid",
+                             gjs_console_is_valid_js, 1,
                              GJS_MODULE_PROP_FLAGS) &&
            JS_DefineFunction(context, module, "get_size",
                              gjs_console_get_terminal_size, 0,
diff --git a/modules/esm/repl.js b/modules/esm/repl.js
index f8b72bfd..d365938a 100644
--- a/modules/esm/repl.js
+++ b/modules/esm/repl.js
@@ -1,6 +1,6 @@
 import Gio from 'gi://Gio';
 
-import { Ansi, AnsiColors, Keycode, Figures } from '_cliffy';
+import { Ansi, AnsiColors, Keycode, Figures } from './_cliffy.js';
 
 const stdin = Gio.UnixInputStream.new(0, false);
 const stdout = Gio.UnixOutputStream.new(1, false);
@@ -33,10 +33,12 @@ class ReplInput {
         this.history = [];
         this.inputChars = [];
         this.inputIndex = 0;
+
+        this.lines = [];
     }
-    
+
     /**
-     * @returns {readonly string[]}
+     * @returns {string}
      */
     getValue() {
         if (this.historyIndex >= 0) {
@@ -50,18 +52,20 @@ class ReplInput {
      */
     getEditableValue() {
         if (this.historyIndex > -1) {
+            // TODO(ewlsh): This allows editing each history entry
+            // 'in place'.
             return this.history[this.historyIndex];
         }
         return this.inputChars;
     }
 
     validate(input) {
-        return true;
+        return Console.isValid(input);
     }
 
-    clear() {
+    clear(lines = 1) {
         this.writeSync(Ansi.cursorLeft);
-        this.writeSync(Ansi.eraseLine);
+        this.writeSync(Ansi.eraseDown(lines));
     }
 
     historyUp() {
@@ -113,14 +117,30 @@ class ReplInput {
 
     processLine() {
         const value = this.getValue();
-        this.writeSync('\n');
-        this.flush();
+        // Rebuild the input...
+        const js = [...this.lines, value].join('\n');
+
+        // Reset state...
         this.history.unshift(value.split(''));
         this.historyIndex = -1;
         this.inputChars = [];
         this.inputIndex = 0;
 
-        this._input?.(value);
+        // Append the new line...
+        this.writeSync('\n');
+        this.flush();
+
+        // Only trigger input if this is a compilable unit...
+        if (this.validate(js)) {
+            // Reset lines before input is triggered
+            this.lines = [];
+            this._input?.(js);
+        } else {
+            // Buffer the input until a compilable unit is found...
+            this.lines.push(value);
+        }
+
+
     }
 
     handleEvent(key) {
@@ -177,10 +197,19 @@ class ReplInput {
     render() {
         const value = this.getValue();
 
+        // Prevent the cursor from flashing while we render...
         this.writeSync(Ansi.cursorHide);
         this.clear();
-       
-        this.writeSync(this.$prompt + value);
+
+        if (this.lines.length > 0) {
+            // Create a prefix like '... ' that matches the current
+            // prompt length...
+            const prefix = ' '.padStart(this.$promptLength, '.');
+            this.writeSync(prefix + value);
+        } else {
+            this.writeSync(this.$prompt + value);
+        }
+
         this.updateInputIndex(value);
         this.writeSync(Ansi.cursorTo(this.$promptLength + this.inputIndex + 1));
         this.writeSync(Ansi.cursorShow);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]