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

PATCH: isearch match highlighting



This one is worth posting.

With pattern matching in isearch, it's now quite useful to have the
matched string highlighted.  I've made the default underlining rather
than standout because of the annoyance that standout on a cursor that's
shown in inverse video flips it to normal video, then when you've just
matched one character there's nothing at all on the line.  This seems to
be common behaviour.

You could argue that if highlighting is done this way it would make more
sense for the cursor position to be where the user is typing, but I
it's non-trivial to change, hard to be sure the highlighting selected is
going to work, incompatible with usage when there's no highlighting, and
I don't feel like monkeying with it anyway.

I can't help feeling I must have screwed up this one somehow.

Index: Doc/Zsh/zle.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/zle.yo,v
retrieving revision 1.63
diff -u -r1.63 zle.yo
--- Doc/Zsh/zle.yo	26 Apr 2008 19:51:09 -0000	1.63
+++ Doc/Zsh/zle.yo	26 Apr 2008 22:41:58 -0000
@@ -2064,6 +2064,10 @@
 startitem()
 cindex(region, highlighting)
 cindex(highlighting, region)
+item(tt(isearch))(
+When one of the incremental history search widgets is active, the
+area of the command line matched by the search string or pattern.
+)
 item(tt(region))(
 The region between the cursor (point) and the mark as set with
 tt(set-mark-command).  The region is only highlighted if it is active,
@@ -2102,7 +2106,7 @@
 is inverse video.  On some such terminals, where the cursor does not blink
 it appears with standout mode negated, making it less than clear where
 the cursor actually is.  On such terminals one of the other effects
-may be preferable for highlighting the region.
+may be preferable for highlighting the region and matched search string.
 )
 item(tt(underline))(
 The characters in the given context are shown underlined.  Some
@@ -2133,7 +2137,8 @@
 If tt(zle_highlight) is not set or no value applies to a particular
 context, the defaults applied are equivalent to
 
-example(zle_highlight=LPAR()region:standout special:standout+RPAR())
+example(zle_highlight=LPAR()region:standout special:standout
+isearch:underline+RPAR())
 
 i.e. both the region and special characters are shown in standout mode.
 
Index: Src/Zle/zle_hist.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_hist.c,v
retrieving revision 1.48
diff -u -r1.48 zle_hist.c
--- Src/Zle/zle_hist.c	26 Apr 2008 21:26:28 -0000	1.48
+++ Src/Zle/zle_hist.c	26 Apr 2008 22:41:59 -0000
@@ -907,6 +907,7 @@
     int pat_hl;			/* histline where pattern search started */
     unsigned short pos;		/* The search position in our metafied str */
     unsigned short pat_pos;     /* pos where pattern search started */
+    unsigned short end_pos;	/* The position of the end of the matched str */
     unsigned short cs;		/* The visible search position to the user */
     unsigned short len;		/* The search string's length */
     unsigned short flags;	/* This spot's flags */
@@ -928,7 +929,7 @@
 /**/
 static void
 set_isrch_spot(int num, int hl, int pos, int pat_hl, int pat_pos,
-	       int cs, int len, int dir, int nomatch)
+	       int end_pos, int cs, int len, int dir, int nomatch)
 {
     if (num >= max_spot) {
 	if (!isrch_spots) {
@@ -944,6 +945,7 @@
     isrch_spots[num].pos = (unsigned short)pos;
     isrch_spots[num].pat_hl = pat_hl;
     isrch_spots[num].pat_pos = (unsigned short)pat_pos;
+    isrch_spots[num].end_pos = (unsigned short)end_pos;
     isrch_spots[num].cs = (unsigned short)cs;
     isrch_spots[num].len = (unsigned short)len;
     isrch_spots[num].flags = (dir > 0? ISS_FORWARD : 0)
@@ -953,12 +955,13 @@
 /**/
 static void
 get_isrch_spot(int num, int *hlp, int *posp, int *pat_hlp, int *pat_posp,
-	       int *csp, int *lenp, int *dirp, int *nomatch)
+	       int *end_posp, int *csp, int *lenp, int *dirp, int *nomatch)
 {
     *hlp = isrch_spots[num].hl;
     *posp = (int)isrch_spots[num].pos;
     *pat_hlp = isrch_spots[num].pat_hl;
     *pat_posp = (int)isrch_spots[num].pat_pos;
+    *end_posp = (int)isrch_spots[num].end_pos;
     *csp = (int)isrch_spots[num].cs;
     *lenp = (int)isrch_spots[num].len;
     *dirp = (isrch_spots[num].flags & ISS_FORWARD)? 1 : -1;
@@ -1011,6 +1014,9 @@
 #define FIRST_SEARCH_CHAR	(NORM_PROMPT_POS + 14)
 
 /**/
+int isearch_active, isearch_startpos, isearch_endpos;
+
+/**/
 static void
 doisearch(char **args, int dir, int pattern)
 {
@@ -1088,6 +1094,12 @@
      */
     int dup_ok = 0;
     /*
+     * End position of the match.
+     * When forward matching, this is the position for the cursor.
+     * When backward matching, the cursor position is pos.
+     */
+    int end_pos = 0;
+    /*
      * savekeys records the unget buffer, so that if we have arguments
      * they don't pollute the input.
      * feep indicates we should feep.  This is a well-known word
@@ -1138,7 +1150,7 @@
     pat_pos = pos = zlemetacs;
     for (;;) {
 	/* Remember the current values in case search fails (doesn't push). */
-	set_isrch_spot(top_spot, hl, pos, pat_hl, pat_pos,
+	set_isrch_spot(top_spot, hl, pos, pat_hl, pat_pos, end_pos,
 		       zlemetacs, sbptr, dir, nomatch);
 	if (sbptr == 1 && sbuf[0] == '^') {
 	    zlemetacs = 0;
@@ -1147,11 +1159,6 @@
 	} else if (sbptr > 0) {
 	    /* The matched text, used as flag that we matched */
 	    char *t = NULL;
-	    /*
-	     * When forward matching, position for the cursor.
-	     * When backward matching, the position is pos.
-	     */
-	    int forwardmatchpos = 0;
 	    last_line = zt;
 
 	    sbuf[sbptr] = '\0';
@@ -1233,7 +1240,7 @@
 			     */
 			    if (!skip_pos &&
 				pattryrefs(patprog, zt, -1, -1, 0, NULL, NULL,
-					   &forwardmatchpos))
+					   &end_pos))
 				t = zt;
 			} else {
 			    if (!matchlist && !skip_pos) {
@@ -1268,7 +1275,7 @@
 					newpos = pos + 1;
 				}
 				newpos = isearch_newpos(matchlist, newpos,
-							dir, &forwardmatchpos);
+							dir, &end_pos);
 				/* need a new list next time if off the end */
 				if (newpos < 0) {
 				    freematchlist(matchlist);
@@ -1316,7 +1323,7 @@
 			} else
 			    t = zlinefind(zt, pos, sbuf, dir, sens);
 			if (t)
-			    forwardmatchpos = pos + sbptr - (sbuf[0] == '^');
+			    end_pos = pos + sbptr - (sbuf[0] == '^');
 		    }
 		}
 		if (t) {
@@ -1333,7 +1340,8 @@
 		     && (isrch_spots[top_spot-1].flags >> ISS_NOMATCH_SHIFT))
 			top_spot--;
 		    get_isrch_spot(top_spot, &hl, &pos, &pat_hl, &pat_pos,
-				   &zlemetacs, &sbptr, &dir, &nomatch);
+				   &end_pos, &zlemetacs, &sbptr, &dir,
+				   &nomatch);
 		    if (!nomatch) {
 			feep = 1;
 			nomatch = 1;
@@ -1366,7 +1374,7 @@
 	    if (t || (nosearch && !nomatch)) {
 		zle_setline(he);
 		if (dir == 1)
-		    zlemetacs = forwardmatchpos;
+		    zlemetacs = end_pos;
 		else
 		    zlemetacs = pos;
 		statusline = ibuf + NORM_PROMPT_POS;
@@ -1384,11 +1392,39 @@
 	}
 	sbuf[sbptr] = '_';
 	sbuf[sbptr+1] = '\0';
+	if (!nomatch && sbptr && (sbptr > 1 || sbuf[0] != '^')) {
+#ifdef MULTIBYTE_SUPPORT
+	    int charpos = 0, charcount = 0, ret;
+	    wint_t wc;
+	    mbstate_t mbs;
+
+	    /*
+	     * Count unmetafied character positions for the
+	     * start and end of the match for the benefit of
+	     * highlighting.
+	     */
+	    memset(&mbs, 0, sizeof(mbs));
+	    while (charpos < end_pos) {
+		ret = mb_metacharlenconv_r(zlemetaline + charpos, &wc, &mbs);
+		if (charpos <= pos && pos < charpos + ret)
+		    isearch_startpos = charcount;
+		charcount++;
+		charpos += ret;
+	    }
+	    isearch_endpos = charcount;
+#else
+	    isearch_startpos = ztrsub(zlemetaline + pos, zlemetaline);
+	    isearch_endpos = ztrsub(zlemetaline + end_pos,
+				    zlemetaline);
+#endif
+	    isearch_active = 1;
+	} else
+	    isearch_active = 0;
     ref:
 	zrefresh();
 	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
 	    int i;
-	    get_isrch_spot(0, &hl, &pos, &pat_hl, &pat_pos,
+	    get_isrch_spot(0, &hl, &pos, &pat_hl, &pat_pos, &end_pos,
 			   &i, &sbptr, &dir, &nomatch);
 	    he = quietgethist(hl);
 	    zle_setline(he);
@@ -1410,7 +1446,7 @@
 	    	cmd == Th(z_backwarddeletechar)) {
 	    if (top_spot) {
 		get_isrch_spot(--top_spot, &hl, &pos, &pat_hl, &pat_pos,
-			       &zlemetacs, &sbptr, &dir, &nomatch);
+			       &end_pos, &zlemetacs, &sbptr, &dir, &nomatch);
 		patprog = NULL;
 		nosearch = 1;
 	    } else
@@ -1452,7 +1488,7 @@
 		  cmd == Th(z_historyincrementalpatternsearchbackward)) {
 	    pat_hl = hl;
 	    pat_pos = pos;
-	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos,
+	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos,
 			   zlemetacs, sbptr, dir, nomatch);
 	    if (dir != -1)
 		dir = -1;
@@ -1463,7 +1499,7 @@
 		  cmd == Th(z_historyincrementalpatternsearchforward)) {
 	    pat_hl = hl;
 	    pat_pos = pos;
-	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos,
+	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos,
 			   zlemetacs, sbptr, dir, nomatch);
 	    if (dir != 1)
 		dir = 1;
@@ -1473,7 +1509,7 @@
 	} else if(cmd == Th(z_virevrepeatsearch)) {
 	    pat_hl = hl;
 	    pat_pos = pos;
-	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos,
+	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos,
 			   zlemetacs, sbptr, dir, nomatch);
 	    dir = -odir;
 	    skip_pos = 1;
@@ -1481,7 +1517,7 @@
 	} else if(cmd == Th(z_virepeatsearch)) {
 	    pat_hl = hl;
 	    pat_pos = pos;
-	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos,
+	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos,
 			   zlemetacs, sbptr, dir, nomatch);
 	    dir = odir;
 	    skip_pos = 1;
@@ -1534,7 +1570,7 @@
 		feep = 1;
 		continue;
 	    }
-	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos,
+	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos,
 			   zlemetacs, sbptr, dir, nomatch);
 	    if (sbptr >= sibuf - FIRST_SEARCH_CHAR - 2 
 #ifdef MULTIBYTE_SUPPORT
@@ -1569,6 +1605,7 @@
     zsfree(okeymap);
     if (matchlist)
 	freematchlist(matchlist);
+    isearch_active = 0;
     /*
      * Don't allow unused characters provided as a string to the
      * widget to overflow and be used as separated commands.
Index: Src/Zle/zle_refresh.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_refresh.c,v
retrieving revision 1.61
diff -u -r1.61 zle_refresh.c
--- Src/Zle/zle_refresh.c	22 Apr 2008 15:08:14 -0000	1.61
+++ Src/Zle/zle_refresh.c	26 Apr 2008 22:42:00 -0000
@@ -242,8 +242,15 @@
  */
 struct region_highlight *region_highlights;
 /*
+ * Count of special uses of region highlighting, which account
+ * for the first few elements of region_highlights.
+ * 0: region between point and mark
+ * 1: isearch region
+ */
+#define N_SPECIAL_HIGHLIGHTS	(2)
+/*
  * Number of elements in region_highlights.
- * This includes the region between point and mark, element 0.
+ * This includes the special elements above.
  */
 int n_region_highlights;
 
@@ -380,12 +387,14 @@
     char **atrs = getaparam("zle_highlight");
     int special_atr_on_set = 0;
     int region_atr_on_set = 0;
+    int isearch_atr_on_set = 0;
+    struct region_highlight *rhp;
 
     special_atr_on = 0;
     if (!region_highlights) {
 	region_highlights = (struct region_highlight *)
-	    zshcalloc(sizeof(struct region_highlight));
-	n_region_highlights = 1;
+	    zshcalloc(N_SPECIAL_HIGHLIGHTS*sizeof(struct region_highlight));
+	n_region_highlights = N_SPECIAL_HIGHLIGHTS;
     } else {
 	region_highlights->atr = 0;
     }
@@ -394,14 +403,23 @@
 	for (; *atrs; atrs++) {
 	    if (!strcmp(*atrs, "none")) {
 		/* reset attributes for consistency... usually unnecessary */
-		special_atr_on = region_highlights->atr = 0;
-		special_atr_on_set = region_atr_on_set = 1;
+		special_atr_on = 0;
+		special_atr_on_set = region_atr_on_set =
+		    isearch_atr_on_set = 1;
+		for (rhp = region_highlights;
+		     rhp < region_highlights + N_SPECIAL_HIGHLIGHTS;
+		     rhp++) {
+		    rhp->atr = 0;
+		}
 	    } else if (strpfx("special:", *atrs)) {
 		match_highlight(*atrs + 8, &special_atr_on);
 		special_atr_on_set = 1;
 	    } else if (strpfx("region:", *atrs)) {
 		match_highlight(*atrs + 7, &region_highlights->atr);
 		region_atr_on_set = 1;
+	    } else if (strpfx("isearch:", *atrs)) {
+		match_highlight(*atrs + 8, &(region_highlights[1].atr));
+		isearch_atr_on_set = 1;
 	    }
 	}
     }
@@ -411,6 +429,8 @@
 	special_atr_on = TXTSTANDOUT;
     if (!region_atr_on_set)
 	region_highlights->atr = TXTSTANDOUT;
+    if (!isearch_atr_on_set)
+	region_highlights[1].atr = TXTUNDERLINE;
     special_atr_off = special_atr_on << TXT_ATTR_OFF_ON_SHIFT;
 }
 
@@ -432,15 +452,17 @@
 
     /* region_highlights may not have been set yet */
     if (!arrsize)
-	arrsize = 1;
+	arrsize = N_SPECIAL_HIGHLIGHTS;
     arrp = retarr = (char **)zhalloc(arrsize*sizeof(char *));
     /* ignore NULL termination */
     arrsize--;
     if (arrsize) {
 	struct region_highlight *rhp;
 
-	/* ignore point/mark at start */
-	for (rhp = region_highlights+1; arrsize--; rhp++, arrp++) {
+	/* ignore special highlighting */
+	for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
+	     arrsize--;
+	     rhp++, arrp++) {
 	    char digbuf1[DIGBUFSIZE], digbuf2[DIGBUFSIZE];
 	    int atrlen = 0, alloclen, done1;
 	    const struct highlight *hp;
@@ -503,9 +525,9 @@
     struct region_highlight *rhp;
 
     len = aval ? arrlen(aval) : 0;
-    if (n_region_highlights != len + 1) {
-	/* no null termination, but include point/mark region at start */
-	n_region_highlights = len + 1;
+    if (n_region_highlights != len + N_SPECIAL_HIGHLIGHTS) {
+	/* no null termination, but include special highlighting at start */
+	n_region_highlights = len + N_SPECIAL_HIGHLIGHTS;
 	region_highlights = (struct region_highlight *)
 	    zrealloc(region_highlights,
 		     sizeof(struct region_highlight) * n_region_highlights);
@@ -514,7 +536,9 @@
     if (!aval)
 	return;
 
-    for (rhp = region_highlights + 1; *aval; rhp++, aval++) {
+    for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
+	 *aval;
+	 rhp++, aval++) {
 	char *strp, *oldstrp;
 
 	oldstrp = *aval;
@@ -1050,6 +1074,13 @@
     } else {
 	region_highlights->start = region_highlights->end = -1;
     }
+    /* check for isearch string to highlight */
+    if (isearch_active) {
+	region_highlights[1].start = isearch_startpos;
+	region_highlights[1].end = isearch_endpos;
+    } else {
+	region_highlights[1].start = region_highlights[1].end = -1;
+    }
 
     if (clearlist && listshown > 0) {
 	if (tccan(TCCLEAREOD)) {


-- 
Peter Stephenson <p.w.stephenson@xxxxxxxxxxxx>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/



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