changeset 7266:6ba7182fb7bd v7.4.941

commit https://github.com/vim/vim/commit/0f6562e9036f889185dff49a75c7fc5ffb28b307 Author: Bram Moolenaar <Bram@vim.org> Date: Tue Nov 24 18:48:14 2015 +0100 patch 7.4.941 Problem: There is no way to ignore case only for tag searches. Solution: Add the 'tagcase' option. (Gary Johnson)
author Christian Brabandt <cb@256bit.org>
date Tue, 24 Nov 2015 19:00:06 +0100
parents b985668725fa
children fd72cd1b6f1a
files runtime/doc/options.txt runtime/doc/quickref.txt runtime/doc/tagsrch.txt runtime/doc/usr_29.txt runtime/optwin.vim src/Makefile src/buffer.c src/option.c src/option.h src/structs.h src/tag.c src/testdir/Make_amiga.mak src/testdir/Make_dos.mak src/testdir/Make_ming.mak src/testdir/Make_os2.mak src/testdir/Make_vms.mms src/testdir/Makefile src/testdir/test_tagcase.in src/testdir/test_tagcase.ok src/version.c
diffstat 20 files changed, 254 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -4109,7 +4109,7 @@ A jump table for the options with a shor
 			global
 	Ignore case in search patterns.  Also used when searching in the tags
 	file.
-	Also see 'smartcase'.
+	Also see 'smartcase' and 'tagcase'.
 	Can be overruled by using "\c" or "\C" in the pattern, see
 	|/ignorecase|.
 
@@ -7339,19 +7339,22 @@ A jump table for the options with a shor
 <	[The whitespace before and after the '0' must be a single <Tab>]
 
 	When a binary search was done and no match was found in any of the
-	files listed in 'tags', and 'ignorecase' is set or a pattern is used
+	files listed in 'tags', and case is ignored or a pattern is used
 	instead of a normal tag name, a retry is done with a linear search.
 	Tags in unsorted tags files, and matches with different case will only
 	be found in the retry.
 
 	If a tag file indicates that it is case-fold sorted, the second,
-	linear search can be avoided for the 'ignorecase' case.  Use a value
-	of '2' in the "!_TAG_FILE_SORTED" line for this.  A tag file can be
-	case-fold sorted with the -f switch to "sort" in most unices, as in
-	the command: "sort -f -o tags tags".  For "Exuberant ctags" version
-	5.x or higher (at least 5.5) the --sort=foldcase switch can be used
-	for this as well.  Note that case must be folded to uppercase for this
-	to work.
+	linear search can be avoided when case is ignored.  Use a value of '2'
+	in the "!_TAG_FILE_SORTED" line for this.  A tag file can be case-fold
+	sorted with the -f switch to "sort" in most unices, as in the command:
+	"sort -f -o tags tags".  For "Exuberant ctags" version 5.x or higher
+	(at least 5.5) the --sort=foldcase switch can be used for this as
+	well.  Note that case must be folded to uppercase for this to work.
+
+	By default, tag searches are case-sensitive.  Case is ignored when
+	'ignorecase' is set and 'tagcase' is "followic", or when 'tagcase' is
+	"ignore".
 
 	When 'tagbsearch' is off, tags searching is slower when a full match
 	exists, but faster when no full match exists.  Tags in unsorted tags
@@ -7363,6 +7366,16 @@ A jump table for the options with a shor
 	command-line completion and ":help").
 	{Vi: always uses binary search in some versions}
 
+							*'tagcase'* *'tc'*
+'tagcase' 'tc'		string	(default "followic")
+			global or local to buffer |global-local|
+			{not in Vi}
+	This option specifies how case is handled when searching the tags
+	file:
+	   followic	Follow the 'ignorecase' option
+	   ignore	Ignore case
+	   match	Match case
+
 						*'taglength'* *'tl'*
 'taglength' 'tl'	number	(default 0)
 			global
--- a/runtime/doc/quickref.txt
+++ b/runtime/doc/quickref.txt
@@ -904,6 +904,7 @@ Short explanation of each option:		*opti
 'tabpagemax'	  'tpm'     maximum number of tab pages for |-p| and "tab all"
 'tabstop'	  'ts'	    number of spaces that <Tab> in file uses
 'tagbsearch'	  'tbs'     use binary searching in tags files
+'tagcase'	  'tc'      how to handle case when searching in tags files
 'taglength'	  'tl'	    number of significant characters for a tag
 'tagrelative'	  'tr'	    file names in tag file are relative
 'tags'		  'tag'     list of file names used by the tag command
--- a/runtime/doc/tagsrch.txt
+++ b/runtime/doc/tagsrch.txt
@@ -85,11 +85,13 @@ changed, to avoid confusion when using "
 ":tag {ident}".
 
 The ignore-case matches are not found for a ":tag" command when the
-'ignorecase' option is off.  They are found when a pattern is used (starting
-with a "/") and for ":tselect", also when 'ignorecase' is off.  Note that
-using ignore-case tag searching disables binary searching in the tags file,
-which causes a slowdown.  This can be avoided by fold-case sorting the tag
-file.  See the 'tagbsearch' option for an explanation.
+'ignorecase' option is off and 'tagcase' is "followic" or when 'tagcase' is
+"match".  They are found when a pattern is used (starting with a "/") and for
+":tselect", also when 'ignorecase' is off and 'tagcase' is "followic" or when
+'tagcase' is "match".  Note that using ignore-case tag searching disables
+binary searching in the tags file, which causes a slowdown.  This can be
+avoided by fold-case sorting the tag file.  See the 'tagbsearch' option for an
+explanation.
 
 ==============================================================================
 2. Tag stack				*tag-stack* *tagstack* *E425*
@@ -440,12 +442,13 @@ file "tags".  It can also be used to acc
 The next file in the list is not used when:
 - A matching static tag for the current buffer has been found.
 - A matching global tag has been found.
-This also depends on the 'ignorecase' option.  If it is off, and the tags file
-only has a match without matching case, the next tags file is searched for a
-match with matching case.  If no tag with matching case is found, the first
-match without matching case is used.  If 'ignorecase' is on, and a matching
-global tag with or without matching case is found, this one is used, no
-further tags files are searched.
+This also depends on whether case is ignored.  Case is ignored when
+'ignorecase' is set and 'tagcase' is "followic", or when 'tagcase' is
+"ignore".  If case is not ignored, and the tags file only has a match without
+matching case, the next tags file is searched for a match with matching case.
+If no tag with matching case is found, the first match without matching case
+is used.  If case is ignored, and a matching global tag with or without
+matching case is found, this one is used, no further tags files are searched.
 
 When a tag file name starts with "./", the '.' is replaced with the path of
 the current file.  This makes it possible to use a tags file in the directory
@@ -579,8 +582,10 @@ that indicates if the file was sorted.  
 binary searching for the tags file:
 	!_TAG_FILE_SORTED<Tab>1<Tab>{anything} ~
 
-A tag file may be case-fold sorted to avoid a linear search when 'ignorecase'
-is on.  See 'tagbsearch' for details.  The value '2' should be used then:
+A tag file may be case-fold sorted to avoid a linear search when case is
+ignored.  (Case is ignored when 'ignorecase' is set and 'tagcase' is
+"followic", or when 'tagcase' is "ignore".)  See 'tagbsearch' for details.
+The value '2' should be used then:
 	!_TAG_FILE_SORTED<Tab>2<Tab>{anything} ~
 
 The other tag that Vim recognizes, but only when compiled with the
--- a/runtime/doc/usr_29.txt
+++ b/runtime/doc/usr_29.txt
@@ -255,7 +255,8 @@ function.
 
 RELATED ITEMS
 
-You can set 'ignorecase' to make case in tag names be ignored.
+To make case in tag names be ignored, you can set 'ignorecase' while leaving
+'tagcase' as "followic", or set 'tagcase' to "ignore".
 
 The 'tagbsearch' option tells if the tags file is sorted or not.  The default
 is to assume a sorted tags file, which makes a tags search a lot faster, but
--- a/runtime/optwin.vim
+++ b/runtime/optwin.vim
@@ -289,6 +289,10 @@ call append("$", " \tset tl=" . &tl)
 call append("$", "tags\tlist of file names to search for tags")
 call append("$", "\t(global or local to buffer)")
 call <SID>OptionG("tag", &tag)
+call append("$", "tagcase\thow to handle case when searching in tags files:")
+call append("$", "\t\"followic\" to follow 'ignorecase', \"ignore\" or \"match\"")
+call append("$", "\t(global or local to buffer)")
+call <SID>OptionG("tc", &tc)
 call append("$", "tagrelative\tfile names in a tags file are relative to the tags file")
 call <SID>BinOptionG("tr", &tr)
 call append("$", "tagstack\ta :tag command will use the tagstack")
--- a/src/Makefile
+++ b/src/Makefile
@@ -1930,6 +1930,7 @@ test1 \
 	test_search_mbyte \
 	test_set \
 	test_signs \
+	test_tagcase \
 	test_textobjects \
 	test_utf8 \
 	test_writefile \
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1991,6 +1991,7 @@ free_buf_options(buf, free_p_ff)
     clear_string_option(&buf->b_p_ep);
     clear_string_option(&buf->b_p_path);
     clear_string_option(&buf->b_p_tags);
+    clear_string_option(&buf->b_p_tc);
 #ifdef FEAT_INS_EXPAND
     clear_string_option(&buf->b_p_dict);
     clear_string_option(&buf->b_p_tsr);
--- a/src/option.c
+++ b/src/option.c
@@ -174,6 +174,7 @@
 #define PV_SW		OPT_BUF(BV_SW)
 #define PV_SWF		OPT_BUF(BV_SWF)
 #define PV_TAGS		OPT_BOTH(OPT_BUF(BV_TAGS))
+#define PV_TC		OPT_BOTH(OPT_BUF(BV_TC))
 #define PV_TS		OPT_BUF(BV_TS)
 #define PV_TW		OPT_BUF(BV_TW)
 #define PV_TX		OPT_BUF(BV_TX)
@@ -2602,6 +2603,9 @@ static struct vimoption
 			    {(char_u *)TRUE, (char_u *)0L}
 #endif
 			    SCRIPTID_INIT},
+    {"tagcase",	    "tc",   P_STRING|P_VIM,
+			    (char_u *)&p_tc, PV_TC,
+			    {(char_u *)"followic", (char_u *)"followic"} SCRIPTID_INIT},
     {"taglength",   "tl",   P_NUM|P_VI_DEF,
 			    (char_u *)&p_tl, PV_NONE,
 			    {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT},
@@ -5363,6 +5367,7 @@ didset_options()
     (void)opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, TRUE);
 #endif
     (void)opt_strings_flags(p_dy, p_dy_values, &dy_flags, TRUE);
+    (void)opt_strings_flags(p_tc, p_tc_values, &tc_flags, FALSE);
 #ifdef FEAT_VIRTUALEDIT
     (void)opt_strings_flags(p_ve, p_ve_values, &ve_flags, TRUE);
 #endif
@@ -5525,6 +5530,7 @@ check_buf_options(buf)
     check_string_option(&buf->b_p_ep);
     check_string_option(&buf->b_p_path);
     check_string_option(&buf->b_p_tags);
+    check_string_option(&buf->b_p_tc);
 #ifdef FEAT_INS_EXPAND
     check_string_option(&buf->b_p_dict);
     check_string_option(&buf->b_p_tsr);
@@ -7044,6 +7050,30 @@ did_set_string_option(opt_idx, varp, new
 	    errmsg = e_invarg;
     }
 
+    /* 'tagcase' */
+    else if (gvarp == &p_tc)
+    {
+	unsigned int	*flags;
+
+	if (opt_flags & OPT_LOCAL)
+	{
+	    p = curbuf->b_p_tc;
+	    flags = &curbuf->b_tc_flags;
+	}
+	else
+	{
+	    p = p_tc;
+	    flags = &tc_flags;
+	}
+
+	if ((opt_flags & OPT_LOCAL) && *p == NUL)
+	    /* make the local value empty: use the global value */
+	    *flags = 0;
+	else if (*p == NUL
+		|| opt_strings_flags(p, p_tc_values, flags, FALSE) != OK)
+	    errmsg = e_invarg;
+    }
+
 #ifdef FEAT_MBYTE
     /* 'casemap' */
     else if (varp == &p_cmp)
@@ -10083,6 +10113,10 @@ unset_global_local_option(name, from)
 	case PV_TAGS:
 	    clear_string_option(&buf->b_p_tags);
 	    break;
+	case PV_TC:
+	    clear_string_option(&buf->b_p_tc);
+	    buf->b_tc_flags = 0;
+	    break;
 #ifdef FEAT_FIND_ID
 	case PV_DEF:
 	    clear_string_option(&buf->b_p_def);
@@ -10164,6 +10198,7 @@ get_varp_scope(p, opt_flags)
 	    case PV_PATH: return (char_u *)&(curbuf->b_p_path);
 	    case PV_AR:   return (char_u *)&(curbuf->b_p_ar);
 	    case PV_TAGS: return (char_u *)&(curbuf->b_p_tags);
+	    case PV_TC:   return (char_u *)&(curbuf->b_p_tc);
 #ifdef FEAT_FIND_ID
 	    case PV_DEF:  return (char_u *)&(curbuf->b_p_def);
 	    case PV_INC:  return (char_u *)&(curbuf->b_p_inc);
@@ -10218,6 +10253,8 @@ get_varp(p)
 				    ? (char_u *)&(curbuf->b_p_ar) : p->var;
 	case PV_TAGS:	return *curbuf->b_p_tags != NUL
 				    ? (char_u *)&(curbuf->b_p_tags) : p->var;
+	case PV_TC:	return *curbuf->b_p_tc != NUL
+				    ? (char_u *)&(curbuf->b_p_tc) : p->var;
 	case PV_BKC:	return *curbuf->b_p_bkc != NUL
 				    ? (char_u *)&(curbuf->b_p_bkc) : p->var;
 #ifdef FEAT_FIND_ID
@@ -10826,6 +10863,8 @@ buf_copy_options(buf, flags)
 	    buf->b_p_kp = empty_option;
 	    buf->b_p_path = empty_option;
 	    buf->b_p_tags = empty_option;
+	    buf->b_p_tc = empty_option;
+	    buf->b_tc_flags = 0;
 #ifdef FEAT_FIND_ID
 	    buf->b_p_def = empty_option;
 	    buf->b_p_inc = empty_option;
--- a/src/option.h
+++ b/src/option.h
@@ -820,6 +820,14 @@ static char *(p_swb_values[]) = {"useope
 #define SWB_NEWTAB		0x008
 #define SWB_VSPLIT		0x010
 EXTERN int	p_tbs;		/* 'tagbsearch' */
+EXTERN char_u	*p_tc;		/* 'tagcase' */
+EXTERN unsigned tc_flags;       /* flags from 'tagcase' */
+#ifdef IN_OPTION_C
+static char *(p_tc_values[]) = {"followic", "ignore", "match", NULL};
+#endif
+#define TC_FOLLOWIC		0x01
+#define TC_IGNORE		0x02
+#define TC_MATCH		0x04
 EXTERN long	p_tl;		/* 'taglength' */
 EXTERN int	p_tr;		/* 'tagrelative' */
 EXTERN char_u	*p_tags;	/* 'tags' */
@@ -1081,6 +1089,7 @@ enum
     , BV_SW
     , BV_SWF
     , BV_TAGS
+    , BV_TC
     , BV_TS
     , BV_TW
     , BV_TX
--- a/src/structs.h
+++ b/src/structs.h
@@ -1674,6 +1674,8 @@ struct file_buffer
     char_u	*b_p_path;	/* 'path' local value */
     int		b_p_ar;		/* 'autoread' local value */
     char_u	*b_p_tags;	/* 'tags' local value */
+    char_u	*b_p_tc;	/* 'tagcase' local value */
+    unsigned	b_tc_flags;     /* flags for 'tagcase' */
 #ifdef FEAT_INS_EXPAND
     char_u	*b_p_dict;	/* 'dictionary' local value */
     char_u	*b_p_tsr;	/* 'thesaurus' local value */
@@ -2063,7 +2065,7 @@ struct window_S
 				       current virtual column */
 
     /*
-     * the next six are used to update the visual part
+     * the next seven are used to update the visual part
      */
     char	w_old_visual_mode;  /* last known VIsual_mode */
     linenr_T	w_old_cursor_lnum;  /* last known end of visual part */
--- a/src/tag.c
+++ b/src/tag.c
@@ -1385,6 +1385,18 @@ find_tags(pat, num_matches, matchesp, fl
     int		use_cscope = (flags & TAG_CSCOPE);
 #endif
     int		verbose = (flags & TAG_VERBOSE);
+    int		save_p_ic = p_ic;
+
+    /*
+     * Change the value of 'ignorecase' according to 'tagcase' for the
+     * duration of this function.
+     */
+    switch (curbuf->b_tc_flags ? curbuf->b_tc_flags : tc_flags)
+    {
+	case TC_FOLLOWIC:               break;
+	case TC_IGNORE:   p_ic = TRUE;  break;
+	case TC_MATCH:    p_ic = FALSE; break;
+    }
 
     help_save = curbuf->b_help;
     orgpat.pat = pat;
@@ -2552,6 +2564,8 @@ findtag_end:
     vim_free(saved_pat);
 #endif
 
+    p_ic = save_p_ic;
+
     return retval;
 }
 
--- a/src/testdir/Make_amiga.mak
+++ b/src/testdir/Make_amiga.mak
@@ -66,6 +66,7 @@ SCRIPTS = test1.out test3.out test4.out 
 		test_search_mbyte.out \
 		test_set.out \
 		test_signs.out \
+		test_tagcase.out \
 		test_textobjects.out \
 		test_utf8.out
 
@@ -221,5 +222,6 @@ test_ruby.out: test_ruby.in
 test_search_mbyte.out: test_search_mbyte.in
 test_set.out: test_set.in
 test_signs.out: test_signs.in
+test_tagcase.out: test_tagcase.in
 test_textobjects.out: test_textobjects.in
 test_utf8.out: test_utf8.in
--- a/src/testdir/Make_dos.mak
+++ b/src/testdir/Make_dos.mak
@@ -65,6 +65,7 @@ SCRIPTS =	test3.out test4.out test5.out 
 		test_search_mbyte.out \
 		test_set.out \
 		test_signs.out \
+		test_tagcase.out \
 		test_textobjects.out \
 		test_utf8.out
 
--- a/src/testdir/Make_ming.mak
+++ b/src/testdir/Make_ming.mak
@@ -87,6 +87,7 @@ SCRIPTS =	test3.out test4.out test5.out 
 		test_search_mbyte.out \
 		test_set.out \
 		test_signs.out \
+		test_tagcase.out \
 		test_textobjects.out \
 		test_utf8.out
 
--- a/src/testdir/Make_os2.mak
+++ b/src/testdir/Make_os2.mak
@@ -67,6 +67,7 @@ SCRIPTS = test1.out test3.out test4.out 
 		test_search_mbyte.out \
 		test_set.out \
 		test_signs.out \
+		test_tagcase.out \
 		test_textobjects.out \
 		test_utf8.out
 
--- a/src/testdir/Make_vms.mms
+++ b/src/testdir/Make_vms.mms
@@ -126,6 +126,7 @@ SCRIPT = test1.out  test2.out  test3.out
 	 test_search_mbyte.out \
 	 test_set.out \
 	 test_signs.out \
+	 test_tagcase.out \
 	 test_textobjects.out \
 	 test_utf8.out
 
--- a/src/testdir/Makefile
+++ b/src/testdir/Makefile
@@ -63,6 +63,7 @@ SCRIPTS = test1.out test2.out test3.out 
 		test_search_mbyte.out \
 		test_set.out \
 		test_signs.out \
+		test_tagcase.out \
 		test_textobjects.out \
 		test_utf8.out
 
new file mode 100644
--- /dev/null
+++ b/src/testdir/test_tagcase.in
@@ -0,0 +1,55 @@
+Tests for 'tagcase' option
+
+STARTTEST
+:/^start text$/+1,/^end text$/w! Xtext
+:/^start tags$/+1,/^end tags$/-1w! Xtags
+:set tags=Xtags
+:e Xtext
+:"
+:" Verify default values.
+:set ic& | setg tc& | setl tc&
+:call append('$', "ic=".&ic." g:tc=".&g:tc." l:tc=".&l:tc." tc=".&tc)
+:"
+:" Verify that the local setting accepts <empty> but that the global setting
+:" does not.  The first of these (setting the local value to <empty>) should
+:" succeed; the other two should fail.
+:let v:errmsg = ""
+:setl tc=
+:call append('$', v:errmsg)
+:let v:errmsg = ""
+:setg tc=
+:call append('$', v:errmsg)
+:let v:errmsg = ""
+:set tc=
+:call append('$', v:errmsg)
+:"
+:" Verify that the correct number of matching tags is found for all values of
+:" 'ignorecase' and global and local values 'tagcase', in all combinations.
+:for &ic in [0, 1]
+:  for &g:tc in ["followic", "ignore", "match"]
+:    for &l:tc in ["", "followic", "ignore", "match"]
+:      call append('$', "ic=".&ic." g:tc=".&g:tc." l:tc=".&l:tc." tc=".&tc)
+:      call append('$', len(taglist("^foo$")))
+:      call append('$', len(taglist("^Foo$")))
+:    endfor
+:  endfor
+:endfor
+:"
+:1,/^end text$/d
+:w! test.out
+:qa!
+ENDTEST
+
+start text
+
+Foo
+Bar
+foo
+
+end text
+
+start tags
+Bar	Xtext	3
+Foo	Xtext	2
+foo	Xtext	4
+end tags
new file mode 100644
--- /dev/null
+++ b/src/testdir/test_tagcase.ok
@@ -0,0 +1,76 @@
+ic=0 g:tc=followic l:tc=followic tc=followic
+
+E474: Invalid argument: tc=
+E474: Invalid argument: tc=
+ic=0 g:tc=followic l:tc= tc=followic
+1
+1
+ic=0 g:tc=followic l:tc=followic tc=followic
+1
+1
+ic=0 g:tc=followic l:tc=ignore tc=ignore
+2
+2
+ic=0 g:tc=followic l:tc=match tc=match
+1
+1
+ic=0 g:tc=ignore l:tc= tc=ignore
+2
+2
+ic=0 g:tc=ignore l:tc=followic tc=followic
+1
+1
+ic=0 g:tc=ignore l:tc=ignore tc=ignore
+2
+2
+ic=0 g:tc=ignore l:tc=match tc=match
+1
+1
+ic=0 g:tc=match l:tc= tc=match
+1
+1
+ic=0 g:tc=match l:tc=followic tc=followic
+1
+1
+ic=0 g:tc=match l:tc=ignore tc=ignore
+2
+2
+ic=0 g:tc=match l:tc=match tc=match
+1
+1
+ic=1 g:tc=followic l:tc= tc=followic
+2
+2
+ic=1 g:tc=followic l:tc=followic tc=followic
+2
+2
+ic=1 g:tc=followic l:tc=ignore tc=ignore
+2
+2
+ic=1 g:tc=followic l:tc=match tc=match
+1
+1
+ic=1 g:tc=ignore l:tc= tc=ignore
+2
+2
+ic=1 g:tc=ignore l:tc=followic tc=followic
+2
+2
+ic=1 g:tc=ignore l:tc=ignore tc=ignore
+2
+2
+ic=1 g:tc=ignore l:tc=match tc=match
+1
+1
+ic=1 g:tc=match l:tc= tc=match
+1
+1
+ic=1 g:tc=match l:tc=followic tc=followic
+2
+2
+ic=1 g:tc=match l:tc=ignore tc=ignore
+2
+2
+ic=1 g:tc=match l:tc=match tc=match
+1
+1
--- a/src/version.c
+++ b/src/version.c
@@ -742,6 +742,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    941,
+/**/
     940,
 /**/
     939,