diff --git a/src/module/repl.wren b/src/module/repl.wren index 105e53dc..d0fbe6ac 100644 --- a/src/module/repl.wren +++ b/src/module/repl.wren @@ -25,6 +25,8 @@ class Repl { var byte = Stdin.readByte() if (byte == Chars.ctrlA) { _cursor = 0 + } else if (byte == Chars.ctrlB) { + cursorLeft() } else if (byte == Chars.ctrlC) { System.print() return @@ -39,6 +41,24 @@ class Repl { deleteRight() } else if (byte == Chars.ctrlE) { _cursor = _line.count + } else if (byte == Chars.ctrlF) { + cursorRight() + } else if (byte == Chars.ctrlK) { + // Delete everything after the cursor. + _line = _line[0..._cursor] + } else if (byte == Chars.ctrlL) { + // Clear the screen. + System.write("\x1b[2J") + // Move cursor to top left. + System.write("\x1b[H") + } else if (byte == Chars.ctrlU) { + // Clear the line. + _line = "" + _cursor = 0 + } else if (byte == Chars.ctrlN) { + nextHistory() + } else if (byte == Chars.ctrlP) { + previousHistory() } else if (byte == Chars.escape) { handleEscape() } else if (byte == Chars.carriageReturn) { @@ -48,7 +68,12 @@ class Repl { } else if (byte >= Chars.space && byte <= Chars.tilde) { insertChar(byte) } else { - // TODO: Handle other non-printing characters. + // TODO: Ctrl-T to swap chars. + // TODO: ESC H and F to move to beginning and end of line. (Both ESC + // [ and ESC 0 sequences?) + // TODO: Ctrl-W delete previous word. + // TODO: Completion. + // TODO: Other shortcuts? System.print("Unhandled byte: %(byte)") } @@ -90,18 +115,26 @@ class Repl { } else if (value == EscapeBracket.down) { nextHistory() } else if (value == EscapeBracket.left) { - // Move the cursor left one. - if (_cursor > 0) _cursor = _cursor - 1 + cursorLeft() } else if (value == EscapeBracket.right) { - // Move the cursor right one. - // TODO: Take into account multi-byte characters? - if (_cursor < _line.count) _cursor = _cursor + 1 + cursorRight() } } else { // TODO: Handle ESC 0 sequences. } } + /// Move the cursor left one character. + cursorLeft() { + if (_cursor > 0) _cursor = _cursor - 1 + } + + /// Move the cursor right one character. + cursorRight() { + // TODO: Take into account multi-byte characters? + if (_cursor < _line.count) _cursor = _cursor + 1 + } + previousHistory() { if (_historyIndex == 0) return @@ -239,12 +272,19 @@ class Color { /// Utilities for working with characters. class Chars { static ctrlA { 0x01 } + static ctrlB { 0x02 } static ctrlC { 0x03 } static ctrlD { 0x04 } static ctrlE { 0x05 } + static ctrlF { 0x06 } static tab { 0x09 } static lineFeed { 0x0a } + static ctrlK { 0x0b } + static ctrlL { 0x0c } static carriageReturn { 0x0d } + static ctrlN { 0x0e } + static ctrlP { 0x10 } + static ctrlU { 0x15 } static escape { 0x1b } static space { 0x20 } static bang { 0x21 } diff --git a/src/module/repl.wren.inc b/src/module/repl.wren.inc index c19ddbae..9af2686a 100644 --- a/src/module/repl.wren.inc +++ b/src/module/repl.wren.inc @@ -27,6 +27,8 @@ static const char* replModuleSource = " var byte = Stdin.readByte()\n" " if (byte == Chars.ctrlA) {\n" " _cursor = 0\n" +" } else if (byte == Chars.ctrlB) {\n" +" cursorLeft()\n" " } else if (byte == Chars.ctrlC) {\n" " System.print()\n" " return\n" @@ -41,6 +43,24 @@ static const char* replModuleSource = " deleteRight()\n" " } else if (byte == Chars.ctrlE) {\n" " _cursor = _line.count\n" +" } else if (byte == Chars.ctrlF) {\n" +" cursorRight()\n" +" } else if (byte == Chars.ctrlK) {\n" +" // Delete everything after the cursor.\n" +" _line = _line[0..._cursor]\n" +" } else if (byte == Chars.ctrlL) {\n" +" // Clear the screen.\n" +" System.write(\"\x1b[2J\")\n" +" // Move cursor to top left.\n" +" System.write(\"\x1b[H\")\n" +" } else if (byte == Chars.ctrlU) {\n" +" // Clear the line.\n" +" _line = \"\"\n" +" _cursor = 0\n" +" } else if (byte == Chars.ctrlN) {\n" +" nextHistory()\n" +" } else if (byte == Chars.ctrlP) {\n" +" previousHistory()\n" " } else if (byte == Chars.escape) {\n" " handleEscape()\n" " } else if (byte == Chars.carriageReturn) {\n" @@ -50,7 +70,13 @@ static const char* replModuleSource = " } else if (byte >= Chars.space && byte <= Chars.tilde) {\n" " insertChar(byte)\n" " } else {\n" -" // TODO: Handle other non-printing characters.\n" +" // TODO: Ctrl-T to swap chars.\n" +" // TODO: ESC H and F to move to beginning and end of line. (Both ESC\n" +" // [ and ESC 0 sequences?)\n" +" // TODO: Ctrl-L clear screen.\n" +" // TODO: Ctrl-W delete previous word.\n" +" // TODO: Completion.\n" +" // TODO: Other shortcuts?\n" " System.print(\"Unhandled byte: %(byte)\")\n" " }\n" "\n" @@ -92,18 +118,26 @@ static const char* replModuleSource = " } else if (value == EscapeBracket.down) {\n" " nextHistory()\n" " } else if (value == EscapeBracket.left) {\n" -" // Move the cursor left one.\n" -" if (_cursor > 0) _cursor = _cursor - 1\n" +" cursorLeft()\n" " } else if (value == EscapeBracket.right) {\n" -" // Move the cursor right one.\n" -" // TODO: Take into account multi-byte characters?\n" -" if (_cursor < _line.count) _cursor = _cursor + 1\n" +" cursorRight()\n" " }\n" " } else {\n" " // TODO: Handle ESC 0 sequences.\n" " }\n" " }\n" "\n" +" /// Move the cursor left one character.\n" +" cursorLeft() {\n" +" if (_cursor > 0) _cursor = _cursor - 1\n" +" }\n" +"\n" +" /// Move the cursor right one character.\n" +" cursorRight() {\n" +" // TODO: Take into account multi-byte characters?\n" +" if (_cursor < _line.count) _cursor = _cursor + 1\n" +" }\n" +"\n" " previousHistory() {\n" " if (_historyIndex == 0) return\n" "\n" @@ -241,12 +275,19 @@ static const char* replModuleSource = "/// Utilities for working with characters.\n" "class Chars {\n" " static ctrlA { 0x01 }\n" +" static ctrlB { 0x02 }\n" " static ctrlC { 0x03 }\n" " static ctrlD { 0x04 }\n" " static ctrlE { 0x05 }\n" +" static ctrlF { 0x06 }\n" " static tab { 0x09 }\n" " static lineFeed { 0x0a }\n" +" static ctrlK { 0x0b }\n" +" static ctrlL { 0x0c }\n" " static carriageReturn { 0x0d }\n" +" static ctrlN { 0x0e }\n" +" static ctrlP { 0x10 }\n" +" static ctrlU { 0x15 }\n" " static escape { 0x1b }\n" " static space { 0x20 }\n" " static bang { 0x21 }\n"