From f2e9cd3075cdf12e7c86f28aad63ee016a039d14 Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Fri, 2 Apr 2021 11:57:24 -0400 Subject: [PATCH] Refactor input mapping --- editor/editor.go | 174 +++++++++++++++++++++++++++++------------------ 1 file changed, 109 insertions(+), 65 deletions(-) diff --git a/editor/editor.go b/editor/editor.go index cb31ef5..f02d3ed 100644 --- a/editor/editor.go +++ b/editor/editor.go @@ -80,6 +80,13 @@ func (e *editor) SetStatusMessage(template string, a ...interface{}) { func (e *editor) ProcessKeypress() bool { ch, _ := terminal.ReadKey() + return e.processKeypressChar(ch) +} + +/** + * Determine what to do with an individual character of input + */ +func (e *editor) processKeypressChar(ch rune) bool { switch ch { case key.Ctrl('q'): if e.document.dirty && e.quitTimes > 0 { @@ -106,34 +113,51 @@ func (e *editor) ProcessKeypress() bool { // Modifier keys that return ANSI escape sequences str := parseEscapeSequence() - switch str { - case keyUp, - keyDown, - keyLeft, - keyRight, - keyPageUp, - keyPageDown, - keyHome, - keyEnd: - - e.moveCursor(str) - - case keyDelete: - e.moveCursor(keyRight) - e.delChar() + // Don't swallow a character after ESC if it doesn't + // start an ANSI escape sequence + if len(str) == 1 { + return e.processKeypressChar(rune(str[0])) } + e.processKeypressStr(str) + default: e.insertChar(ch) } + // Clear the quit message and restart the + // confirmation count if confirmation is not + // completed if e.quitTimes != KiloQuitTimes { e.quitTimes = KiloQuitTimes + e.SetStatusMessage("") } return true } +/** + * Determine what do do with a parsed ANSI escape sequence + */ +func (e *editor) processKeypressStr(key string) { + switch key { + case keyUp, + keyDown, + keyLeft, + keyRight, + keyPageUp, + keyPageDown, + keyHome, + keyEnd: + + e.moveCursor(key) + + case keyDelete: + e.moveCursor(keyRight) + e.delChar() + } +} + func (e *editor) moveCursor(key string) { var row *row if e.cursor.y >= e.document.rowCount() { @@ -229,64 +253,84 @@ func (e *editor) delChar() { // Convert the raw ANSI escape sequences to the type of key input func parseEscapeSequence() string { - var runes []rune + // If we aren't starting an escape sequence, + // return the character + startChar, _ := terminal.ReadKey() + if startChar != '[' && startChar != 'O' { + return string(startChar) + } - for i := 0; i < 3; i++ { + // Read one or two characters after + // \e[ or \eO, which is the end of the + // handled escape sequences + runes := [2]rune{'\000', '\000'} + for i := 0; i < 2; i++ { ch, size := terminal.ReadKey() if size == 0 { - return "\x1b" + return string(rune(key.Esc)) } - runes = append(runes, ch) + runes[i] = ch - if i == 1 && runes[1] >= 'A' { - // \eOH \eOF - if runes[0] == 'O' { - switch runes[1] { - case 'H': - return keyHome - case 'F': - return keyEnd - } - } - // \e[A - if runes[0] == '[' { - switch runes[1] { - case 'A': - return keyUp - case 'B': - return keyDown - case 'C': - return keyRight - case 'D': - return keyLeft - case 'H': - return keyHome - case 'F': - return keyEnd - } - } + if i == 0 && runes[0] >= 'A' && runes[0] <= 'Z' { + return escSeqToKey([]rune{startChar, runes[0]}) } - // \e[1~ - if i == 2 && runes[0] == '[' && runes[2] == '~' { - switch runes[1] { - case '1': - return keyHome - case '3': - return keyDelete - case '4': - return keyEnd - case '5': - return keyPageUp - case '6': - return keyPageDown - case '7': - return keyHome - case '8': - return keyEnd - } + // \e[*~ + if i == 1 && startChar == '[' && runes[1] == '~' { + return escSeqToKey([]rune{startChar, runes[0], runes[1]}) } } - return string('\x1b') + return string(rune(key.Esc)) +} + +func escSeqToKey (seq []rune) string { + // \eO* + // \e[* + if len(seq) == 2 { + startChar, cmd := seq[0], seq[1] + if startChar == 'O' { + switch cmd { + case 'H': + return keyHome + case 'F': + return keyEnd + } + } else if startChar == '[' { + switch cmd { + case 'A': + return keyUp + case 'B': + return keyDown + case 'C': + return keyRight + case 'D': + return keyLeft + case 'H': + return keyHome + case 'F': + return keyEnd + } + } + } else if len(seq) == 3 { // \e[*~ + cmd := seq[1] + switch cmd { + case '1': + return keyHome + case '3': + return keyDelete + case '4': + return keyEnd + case '5': + return keyPageUp + case '6': + return keyPageDown + case '7': + return keyHome + case '8': + return keyEnd + } + } + + return string(rune(key.Esc)) }