[gjs/ewlsh/nova-repl] WIP - Round out keyboard shortcut support



commit b0a4bec8e88236d0d637e993ea5fa718835ad46a
Author: Evan Welsh <contact evanwelsh com>
Date:   Wed Sep 1 07:25:00 2021 -0700

    WIP - Round out keyboard shortcut support
    
    Let's not reinvent the wheel for word detection

 modules/esm/repl.js | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 151 insertions(+), 1 deletion(-)
---
diff --git a/modules/esm/repl.js b/modules/esm/repl.js
index d365938a..c1442eaa 100644
--- a/modules/esm/repl.js
+++ b/modules/esm/repl.js
@@ -44,6 +44,7 @@ class ReplInput {
         if (this.historyIndex >= 0) {
             return this.history[this.historyIndex].join('');
         }
+
         return this.inputChars.join('');
     }
 
@@ -59,6 +60,14 @@ class ReplInput {
         return this.inputChars;
     }
 
+    editValue(editor) {
+        if (this.historyIndex > -1) {
+            return this.history[this.historyIndex] = editor(this.history[this.historyIndex]);
+        }
+
+        return this.inputChars = editor(this.inputChars);
+    }
+
     validate(input) {
         return Console.isValid(input);
     }
@@ -115,6 +124,94 @@ class ReplInput {
         this.moveCursorLeft();
     }
 
+    deleteCharRightOrClose() {
+        const editableValue = this.getEditableValue();
+        if (this.inputIndex < editableValue.length - 1) {
+            editableValue.splice(this.inputIndex, 1);
+        } else {
+            this.stop();
+        }
+    }
+
+    deleteToBeginning() {
+        const editableValue = this.getEditableValue();
+
+        editableValue.splice(0, this.inputIndex);
+    }
+
+    deleteToEnd() {
+        const editableValue = this.getEditableValue();
+
+        editableValue.splice(this.inputIndex);
+    }
+
+    /**
+     * Adapted from lib/readline.js in Node.js
+     */
+    _deleteWordLeft() {
+        this.editValue((value) => {
+            if (this.inputIndex > 0) {
+                // Reverse the string and match a word near beginning
+                // to avoid quadratic time complexity
+                let leading = value.slice(0, this.inputIndex);
+                const reversed = [...leading].reverse().join('');
+                const match = reversed.match(/^\s*(?:[^\w\s]+|\w+)?/);
+                leading = leading.slice(0,
+                    leading.length - match[0].length);
+                value = leading.concat(value.slice(this.inputIndex));
+                this.inputIndex = leading.length;
+
+                return value;
+            }
+        });
+    }
+
+    /**
+     * Adapted from lib/readline.js in Node.js
+     */
+    _deleteWordRight() {
+        this.editValue((value) => {
+            if (this.inputChars.length > 0 && this.inputIndex < value.length) {
+                const trailing = value.slice(this.inputIndex).join('');
+                const match = trailing.match(/^(?:\s+|\W+|\w+)\s*/);
+                value = value.slice(0, this.inputIndex).concat(
+                    trailing.slice(match[0].length));
+                return value;
+            }
+        });
+    }
+
+    /**
+     * Adapted from lib/readline.js in Node.js
+     */
+    _wordLeft() {
+        if (this.inputIndex > 0) {
+            const value = this.getValue();
+            // Reverse the string and match a word near beginning
+            // to avoid quadratic time complexity
+            const leading = value.slice(0, this.inputIndex);
+            const reversed = Array.from(leading).reverse().join('');
+            const match = reversed.match(/^\s*(?:[^\w\s]+|\w+)?/);
+
+            this.inputIndex -= match[0].length;
+            this.inputIndex = Math.max(0, this.inputIndex);
+        }
+    }
+
+    /**
+     * Adapted from lib/readline.js in Node.js
+     */
+    _wordRight() {
+        const value = this.getValue();
+
+        if (this.inputIndex < value.length) {
+            const trailing = value.slice(this.inputIndex);
+            const match = trailing.match(/^(?:\s+|[^\w\s]+|\w+)\s*/);
+
+            this.inputIndex += match[0].length;
+        }
+    }
+
     processLine() {
         const value = this.getValue();
         // Rebuild the input...
@@ -144,8 +241,20 @@ class ReplInput {
     }
 
     handleEvent(key) {
-        if (key.ctrl) {
+        if (key.ctrl && !key.meta && !key.shift) {
             switch (key.name) {
+                case 'h':
+                    this.deleteChar();
+                    return;
+                case 'd':
+                    this.deleteCharRightOrClose();
+                    return;
+                case 'u':
+                    this.deleteToBeginning();
+                    return;
+                case 'k':
+                    this.deleteToEnd();
+                    return;
                 case 'a':
                     this.moveCursorToBeginning();
                     return;
@@ -158,6 +267,47 @@ class ReplInput {
                 case 'f':
                     this.moveCursorRight();
                     return;
+                case 'l':
+                    // TODO(ewlsh): Use internal API instead...
+                    console.clear();
+                    return;
+                case 'n':
+                    this.historyDown();
+                    return;
+                case 'p':
+                    this.historyUp();
+                    return;
+                case 'z':
+                    // Pausing is unsupported.
+                    return;
+                case 'w':
+                case 'backspace':
+                    this._deleteWordLeft();
+                    return;
+                case 'delete':
+                    this._deleteWordRight();
+                    return;
+                case 'left':
+                    this._wordLeft();
+                    return;
+                case 'right':
+                    this._wordRight();
+                    return;
+            }
+        } else if (key.meta && !key.shift) {
+            switch (key.name) {
+                case 'd':
+                    this._deleteWordRight();
+                    return;
+                case 'backspace':
+                    this._deleteWordLeft();
+                    return;
+                case 'b':
+                    this._wordLeft();
+                    return;
+                case 'f':
+                    this._wordRight();
+                    return;
             }
         }
 


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