summaryrefslogtreecommitdiff
path: root/Src/Zle/zle_hist.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Zle/zle_hist.c')
-rw-r--r--Src/Zle/zle_hist.c138
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);