about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2002-03-05 16:33:19 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2002-03-05 16:33:19 +0000
commit849f4068de9831fdaa635c2372dada9131fb5a39 (patch)
treeb1d1696df228fc9c34412920f74d03e1794eba8b
parent15630b234ad7d2e91b165ba0bf584ee0e6e6efce (diff)
downloadzsh-849f4068de9831fdaa635c2372dada9131fb5a39.tar.gz
zsh-849f4068de9831fdaa635c2372dada9131fb5a39.tar.xz
zsh-849f4068de9831fdaa635c2372dada9131fb5a39.zip
16767: Src/Zle/zle_hist.c, Doc/Zsh/zle.yo, Doc/Zsh/contrib.yo,
Functions/Zle/copy-earlier-word:  Enhance insert-last-word to
pick different lines from the history (including the current
line) and different words from that line.  Add copy-earlier-word
as suggested by Dominik Vogt to copy words from either the
current line, or (following an insert-last-word) a previous
history line.
-rw-r--r--ChangeLog10
-rw-r--r--Doc/Zsh/contrib.yo14
-rw-r--r--Doc/Zsh/zle.yo31
-rw-r--r--Functions/Zle/copy-earlier-word19
-rw-r--r--Src/Zle/zle_hist.c138
5 files changed, 189 insertions, 23 deletions
diff --git a/ChangeLog b/ChangeLog
index d4cda0a26..3c75a655c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2002-03-05  Peter Stephenson  <pws@csr.com>
+
+	* 16767: Src/Zle/zle_hist.c, Doc/Zsh/zle.yo, Doc/Zsh/contrib.yo,
+	Functions/Zle/copy-earlier-word:  Enhance insert-last-word to
+	pick different lines from the history (including the current
+	line) and different words from that line.  Add copy-earlier-word
+	as suggested by Dominik Vogt to copy words from either the
+	current line, or (following an insert-last-word) a previous
+	history line.
+
 2002-03-04  Peter Stephenson  <pws@csr.com>
 
 	* 16759: Src/builtin.c: from Eric Norum <eric.norum@usask.ca>:
diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index 654fa699c..21997b7d8 100644
--- a/Doc/Zsh/contrib.yo
+++ b/Doc/Zsh/contrib.yo
@@ -524,6 +524,20 @@ example(zle -N insert-last-assignment smart-insert-last-word
 zstyle :insert-last-assignment match '[[:alpha:]][][[:alnum:]]#=*'
 bindkey '\e=' insert-last-assignment)
 )
+findex(copy-earlier-word)
+item(tt(copy-earlier-word))(
+This widget works like a combination of tt(insert-last-word) and
+tt(copy-prev-shell-word).  Repeated invocations of the widget retrieve
+earlier words on the relevant history line.  With a numeric argument
+var(N), insert the var(N)th word from the history line; var(N) may be
+negative to count from the end of the line.
+
+If tt(insert-last-word) has been used to retrieve the last word on a
+previous history line, repeated invocations will replace that word with
+earlier words from the same line.
+
+Otherwise, the widget applies to words on the line currently being edited.
+)
 enditem()
 
 subsect(Styles)
diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo
index c184f0a87..aee624eb7 100644
--- a/Doc/Zsh/zle.yo
+++ b/Doc/Zsh/zle.yo
@@ -941,6 +941,37 @@ left (zero inserts the previous command word).  Repeating this command
 replaces the word just inserted with the last word from the
 history event prior to the one just used; numeric arguments can be used in
 the same way to pick a word from that event.
+
+When called from a shell function invoked from a user-defined widget, the
+command can take one to three arguments.  The first argument specifies a
+history offset which applies to successive calls to this widget: if is -1,
+the default behaviour is used, while if it is 1, successive calls will move
+forwards through the history.  The value 0 can be used to indicate that the
+history line examined by the previous execution of the command will be
+reexamined.  Note that negative numbers should be preceeded with a
+`tt(-)tt(-)' argument to avoid confusing them with options.
+
+If two arguments are given, the second specifies the word on the command
+line in normal array index notation (as a more natural alternative to the
+prefix argument).  Hence 1 is the first word, and -1 (the default) is the
+last word.
+
+If a third argument is given, its value is ignored, but it is used to
+signify that the history offset is relative to the current history line,
+rather than the one remembered after the previous invocations of
+tt(insert-last-word).
+
+For example, the default behaviour of the command corresponds to
+
+example(zle insert-last-word -- -1 -1)
+
+while the command
+
+example(zle insert-last-word -- -1 1 -)
+
+always copies the first word of the line in the history immediately before
+the line being edited.  This has the side effect that later invocations of
+the widget will be relative to that line.
 )
 tindex(vi-repeat-search)
 item(tt(vi-repeat-search) (unbound) (n) (unbound))(
diff --git a/Functions/Zle/copy-earlier-word b/Functions/Zle/copy-earlier-word
new file mode 100644
index 000000000..bbc8af35c
--- /dev/null
+++ b/Functions/Zle/copy-earlier-word
@@ -0,0 +1,19 @@
+# Copy the word before the one you last copied --- call repeatedly
+# to cycle through the list of words on the history line.
+#
+# Words in combination with insert-last-word to use the line reached,
+# and start from the word before last.  Otherwise, it will operate on
+# the current line.
+
+if (( ${NUMERIC:-0} )); then
+   # 1 means last word, 2 second last, etc.
+   (( __copyword = ${NUMERIC:-0} ))
+elif [[ -n $__copyword && $WIDGET = $LASTWIDGET ]]; then
+  (( __copyword-- ))
+elif [[ $LASTWIDGET = *insert-last-word ]]; then
+  __copyword=-2
+else
+  __copyword=-1
+fi
+
+zle .insert-last-word 0 $__copyword
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);