diff options
Diffstat (limited to 'Src/Zle/zle_vi.c')
-rw-r--r-- | Src/Zle/zle_vi.c | 925 |
1 files changed, 925 insertions, 0 deletions
diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c new file mode 100644 index 000000000..a599d8091 --- /dev/null +++ b/Src/Zle/zle_vi.c @@ -0,0 +1,925 @@ +/* + * zle_vi.c - vi-specific functions + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zle.mdh" +#include "zle_vi.pro" + +/* != 0 if we're getting a vi range */ + +/**/ +int virangeflag; + +/* kludge to get cw and dw to work right */ + +/**/ +int wordflag; + +/* != 0 if we're killing lines into a buffer, vi-style */ + +/**/ +int vilinerange; + +/* last vi change buffer, for vi change repetition */ + +/**/ +int vichgbufsz, vichgbufptr, vichgflag; + +/**/ +char *vichgbuf; + +/* point where vi insert mode was last entered */ + +/**/ +int viinsbegin; + +static struct modifier lastmod; +static int inrepeat, vichgrepeat; + +/**/ +static void +startvichange(int im) +{ + if (im != -1) { + insmode = im; + vichgflag = 1; + } + if (inrepeat) { + zmod = lastmod; + inrepeat = vichgflag = 0; + vichgrepeat = 1; + } else { + lastmod = zmod; + if (vichgbuf) + free(vichgbuf); + vichgbuf = (char *)zalloc(vichgbufsz = 16); + vichgbuf[0] = c; + vichgbufptr = 1; + vichgrepeat = 0; + } +} + +/**/ +static void +startvitext(int im) +{ + startvichange(im); + selectkeymap("main", 1); + undoing = 0; + viinsbegin = cs; +} + +/**/ +int +vigetkey(void) +{ + Keymap mn = openkeymap("main"); + char m[3], *str; + Thingy cmd; + + if((c = getkey(0)) == EOF) { + feep(); + return -1; + } + + m[0] = c; + metafy(m, 1, META_NOALLOC); + if(mn) + cmd = keybind(mn, m, &str); + else + cmd = t_undefinedkey; + + if (!cmd || cmd == Th(z_sendbreak)) { + feep(); + return -1; + } else if (cmd == Th(z_quotedinsert)) { + if ((c = getkey(0)) == EOF) { + feep(); + return -1; + } + } else if(cmd == Th(z_viquotedinsert)) { + char sav = line[cs]; + + line[cs] = '^'; + refresh(); + c = getkey(0); + line[cs] = sav; + if(c == EOF) { + feep(); + return -1; + } + } else if (cmd == Th(z_vicmdmode)) + return -1; + return c; +} + +/**/ +static int +getvirange(int wf) +{ + int pos = cs; + int mult1 = zmult, hist1 = histline; + Thingy k2; + + virangeflag = 1; + wordflag = wf; + /* Now we need to execute the movement command, to see where it * + * actually goes. virangeflag here indicates to the movement * + * function that it should place the cursor at the end of the * + * range, rather than where the cursor would actually go if it * + * were executed normally. This makes a difference to some * + * commands, but not all. For example, if searching forward * + * for a character, under normal circumstances the cursor lands * + * on the character. For a range, the range must include the * + * character, so the cursor gets placed after the character if * + * virangeflag is set. vi-match-bracket needs to change the * + * value of virangeflag under some circumstances, meaning that * + * we need to change the *starting* position. */ + zmod.flags &= ~MOD_TMULT; + do { + vilinerange = 0; + prefixflag = 0; + if (!(k2 = getkeycmd()) || (k2->flags & DISABLED) || + k2 == Th(z_sendbreak)) { + wordflag = 0; + virangeflag = 0; + feep(); + return -1; + } + if(k2 == bindk) + /* The command key is repeated: a number of lines is used. */ + dovilinerange(); + else + execzlefunc(k2); + if(vichgrepeat) + zmult = mult1; + else + zmult = mult1 * zmod.tmult; + } while(prefixflag); + wordflag = 0; + virangeflag = 0; + + /* It is an error to use a non-movement command to delimit the * + * range. We here reject the case where the command modified * + * the line, or selected a different history line. */ + if(histline != hist1 || ll != lastll || memcmp(line, lastline, ll)) { + histline = hist1; + memcpy(line, lastline, ll = lastll); + cs = pos; + feep(); + return -1; + } + + /* Can't handle an empty file. Also, if the movement command * + * failed, or didn't move, it is an error. */ + if (!ll || (cs == pos && virangeflag != 2)) { + feep(); + return -1; + } + + /* vi-match-bracket changes the value of virangeflag when * + * moving to the opening bracket, meaning that we need to * + * change the *starting* position. */ + if(virangeflag == -1) + pos++; + + /* Get the range the right way round. cs is placed at the * + * start of the range, and pos (the return value of this * + * function) is the end. */ + if (cs > pos) { + int tmp = cs; + cs = pos; + pos = tmp; + } + + /* Was it a line-oriented move? If so, the command will have set * + * the vilinerange flag. In this case, entire lines are taken, * + * rather than just the sequence of characters delimited by pos * + * and cs. The terminating newline is left out of the range, * + * which the real command must deal with appropriately. At this * + * point we just need to make the range encompass entire lines. */ + if(vilinerange) { + int newcs = findbol(); + cs = pos; + pos = findeol(); + cs = newcs; + } + return pos; +} + +/**/ +static void +dovilinerange(void) +{ + int pos = cs, n = zmult; + + /* A number of lines is taken as the range. The current line * + * is included. If the repeat count is positive the lines go * + * downward, otherwise upward. The repeat count gives the * + * number of lines. */ + vilinerange = 1; + if (!n) { + feep(); + return; + } + if (n > 0) { + while(n-- && cs <= ll) + cs = findeol() + 1; + if (n != -1) { + cs = pos; + feep(); + return; + } + cs--; + } else { + while(n++ && cs >= 0) + cs = findbol() - 1; + if (n != 1) { + cs = pos; + feep(); + return; + } + cs++; + } + virangeflag = 2; +} + +/**/ +void +viaddnext(void) +{ + if (cs != findeol()) + cs++; + startvitext(1); +} + +/**/ +void +viaddeol(void) +{ + cs = findeol(); + startvitext(1); +} + +/**/ +void +viinsert(void) +{ + startvitext(1); +} + +/**/ +void +viinsertbol(void) +{ + vifirstnonblank(); + startvitext(1); +} + +/**/ +void +videlete(void) +{ + int c2; + + startvichange(1); + if ((c2 = getvirange(0)) != -1) { + forekill(c2 - cs, 0); + if (vilinerange && ll) { + if (cs == ll) + cs--; + foredel(1); + vifirstnonblank(); + } + } + vichgflag = 0; +} + +/**/ +void +videletechar(void) +{ + int n = zmult; + + startvichange(-1); + /* handle negative argument */ + if (n < 0) { + zmult = -n; + vibackwarddeletechar(); + zmult = n; + return; + } + /* it is an error to be on the end of line */ + if (cs == ll || line[cs] == '\n') { + feep(); + return; + } + /* Put argument into the acceptable range -- it is not an error to * + * specify a greater count than the number of available characters. */ + if (n > findeol() - cs) + n = findeol() - cs; + /* do the deletion */ + forekill(n, 0); +} + +/**/ +void +vichange(void) +{ + int c2; + + startvichange(1); + if ((c2 = getvirange(1)) != -1) { + forekill(c2 - cs, 0); + selectkeymap("main", 1); + viinsbegin = cs; + undoing = 0; + } +} + +/**/ +void +visubstitute(void) +{ + int n = zmult; + + startvichange(1); + if (n < 0) { + feep(); + return; + } + /* it is an error to be on the end of line */ + if (cs == ll || line[cs] == '\n') { + feep(); + return; + } + /* Put argument into the acceptable range -- it is not an error to * + * specify a greater count than the number of available characters. */ + if (n > findeol() - cs) + n = findeol() - cs; + /* do the substitution */ + forekill(n, 0); + startvitext(1); +} + +/**/ +void +vichangeeol(void) +{ + forekill(findeol() - cs, 0); + startvitext(1); +} + +/**/ +void +vichangewholeline(void) +{ + vifirstnonblank(); + vichangeeol(); +} + +/**/ +void +viyank(void) +{ + int oldcs = cs, c2; + + startvichange(1); + if ((c2 = getvirange(0)) != -1) + cut(cs, c2 - cs, 0); + vichgflag = 0; + cs = oldcs; +} + +/**/ +void +viyankeol(void) +{ + int x = findeol(); + + startvichange(-1); + if (x == cs) { + feep(); + return; + } + cut(cs, x - cs, 0); +} + +/**/ +void +viyankwholeline(void) +{ + int bol = findbol(), oldcs = cs; + int n = zmult; + + startvichange(-1); + if (n < 1) + return; + while(n--) { + if (cs > ll) { + feep(); + cs = oldcs; + return; + } + cs = findeol() + 1; + } + vilinerange = 1; + cut(bol, cs - bol - 1, 0); + cs = oldcs; +} + +/**/ +void +vireplace(void) +{ + startvitext(0); +} + +/* vi-replace-chars has some oddities relating to vi-repeat-change. In * + * the real vi, if one does 3r at the end of a line, it feeps without * + * reading the argument, and won't repeat the action. A successful rx * + * followed by 3. at the end of a line (or 3rx followed by . at the end * + * of a line) will obviously feep after the ., even though it has the * + * argument available. Here repeating is tied very closely to argument * + * reading, so some trickery is needed to emulate this. When repeating * + * 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 * + * given, we squash the repeat buffer to avoid repeating the partial * + * command; we've lost the previous change, but that can't be avoided * + * without a rewrite of the repeat code. */ + +/**/ +void +vireplacechars(void) +{ + int ch, n = zmult; + + startvichange(1); + /* check argument range */ + if (n < 1 || n + cs > findeol()) { + if(vichgrepeat) { + int ofeep = feepflag; + vigetkey(); + feepflag = ofeep; + } + if(vichgflag) { + free(vichgbuf); + vichgbuf = NULL; + vichgflag = 0; + } + feep(); + return; + } + /* get key */ + if((ch = vigetkey()) == -1) { + vichgflag = 0; + feep(); + return; + } + /* do change */ + if (ch == '\r' || ch == '\n') { + /* <return> handled specially */ + cs += n - 1; + backkill(n - 1, 0); + line[cs++] = '\n'; + } else { + while (n--) + line[cs++] = ch; + cs--; + } + vichgflag = 0; +} + +/**/ +void +vicmdmode(void) +{ + if (invicmdmode() || selectkeymap("vicmd", 0)) + feep(); + undoing = 1; + vichgflag = 0; + if (cs != findbol()) + cs--; +} + +/**/ +void +viopenlinebelow(void) +{ + cs = findeol(); + spaceinline(1); + line[cs++] = '\n'; + startvitext(1); +} + +/**/ +void +viopenlineabove(void) +{ + cs = findbol(); + spaceinline(1); + line[cs] = '\n'; + startvitext(1); +} + +/**/ +void +vioperswapcase(void) +{ + int oldcs, c2; + + /* get the range */ + startvichange(1); + if ((c2 = getvirange(0)) != -1) { + oldcs = cs; + /* swap the case of all letters within range */ + while (cs < c2) { + if (islower(line[cs])) + line[cs] = tuupper(line[cs]); + else if (isupper(line[cs])) + line[cs] = tulower(line[cs]); + cs++; + } + /* go back to the first line of the range */ + cs = oldcs; + vifirstnonblank(); + } + vichgflag = 0; +} + +/**/ +void +virepeatchange(void) +{ + /* make sure we have a change to repeat */ + if (!vichgbuf || vichgflag) { + feep(); + return; + } + /* restore or update the saved count and buffer */ + if (zmod.flags & MOD_MULT) { + lastmod.mult = zmod.mult; + lastmod.flags |= MOD_MULT; + } + if (zmod.flags & MOD_VIBUF) { + lastmod.vibuf = zmod.vibuf; + lastmod.flags = (lastmod.flags & ~MOD_VIAPP) | + MOD_VIBUF | (zmod.flags & MOD_VIAPP); + } + /* repeat the command */ + inrepeat = 1; + ungetkeys(vichgbuf, vichgbufptr); +} + +/**/ +void +viindent(void) +{ + int oldcs = cs, c2; + + /* get the range */ + startvichange(1); + if ((c2 = getvirange(0)) == -1) { + vichgflag = 0; + return; + } + vichgflag = 0; + /* must be a line range */ + if (!vilinerange) { + feep(); + cs = oldcs; + return; + } + oldcs = cs; + /* add a tab to the beginning of each line within range */ + while (cs < c2) { + spaceinline(1); + line[cs] = '\t'; + cs = findeol() + 1; + } + /* go back to the first line of the range */ + cs = oldcs; + vifirstnonblank(); +} + +/**/ +void +viunindent(void) +{ + int oldcs = cs, c2; + + /* get the range */ + startvichange(1); + if ((c2 = getvirange(0)) == -1) { + vichgflag = 0; + return; + } + vichgflag = 0; + /* must be a line range */ + if (!vilinerange) { + feep(); + cs = oldcs; + return; + } + oldcs = cs; + /* remove a tab from the beginning of each line within range */ + while (cs < c2) { + if (line[cs] == '\t') + foredel(1); + cs = findeol() + 1; + } + /* go back to the first line of the range */ + cs = oldcs; + vifirstnonblank(); +} + +/**/ +void +vibackwarddeletechar(void) +{ + int n = zmult; + + if (invicmdmode()) + startvichange(-1); + /* handle negative argument */ + if (n < 0) { + zmult = -n; + videletechar(); + zmult = n; + return; + } + /* It is an error to be at the beginning of the line, or (in * + * insert mode) to delete past the beginning of insertion. */ + if ((!invicmdmode() && cs - n < viinsbegin) || cs == findbol()) { + feep(); + return; + } + /* Put argument into the acceptable range -- it is not an error to * + * specify a greater count than the number of available characters. */ + if (n > cs - findbol()) + n = cs - findbol(); + /* do the deletion */ + backkill(n, 1); +} + +/**/ +void +vikillline(void) +{ + if (viinsbegin > cs) { + feep(); + return; + } + backdel(cs - viinsbegin); +} + +/**/ +void +viputbefore(void) +{ + Cutbuffer buf = &cutbuf; + int n = zmult; + + startvichange(-1); + if (n < 0) + return; + if (zmod.flags & MOD_VIBUF) + buf = &vibuf[zmod.vibuf]; + if (!buf->buf) { + feep(); + return; + } + if(buf->flags & CUTBUFFER_LINE) { + cs = findbol(); + spaceinline(buf->len + 1); + memcpy((char *)line + cs, buf->buf, buf->len); + line[cs + buf->len] = '\n'; + vifirstnonblank(); + } else { + while (n--) { + spaceinline(buf->len); + memcpy((char *)line + cs, buf->buf, buf->len); + cs += buf->len; + } + if (cs) + cs--; + } +} + +/**/ +void +viputafter(void) +{ + Cutbuffer buf = &cutbuf; + int n = zmult; + + startvichange(-1); + if (n < 0) + return; + if (zmod.flags & MOD_VIBUF) + buf = &vibuf[zmod.vibuf]; + if (!buf->buf) { + feep(); + return; + } + if(buf->flags & CUTBUFFER_LINE) { + cs = findeol(); + spaceinline(buf->len + 1); + line[cs++] = '\n'; + memcpy((char *)line + cs, buf->buf, buf->len); + vifirstnonblank(); + } else { + if (cs != findeol()) + cs++; + while (n--) { + spaceinline(buf->len); + memcpy((char *)line + cs, buf->buf, buf->len); + cs += buf->len; + } + if (cs) + cs--; + } + +} + +/**/ +void +vijoin(void) +{ + int x; + + startvichange(-1); + if ((x = findeol()) == ll) { + feep(); + return; + } + cs = x + 1; + for (x = 1; cs != ll && iblank(line[cs]); cs++, x++); + backdel(x); + if (cs && iblank(line[cs-1])) + cs--; + else { + spaceinline(1); + line[cs] = ' '; + } +} + +/**/ +void +viswapcase(void) +{ + int eol, n = zmult; + + startvichange(-1); + if (n < 1) + return; + eol = findeol(); + while (cs < eol && n--) { + if (islower(line[cs])) + line[cs] = tuupper(line[cs]); + else if (isupper(line[cs])) + line[cs] = tulower(line[cs]); + cs++; + } + if (cs && cs == eol) + cs--; +} + +/**/ +void +vicapslockpanic(void) +{ + beep(); + statusline = "press a lowercase key to continue"; + statusll = strlen(statusline); + refresh(); + while (!islower(getkey(0))); + statusline = NULL; +} + +/**/ +void +visetbuffer(void) +{ + int ch; + + if ((zmod.flags & MOD_VIBUF) || + (((ch = getkey(0)) < '1' || ch > '9') && + (ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z'))) { + feep(); + return; + } + if (ch >= 'A' && ch <= 'Z') /* needed in cut() */ + zmod.flags |= MOD_VIAPP; + else + zmod.flags &= ~MOD_VIAPP; + zmod.vibuf = tulower(ch) + (idigit(ch) ? -'1' + 26 : -'a'); + zmod.flags |= MOD_VIBUF; + prefixflag = 1; +} + +/**/ +void +vikilleol(void) +{ + int n = findeol() - cs; + + startvichange(-1); + if (!n) { + /* error -- line already empty */ + feep(); + return; + } + /* delete to end of line */ + forekill(findeol() - cs, 0); +} + +/**/ +void +vipoundinsert(void) +{ + int oldcs = cs; + + startvichange(-1); + vifirstnonblank(); + if(line[cs] != '#') { + spaceinline(1); + line[cs] = '#'; + if(cs <= viinsbegin) + viinsbegin++; + cs = oldcs + (cs <= oldcs); + } else { + foredel(1); + if (cs < viinsbegin) + viinsbegin--; + cs = oldcs - (cs < oldcs); + } +} + +/**/ +void +viquotedinsert(void) +{ +#ifndef HAS_TIO + struct sgttyb sob; +#endif + + spaceinline(1); + line[cs] = '^'; + refresh(); +#ifndef HAS_TIO + sob = shttyinfo.sgttyb; + sob.sg_flags = (sob.sg_flags | RAW) & ~ECHO; + ioctl(SHTTY, TIOCSETN, &sob); +#endif + c = getkey(0); +#ifndef HAS_TIO + setterm(); +#endif + foredel(1); + if(c < 0) + feep(); + else + selfinsert(); +} + +/* the 0 key in vi: continue a repeat count in the manner of * + * digit-argument if possible, otherwise do vi-beginning-of-line. */ + +/**/ +void +vidigitorbeginningofline(void) +{ + if(zmod.flags & MOD_TMULT) + digitargument(); + else { + removesuffix(); + invalidatelist(); + vibeginningofline(); + } +} |