diff options
Diffstat (limited to 'Src/Zle')
-rw-r--r-- | Src/Zle/zle_hist.c | 138 |
1 files changed, 115 insertions, 23 deletions
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c index 2b0f582ba..5bea98b26 100644 --- a/Src/Zle/zle_hist.c +++ b/Src/Zle/zle_hist.c @@ -410,46 +410,138 @@ endofhistory(char **args) int insertlastword(char **args) { - int n; + int n, nwords, histstep = -1, wordpos = 0, deleteword = 0; char *s, *t; - Histent he; + Histent he = NULL; + LinkList l = NULL; + LinkNode node; -/* multiple calls will now search back through the history, pem */ static char *lastinsert; - static int lasthist, lastpos; - int evhist = addhistnum(curhist, -1, HIST_FOREIGN), save; + static int lasthist, lastpos, lastlen; + int evhist, save; - if (lastinsert) { - int lastlen = ztrlen(lastinsert); - int pos = cs; + /* + * If we have at least one argument, the first is the history + * step. The default is -1 (go back). Repeated calls take + * a step in this direction. A value of 0 is allowed and doesn't + * move the line. + * + * If we have two arguments, the second is the position of + * the word to extract, 1..N. The default is to use the + * numeric argument, or the last word if that is not set. + * + * If we have three arguments, we reset the history pointer to + * the current history event before applying the history step. + */ + if (*args) + { + histstep = (int)zstrtol(*args, NULL, 10); + if (*++args) + { + wordpos = (int)zstrtol(*args, NULL, 10); + if (*++args) + lasthist = curhist; + } + } - if (lastpos <= pos && - lastlen == pos - lastpos && - memcmp(lastinsert, (char *)&line[lastpos], lastlen) == 0) { - evhist = addhistnum(lasthist, -1, HIST_FOREIGN); + if (lastinsert && lastlen && + lastpos <= cs && + lastlen == cs - lastpos && + memcmp(lastinsert, (char *)&line[lastpos], lastlen) == 0) + deleteword = 1; + else + lasthist = curhist; + evhist = histstep ? addhistnum(lasthist, histstep, HIST_FOREIGN) : + lasthist; + + if (evhist == curhist) { + /* + * The line we are currently editing. If we are going to + * replace an existing word, delete the old one now to avoid + * confusion. + */ + if (deleteword) { + int pos = cs; cs = lastpos; foredel(pos - cs); + /* + * Mark that this has been deleted. + * For consistency with history lines, we really ought to + * insert it back if the current command later fails. But + * - we can't be bothered + * - the problem that this can screw up going to other + * lines in the history because we don't update + * the history line isn't really relevant + * - you can see what you're copying, dammit, so you + * shouldn't make errors. + * Of course, I could have implemented it in the time + * it took to say why I haven't. + */ + deleteword = 0; } - zsfree(lastinsert); - lastinsert = NULL; + /* + * Can only happen fail if the line is empty, I hope. + * In that case, we don't need to worry about restoring + * a deleted word, because that can only have come + * from a non-empty line. I think. + */ + if (!(l = bufferwords(NULL, NULL, NULL))) + return 1; + nwords = countlinknodes(l); + } else { + /* Some stored line. */ + if (!(he = quietgethist(evhist)) || !he->nwords) + return 1; + nwords = he->nwords; } - if (!(he = quietgethist(evhist)) || !he->nwords) - return 1; - if (zmult > 0) { - n = he->nwords - (zmult - 1); + if (wordpos) { + n = (wordpos > 0) ? wordpos : nwords + wordpos + 1; + } else if (zmult > 0) { + n = nwords - (zmult - 1); } else { n = 1 - zmult; } - if (n < 1 || n > he->nwords) + if (n < 1 || n > nwords) { + /* + * We can't put in the requested word, but we did find the + * history entry, so we remember the position in the history + * list. This avoids getting stuck on a history line with + * fewer words than expected. The cursor location cs + * has not changed, and lastinsert is still valid. + */ + lasthist = evhist; return 1; - s = he->text + he->words[2*n-2]; - t = he->text + he->words[2*n-1]; + } + /* + * Only remove the old word from the command line if we have + * successfully found a new one to insert. + */ + if (deleteword > 0) { + int pos = cs; + cs = lastpos; + foredel(pos - cs); + } + if (lastinsert) { + zfree(lastinsert, lastlen); + lastinsert = NULL; + } + if (l) { + for (node = firstnode(l); --n; incnode(node)) + ; + s = (char *)getdata(node); + t = s + strlen(s); + } else { + s = he->text + he->words[2*n-2]; + t = he->text + he->words[2*n-1]; + } + save = *t; *t = '\0'; /* ignore trailing whitespace */ - lasthist = evhist; lastpos = cs; - lastinsert = ztrdup(s); + lastlen = t - s; + lastinsert = zalloc(t - s); + memcpy(lastinsert, s, lastlen); n = zmult; zmult = 1; doinsert(s); |