Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: size of kill-ring?



Peter Stephenson wrote:
> Andy Spiegl wrote:
> > is there any way I can change the size of the kill-ring?
> >  Andy.
> 
> Rewrite zle to make the constant KRINGCT dynamic.  This looks a fairly
> easy project, and (in 4.1) there's already an interface for changing the
> size via the new $killring zle parameter.

This is the first attempt.  I had some crashes in the early stages
and although I haven't seen any with the version below it's worth
playing around to see if there is anything amiss.

A simple inc-kill-ring-size widget looks like:


# Increase (or decrease) size of zle kill ring, depending on the
# widget name.
#   zle -N inc-kill-ring-size
#   zle -N dec-kill-ring-size inc-kill-ring-size

emulate -L zsh

if [[ $WIDGET = inc* ]]; then
    killring=(${killring} '')
elif (( ${#killring} )); then
    killring=(${killring[1,-2]})
fi


which can be extended in any number of ways --- the fixed size
limitation on $killring has gone, so it really does work like a normal
array now.  Remember, however, that zle parameters are only special in
zle widgets, to avoid namespace pollution.

I've fixed two existing inconsistencies on the way, the first actually a
by-product:

1. Previously, if the kill ring wasn't full, yank-pop got to the last
   entry and stuck instead of cycling back to the beginning.  Now it
   always cycles even if it's not full.  This is partly to get around the
   fact that with the array there's no distinction between an empty
   buffer and a buffer with a zero-length string in it --- I am assuming
   that yanking zero-length text is not useful, so such buffers are always
   skipped when yank-pop'ing.

2. Previously yank-pop didn't cycle back to the original cut buffer,
   which should logically appear just before the second time you loop
   through the kill ring.  Now this is part of the sequence.  I haven't
   documented this bit since it seems to me the obvious behaviour
   anyway.  It's certainly what Emacs does.

Index: Doc/Zsh/zle.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/zle.yo,v
retrieving revision 1.27
diff -u -r1.27 zle.yo
--- Doc/Zsh/zle.yo	6 Dec 2002 23:24:07 -0000	1.27
+++ Doc/Zsh/zle.yo	24 Jan 2003 12:05:21 -0000
@@ -622,10 +622,14 @@
 item(tt(killring) (array))(
 The array of previously killed items, with the most recently killed first.
 This gives the items that would be retrieved by a tt(yank-pop) in the
-same order, up to eight (which is the maximum stored internally).
-Unlike a normal array, only a maximum of eight elements may be written;
-any extra are ignored.  If fewer than eight elements are given, the
-remaining elements of the kill ring will be treated as undefined.
+same order.
+
+The default size for the kill ring is eight, however the length may be
+changed by normal array operations.  Any empty string in the kill ring is
+ignored by the tt(yank-pop) command, hence the size of the array
+effectively sets the maximum length of the kill ring, while the number of
+non-zero strings gives the current length, both as seen by the user at the
+command line.
 )
 
 vindex(LASTSEARCH)
Index: Src/Zle/zle.h
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle.h,v
retrieving revision 1.2
diff -u -r1.2 zle.h
--- Src/Zle/zle.h	23 May 2000 08:20:57 -0000	1.2
+++ Src/Zle/zle.h	24 Jan 2003 12:05:21 -0000
@@ -142,7 +142,7 @@
 
 #define CUTBUFFER_LINE 1   /* for vi: buffer contains whole lines of data */
 
-#define KRINGCT 8   /* number of buffers in the kill ring */
+#define KRINGCTDEF 8   /* default number of buffers in the kill ring */
 
 /* Types of completion. */
 
Index: Src/Zle/zle_main.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_main.c,v
retrieving revision 1.28
diff -u -r1.28 zle_main.c
--- Src/Zle/zle_main.c	27 Aug 2002 21:10:34 -0000	1.28
+++ Src/Zle/zle_main.c	24 Jan 2003 12:05:21 -0000
@@ -1406,8 +1406,11 @@
     if (rdstrs)
         freelinklist(rdstrs, freestr);
     zfree(cutbuf.buf, cutbuf.len);
-    for(i = KRINGCT; i--; )
-	zfree(kring[i].buf, kring[i].len);
+    if (kring) {
+	for(i = kringsize; i--; )
+	    zfree(kring[i].buf, kring[i].len);
+	zfree(kring, kringsize * sizeof(struct cutbuffer));
+    }
     for(i = 35; i--; )
 	zfree(vibuf[i].buf, vibuf[i].len);
 
Index: Src/Zle/zle_misc.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_misc.c,v
retrieving revision 1.6
diff -u -r1.6 zle_misc.c
--- Src/Zle/zle_misc.c	3 Sep 2001 01:39:20 -0000	1.6
+++ Src/Zle/zle_misc.c	24 Jan 2003 12:05:21 -0000
@@ -331,28 +331,35 @@
     return 0;
 }
 
+/*
+ * kct: index into kill ring, or -1 for original cutbuffer of yank.
+ * yankb, yanke: mark the start and end of last yank in editing buffer.
+ */
 static int kct, yankb, yanke;
+/* The original cutbuffer, either cutbuf or one of the vi buffers. */
+static Cutbuffer kctbuf;
 
 /**/
 int
 yank(char **args)
 {
-    Cutbuffer buf = &cutbuf;
     int n = zmult;
 
     if (n < 0)
 	return 1;
     if (zmod.flags & MOD_VIBUF)
-	buf = &vibuf[zmod.vibuf];
-    if (!buf->buf)
+	kctbuf = &vibuf[zmod.vibuf];
+    else
+	kctbuf = &cutbuf;
+    if (!kctbuf->buf)
 	return 1;
     mark = cs;
     yankb = cs;
     while (n--) {
-	kct = kringnum;
-	spaceinline(buf->len);
-	memcpy((char *)line + cs, buf->buf, buf->len);
-	cs += buf->len;
+	kct = -1;
+	spaceinline(kctbuf->len);
+	memcpy((char *)line + cs, kctbuf->buf, kctbuf->len);
+	cs += kctbuf->len;
 	yanke = cs;
     }
     return 0;
@@ -362,18 +369,56 @@
 int
 yankpop(char **args)
 {
-    int cc;
+    int cc, kctstart = kct;
+    Cutbuffer buf;
 
-    if (!(lastcmd & ZLE_YANK) || !kring[kct].buf)
+    if (!(lastcmd & ZLE_YANK))
 	return 1;
+    do {
+	/*
+	 * This is supposed to make the yankpop loop
+	 *   original buffer -> kill ring in order -> original buffer -> ...
+	 * where the original buffer is -1 and the remainder are
+	 * indices into the kill ring, remember that we need to start
+	 * that at kringnum rather than zero.
+	 */
+	if (kct == -1)
+	    kct = kringnum;
+	else {
+	    int kctnew = (kct + kringsize - 1) % kringsize;
+	    if (kctnew == kringnum)
+		kct = -1;
+	    else
+		kct = kctnew;
+	}
+	if (kct == -1)
+	    buf = kctbuf;	/* Use original cutbuffer */
+	else
+	    buf = kring+kct;	/* Use somewhere in the kill ring */
+	/* Careful to prevent infinite looping */
+	if (kct == kctstart)
+	    return 1;
+	/*
+	 * Skip unset buffers instead of stopping as we used to do.
+	 * Also skip zero-length buffers.
+	 * There are two reasons for this:
+	 * 1. We now map the array $killring directly into the
+	 *    killring, instead of via some extra size-checking logic.
+	 *    When $killring has been set, a buffer will always have
+	 *    at least a zero-length string in it.
+	 * 2. The old logic was inconsistent; when the kill ring
+	 *    was full, we could loop round and round it, otherwise
+	 *    we just stopped when we hit the first empty buffer.
+	 */
+    } while (!buf->buf || !*buf->buf);
+
     cs = yankb;
     foredel(yanke - yankb);
-    cc = kring[kct].len;
+    cc = buf->len;
     spaceinline(cc);
-    memcpy((char *)line + cs, kring[kct].buf, cc);
+    memcpy((char *)line + cs, buf->buf, cc);
     cs += cc;
     yanke = cs;
-    kct = (kct + KRINGCT - 1) % KRINGCT;
     return 0;
 }
 
Index: Src/Zle/zle_params.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_params.c,v
retrieving revision 1.7
diff -u -r1.7 zle_params.c
--- Src/Zle/zle_params.c	6 Dec 2002 23:24:07 -0000	1.7
+++ Src/Zle/zle_params.c	24 Jan 2003 12:05:21 -0000
@@ -391,43 +391,41 @@
 static void
 set_killring(Param pm, char **x)
 {
-    int kpos, kcnt;
+    int kcnt;
+    Cutbuffer kptr;
     char **p;
 
-    kcnt = 0;
-    kpos = kringnum;
-
+    if (kring) {
+	for (kptr = kring, kcnt = 0; kcnt < kringsize; kcnt++, kptr++)
+	    if (kptr->buf)
+		free(kptr->buf);
+	zfree(kring, kringsize * sizeof(struct cutbuffer));
+	kring = NULL;
+	kringsize = kringnum = 0;
+    }
     if (x) {
 	/*
-	 * Insert the elements into the kill ring, up to a maximum
-	 * of KRINGCT.  We always handle the ring in the order
-	 * a series of yank-pops would show, i.e. starting with
-	 * the most recently cut and going backwards.
+	 * Insert the elements into the kill ring.
+	 * Regardless of the old order, we number it with the current
+	 * entry first.
 	 */
-	for (p = x; kcnt < KRINGCT && *p; kcnt++, p++) {
-	    Cutbuffer kptr = kring + kpos;
-	    if (kptr->buf)
-		free(kptr->buf);
-	    kptr->buf = (char *)zalloc(strlen(*p));
+	kringsize = arrlen(x);
+	kring = (Cutbuffer)zcalloc(kringsize * sizeof(struct cutbuffer));
+	for (p = x, kptr = kring; *p; p++, kptr++) {
+	    int len = strlen(*p);
+	    kptr->buf = (char *)zalloc(len);
 	    strcpy(kptr->buf, *p);
 	    unmetafy(kptr->buf, &kptr->len);
-	    kptr->flags = 0;
-	    kpos = (kpos + KRINGCT - 1) % KRINGCT;
+	    if (len != kptr->len) {
+		/* Might as well have the lengths consistent. */
+		char *p2 = zalloc(kptr->len);
+		memcpy(p2, kptr->buf, kptr->len);
+		zfree(kptr->buf, len);
+		kptr->buf = p2;
+	    }
 	}
 	freearray(x);
     }
-    /*
-     * Any values unsupplied are to be unset.
-     */
-    for (; kcnt < KRINGCT; kcnt++) {
-	Cutbuffer kptr = kring + kpos;
-	if (kptr->buf) {
-	    free(kptr->buf);
-	    kptr->buf = NULL;
-	    kptr->flags = kptr->len = 0;
-	}
-	kpos = (kpos + KRINGCT - 1) % KRINGCT;
-    }
 }
 
 /**/
@@ -436,23 +434,28 @@
 {
     /*
      * Return the kill ring with the most recently killed first.
-     * Stop as soon as we find something which isn't set, i.e.
-     * don't fill in bogus entries.
+     * Since the kill ring is no longer a fixed length, we return
+     * all entries even if empty.
      */
     int kpos, kcnt;
     char **ret, **p;
 
-    for (kpos = kringnum, kcnt = 0; kcnt < KRINGCT; kcnt++) {
+    /* Supposed to work even if kring is NULL */
+    for (kpos = kringnum, kcnt = 0; kcnt < kringsize; kcnt++) {
 	if (!kring[kpos].buf)
 	    break;
-	kpos = (kpos + KRINGCT - 1) % KRINGCT;
+	kpos = (kpos + kringsize - 1) % kringsize;
     }
 
-    p = ret = (char **)zhalloc((kcnt+1) * sizeof(char *));
+    p = ret = (char **)zhalloc((kringsize+1) * sizeof(char *));
 
-    for (kpos = kringnum; kcnt; kcnt--) {
-	*p++ = metafy((char *)kring[kpos].buf, kring[kpos].len, META_HEAPDUP);
-	kpos = (kpos + KRINGCT - 1) % KRINGCT;
+    for (kpos = kringnum, kcnt = 0; kcnt < kringsize; kcnt++) {
+	Cutbuffer kptr = kring + kpos;
+	if (kptr->buf)
+	    *p++ = metafy((char *)kptr->buf, kptr->len, META_HEAPDUP);
+	else
+	    *p++ = dupstring("");
+	kpos = (kpos + kringsize - 1) % kringsize;
     }
     *p = NULL;
 
Index: Src/Zle/zle_utils.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_utils.c,v
retrieving revision 1.6
diff -u -r1.6 zle_utils.c
--- Src/Zle/zle_utils.c	26 Mar 2001 08:58:34 -0000	1.6
+++ Src/Zle/zle_utils.c	24 Jan 2003 12:05:21 -0000
@@ -38,9 +38,9 @@
 /* Emacs-style kill buffer ring */
 
 /**/
-struct cutbuffer kring[KRINGCT];
+struct cutbuffer *kring;
 /**/
-int kringnum;
+int kringsize, kringnum;
 
 /* Vi named cut buffers.  0-25 are the named buffers "a to "z, and *
  * 26-34 are the numbered buffer stack "1 to "9.                   */
@@ -167,10 +167,16 @@
 	cutbuf.buf = ztrdup("");
 	cutbuf.len = cutbuf.flags = 0;
     } else if (!(lastcmd & ZLE_KILL)) {
-	kringnum = (kringnum + 1) % KRINGCT;
-	if (kring[kringnum].buf)
-	    free(kring[kringnum].buf);
-	kring[kringnum] = cutbuf;
+	Cutbuffer kptr;
+	if (!kring) {
+	    kringsize = KRINGCTDEF;
+	    kring = (Cutbuffer)zcalloc(kringsize * sizeof(struct cutbuffer));
+	} else
+	    kringnum = (kringnum + 1) % kringsize;
+	kptr = kring + kringnum;
+	if (kptr->buf)
+	    zfree(kptr->buf, kptr->len);
+	*kptr = cutbuf;
 	cutbuf.buf = ztrdup("");
 	cutbuf.len = cutbuf.flags = 0;
     }

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


**********************************************************************
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.
**********************************************************************



Messages sorted by: Reverse Date, Date, Thread, Author