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

tcsh-like history-based dabbrev-expand patch



Hi all,

Have to confess I'm a complete newbie at this game, but I've
written a patch to implement similar functionality to tcsh's
dabbrev-expand function and I think it might be good enough
(maybe with a few tweaks here and there) to include in the
distribution.  Here's the relevant quote from the tcsh man page:

    dabbrev-expand (M-/)
           Expands the current word to the most  recent  pre-
           ceding one for which the current is a leading sub-
           string, wrapping around the history list (once) if
           necessary.   Repeating  dabbrev-expand without any
           intervening typing changes to  the  next  previous
           word  etc.,  skipping  identical matches much like
           history-search-backward does.

It applies cleanly over 3.0.5 and introduces two new key
commands:

history-dabbrev-expand (default binding M-/)
reverse-history-dabbrev-expand (default binding M-,)

I'm no expert C coder, but I think it should be a fairly clean
patch.  The only bit which slightly bothers me is the setting of
showinglist to 0 -- I wanted to disable the message

   zsh: do you wish to see all %n possibilities?

when completing via this command, and didn't know enough about
the refresh() function to know for certain how I should do it.
I guessed at showinglist = 0 and it seemed to work :-)

All comments welcome.  It doesn't patch the man/info pages yet;
I'm short of time so I thought I'd only bother if you decided it
was worth including in the distribution :-)  If you think it is
(I would be very keen to see it go in because I think
dabbrev-expand is SO useful) then I will make it patch the
documentation too.


-------- 8< -------- 8< --------
*** zsh-3.0.5/Src/globals.h	Fri Feb 13 17:59:56 1998
--- zsh-3.0.5-as.dabbrev-expand.patch/Src/globals.h	Fri Feb 13 14:19:32 1998
***************
*** 290,296 ****
   
  /* default completion infos */
   
! EXTERN struct compctl cc_compos, cc_default, cc_first, cc_dummy;
   
  /* the job table */
   
--- 290,296 ----
   
  /* default completion infos */
   
! EXTERN struct compctl cc_compos, cc_default, cc_first, cc_dummy, cc_histdummy;
   
  /* the job table */
   
*** zsh-3.0.5/Src/zle.h	Fri Feb 13 17:59:56 1998
--- zsh-3.0.5-as.dabbrev-expand.patch/Src/zle.h	Thu Jan  8 00:58:52 1998
***************
*** 323,328 ****
--- 323,329 ----
      z_gosmacstransposechars,
      z_historybeginningsearchbackward,
      z_historybeginningsearchforward,
+     z_historydabbrevexpand,
      z_historyincrementalsearchbackward,
      z_historyincrementalsearchforward,
      z_historysearchbackward,
***************
*** 351,356 ****
--- 352,358 ----
      z_quoteregion,
      z_redisplay,
      z_reversemenucomplete,
+     z_reversehistorydabbrevexpand,
      z_runhelp,
      z_selfinsert,
      z_selfinsertunmeta,
*** zsh-3.0.5/Src/zle_bindings.c	Fri Feb 13 17:59:56 1998
--- zsh-3.0.5-as.dabbrev-expand.patch/Src/zle_bindings.c	Thu Jan  8 00:59:22 1998
***************
*** 83,88 ****
--- 83,89 ----
      {"gosmacs-transpose-chars", gosmacstransposechars, 0},
      {"history-beginning-search-backward", historybeginningsearchbackward, ZLE_HISTSEARCH},
      {"history-beginning-search-forward", historybeginningsearchforward, ZLE_HISTSEARCH},
+     {"history-dabbrev-expand", historydabbrevexpand, ZLE_MENUCMP},
      {"history-incremental-search-backward", historyincrementalsearchbackward, 0},
      {"history-incremental-search-forward", historyincrementalsearchforward, 0},
      {"history-search-backward", historysearchbackward, ZLE_HISTSEARCH},
***************
*** 110,115 ****
--- 111,117 ----
      {"quote-line", quoteline, 0},
      {"quote-region", quoteregion, 0},
      {"redisplay", redisplay, ZLE_MENUCMP},
+     {"reverse-history-dabbrev-expand", reversehistorydabbrevexpand, ZLE_MENUCMP},
      {"reverse-menu-complete", reversemenucomplete, ZLE_MENUCMP},
      {"run-help", processcmd, ZLE_MENUCMP},
      {"self-insert", selfinsert, ZLE_INSERT},
***************
*** 373,382 ****
      /* M-) */ z_undefinedkey,
      /* M-* */ z_undefinedkey,
      /* M-+ */ z_undefinedkey,
!     /* M-, */ z_undefinedkey,
      /* M-- */ z_negargument,
      /* M-. */ z_insertlastword,
!     /* M-/ */ z_undefinedkey,
      /* M-0 */ z_digitargument,
      /* M-1 */ z_digitargument,
      /* M-2 */ z_digitargument,
--- 375,384 ----
      /* M-) */ z_undefinedkey,
      /* M-* */ z_undefinedkey,
      /* M-+ */ z_undefinedkey,
!     /* M-, */ z_reversehistorydabbrevexpand,
      /* M-- */ z_negargument,
      /* M-. */ z_insertlastword,
!     /* M-/ */ z_historydabbrevexpand,
      /* M-0 */ z_digitargument,
      /* M-1 */ z_digitargument,
      /* M-2 */ z_digitargument,
*** zsh-3.0.5/Src/zle_misc.c	Fri Feb 13 17:59:56 1998
--- zsh-3.0.5-as.dabbrev-expand.patch/Src/zle_misc.c	Tue Jan  6 21:42:26 1998
***************
*** 631,638 ****
  	    }
  	    if (cmd == z_listchoices || cmd == z_deletecharorlist ||
  		cmd == z_expandorcomplete || cmd == z_expandorcompleteprefix ||
! 		cmd == z_completeword || cmd == z_vicmdmode ||
! 		cmd == z_acceptline || c == ' ' || c == '\t') {
  		LinkList cmdll;
  		int ambig = 100;
  
--- 631,639 ----
  	    }
  	    if (cmd == z_listchoices || cmd == z_deletecharorlist ||
  		cmd == z_expandorcomplete || cmd == z_expandorcompleteprefix ||
! 		cmd == z_completeword || cmd == z_historydabbrevexpand ||
! 		cmd == z_vicmdmode || cmd == z_acceptline || 
! 		c == ' ' || c == '\t') {
  		LinkList cmdll;
  		int ambig = 100;
  
*** zsh-3.0.5/Src/zle_tricky.c	Fri Feb 13 17:59:56 1998
--- zsh-3.0.5-as.dabbrev-expand.patch/Src/zle_tricky.c	Fri Feb 13 18:02:07 1998
***************
*** 80,86 ****
  /* These control the type of completion that will be done.  They are    *
   * affected by the choice of ZLE command and by relevant shell options. */
  
! static int usemenu, useglob;
  
  /* A pointer to the current position in the menu-completion array (the one *
   * that was put in the command line last).                                 */
--- 80,86 ----
  /* These control the type of completion that will be done.  They are    *
   * affected by the choice of ZLE command and by relevant shell options. */
  
! static int usemenu, useglob, usehistonly = 0;
  
  /* A pointer to the current position in the menu-completion array (the one *
   * that was put in the command line last).                                 */
***************
*** 114,120 ****
  
  static int nmatches;
  
! /* !=0 if we have a valid completion list. */
  
  static int validlist;
  
--- 114,121 ----
  
  static int nmatches;
  
! /* 1 if we have a valid completion list;
!    2 if we have a valid completion list from history only. */
  
  static int validlist;
  
***************
*** 230,235 ****
--- 231,247 ----
  
  /**/
  void
+ historydabbrevexpand(void)
+ {
+     usehistonly = 1;
+     usemenu = 1;
+     useglob = 0;
+     docomplete(COMP_COMPLETE);
+     usehistonly = showinglist = 0;
+ }
+ 
+ /**/
+ void
  menucomplete(void)
  {
      usemenu = 1;
***************
*** 337,342 ****
--- 349,374 ----
      } LASTALLOC;
  }
  
+ /**/
+ void
+ reversehistorydabbrevexpand(void)
+ {
+     if (!menucmp) {
+ 	historydabbrevexpand();
+ 	return;
+     }
+     HEAPALLOC {
+ 	if (menucur == amatches)
+ 	    menucur = amatches + nmatches - 1;
+ 	else
+ 	    menucur--;
+ 	complexpect = menuce;
+ 	metafy_line();
+ 	do_single(*menucur);
+ 	unmetafy_line();
+     } LASTALLOC;
+ }
+ 
  /* Accepts the current completion and starts a new arg, *
   * with the next completions. This gives you a way to   *
   * accept several selections from the list of matches.  */
***************
*** 2168,2176 ****
  
  /* Create the completion list.  This is called whenever some bit of  *
   * completion code needs the list.  If the list is already available *
!  * (validlist!=0), this function doesn't do anything.  Along with    *
!  * the list is maintained the prefixes/suffixes etc.  When any of    *
!  * this becomes invalid -- e.g. if some text is changed on the       *
   * command line -- invalidatelist() should be called, to set         *
   * validlist to zero and free up the memory used.  This function     *
   * returns non-zero on error.  delit and compadd return information  *
--- 2200,2208 ----
  
  /* Create the completion list.  This is called whenever some bit of  *
   * completion code needs the list.  If the list is already available *
!  * and of the right type, this function doesn't do anything.  Along  *
!  * with the list is maintained the prefixes/suffixes etc.  When any  *
!  * of this becomes invalid -- e.g. if some text is changed on the    *
   * command line -- invalidatelist() should be called, to set         *
   * validlist to zero and free up the memory used.  This function     *
   * returns non-zero on error.  delit and compadd return information  *
***************
*** 2188,2195 ****
  
      /* If we already have a list from a previous execution of this *
       * function, skip the list building code.                      */
!     if (validlist)
! 	return !nmatches;
  
      os = dupstring(s);
      ol = (unsigned char *)dupstring((char *)line);
--- 2220,2235 ----
  
      /* If we already have a list from a previous execution of this *
       * function, skip the list building code.                      */
! 
!     if (((validlist == 1) && !usehistonly) ||
!         ((validlist == 2) && usehistonly))
!         return !nmatches;
! 
!     /* If we already have a list from a previous execution of this *
!      * function, but it is of the wrong sort, invalidate the list. */
!     if (((validlist == 1) && usehistonly) ||
!         ((validlist == 2) && !usehistonly))
!         invalidatelist();
  
      os = dupstring(s);
      ol = (unsigned char *)dupstring((char *)line);
***************
*** 2219,2224 ****
--- 2259,2278 ----
      if (!cc || cc->ext)
  	cc = get_ccompctl(cc, compadd, incmd);
  
+     /* If activated with history-dabbrev-expand, override user-specified *
+      * compctl settings with these.					 */
+     if (usehistonly) {
+ 	cc = &cc_histdummy;
+ 	cc->next = NULL;
+ 	cc->mask = 0;
+ 	cc->keyvar = cc->glob = cc->str = cc->func = cc->explain =
+ 	    cc->prefix = cc->suffix = cc->subcmd = NULL;
+ 	cc->hpat = "*";
+ 	cc->hnum = 0;
+ 	cc->ext = cc->xor = NULL;
+ 	cc->cond = NULL;
+     }
+ 
      /* *compadd is the number of characters we have to ignore at the *
       * beginning of the word.                                        */
      wb += *compadd;
***************
*** 2856,2862 ****
  	/* Now search the history. */
  	while (n-- && (he = quietgethist(i--))) {
  	    int iwords;
! 	    for (iwords = 0; iwords < he->nwords; iwords++) {
  		h = he->text + he->words[iwords*2];
  		e = he->text + he->words[iwords*2+1];
  		hpatsav = *e;
--- 2910,2916 ----
  	/* Now search the history. */
  	while (n-- && (he = quietgethist(i--))) {
  	    int iwords;
! 	    for (iwords = he->nwords-1; iwords >= 0; iwords--) {
  		h = he->text + he->words[iwords*2];
  		e = he->text + he->words[iwords*2+1];
  		hpatsav = *e;
***************
*** 2933,2939 ****
      remsuffix = (cc->mask & CC_REMOVE);
      ccsuffix = cc->suffix;
  
!     validlist = 1;
      if ((nmatches || expl) && !errflag)
  	return 0;
  
--- 2987,2993 ----
      remsuffix = (cc->mask & CC_REMOVE);
      ccsuffix = cc->suffix;
  
!     validlist = usehistonly ? 2 : 1;
      if ((nmatches || expl) && !errflag)
  	return 0;
  
***************
*** 3095,3103 ****
  	*ap++ = (char *)getdata(nod);
      *ap = NULL;
  
!     /* Now sort the array. */
!     qsort((void *) amatches, nmatches, sizeof(char *),
! 	       (int (*) _((const void *, const void *)))strbpcmp);
  
      /* And delete the ones that occur more than once. */
      for (ap = cp = amatches; *ap; ap++) {
--- 3149,3159 ----
  	*ap++ = (char *)getdata(nod);
      *ap = NULL;
  
!     if (!usehistonly) {
! 	/* Now sort the array. */
! 	qsort((void *) amatches, nmatches, sizeof(char *),
! 	      (int (*) _((const void *, const void *)))strbpcmp);
!     }
  
      /* And delete the ones that occur more than once. */
      for (ap = cp = amatches; *ap; ap++) {
***************
*** 3558,3564 ****
      }
  
      /* Maybe we have to ask if the user wants to see the list. */
!     if ((listmax && ct > listmax) || (!listmax && up >= lines)) {
  	int qup;
  	setterm();
  	qup = printfmt("zsh: do you wish to see all %n possibilities? ", ct, 1);
--- 3614,3620 ----
      }
  
      /* Maybe we have to ask if the user wants to see the list. */
!     if (!usehistonly && ((listmax && ct > listmax) || (!listmax && up >= lines))) {
  	int qup;
  	setterm();
  	qup = printfmt("zsh: do you wish to see all %n possibilities? ", ct, 1);
-------- 8< -------- 8< --------


--
Adam Spiers, Computing Officer, New College, Oxford University, UK **
cello/modern jazz/juggling/cycling/Perl/Linux/Quake fanatic, M$ hater
e-mail: adam.spiers@xxxxxxxxxxxx  WWW: http://www.new.ox.ac.uk/~adam/



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