zsh-workers
 help / color / mirror / code / Atom feed
* PATCH: Re: insert-last-word/copy-prev-word/... question
       [not found] <20020301153313.A8129@lifebits.de>
@ 2002-03-05 14:48 ` Peter Stephenson
  2002-03-05 16:22   ` Peter Stephenson
  2002-03-06 18:21   ` Bart Schaefer
  0 siblings, 2 replies; 4+ messages in thread
From: Peter Stephenson @ 2002-03-05 14:48 UTC (permalink / raw)
  To: Zsh hackers list

Dominik Vogt wrote:
> Let's assume I have this in the history
> 
>   ls xxx xxx foo
>   ls xxx xxx bar
>   ls yyy zzz baz
> 
> And I'm typing a new command line:
> 
>   $ ls first second next 
>                          ^
>                          cursor
> 
> with insert-last word, I can copy "baz", "bar", "foo" to the
> cursor position.  With copy-prev-word I can copy "next".  But I'd
> like to
> 
>  - Call some function multiple times.  With the first call I get
>    "next".  WIth the second call I get "second" and with the
>    third call I get "first".
>  - The same should work on previous lines in the history:  First I
>    call insert-last and get "baz", then I call said function and
>    get "zzz", then I call it again and get "yyy".
> 
> Is that possible?

Here's a proposed addition to the internals which should make much of
that possible (comments welcome).  Extending insert-last-word was by far
the simplest method of doing this, although the name is now inaccurate.
You can try out some function widgets to use this.  For example,

copyearlierword() { 
  if [[ -n $__copyword && $WIDGET = $LASTWIDGET ]]; then
    (( __copyword-- ))
  else
    # assume this follows a normal insert-last-word...
    __copyword=-2
  fi
  zle insert-last-word 0 $__copyword
}
zle -N copyearlierword
bindkey '\eE' copyearlierword

Currently this only works properly after a normal insert-last-word.

What I'm still thinking about (and why this has taken a long time) is
how to make this work on the current command line in the same was as on
the others.  You would refer to the current command line with first
argument 0 and a non-empty last argument.  However, it's more difficult
to get to work.  I've only just remembered bufferwords(), which ought to
do the trick (which saves a visit to our dirty-tricks-with-the-lexer
department in Berlin).  I may present a more complete solution when I've
looked at that.  Meanwhile, someone may have explained why this is all
pointless.


Index: Src/Zle/zle_hist.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_hist.c,v
retrieving revision 1.6
diff -u -r1.6 zle_hist.c
--- Src/Zle/zle_hist.c	17 Sep 2001 18:30:49 -0000	1.6
+++ Src/Zle/zle_hist.c	5 Mar 2002 14:35:50 -0000
@@ -410,38 +410,81 @@
 int
 insertlastword(char **args)
 {
-    int n;
+    int n, histstep = -1, wordpos = 0, deleteword = 0, lastlen;
     char *s, *t;
     Histent he;
 
-/* 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;
+    int evhist, save;
 
-    if (lastinsert) {
-	int lastlen = ztrlen(lastinsert);
-	int pos = cs;
-
-	if (lastpos <= pos &&
-	    lastlen == pos - lastpos &&
-	    memcmp(lastinsert, (char *)&line[lastpos], lastlen) == 0) {
-	    evhist = addhistnum(lasthist, -1, HIST_FOREIGN);
-	    cs = lastpos;
-	    foredel(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;
 	}
-	zsfree(lastinsert);
-	lastinsert = NULL;
     }
+
+    if (lastinsert && (lastlen = ztrlen(lastinsert)) &&
+	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 (!(he = quietgethist(evhist)) || !he->nwords)
 	return 1;
-    if (zmult > 0) {
+    if (wordpos) {
+	n = (wordpos > 0) ? wordpos : he->nwords + wordpos + 1;
+    } else if (zmult > 0) {
 	n = he->nwords - (zmult - 1);
     } else {
 	n = 1 - zmult;
     }
-    if (n < 1 || n > he->nwords)
+    if (n < 1 || n > he->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;
+    }
+    /*
+     * Only remove the old word from the command line if we have
+     * successfully found a new one to insert.
+     */
+    if (deleteword) {
+	int pos = cs;
+	cs = lastpos;
+	foredel(pos - cs);
+    }
+    if (lastinsert) {
+	zsfree(lastinsert);
+	lastinsert = NULL;
+    }
     s = he->text + he->words[2*n-2];
     t = he->text + he->words[2*n-1];
     save = *t;
Index: Doc/Zsh/zle.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/zle.yo,v
retrieving revision 1.16
diff -u -r1.16 zle.yo
--- Doc/Zsh/zle.yo	15 Sep 2001 20:58:33 -0000	1.16
+++ Doc/Zsh/zle.yo	5 Mar 2002 14:35:51 -0000
@@ -941,6 +941,36 @@
 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 is valid and 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 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 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))(

-- 
Peter Stephenson <pws@csr.com>                  Software Engineer
CSR Ltd., Science Park, Milton Road,
Cambridge, CB4 0WH, UK                          Tel: +44 (0)1223 392070


**********************************************************************
The information transmitted is intended only for the person or
entity to which it is addressed and may contain confidential 
and/or privileged material. 
Any review, retransmission, dissemination or other use of, or
taking of any action in reliance upon, this information by 
persons or entities other than the intended recipient is 
prohibited.  
If you received this in error, please contact the sender and 
delete the material from any computer.
**********************************************************************


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: PATCH: Re: insert-last-word/copy-prev-word/... question
  2002-03-05 14:48 ` PATCH: Re: insert-last-word/copy-prev-word/... question Peter Stephenson
@ 2002-03-05 16:22   ` Peter Stephenson
  2002-03-06 18:21   ` Bart Schaefer
  1 sibling, 0 replies; 4+ messages in thread
From: Peter Stephenson @ 2002-03-05 16:22 UTC (permalink / raw)
  To: Zsh hackers list

Peter Stephenson wrote:
> I may present a more complete solution when I've looked at that.

It does appear Sven has done all the hard work, so I could have waited.

The following looks like a reasonably complete solution, even if it
needs tweaking, so I'll commit it unless anyone screams soon.
copy-earlier-word should do essentially what Dominik was asking for.

I think there was a bug with lines containing unusual characters, which
I've avoided by keeping the length of the word we remembered from the
previous invocation.

Index: Src/Zle/zle_hist.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_hist.c,v
retrieving revision 1.6
diff -u -r1.6 zle_hist.c
--- Src/Zle/zle_hist.c	17 Sep 2001 18:30:49 -0000	1.6
+++ Src/Zle/zle_hist.c	5 Mar 2002 16:15:57 -0000
@@ -410,46 +410,138 @@
 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 (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 (lastpos <= pos &&
-	    lastlen == pos - lastpos &&
-	    memcmp(lastinsert, (char *)&line[lastpos], lastlen) == 0) {
-	    evhist = addhistnum(lasthist, -1, HIST_FOREIGN);
+    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);
Index: Doc/Zsh/zle.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/zle.yo,v
retrieving revision 1.16
diff -u -r1.16 zle.yo
--- Doc/Zsh/zle.yo	15 Sep 2001 20:58:33 -0000	1.16
+++ Doc/Zsh/zle.yo	5 Mar 2002 16:15:58 -0000
@@ -941,6 +941,37 @@
 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 is valid and 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 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))(
Index: Doc/Zsh/contrib.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/contrib.yo,v
retrieving revision 1.17
diff -u -r1.17 contrib.yo
--- Doc/Zsh/contrib.yo	7 Dec 2001 12:59:09 -0000	1.17
+++ Doc/Zsh/contrib.yo	5 Mar 2002 16:15:58 -0000
@@ -524,6 +524,20 @@
 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)
Index: Functions/Zle/copy-earlier-word
===================================================================
RCS file: Functions/Zle/copy-earlier-word
diff -N Functions/Zle/copy-earlier-word
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Functions/Zle/copy-earlier-word	5 Mar 2002 16:15:58 -0000
@@ -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

-- 
Peter Stephenson <pws@csr.com>                  Software Engineer
CSR Ltd., Science Park, Milton Road,
Cambridge, CB4 0WH, UK                          Tel: +44 (0)1223 392070


**********************************************************************
The information transmitted is intended only for the person or
entity to which it is addressed and may contain confidential 
and/or privileged material. 
Any review, retransmission, dissemination or other use of, or
taking of any action in reliance upon, this information by 
persons or entities other than the intended recipient is 
prohibited.  
If you received this in error, please contact the sender and 
delete the material from any computer.
**********************************************************************


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: PATCH: Re: insert-last-word/copy-prev-word/... question
  2002-03-05 14:48 ` PATCH: Re: insert-last-word/copy-prev-word/... question Peter Stephenson
  2002-03-05 16:22   ` Peter Stephenson
@ 2002-03-06 18:21   ` Bart Schaefer
  2002-03-12 17:06     ` Oliver Kiddle
  1 sibling, 1 reply; 4+ messages in thread
From: Bart Schaefer @ 2002-03-06 18:21 UTC (permalink / raw)
  To: Peter Stephenson, Zsh hackers list

On Mar 5,  2:48pm, Peter Stephenson wrote:
}
} +     * 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.

Doesn't this conflict with what the numeric argument used to mean?

I think this whole thing could have been written as a shell widget
without resorting to modifying insert-last-word.  I just haven't had
a lot of time to think about it yet.

-- 
Bart Schaefer                                 Brass Lantern Enterprises
http://www.well.com/user/barts              http://www.brasslantern.com

Zsh: http://www.zsh.org | PHPerl Project: http://phperl.sourceforge.net   


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: PATCH: Re: insert-last-word/copy-prev-word/... question
  2002-03-06 18:21   ` Bart Schaefer
@ 2002-03-12 17:06     ` Oliver Kiddle
  0 siblings, 0 replies; 4+ messages in thread
From: Oliver Kiddle @ 2002-03-12 17:06 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Peter Stephenson, Zsh hackers list

On Wed, Mar 06, 2002 at 06:21:11PM +0000, Bart Schaefer wrote:
> On Mar 5,  2:48pm, Peter Stephenson wrote:
> }
> } +     * 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.
> 
> Doesn't this conflict with what the numeric argument used to mean?
> 
> I think this whole thing could have been written as a shell widget
> without resorting to modifying insert-last-word.  I just haven't had
> a lot of time to think about it yet.

Sorry for being very slow here. I was busy and only skim read the
thread before.

If we just want a shell widget, I've been using this for years:

copy-last-word() {
  setopt localoptions noksharrays
  local words
  words=( "${=LBUFFER}" )
  if [[ $LASTWIDGET = copy-last-word ]] && (( clwcount > -$#words )); then
    (( clwcount-- ))
  else
    (( clwcount=-2 ))
  fi
  LBUFFER="${LBUFFER%$words[-1]}$words[clwcount]"
}

It doesn't duplicate the current word at the first invocation like
copy-prev-word does - I find that useless. I suspect that Peter's
patch does a better job of using WORDCHARS or whatever. This just
worked for me. I'm sure it could be better.

What I've been meaning to do in ages is combine this with 
insert-last-word so you can use insert-last-word to go up a few
lines and then go back along the words of that previous line.

Oliver

This e-mail and any attachment is for authorised use by the intended recipient(s) only.  It may contain proprietary material, confidential information and/or be subject to legal privilege.  It should not be copied, disclosed to, retained or used by, any other party.  If you are not an intended recipient then please promptly delete this e-mail and any attachment and all copies and inform the sender.  Thank you.


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2002-03-12 17:06 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20020301153313.A8129@lifebits.de>
2002-03-05 14:48 ` PATCH: Re: insert-last-word/copy-prev-word/... question Peter Stephenson
2002-03-05 16:22   ` Peter Stephenson
2002-03-06 18:21   ` Bart Schaefer
2002-03-12 17:06     ` Oliver Kiddle

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).