From d29e02c1a30c136cbd8847a4dc4628da90566716 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Mon, 17 Nov 2014 23:00:49 +0100 Subject: 33704: keybindings, documentation, tests and minor fixes for vim style visual selection changes --- ChangeLog | 5 +++ Doc/Zsh/zle.yo | 38 +++++++++++++++-- Src/Zle/zle_bindings.c | 4 +- Src/Zle/zle_keymap.c | 22 +++++++++- Src/Zle/zle_refresh.c | 5 +-- Src/Zle/zle_vi.c | 8 +--- Test/X02zlevi.ztst | 109 +++++++++++++++++++++++++++++++++++++++++++++++++ Test/comptest | 2 +- 8 files changed, 176 insertions(+), 17 deletions(-) diff --git a/ChangeLog b/ChangeLog index e181b3852..6fd41e4a9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2014-11-17 Oliver Kiddle + * 33704: Doc/Zsh/zle.yo, Src/Zle/zle_bindings.c, + Src/Zle/zle_keymap.c, Src/Zle/zle_refresh.c, Src/Zle/zle_vi.c, + Test/X02zlevi.ztst, Test/comptest: key bindings, documentation, + tests and minor fixes for vim style visual selection changes + * 33636: Src/Zle/iwidgets.list, Src/Zle/zle_misc.c, Src/Zle/zle_move.c, Src/Zle/zle_refresh.c, Src/Zle/zle_vi.c: add support for a linewise visual selection mode diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo index 998bf4a10..aa7ff4b57 100644 --- a/Doc/Zsh/zle.yo +++ b/Doc/Zsh/zle.yo @@ -60,12 +60,14 @@ or more names. If all of a keymap's names are deleted, it disappears. findex(bindkey, use of) tt(bindkey) can be used to manipulate keymap names. -Initially, there are six keymaps: +Initially, there are eight keymaps: startsitem() sitem(tt(emacs))(EMACS emulation) sitem(tt(viins))(vi emulation - insert mode) sitem(tt(vicmd))(vi emulation - command mode) +sitem(tt(viopp))(vi emulation - operator pending) +sitem(tt(visual))(vi emulation - selection active) sitem(tt(isearch))(incremental search mode) sitem(tt(command))(read a command name) sitem(tt(.safe))(fallback keymap) @@ -122,6 +124,21 @@ in user-defined widgets with the tt(read-command) widget, described ifzman(below)\ ifnzman(in noderef(Miscellaneous) below)\ . +subsect(Local Keymaps) +cindex(local keymaps) +While for normal editing a single keymap is used exclusively, in many +modes a local keymap allows for some keys to be customised. For example, +in an incremental search mode, a binding in the tt(isearch) keymap will +override a binding in the tt(main) keymap but all keys that are not +overriden can still be used. + +If a key sequence is defined in a local keymap, it will hide a key +sequence in the global keymap that is a prefix of that sequence. An +example of this occurs with the binding of tt(iw) in tt(viopp) as this +hides the binding of tt(i) in tt(vicmd). However, a longer sequence in +the global keymap that shares the same prefix can still apply so for +example the binding of tt(^Xa) in the global keymap will be unaffected +by the binding of tt(^Xb) in the local keymap. texinode(Zle Builtins)(Zle Widgets)(Keymaps)(Zsh Line Editor) sect(Zle Builtins) @@ -817,7 +834,10 @@ cursor remains between the new tt($LBUFFER) and the old tt($RBUFFER). ) vindex(MARK) item(tt(MARK) (integer))( -Like tt(CURSOR), but for the mark. +Like tt(CURSOR), but for the mark. With vi-mode operators that wait for +a movement command to select a region of text, setting tt(MARK) allows +the selection to extend in both directions from the the initial cursor +position. ) vindex(NUMERIC) item(tt(NUMERIC) (integer))( @@ -863,7 +883,9 @@ cursor remains between the old tt($LBUFFER) and the new tt($RBUFFER). vindex(REGION_ACTIVE) item(tt(REGION_ACTIVE) (integer))( Indicates if the region is currently active. It can be assigned 0 or 1 -to deactivate and activate the region respectively; +to deactivate and activate the region respectively. A value of 2 +activates the region in line-wise mode with the highlighted text +extending for whole lines only; ifzman(see em(Character Highlighting) below)\ ifnzman(noderef(Character Highlighting)). ) @@ -2275,6 +2297,16 @@ item(tt(vi-undo-change) (unbound) (u) (unbound))( Undo the last text modification. If repeated, redo the modification. ) +tindex(visual-mode) +item(tt(visual-mode) (unbound) (v) (unbound))( +Toggle vim-style visual selection mode. If line-wise visual mode is +currently enabled then it is changed to being character-wise. +) +tindex(visual-line-mode) +item(tt(visual-line-mode) (unbound) (V) (unbound))( +Toggle vim-style line-wise visual selection mode. If character-wise +visual mode is currently enabled then it is changed to being line-wise. +) tindex(what-cursor-position) item(tt(what-cursor-position) (^X=) (unbound) (unbound))( Print the character under the cursor, its code as an octal, decimal and diff --git a/Src/Zle/zle_bindings.c b/Src/Zle/zle_bindings.c index 682691347..50a29551d 100644 --- a/Src/Zle/zle_bindings.c +++ b/Src/Zle/zle_bindings.c @@ -376,7 +376,7 @@ int vicmdbind[128] = { /* S */ z_vichangewholeline, /* T */ z_vifindprevcharskip, /* U */ z_undefinedkey, - /* V */ z_undefinedkey, + /* V */ z_visuallinemode, /* W */ z_viforwardblankword, /* X */ z_vibackwarddeletechar, /* Y */ z_viyankwholeline, @@ -408,7 +408,7 @@ int vicmdbind[128] = { /* s */ z_visubstitute, /* t */ z_vifindnextcharskip, /* u */ z_viundochange, - /* v */ z_undefinedkey, + /* v */ z_visualmode, /* w */ z_viforwardword, /* x */ z_videletechar, /* y */ z_viyank, diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c index 6a7107609..216e302d0 100644 --- a/Src/Zle/zle_keymap.c +++ b/Src/Zle/zle_keymap.c @@ -1277,8 +1277,10 @@ default_bindings(void) Keymap vmap = newkeymap(NULL, "viins"); Keymap emap = newkeymap(NULL, "emacs"); Keymap amap = newkeymap(NULL, "vicmd"); + Keymap oppmap = newkeymap(NULL, "viopp"); + Keymap vismap = newkeymap(NULL, "visual"); Keymap smap = newkeymap(NULL, ".safe"); - Keymap vimaps[2], kptr; + Keymap vimaps[2], vilmaps[2], kptr; char buf[3], *ed; int i; @@ -1332,6 +1334,22 @@ default_bindings(void) add_cursor_key(kptr, TCLEFTCURSOR, t_vibackwardchar, 'D'); add_cursor_key(kptr, TCRIGHTCURSOR, t_viforwardchar, 'C'); } + vilmaps[0] = oppmap; + vilmaps[1] = vismap; + for (i = 0; i < 2; i++) { + /* vi visual selection and operator pending local maps */ + kptr = vilmaps[i]; + add_cursor_key(kptr, TCUPCURSOR, t_upline, 'A'); + add_cursor_key(kptr, TCDOWNCURSOR, t_downline, 'B'); + bindkey(kptr, "k", refthingy(t_upline), NULL); + bindkey(kptr, "j", refthingy(t_downline), NULL); + } + /* escape in operator pending cancels the operation */ + bindkey(oppmap, "\33", refthingy(t_vicmdmode), NULL); + bindkey(vismap, "o", refthingy(t_exchangepointandmark), NULL); + bindkey(vismap, "p", refthingy(t_putreplaceselection), NULL); + bindkey(vismap, "x", refthingy(t_videlete), NULL); + bindkey(vismap, "~", refthingy(t_vioperswapcase), NULL); /* emacs mode: arrow keys */ add_cursor_key(emap, TCUPCURSOR, t_uplineorhistory, 'A'); @@ -1373,6 +1391,8 @@ default_bindings(void) linkkeymap(vmap, "viins", 0); linkkeymap(emap, "emacs", 0); linkkeymap(amap, "vicmd", 0); + linkkeymap(oppmap, "viopp", 0); + linkkeymap(vismap, "visual", 0); linkkeymap(smap, ".safe", 1); if (((ed = zgetenv("VISUAL")) && strstr(ed, "vi")) || ((ed = zgetenv("EDITOR")) && strstr(ed, "vi"))) diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index f0351ad15..467629d25 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -1037,8 +1037,6 @@ zrefresh(void) region_highlights[0].start = mark; region_highlights[0].end = zlecs; } - if (invicmdmode()) - INCPOS(region_highlights[0].end); if (region_active == 2) { int origcs = zlecs; zlecs = region_highlights[0].end; @@ -1046,7 +1044,8 @@ zrefresh(void) zlecs = region_highlights[0].start; region_highlights[0].start = findbol(); zlecs = origcs; - } + } else if (invicmdmode()) + INCPOS(region_highlights[0].end); } else { region_highlights[0].start = region_highlights[0].end = -1; } diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c index 3a4304ced..84cba7759 100644 --- a/Src/Zle/zle_vi.c +++ b/Src/Zle/zle_vi.c @@ -258,7 +258,7 @@ getvirange(int wf) pos = tmp; } - if (visual && invicmdmode()) + if (visual == 1 && invicmdmode()) INCPOS(pos); /* Was it a line-oriented move? If so, the command will have set * @@ -389,9 +389,6 @@ videletechar(char **args) startvichange(-1); - if (region_active) - return killregion(args); - /* handle negative argument */ if (n < 0) { int ret; @@ -804,9 +801,6 @@ vibackwarddeletechar(char **args) if (invicmdmode()) startvichange(-1); - if (region_active) - return killregion(args); - /* handle negative argument */ if (n < 0) { int ret; diff --git a/Test/X02zlevi.ztst b/Test/X02zlevi.ztst index 297fb9aee..94afb60eb 100644 --- a/Test/X02zlevi.ztst +++ b/Test/X02zlevi.ztst @@ -273,6 +273,115 @@ >BUFFER: one wo >CURSOR: 2 + zletest $'one two\evbcx' +0:change selection +>BUFFER: one x +>CURSOR: 5 + + zletest $'four\eOthree\eOtwo\eOone\evjjhCnew' +0:change character wise selection with C acts linewise +>BUFFER: new +>four +>CURSOR: 3 + + zletest $'x testing\ehvbx' +0:x kills selections +>BUFFER: x g +>CURSOR: 2 + + zletest $'one two\eyb0vep' +0:put over selection at start of buffer +>BUFFER: tw two +>CURSOR: 1 + + zletest $'hello\C-wbye\evhp' +0:put over selection at end of buffer +>BUFFER: bhello +>CURSOR: 5 + + zletest $'one\eotwo\eyykVp' +0:yank linewise and put over linewise selection at start of buffer +>BUFFER: two +>two +>CURSOR: 0 + + zletest $'one\eotwo\eothree\eyykVp' +0:yank linewise and put over linewise selection in middle of buffer +>BUFFER: one +>three +>three +>CURSOR: 4 + + zletest $'two\eOone\eyyjVp' +0:yank linewise and put over linewise selection at end of buffer +>BUFFER: one +>one +>CURSOR: 4 + + zletest $'one\eyhVp' +0:yank character-wise and put over linewise selection +>BUFFER: n +>CURSOR: 0 + +# vim puts a blank line above in this test + zletest $'one\eotwo\eyy0kvlp' +0:yank linewise and put over character-wise selection at start of buffer +>BUFFER: two +>e +>two +>CURSOR: 0 + + zletest $'one\eyyhvp' +0:yank linewise and put over character-wise selection in middle of buffer +>BUFFER: o +>one +>e +>CURSOR: 2 + +# vim behaviour on this one really looks like a bug + zletest $'two\eOone\eyyjvhp' +0:yank linewise and put over character-wise selection at end of buffer +>BUFFER: one +>t +>one +>CURSOR: 6 + + zletest $'abc123456789\exxxxxxxxxhv"9p0P' +0:paste last (9th) register over a selection +>BUFFER: ba9c +>CURSOR: 0 + + zletest $'one\eo\eo\eotwo\ekkVdvd' +0:delete blank line using selection +>BUFFER: one +>two +>CURSOR: 4 + + zletest $'One Two Three\e2bvw~' +0:toggle case of selection +>BUFFER: One tWO three +>CURSOR: 4 + + zletest $' ----word ---- word word---- ----\e42|daw30|daw22|daw14|daw2|daw' +0:delete all word on blanks +>BUFFER: word +>CURSOR: 0 + + zletest $' word----word word----word word \e38|daw30|daw22|daw14|daw6|daw' +0:delete all word on alphanumerics +>BUFFER: -------- +>CURSOR: 4 + + zletest $' ----word---- ----word---- ---- \e38|daw30|daw22|daw14|daw6|daw' +0:delete all word on other characters +>BUFFER: wordword +>CURSOR: 4 + + zletest $'- word word\e4|2daw' +0:delete all word with numeric argument +>BUFFER: - +>CURSOR: 0 + %clean zmodload -ui zsh/zpty diff --git a/Test/comptest b/Test/comptest index 654c0f168..c67237a9a 100644 --- a/Test/comptest +++ b/Test/comptest @@ -34,7 +34,7 @@ comptestinit () { "fpath=( $fpath )" \ "bindkey -$comptest_keymap" \ 'LISTMAX=10000000 -stty 38400 columns 80 rows 24 werase undef +stty 38400 columns 80 rows 24 werase undef tabs TERM=vt100 KEYTIMEOUT=1 setopt zle -- cgit 1.4.1