diff --git a/ssh/terminal/terminal.go b/ssh/terminal/terminal.go index 86853d6..18ac2ba 100644 --- a/ssh/terminal/terminal.go +++ b/ssh/terminal/terminal.go @@ -108,6 +108,7 @@ func NewTerminal(c io.ReadWriter, prompt string) *Terminal { const ( keyCtrlD = 4 + keyCtrlU = 21 keyEnter = '\r' keyEscape = 27 keyBackspace = 127 @@ -122,6 +123,7 @@ const ( keyEnd keyDeleteWord keyDeleteLine + keyClearScreen ) // bytesToKey tries to parse a key sequence from b. If successful, it returns @@ -140,6 +142,8 @@ func bytesToKey(b []byte) (rune, []byte) { return keyBackspace, b[1:] case 11: // ^K return keyDeleteLine, b[1:] + case 12: // ^L + return keyClearScreen, b[1:] case 23: // ^W return keyDeleteWord, b[1:] } @@ -453,6 +457,23 @@ func (t *Terminal) handleKey(key rune) (line string, ok bool) { } t.line = t.line[:t.pos] t.moveCursorToPos(t.pos) + case keyCtrlD: + // Erase the character under the current position. + // The EOF case when the line is empty is handled in + // readLine(). + if t.pos < len(t.line) { + t.pos++ + t.eraseNPreviousChars(1) + } + case keyCtrlU: + t.eraseNPreviousChars(t.pos) + case keyClearScreen: + // Erases the screen and moves the cursor to the home position. + t.queue([]rune("\x1b[2J\x1b[H")) + t.queue([]rune(t.prompt)) + t.cursorX = len(t.prompt) + t.cursorY = 0 + t.setLine(t.line, t.pos) default: if t.AutoCompleteCallback != nil { prefix := string(t.line[:t.pos]) @@ -604,7 +625,9 @@ func (t *Terminal) readLine() (line string, err error) { break } if key == keyCtrlD { - return "", io.EOF + if len(t.line) == 0 { + return "", io.EOF + } } line, lineOk = t.handleKey(key) } diff --git a/ssh/terminal/terminal_test.go b/ssh/terminal/terminal_test.go index 641576c..fb42d76 100644 --- a/ssh/terminal/terminal_test.go +++ b/ssh/terminal/terminal_test.go @@ -163,6 +163,22 @@ var keyPressTests = []struct { line: "£", throwAwayLines: 1, }, + { + // Ctrl-D at the end of the line should be ignored. + in: "a\004\r", + line: "a", + }, + { + // a, b, left, Ctrl-D should erase the b. + in: "ab\x1b[D\004\r", + line: "a", + }, + { + // a, b, c, d, left, left, ^U should erase to the beginning of + // the line. + in: "abcd\x1b[D\x1b[D\025\r", + line: "cd", + }, } func TestKeyPresses(t *testing.T) {