package editor import ( "timshome.page/gilo/key" "timshome.page/gilo/terminal" ) /** * 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 { e.SetStatusMessage("WARNING!!! File has unsaved changes. Press Ctrl-Q %d more tiems to quite.", e.quitTimes) e.quitTimes -= 1 return true } // Clean up on exit terminal.Write(terminal.ClearScreen + terminal.ResetCursor) return false case key.Ctrl('s'): e.Save() case key.Enter: case key.Backspace, key.Ctrl('h'): e.delChar() case key.Esc, key.Ctrl('l'): // Modifier keys that return ANSI escape sequences str := parseEscapeSequence() // 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() { row = nil } else { row = e.document.rows[e.cursor.y] } switch key { case keyLeft: if e.cursor.x != 0 { e.cursor.x -= 1 } // Move from beginning of current row to end of previous row if e.cursor.y > 0 { e.cursor.y -= 1 e.cursor.x = e.document.rows[e.cursor.y].size() } case keyRight: if row != nil && e.cursor.x < row.size() { e.cursor.x += 1 } // Move from end of current line to beginning of next line if row != nil && e.cursor.x == row.size() && e.cursor.y < e.document.rowCount()-1 { e.cursor.y += 1 e.cursor.x = 0 } case keyUp: if e.cursor.y != 0 { e.cursor.y -= 1 } case keyDown: if e.cursor.y < e.document.rowCount() { e.cursor.y += 1 } case keyPageUp: if e.cursor.y > e.screen.Rows { e.cursor.y -= e.screen.Rows } else { e.cursor.y = 0 } case keyPageDown: if e.cursor.y+e.screen.Rows > e.document.rowCount() { e.cursor.y += e.screen.Rows } else { e.cursor.y = e.document.rowCount() - 1 } case keyHome: e.cursor.x = 0 case keyEnd: if row != nil { e.cursor.x = row.size() } } if e.cursor.y < e.document.rowCount() { row = e.document.rows[e.cursor.y] rowLen := row.size() // Snap to the end of a shorter line from a longer one if e.cursor.x > rowLen { e.cursor.x = rowLen } } } // Convert the raw ANSI escape sequences to the type of key input func parseEscapeSequence() string { // If we aren't starting an escape sequence, // return the character startChar, _ := terminal.ReadKey() if startChar != '[' && startChar != 'O' { return string(startChar) } // 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 string(rune(key.Esc)) } runes[i] = ch if i == 0 && runes[0] >= 'A' && runes[0] <= 'Z' { return escSeqToKey([]rune{startChar, runes[0]}) } // \e[*~ if i == 1 && startChar == '[' && runes[1] == '~' { return escSeqToKey([]rune{startChar, runes[0], runes[1]}) } } 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)) }