Recently, I've heard some script authors ponder whether there was a way
to access information about the last character search a user performed.
The ideas being explored were overriding ; and , to always search
forward/backward respectively, or providing visual cues for where a
character search would move the cursor.

There exists a mechanism for script authors to interact with normal
searches via the / register and the v:searchforward variable.

The attached patch implements comparable functionality for character
searches.

* A new, read-write ; register exposes the character which would be the
  target for a subsequent ; or , command.  If this register is set, then
  the direction and type of character search are (re)set as if the user
  had used t to perform the search.

* A new, read-write v:csearchforward variable exposes the direction of
  the character search, similar to v:searchforward.

* A new, read-write v:csearchuntil variable exposes whether the search
  is an "until" (t) search.

* Any normal character searches which happen within a function or
  autocmd will not affect the user's character search state, similar to
  the handling of normal searches.  However, a script author may affect
  that through using the above listed variables/register.

Aside from enabling script authors to better interact with character
searches, this also makes it easier to search for composed characters
using character search.  One currently has to create a keymap file and
use “:loadkeymap” to easily enter a composed character as the character
search value.  With this functionality, one can instead simply “:let
@;="a\u0301"” to search for á.

Cheers,
-- 
James
GPG Key: 4096R/331BA3DB 2011-12-05 James McCoy <[email protected]>

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.
diff --git c/runtime/doc/change.txt w/runtime/doc/change.txt
index 79aa179..c631cd7 100644
--- c/runtime/doc/change.txt
+++ w/runtime/doc/change.txt
@@ -1285,6 +1285,15 @@ Note that the value is restored when returning from a function
 |function-search-undo|.
 {not in Vi}
 
+11. Last character search register	";		*quote_;* *quote;*
+Contains the most recent character search.  This is used for the |;| and |,|
+commands.  It is writable with `:let`.  This register can not be used as a
+destination for a yank or delete.  The search direction is available in
+|v:csearchforward|.
+Note that the value is restored when returning from a function
+|function-search-undo|.
+{not in Vi}
+
 							*@/*
 You can write to a register with a `:let` command |:let-@|.  Example: >
 	:let @/ = "the"
diff --git c/runtime/doc/eval.txt w/runtime/doc/eval.txt
index d9f49ae..b068b3a 100644
--- c/runtime/doc/eval.txt
+++ w/runtime/doc/eval.txt
@@ -1351,6 +1351,22 @@ v:count		The count given for the last Normal mode command.  Can be used
 v:count1	Just like "v:count", but defaults to one when no count is
 		used.
 
+v:csearchforward		*v:csearchforward* *csearchforward-variable*
+		Character search direction: 1 after a forward character
+		search, 0 after a backward search.  It is reset to forward
+		when directly setting the last character search, see |quote;|.
+		Note that the value is restored when returning from a
+		function. |function-search-undo|.
+		Read-write.
+
+				*v:csearchuntil* *csearchuntil-variable*
+v:csearchuntil	Character search type: 1 after a |t| or |T| character search,
+		0 after an |f| or |F| character search.  It is reset to 1 when
+		directly setting the last character search, see |quote;|.
+		Note that the value is restored when returning from a
+		function. |function-search-undo|.
+		Read-write.
+
 						*v:ctype* *ctype-variable*
 v:ctype		The current locale setting for characters of the runtime
 		environment.  This allows Vim scripts to be aware of the
diff --git c/src/eval.c w/src/eval.c
index 0537c4e..24657c5 100644
--- c/src/eval.c
+++ w/src/eval.c
@@ -368,6 +368,8 @@ static struct vimvar
     {VV_NAME("option_new",	 VAR_STRING), VV_RO},
     {VV_NAME("option_old",	 VAR_STRING), VV_RO},
     {VV_NAME("option_type",	 VAR_STRING), VV_RO},
+    {VV_NAME("csearchforward",	 VAR_NUMBER), 0},
+    {VV_NAME("csearchuntil",	 VAR_NUMBER), 0},
 };
 
 /* shorthand */
@@ -892,6 +894,8 @@ eval_init()
 	    hash_add(&compat_hashtab, p->vv_di.di_key);
     }
     set_vim_var_nr(VV_SEARCHFORWARD, 1L);
+    set_vim_var_nr(VV_CSEARCHFORWARD, 1L);
+    set_vim_var_nr(VV_CSEARCHUNTIL, 1L);
     set_vim_var_nr(VV_HLSEARCH, 1L);
     set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc());
     set_reg_var(0);  /* default for v:register is not 0 but '"' */
@@ -21648,6 +21652,10 @@ set_var(name, tv, copy)
 		v->di_tv.vval.v_number = get_tv_number(tv);
 		if (STRCMP(varname, "searchforward") == 0)
 		    set_search_direction(v->di_tv.vval.v_number ? '/' : '?');
+		else if (STRCMP(varname, "csearchforward") == 0)
+		    set_csearch_direction(v->di_tv.vval.v_number ? FORWARD : BACKWARD);
+		else if (STRCMP(varname, "csearchuntil") == 0)
+		    set_csearch_until(!!v->di_tv.vval.v_number);
 #ifdef FEAT_SEARCH_EXTRA
 		else if (STRCMP(varname, "hlsearch") == 0)
 		{
diff --git c/src/ops.c w/src/ops.c
index a9eec34..39b753e 100644
--- c/src/ops.c
+++ w/src/ops.c
@@ -856,9 +856,9 @@ valid_yank_reg(regname, writing)
     if (       (regname > 0 && ASCII_ISALNUM(regname))
 	    || (!writing && vim_strchr((char_u *)
 #ifdef FEAT_EVAL
-				    "/.%:="
+				    "/;.%:="
 #else
-				    "/.%:"
+				    "/;.%:"
 #endif
 					, regname) != NULL)
 	    || regname == '#'
@@ -1528,6 +1528,10 @@ get_spec_reg(regname, argp, allocated, errmsg)
 	    *argp = last_search_pat();
 	    return TRUE;
 
+	case ';':		/* last character search */
+	    *argp = last_csearch();
+	    return TRUE;
+
 	case '.':		/* last inserted text */
 	    *argp = get_last_insert_save();
 	    *allocated = TRUE;
@@ -4230,6 +4234,16 @@ ex_display(eap)
 	dis_msg(last_search_pat(), FALSE);
     }
 
+    /*
+     * display last character search
+     */
+    if (STRCMP(last_csearch(), "") != 0
+		&& (arg == NULL || vim_strchr(arg, ';') != NULL) && !got_int)
+    {
+	MSG_PUTS("\n\";    ");
+	dis_msg(last_csearch(), FALSE);
+    }
+
 #ifdef FEAT_EVAL
     /*
      * display last used expression
@@ -6590,6 +6604,7 @@ write_reg_contents_lst(name, strings, maxlen, must_append, yank_type, block_len)
     struct yankreg  *old_y_previous, *old_y_current;
 
     if (name == '/'
+	    || name == ';'
 #ifdef FEAT_EVAL
 	    || name == '='
 #endif
@@ -6601,8 +6616,8 @@ write_reg_contents_lst(name, strings, maxlen, must_append, yank_type, block_len)
 	    s = (char_u *)"";
 	else if (strings[1] != NULL)
 	{
-	    EMSG(_("E883: search pattern and expression register may not "
-			"contain two or more lines"));
+	    EMSG(_("E883: search pattern, character search, and expression "
+			"register may not contain two or more lines"));
 	    return;
 	}
 	else
@@ -6647,6 +6662,21 @@ write_reg_contents_ex(name, str, maxlen, must_append, yank_type, block_len)
 	return;
     }
 
+    if (name == ';')
+    {
+	if (enc_utf8)
+	{
+	    int pcc[MAX_MCO];
+	    int c = utfc_ptr2char(str, pcc);
+	    set_last_csearch(c, str, utfc_ptr2len(str));
+	}
+	else
+	    set_last_csearch(mb_ptr2char(str), str, mb_ptr2len(str));
+	set_csearch_direction(FORWARD);
+	set_csearch_until(TRUE);
+	return;
+    }
+
     if (name == '#')
     {
 	buf_T	*buf;
diff --git c/src/proto/search.pro w/src/proto/search.pro
index 07f6087..14530d5 100644
--- c/src/proto/search.pro
+++ w/src/proto/search.pro
@@ -8,6 +8,10 @@ void restore_search_patterns __ARGS((void));
 void free_search_patterns __ARGS((void));
 int ignorecase __ARGS((char_u *pat));
 int pat_has_uppercase __ARGS((char_u *pat));
+char_u *last_csearch __ARGS((void));
+void set_last_csearch __ARGS((int c, char_u *s, int len));
+void set_csearch_direction __ARGS((int cdir));
+void set_csearch_until __ARGS((int t_cmd));
 char_u *last_search_pat __ARGS((void));
 void reset_search_dir __ARGS((void));
 void set_last_search_pat __ARGS((char_u *s, int idx, int magic, int setlast));
diff --git c/src/search.c w/src/search.c
index 4905a49..f342649 100644
--- c/src/search.c
+++ w/src/search.c
@@ -89,6 +89,14 @@ static struct spat spats[2] =
 
 static int last_idx = 0;	/* index in spats[] for RE_LAST */
 
+static char_u lastc[2] = { NUL, NUL };	/* last character searched for */
+static int lastcdir = FORWARD;	/* last direction of character search */
+static int last_t_cmd = TRUE;	/* last search t_cmd */
+#ifdef FEAT_MBYTE
+static char_u	lastc_bytes[MB_MAXBYTES + 1];
+static int	lastc_bytelen = 1;	/* >1 for multi-byte char */
+#endif
+
 #if defined(FEAT_AUTOCMD) || defined(FEAT_EVAL) || defined(PROTO)
 /* copy of spats[], for keeping the search patterns while executing autocmds */
 static struct spat  saved_spats[2];
@@ -96,6 +104,13 @@ static int	    saved_last_idx = 0;
 # ifdef FEAT_SEARCH_EXTRA
 static int	    saved_no_hlsearch = 0;
 # endif
+static char_u	saved_lastc[2] = { NUL, NUL };
+static int	saved_lastcdir = FORWARD;
+static int	saved_last_t_cmd = TRUE;
+# ifdef FEAT_MBYTE
+static char_u	saved_bytes[MB_MAXBYTES + 1];
+static int	saved_bytelen = 1;
+# endif
 #endif
 
 static char_u	    *mr_pattern = NULL;	/* pattern used by search_regcomp() */
@@ -315,6 +330,13 @@ save_search_patterns()
 # ifdef FEAT_SEARCH_EXTRA
 	saved_no_hlsearch = no_hlsearch;
 # endif
+	memcpy(saved_lastc, lastc, sizeof(lastc));
+	saved_lastcdir = lastcdir;
+	saved_last_t_cmd = last_t_cmd;
+# ifdef FEAT_MBYTE
+	memcpy(saved_bytes, lastc_bytes, sizeof(lastc_bytes));
+	saved_bytelen = lastc_bytelen;
+# endif
     }
 }
 
@@ -334,6 +356,13 @@ restore_search_patterns()
 # ifdef FEAT_SEARCH_EXTRA
 	SET_NO_HLSEARCH(saved_no_hlsearch);
 # endif
+	memcpy(lastc, saved_lastc, sizeof(lastc));
+	lastcdir = saved_lastcdir;
+	last_t_cmd = saved_last_t_cmd;
+# ifdef FEAT_MBYTE
+	memcpy(lastc_bytes, saved_bytes, sizeof(lastc_bytes));
+	lastc_bytelen = saved_bytelen;
+# endif
     }
 }
 #endif
@@ -419,6 +448,69 @@ pat_has_uppercase(pat)
 }
 
     char_u *
+last_csearch()
+{
+#ifdef FEAT_MBYTE
+    return lastc_bytes;
+#else
+    return lastc;
+#endif
+}
+
+    void
+set_last_csearch(c, s, len)
+    int		c;
+    char_u	*s;
+    int		len;
+{
+    *lastc = c;
+    set_csearch_direction(FORWARD);
+    set_csearch_until(TRUE);
+
+#ifdef FEAT_MBYTE
+    lastc_bytelen = len;
+    if (len)
+	memcpy(lastc_bytes, s, len);
+    else
+	vim_memset(lastc_bytes, 0, sizeof(lastc_bytes));
+#endif
+
+#if defined(FEAT_AUTOCMD) || defined(FEAT_EVAL)
+    *saved_lastc = c;
+    saved_lastcdir = FORWARD;
+    saved_last_t_cmd = TRUE;
+
+# ifdef FEAT_MBYTE
+    saved_bytelen = len;
+    if (len)
+	memcpy(saved_bytes, s, len);
+    else
+	vim_memset(saved_bytes, 0, sizeof(saved_bytes));
+# endif
+#endif
+}
+
+    void
+set_csearch_direction(cdir)
+    int cdir;
+{
+    lastcdir = cdir;
+#ifdef FEAT_EVAL
+    set_vim_var_nr(VV_CSEARCHFORWARD, cdir == FORWARD);
+#endif
+}
+
+    void
+set_csearch_until(t_cmd)
+    int t_cmd;
+{
+    last_t_cmd = t_cmd;
+#ifdef FEAT_EVAL
+    set_vim_var_nr(VV_CSEARCHUNTIL, t_cmd);
+#endif
+}
+
+    char_u *
 last_search_pat()
 {
     return spats[last_idx].pat;
@@ -1559,47 +1651,42 @@ searchc(cap, t_cmd)
     int			c = cap->nchar;	/* char to search for */
     int			dir = cap->arg;	/* TRUE for searching forward */
     long		count = cap->count1;	/* repeat count */
-    static int		lastc = NUL;	/* last character searched for */
-    static int		lastcdir;	/* last direction of character search */
-    static int		last_t_cmd;	/* last search t_cmd */
     int			col;
     char_u		*p;
     int			len;
     int			stop = TRUE;
-#ifdef FEAT_MBYTE
-    static char_u	bytes[MB_MAXBYTES + 1];
-    static int		bytelen = 1;	/* >1 for multi-byte char */
-#endif
 
     if (c != NUL)	/* normal search: remember args for repeat */
     {
 	if (!KeyStuffed)    /* don't remember when redoing */
 	{
-	    lastc = c;
-	    lastcdir = dir;
-	    last_t_cmd = t_cmd;
+	    *lastc = c;
+	    set_csearch_direction(dir);
+	    set_csearch_until(t_cmd);
 #ifdef FEAT_MBYTE
-	    bytelen = (*mb_char2bytes)(c, bytes);
+	    lastc_bytelen = (*mb_char2bytes)(c, lastc_bytes);
 	    if (cap->ncharC1 != 0)
 	    {
-		bytelen += (*mb_char2bytes)(cap->ncharC1, bytes + bytelen);
+		lastc_bytelen += (*mb_char2bytes)(cap->ncharC1,
+			lastc_bytes + lastc_bytelen);
 		if (cap->ncharC2 != 0)
-		    bytelen += (*mb_char2bytes)(cap->ncharC2, bytes + bytelen);
+		    lastc_bytelen += (*mb_char2bytes)(cap->ncharC2,
+			    lastc_bytes + lastc_bytelen);
 	    }
 #endif
 	}
     }
     else		/* repeat previous search */
     {
-	if (lastc == NUL)
+	if (*lastc == NUL)
 	    return FAIL;
 	if (dir)	/* repeat in opposite direction */
 	    dir = -lastcdir;
 	else
 	    dir = lastcdir;
 	t_cmd = last_t_cmd;
-	c = lastc;
-	/* For multi-byte re-use last bytes[] and bytelen. */
+	c = *lastc;
+	/* For multi-byte re-use last lastc_bytes[] and lastc_bytelen. */
 
 	/* Force a move of at least one char, so ";" and "," will move the
 	 * cursor, even if the cursor is right in front of char we are looking
@@ -1636,14 +1723,14 @@ searchc(cap, t_cmd)
 			return FAIL;
 		    col -= (*mb_head_off)(p, p + col - 1) + 1;
 		}
-		if (bytelen == 1)
+		if (lastc_bytelen == 1)
 		{
 		    if (p[col] == c && stop)
 			break;
 		}
 		else
 		{
-		    if (vim_memcmp(p + col, bytes, bytelen) == 0 && stop)
+		    if (vim_memcmp(p + col, lastc_bytes, lastc_bytelen) == 0 && stop)
 			break;
 		}
 		stop = TRUE;
@@ -1671,8 +1758,8 @@ searchc(cap, t_cmd)
 	if (has_mbyte)
 	{
 	    if (dir < 0)
-		/* Landed on the search char which is bytelen long */
-		col += bytelen - 1;
+		/* Landed on the search char which is lastc_bytelen long */
+		col += lastc_bytelen - 1;
 	    else
 		/* To previous char, which may be multi-byte. */
 		col -= (*mb_head_off)(p, p + col);
diff --git c/src/testdir/test_eval.ok w/src/testdir/test_eval.ok
index cda425c..52fe426 100644
--- c/src/testdir/test_eval.ok
+++ w/src/testdir/test_eval.ok
@@ -333,9 +333,9 @@ Vim(call):E731: using Dictionary as a String
 Executing call setreg(1, 2, [])
 Vim(call):E730: using List as a String
 Executing call setreg("/", ["1", "2"])
-Vim(call):E883: search pattern and expression register may not contain two or more lines
+Vim(call):E883: search pattern, character search, and expression register may not contain two or more lines
 Executing call setreg("=", ["1", "2"])
-Vim(call):E883: search pattern and expression register may not contain two or more lines
+Vim(call):E883: search pattern, character search, and expression register may not contain two or more lines
 Executing call setreg(1, ["", "", [], ""])
 Vim(call):E730: using List as a String
 Vim(function):E128: Function name must start with a capital or "s:": g:test()
diff --git c/src/vim.h w/src/vim.h
index 809f312..7b87c2b 100644
--- c/src/vim.h
+++ w/src/vim.h
@@ -1902,7 +1902,9 @@ typedef int proftime_T;	    /* dummy for function prototypes */
 #define VV_OPTION_NEW   59
 #define VV_OPTION_OLD   60
 #define VV_OPTION_TYPE  61
-#define VV_LEN		62	/* number of v: vars */
+#define VV_CSEARCHFORWARD 62
+#define VV_CSEARCHUNTIL 63
+#define VV_LEN		64	/* number of v: vars */
 
 #ifdef FEAT_CLIPBOARD
 

Attachment: signature.asc
Description: Digital signature

Raspunde prin e-mail lui