1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-01-16 22:10:54 +01:00

39986, 39989: improve handling of vi-repeat-change

Save previous vi change and throw away a new change that fails.
Add zle -f vichange to allow shell widget to be a single change.
Fix repeat of command where numeric arguments were multiplied.
This commit is contained in:
Oliver Kiddle 2016-11-20 23:54:45 +01:00
parent fe67ccacf1
commit cb5f100bd3
11 changed files with 148 additions and 88 deletions

View file

@ -1,5 +1,12 @@
2016-11-20 Oliver Kiddle <opk@zsh.org> 2016-11-20 Oliver Kiddle <opk@zsh.org>
* 39986, 39989: Src/Zle/zle.h, Src/Zle/zle_keymap.c,
Src/Zle/zle_main.c, Src/Zle/zle_misc.c, Src/Zle/zle_thingy.c,
Src/Zle/zle_utils.c, Src/Zle/zle_vi.c, Test/X02zlevi.ztst,
Doc/Zsh/zle.yo, Functions/Zle/vi-pipe: make vi-repeat-change
work better with shell based widgets and save old change
when recording a new change in case the new one fails
* 39974: Completion/Unix/Command/_ssh: complete shared * 39974: Completion/Unix/Command/_ssh: complete shared
libraries for -e and -s options to ssh-add libraries for -e and -s options to ssh-add

View file

@ -479,6 +479,13 @@ tt(kill) for indicating that text has been killed into the cutbuffer.
When repeatedly invoking a kill widget, text is appended to the cutbuffer When repeatedly invoking a kill widget, text is appended to the cutbuffer
instead of replacing it, but when wrapping such widgets, it is necessary instead of replacing it, but when wrapping such widgets, it is necessary
to call `tt(zle -f kill)' to retain this effect. to call `tt(zle -f kill)' to retain this effect.
tt(vichange) for indicating that the widget represents a vi change that
can be repeated as a whole with `tt(vi-repeat-change)'. The flag should be set
early in the function before inspecting the value of tt(NUMERIC) or invoking
other widgets. This has no effect for a widget invoked from insert mode. If
insert mode is active when the widget finishes, the change extends until next
returning to command mode.
) )
cindex(completion widgets, creating) cindex(completion widgets, creating)
item(tt(-C) var(widget) var(completion-widget) var(function))( item(tt(-C) var(widget) var(completion-widget) var(function))(

View file

@ -12,6 +12,9 @@ setopt localoptions noksharrays
autoload -Uz read-from-minibuffer autoload -Uz read-from-minibuffer
local _save_cut="$CUTBUFFER" REPLY local _save_cut="$CUTBUFFER" REPLY
# mark this widget as a vi change so it can be repeated as a whole
zle -f vichange
# force movement to default to line mode # force movement to default to line mode
(( REGION_ACTIVE )) || zle -U V (( REGION_ACTIVE )) || zle -U V
# Use the standard vi-change to accept a vi motion. # Use the standard vi-change to accept a vi motion.

View file

@ -284,6 +284,20 @@ struct change {
#define CH_NEXT (1<<0) /* next structure is also part of this change */ #define CH_NEXT (1<<0) /* next structure is also part of this change */
#define CH_PREV (1<<1) /* previous structure is also part of this change */ #define CH_PREV (1<<1) /* previous structure is also part of this change */
/* vi change handling for vi-repeat-change */
/*
* Examination of the code suggests vichgbuf is consistently tied
* to raw byte input, so it is left as a character array rather
* than turned into wide characters. In particular, when we replay
* it we use ungetbytes().
*/
struct vichange {
struct modifier mod; /* value of zmod associated with vi change */
char *buf; /* bytes for keys that make up the vi command */
int bufsz, bufptr; /* allocated and in use sizes of buf */
};
/* known thingies */ /* known thingies */
#define Th(X) (&thingies[X]) #define Th(X) (&thingies[X])

View file

@ -1634,7 +1634,7 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp)
unmetafy(keybuf + lastlen, &keybuflen); unmetafy(keybuf + lastlen, &keybuflen);
ungetbytes(keybuf+lastlen, keybuflen); ungetbytes(keybuf+lastlen, keybuflen);
if(vichgflag) if(vichgflag)
vichgbufptr -= keybuflen; curvichg.bufptr -= keybuflen;
keybuf[keybuflen = lastlen] = 0; keybuf[keybuflen = lastlen] = 0;
} }
*funcp = func; *funcp = func;

View file

@ -924,13 +924,13 @@ getbyte(long do_keytmout, int *timeout)
ret = STOUC(cc); ret = STOUC(cc);
} }
/* /*
* vichgbuf is raw bytes, not wide characters, so is dealt * curvichg.buf is raw bytes, not wide characters, so is dealt
* with here. * with here.
*/ */
if (vichgflag) { if (vichgflag) {
if (vichgbufptr == vichgbufsz) if (curvichg.bufptr == curvichg.bufsz)
vichgbuf = realloc(vichgbuf, vichgbufsz *= 2); curvichg.buf = realloc(curvichg.buf, curvichg.bufsz *= 2);
vichgbuf[vichgbufptr++] = ret; curvichg.buf[curvichg.bufptr++] = ret;
} }
errno = old_errno; errno = old_errno;
return lastchar = ret; return lastchar = ret;
@ -1260,6 +1260,7 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
*zleline = ZWC('\0'); *zleline = ZWC('\0');
virangeflag = lastcmd = done = zlecs = zlell = mark = yankb = yanke = 0; virangeflag = lastcmd = done = zlecs = zlell = mark = yankb = yanke = 0;
vichgflag = 0; vichgflag = 0;
viinrepeat = 0;
viinsbegin = 0; viinsbegin = 0;
statusline = NULL; statusline = NULL;
selectkeymap("main", 1); selectkeymap("main", 1);
@ -1389,6 +1390,8 @@ int
execzlefunc(Thingy func, char **args, int set_bindk) execzlefunc(Thingy func, char **args, int set_bindk)
{ {
int r = 0, ret = 0, remetafy = 0; int r = 0, ret = 0, remetafy = 0;
int nestedvichg = vichgflag;
int isrepeat = (viinrepeat == 3);
Widget w; Widget w;
Thingy save_bindk = bindk; Thingy save_bindk = bindk;
@ -1398,6 +1401,8 @@ execzlefunc(Thingy func, char **args, int set_bindk)
unmetafy_line(); unmetafy_line();
remetafy = 1; remetafy = 1;
} }
if (isrepeat)
viinrepeat = 2;
if (func->flags & DISABLED) { if (func->flags & DISABLED) {
/* this thingy is not the name of a widget */ /* this thingy is not the name of a widget */
@ -1523,6 +1528,25 @@ execzlefunc(Thingy func, char **args, int set_bindk)
CCRIGHT(); CCRIGHT();
if (remetafy) if (remetafy)
metafy_line(); metafy_line();
/* if this widget constituted the vi change, end it */
if (vichgflag == 2 && !nestedvichg) {
if (invicmdmode()) {
if (ret) {
free(curvichg.buf);
} else {
if (lastvichg.buf)
free(lastvichg.buf);
lastvichg = curvichg;
}
vichgflag = 0;
curvichg.buf = NULL;
} else
vichgflag = 1; /* vi change continues while in insert mode */
}
if (isrepeat)
viinrepeat = !invicmdmode();
return ret; return ret;
} }
@ -2230,7 +2254,7 @@ finish_(UNUSED(Module m))
cleanup_keymaps(); cleanup_keymaps();
deletehashtable(thingytab); deletehashtable(thingytab);
zfree(vichgbuf, vichgbufsz); zfree(lastvichg.buf, lastvichg.bufsz);
zfree(kungetbuf, kungetsz); zfree(kungetbuf, kungetsz);
free_isrch_spots(); free_isrch_spots();
if (rdstrs) if (rdstrs)

View file

@ -609,8 +609,10 @@ viputbefore(UNUSED(char **args))
int n = zmult; int n = zmult;
startvichange(-1); startvichange(-1);
if (n < 0 || zmod.flags & MOD_NULL) if (n < 0)
return 1; return 1;
if (zmod.flags & MOD_NULL)
return 0;
if (zmod.flags & MOD_VIBUF) if (zmod.flags & MOD_VIBUF)
kctbuf = &vibuf[zmod.vibuf]; kctbuf = &vibuf[zmod.vibuf];
else else
@ -630,8 +632,10 @@ viputafter(UNUSED(char **args))
int n = zmult; int n = zmult;
startvichange(-1); startvichange(-1);
if (n < 0 || zmod.flags & MOD_NULL) if (n < 0)
return 1; return 1;
if (zmod.flags & MOD_NULL)
return 0;
if (zmod.flags & MOD_VIBUF) if (zmod.flags & MOD_VIBUF)
kctbuf = &vibuf[zmod.vibuf]; kctbuf = &vibuf[zmod.vibuf];
else else

View file

@ -678,7 +678,16 @@ bin_zle_flags(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
else if (!strcmp(*flag, "keepsuffix")) else if (!strcmp(*flag, "keepsuffix"))
w->flags |= ZLE_KEEPSUFFIX; w->flags |= ZLE_KEEPSUFFIX;
*/ */
else { else if (!strcmp(*flag, "vichange")) {
if (invicmdmode()) {
startvichange(-1);
if (zmod.flags & (MOD_MULT|MOD_TMULT)) {
Param pm = (Param) paramtab->getnode(paramtab, "NUMERIC");
if (pm && pm->node.flags & PM_SPECIAL)
pm->node.flags &= ~PM_UNSET;
}
}
} else {
zwarnnam(name, "invalid flag `%s' given to zle -f", *flag); zwarnnam(name, "invalid flag `%s' given to zle -f", *flag);
ret = 1; ret = 1;
} }

View file

@ -1704,6 +1704,7 @@ mergeundo(void)
current->flags |= CH_PREV; current->flags |= CH_PREV;
current->prev->flags |= CH_NEXT; current->prev->flags |= CH_NEXT;
} }
vistartchange = -1;
} }
/* /*

View file

@ -45,46 +45,41 @@ int wordflag;
/**/ /**/
int vilinerange; int vilinerange;
/* last vi change buffer, for vi change repetition */
/* /*
* vichgbufsz: Allocated size of vichgbuf. * lastvichg: last vi change buffer, for vi change repetition
* vichgbufptr: Length in use. * curvichg: current incomplete vi change
* vichgflag: true whilst inputting a vi normal mode; causes it to be
* accumulated in vichgbuf, incrementing vichgbufptr.
*/ */
/**/ /**/
int vichgbufsz, vichgbufptr, vichgflag; struct vichange lastvichg, curvichg;
/* /*
* The bytes that make up the current vi command. See vichgbuf* above. * true whilst a vi change is active causing keys to be
* * accumulated in curvichg.buf
* Examination of the code suggests vichgbuf is consistently tied * first set to 2 and when the initial widget finishes, reduced to 1 if
* to raw byte input, so it is left as a character array rather * in insert mode implying that the change continues until returning to
* than turned into wide characters. In particular, when we replay * normal mode
* it we use ungetbytes().
*/ */
/**/ /**/
char *vichgbuf; int vichgflag;
/*
* analogous to vichgflag for a repeated change with the value following
* a similar pattern (is 3 until first repeated widget starts)
*/
/**/
int viinrepeat;
/* point where vi insert mode was last entered */ /* point where vi insert mode was last entered */
/**/ /**/
int viinsbegin; int viinsbegin;
/* value of zmod associated with vi change */
static struct modifier lastmod;
/*
* inrepeat: current widget is the vi change being repeated
* vichgrepeat: nested widget call within a repeat
*/
static int inrepeat, vichgrepeat;
/** /**
* im: >= 0: is an insertmode * im: >= 0: is an insertmode
* -1: skip setting insert mode * -1: skip setting insert/overwrite mode
* -2: entering viins at start of editing from clean --- don't use * -2: entering viins at start of editing from clean --- don't use
* inrepeat or keybuf, synthesise an entry to insert mode. * inrepeat or keybuf, synthesise an entry to insert mode.
* Note that zmult is updated so this should be called before zmult is used. * Note that zmult is updated so this should be called before zmult is used.
@ -94,30 +89,27 @@ static int inrepeat, vichgrepeat;
void void
startvichange(int im) startvichange(int im)
{ {
if (im != -1) { if (im > -1)
vichgflag = 1; insmode = im;
if (im > -1) if (viinrepeat && im != -2) {
insmode = im; zmod = lastvichg.mod;
} vichgflag = 0;
if (inrepeat && im != -2) { } else if (!vichgflag) {
zmod = lastmod; curvichg.mod = zmod;
inrepeat = vichgflag = 0; if (curvichg.buf)
vichgrepeat = 1; free(curvichg.buf);
} else { curvichg.buf = (char *)zalloc(curvichg.bufsz = 16 + keybuflen);
lastmod = zmod;
if (vichgbuf)
free(vichgbuf);
vichgbuf = (char *)zalloc(vichgbufsz = 16 + keybuflen);
if (im == -2) { if (im == -2) {
vichgbuf[0] = vichgflag = 1;
curvichg.buf[0] =
zlell ? (insmode ? (zlecs < zlell ? 'i' : 'a') : 'R') : 'o'; zlell ? (insmode ? (zlecs < zlell ? 'i' : 'a') : 'R') : 'o';
vichgbuf[1] = '\0'; curvichg.buf[1] = '\0';
vichgbufptr = 1; curvichg.bufptr = 1;
} else { } else {
strcpy(vichgbuf, keybuf); vichgflag = 2;
unmetafy(vichgbuf, &vichgbufptr); strcpy(curvichg.buf, keybuf);
unmetafy(curvichg.buf, &curvichg.bufptr);
} }
vichgrepeat = 0;
} }
} }
@ -226,10 +218,13 @@ getvirange(int wf)
*/ */
if ((k2 == bindk) ? dovilinerange() : execzlefunc(k2, zlenoargs, 1)) if ((k2 == bindk) ? dovilinerange() : execzlefunc(k2, zlenoargs, 1))
ret = -1; ret = -1;
if(vichgrepeat) if (viinrepeat)
zmult = mult1; zmult = mult1;
else else {
zmult = mult1 * zmod.tmult; zmult = mult1 * zmod.tmult;
if (vichgflag == 2)
curvichg.mod.mult = zmult;
}
} while(prefixflag && !ret); } while(prefixflag && !ret);
wordflag = 0; wordflag = 0;
selectlocalmap(NULL); selectlocalmap(NULL);
@ -401,7 +396,6 @@ videlete(UNUSED(char **args))
vifirstnonblank(zlenoargs); vifirstnonblank(zlenoargs);
} }
} }
vichgflag = 0;
return ret; return ret;
} }
@ -518,7 +512,6 @@ viyank(UNUSED(char **args))
cut(zlecs, c2 - zlecs, CUT_YANK); cut(zlecs, c2 - zlecs, CUT_YANK);
ret = 0; ret = 0;
} }
vichgflag = 0;
/* cursor now at the start of the range yanked. For line mode /* cursor now at the start of the range yanked. For line mode
* restore the column position */ * restore the column position */
if (vilinerange && lastcol != -1) { if (vilinerange && lastcol != -1) {
@ -593,8 +586,7 @@ vireplace(UNUSED(char **args))
* a change, we always read the argument normally, even if the count * * a change, we always read the argument normally, even if the count *
* was bad. When recording a change for repeating, and a bad count is * * was bad. When recording a change for repeating, and a bad count is *
* given, we squash the repeat buffer to avoid repeating the partial * * given, we squash the repeat buffer to avoid repeating the partial *
* command; we've lost the previous change, but that can't be avoided * * command. */
* without a rewrite of the repeat code. */
/**/ /**/
int int
@ -644,18 +636,12 @@ vireplacechars(UNUSED(char **args))
/* check argument range */ /* check argument range */
if (n < 1 || fail) { if (n < 1 || fail) {
if(vichgrepeat) if (viinrepeat)
vigetkey(); vigetkey();
if(vichgflag) {
free(vichgbuf);
vichgbuf = NULL;
vichgflag = 0;
}
return 1; return 1;
} }
/* get key */ /* get key */
if((ch = vigetkey()) == ZLEEOF) { if((ch = vigetkey()) == ZLEEOF) {
vichgflag = 0;
return 1; return 1;
} }
/* do change */ /* do change */
@ -682,7 +668,6 @@ vireplacechars(UNUSED(char **args))
zleline[zlecs++] = ch; zleline[zlecs++] = ch;
zlecs--; zlecs--;
} }
vichgflag = 0;
return 0; return 0;
} }
@ -693,7 +678,16 @@ vicmdmode(UNUSED(char **args))
if (invicmdmode() || selectkeymap("vicmd", 0)) if (invicmdmode() || selectkeymap("vicmd", 0))
return 1; return 1;
mergeundo(); mergeundo();
vichgflag = 0; insmode = unset(OVERSTRIKE);
if (vichgflag == 1) {
vichgflag = 0;
if (lastvichg.buf)
free(lastvichg.buf);
lastvichg = curvichg;
curvichg.buf = NULL;
}
if (viinrepeat == 1)
viinrepeat = 0;
if (zlecs != findbol()) if (zlecs != findbol())
DECCS(); DECCS();
return 0; return 0;
@ -748,7 +742,6 @@ vioperswapcase(UNUSED(char **args))
vifirstnonblank(); vifirstnonblank();
#endif #endif
} }
vichgflag = 0;
return ret; return ret;
} }
@ -771,7 +764,6 @@ viupcase(UNUSED(char **args))
zlecs = oldcs; zlecs = oldcs;
ret = 0; ret = 0;
} }
vichgflag = 0;
return ret; return ret;
} }
@ -794,7 +786,6 @@ vidowncase(UNUSED(char **args))
zlecs = oldcs; zlecs = oldcs;
ret = 0; ret = 0;
} }
vichgflag = 0;
return ret; return ret;
} }
@ -803,23 +794,23 @@ int
virepeatchange(UNUSED(char **args)) virepeatchange(UNUSED(char **args))
{ {
/* make sure we have a change to repeat */ /* make sure we have a change to repeat */
if (!vichgbuf || vichgflag || virangeflag) if (!lastvichg.buf || vichgflag || virangeflag)
return 1; return 1;
/* restore or update the saved count and buffer */ /* restore or update the saved count and buffer */
if (zmod.flags & MOD_MULT) { if (zmod.flags & MOD_MULT) {
lastmod.mult = zmod.mult; lastvichg.mod.mult = zmod.mult;
lastmod.flags |= MOD_MULT; lastvichg.mod.flags |= MOD_MULT;
} }
if (zmod.flags & MOD_VIBUF) { if (zmod.flags & MOD_VIBUF) {
lastmod.vibuf = zmod.vibuf; lastvichg.mod.vibuf = zmod.vibuf;
lastmod.flags = (lastmod.flags & ~MOD_VIAPP) | lastvichg.mod.flags = (lastvichg.mod.flags & ~MOD_VIAPP) |
MOD_VIBUF | (zmod.flags & MOD_VIAPP); MOD_VIBUF | (zmod.flags & MOD_VIAPP);
} else if (lastmod.flags & MOD_VIBUF && } else if (lastvichg.mod.flags & MOD_VIBUF &&
lastmod.vibuf >= 27 && lastmod.vibuf <= 34) lastvichg.mod.vibuf >= 27 && lastvichg.mod.vibuf <= 34)
lastmod.vibuf++; /* for "1 to "8 advance to next buffer */ lastvichg.mod.vibuf++; /* for "1 to "8 advance to next buffer */
/* repeat the command */ /* repeat the command */
inrepeat = 1; viinrepeat = 3;
ungetbytes(vichgbuf, vichgbufptr); ungetbytes(lastvichg.buf, lastvichg.bufptr);
return 0; return 0;
} }
@ -835,10 +826,8 @@ viindent(UNUSED(char **args))
region_active = 2; region_active = 2;
/* get the range */ /* get the range */
if ((c2 = getvirange(0)) == -1) { if ((c2 = getvirange(0)) == -1) {
vichgflag = 0;
return 1; return 1;
} }
vichgflag = 0;
/* must be a line range */ /* must be a line range */
if (!vilinerange) { if (!vilinerange) {
zlecs = oldcs; zlecs = oldcs;
@ -873,10 +862,8 @@ viunindent(UNUSED(char **args))
region_active = 2; region_active = 2;
/* get the range */ /* get the range */
if ((c2 = getvirange(0)) == -1) { if ((c2 = getvirange(0)) == -1) {
vichgflag = 0;
return 1; return 1;
} }
vichgflag = 0;
/* must be a line range */ /* must be a line range */
if (!vilinerange) { if (!vilinerange) {
zlecs = oldcs; zlecs = oldcs;

View file

@ -140,6 +140,12 @@
>BUFFER: xxe xxx xxxee >BUFFER: xxe xxx xxxee
>CURSOR: 10 >CURSOR: 10
zletest $'one two three four five six seven eight\e.03d2wk.1.'
0:numeric args to both action and movement are multiplied (and saved for any repeat)
>BUFFER: eight
>seven eight
>CURSOR: 0
zletest $'yankee doodle\ebhDyy0"1P' zletest $'yankee doodle\ebhDyy0"1P'
0:paste register 1 to get last deletion 0:paste register 1 to get last deletion
>BUFFER: doodleyankee >BUFFER: doodleyankee
@ -262,10 +268,8 @@
print -u $ZTST_fd 'This test may hang the shell when it fails...' print -u $ZTST_fd 'This test may hang the shell when it fails...'
zletest $'worm\erdhd..' zletest $'worm\erdhd..'
0:use of vi-repeat as the motion and repeat after a failed change 0:use of vi-repeat as the motion and repeat after a failed change
>BUFFER: word >BUFFER: wodd
>CURSOR: 2 >CURSOR: 2
F:For vi compatibility, "." should repeat the "rd" change after "d."
F:Update result to ">BUFFER: wodd" if compatibility is repaired.
zpty_run 'bindkey "^_" undo' zpty_run 'bindkey "^_" undo'
zletest $'undoc\037e' zletest $'undoc\037e'