From 840f23c21d3e692661d6ee574490880d7524f761 Mon Sep 17 00:00:00 2001 From: Ghislain Rodrigues Date: Wed, 31 Mar 2021 11:08:27 +0200 Subject: keys: Implement unix-word-rubout (Ctrl-W) unix-word-rubout erases all the characters before the cursor until finding either a space or a BOL. --- AUTHORS | 1 + up.go | 22 ++++++++++++++- up_test.go | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 1a7b9a6..4209ea9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -2,5 +2,6 @@ Please keep the contents of this file sorted alphabetically. Александр Крамарев Calum MacRae +Ghislain Rodrigues Mateusz Czapliński Rohan Verma diff --git a/up.go b/up.go index 53ebc09..e83cceb 100644 --- a/up.go +++ b/up.go @@ -30,6 +30,7 @@ import ( "os" "os/exec" "sync" + "unicode" "github.com/gdamore/tcell" "github.com/gdamore/tcell/terminfo" @@ -97,7 +98,7 @@ If a plus '+' is visible in top-left corner, the internal buffer limit KEYS -- alphanumeric & symbol keys, Left, Right, Ctrl-A/E/B/F/K/Y +- alphanumeric & symbol keys, Left, Right, Ctrl-A/E/B/F/K/Y/W - navigate and edit the pipeline command - Enter - execute the pipeline command, updating the pipeline output panel - Up, Dn, PgUp, PgDn, Ctrl-Left, Ctrl-Right @@ -419,6 +420,9 @@ func (e *Editor) HandleKey(ev *tcell.EventKey) bool { case key(tcell.KeyCtrlY), ctrlKey(tcell.KeyCtrlY): e.insert(e.killspace...) + case key(tcell.KeyCtrlW), + ctrlKey(tcell.KeyCtrlW): + e.unixWordRubout() default: // Unknown key/combination, not handled return false @@ -450,6 +454,22 @@ func (e *Editor) kill() { e.value = e.value[:e.cursor] } +// unixWordRubout removes the part of the word on the left of the cursor. A word is +// delimited by whitespaces. +// The term `unix-word-rubout` comes from `readline` (see `man 3 readline`) +func (e *Editor) unixWordRubout() { + if e.cursor <= 0 { + return + } + pos := e.cursor - 1 + for pos != 0 && (unicode.IsSpace(e.value[pos]) || !unicode.IsSpace(e.value[pos-1])) { + pos-- + } + e.killspace = append(e.killspace[:0], e.value[pos:e.cursor]...) + e.value = append(e.value[:pos], e.value[e.cursor:]...) + e.cursor = pos +} + type BufView struct { // TODO: Wrap bool Y int // Y of the view in the Buf, for down/up scrolling diff --git a/up_test.go b/up_test.go index 20e8482..87f5278 100644 --- a/up_test.go +++ b/up_test.go @@ -81,3 +81,95 @@ func Test_Editor_insert(t *testing.T) { } } } + +func Test_Editor_unix_word_rubout(t *testing.T) { + tests := []struct { + comment string + e Editor + wantValue []rune + wantKillspace []rune + }{ + { + comment: "unix-word-rubout at beginning of line", + e: Editor{ + value: []rune(`abc`), + cursor: 0, + }, + wantValue: []rune(`abc`), + wantKillspace: []rune(``), + }, + { + comment: "unix-word-rubout at soft beginning of line", + e: Editor{ + value: []rune(` abc`), + cursor: 1, + }, + wantValue: []rune(`abc`), + wantKillspace: []rune(` `), + }, + { + comment: "unix-word-rubout until soft beginning of line", + e: Editor{ + value: []rune(` abc`), + cursor: 2, + }, + wantValue: []rune(` bc`), + wantKillspace: []rune(`a`), + }, + { + comment: "unix-word-rubout until beginning of line", + e: Editor{ + value: []rune(`abc`), + cursor: 2, + }, + wantValue: []rune(`c`), + wantKillspace: []rune(`ab`), + }, + { + comment: "unix-word-rubout in middle of line", + e: Editor{ + value: []rune(`lorem ipsum dolor`), + cursor: 11, + }, + wantValue: []rune(`lorem dolor`), + wantKillspace: []rune(`ipsum`), + }, + { + comment: "unix-word-rubout cursor at beginning of word", + e: Editor{ + value: []rune(`lorem ipsum dolor`), + cursor: 12, + }, + wantValue: []rune(`lorem dolor`), + wantKillspace: []rune(`ipsum `), + }, + { + comment: "unix-word-rubout cursor between multiple spaces", + e: Editor{ + value: []rune(`a b c`), + cursor: 5, + }, + wantValue: []rune(`a c`), + wantKillspace: []rune(`b `), + }, + { + comment: "unix-word-rubout tab as space char (although is it a realistic case in the context of a command line instruction?)", + e: Editor{ + value: []rune(`a b c`), + cursor: 5, + }, + wantValue: []rune(`a c`), + wantKillspace: []rune(`b `), + }, + } + + for _, tt := range tests { + tt.e.unixWordRubout() + if string(tt.e.value) != string(tt.wantValue) { + t.Errorf("%q: bad value\nwant: %q\nhave: %q", tt.comment, tt.wantValue, tt.e.value) + } + if string(tt.e.killspace) != string(tt.wantKillspace) { + t.Errorf("%q: bad value in killspace\nwant: %q\nhave: %q", tt.comment, tt.wantKillspace, tt.e.value) + } + } +} -- cgit v1.2.3