Skip to content

Commit

Permalink
keys: Implement unix-word-rubout (Ctrl-W)
Browse files Browse the repository at this point in the history
unix-word-rubout erases all the characters before the cursor
until finding either a space or a BOL.
  • Loading branch information
padawin committed Apr 12, 2021
1 parent 88516fd commit 6f800bf
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 1 deletion.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ Please keep the contents of this file sorted alphabetically.

Александр Крамарев <[email protected]>
Calum MacRae <[email protected]>
Ghislain Rodrigues <[email protected]>
Mateusz Czapliński <[email protected]>
Rohan Verma <[email protected]>
22 changes: 21 additions & 1 deletion up.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"os"
"os/exec"
"sync"
"unicode"

"github.com/gdamore/tcell"
"github.com/gdamore/tcell/terminfo"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
92 changes: 92 additions & 0 deletions up_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

0 comments on commit 6f800bf

Please sign in to comment.