changeset 9913:bb00c661b3a4 v7.4.2230

commit https://github.com/vim/vim/commit/66e29d7112e437b2b50efe1f82c7e892736d23e4 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Aug 20 16:57:02 2016 +0200 patch 7.4.2230 Problem: There is no equivalent of 'smartcase' for a tag search. Solution: Add value "followscs" and "smart" to 'tagcase'. (Christian Brabandt, closes https://github.com/vim/vim/issues/712) Turn tagcase test into new style.
author Christian Brabandt <cb@256bit.org>
date Sat, 20 Aug 2016 17:00:07 +0200
parents 4c5014d76564
children a0884512c300
files runtime/doc/options.txt runtime/doc/tagsrch.txt src/Makefile src/option.h src/proto/search.pro src/search.c src/tag.c src/testdir/Make_all.mak src/testdir/test_alot.vim src/testdir/test_tagcase.in src/testdir/test_tagcase.ok src/testdir/test_tagcase.vim src/version.c
diffstat 13 files changed, 132 insertions(+), 157 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -7417,6 +7417,9 @@ A jump table for the options with a shor
 	By default, tag searches are case-sensitive.  Case is ignored when
 	'ignorecase' is set and 'tagcase' is "followic", or when 'tagcase' is
 	"ignore".
+	Also when 'tagcase' is "followscs" and 'smartcase' is set, or
+	'tagcase' is "smart", and the pattern contains only lowercase
+	characters.
 
 	When 'tagbsearch' is off, tags searching is slower when a full match
 	exists, but faster when no full match exists.  Tags in unsorted tags
@@ -7435,8 +7438,10 @@ A jump table for the options with a shor
 	This option specifies how case is handled when searching the tags
 	file:
 	   followic	Follow the 'ignorecase' option
+	   followscs    Follow the 'smartcase' and 'ignorecase' options
 	   ignore	Ignore case
 	   match	Match case
+	   smart	Ignore case unless an upper case letter is used
 
 						*'taglength'* *'tl'*
 'taglength' 'tl'	number	(default 0)
--- a/runtime/doc/tagsrch.txt
+++ b/runtime/doc/tagsrch.txt
@@ -84,14 +84,23 @@ Note that when the current file changes,
 changed, to avoid confusion when using ":tnext".  It is changed when using
 ":tag {ident}".
 
-The ignore-case matches are not found for a ":tag" command when the
-'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.
+The ignore-case matches are not found for a ":tag" command when:
+- the 'ignorecase' option is off and 'tagcase' is "followic"
+- 'tagcase' is "match"
+- 'tagcase' is "smart" and the pattern contains an upper case character.
+- 'tagcase' is "followscs" and 'smartcase' option is on and the pattern
+  contains an upper case character.
+
+The gnore-case matches are found when:
+- a pattern is used (starting with a "/")
+- for ":tselect"
+- when 'tagcase' is "followic" and 'ignorecase' is off
+- when 'tagcase' is "match"
+- when 'tagcase' is "followscs" and the 'smartcase' option 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.
 
 ==============================================================================
 2. Tag stack				*tag-stack* *tagstack* *E425*
@@ -442,13 +451,18 @@ 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 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.
+This also depends on whether case is ignored.  Case is ignored when:
+- 'tagcase' is "followic" and 'ignorecase' is set
+- 'tagcase' is "ignore"
+- 'tagcase' is "smart" and and the pattern only contains lower case
+  characters.
+- 'tagcase' is "followscs" and 'smartcase' is set and and the pattern only
+  contains lower case characters.
+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
--- a/src/Makefile
+++ b/src/Makefile
@@ -2031,7 +2031,6 @@ test1 \
 	test_marks \
 	test_nested_function \
 	test_search_mbyte \
-	test_tagcase \
 	test_utf8 \
 	test_wordcount \
 	test_writefile \
@@ -2123,6 +2122,7 @@ test_arglist \
 	test_syntax \
 	test_tabline \
 	test_tabpage \
+	test_tagcase \
 	test_tagjump \
 	test_textobjects \
 	test_timers \
--- a/src/option.h
+++ b/src/option.h
@@ -822,11 +822,13 @@ 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};
+static char *(p_tc_values[]) = {"followic", "ignore", "match", "followscs", "smart", NULL};
 #endif
 #define TC_FOLLOWIC		0x01
 #define TC_IGNORE		0x02
 #define TC_MATCH		0x04
+#define TC_FOLLOWSCS		0x08
+#define TC_SMART		0x10
 EXTERN long	p_tl;		/* 'taglength' */
 EXTERN int	p_tr;		/* 'tagrelative' */
 EXTERN char_u	*p_tags;	/* 'tags' */
--- a/src/proto/search.pro
+++ b/src/proto/search.pro
@@ -7,6 +7,7 @@ void save_search_patterns(void);
 void restore_search_patterns(void);
 void free_search_patterns(void);
 int ignorecase(char_u *pat);
+int ignorecase_opt(char_u *pat, int ic_in, int scs);
 int pat_has_uppercase(char_u *pat);
 char_u *last_csearch(void);
 int last_csearch_forward(void);
--- a/src/search.c
+++ b/src/search.c
@@ -367,9 +367,18 @@ free_search_patterns(void)
     int
 ignorecase(char_u *pat)
 {
-    int		ic = p_ic;
-
-    if (ic && !no_smartcase && p_scs
+    return ignorecase_opt(pat, p_ic, p_scs);
+}
+
+/*
+ * As ignorecase() put pass the "ic" and "scs" flags.
+ */
+    int
+ignorecase_opt(char_u *pat, int ic_in, int scs)
+{
+    int		ic = ic_in;
+
+    if (ic && !no_smartcase && scs
 #ifdef FEAT_INS_EXPAND
 				&& !(ctrl_x_mode && curbuf->b_p_inf)
 #endif
--- a/src/tag.c
+++ b/src/tag.c
@@ -1385,9 +1385,11 @@ find_tags(
      */
     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;
+	case TC_FOLLOWIC:                break;
+	case TC_IGNORE:    p_ic = TRUE;  break;
+	case TC_MATCH:     p_ic = FALSE; break;
+	case TC_FOLLOWSCS: p_ic = ignorecase(pat); break;
+	case TC_SMART:     p_ic = ignorecase_opt(pat, TRUE, TRUE); break;
     }
 
     help_save = curbuf->b_help;
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -100,7 +100,6 @@ SCRIPTS_ALL = \
 	test_marks.out \
 	test_nested_function.out \
 	test_search_mbyte.out \
-	test_tagcase.out \
 	test_utf8.out \
 	test_wordcount.out \
 	test_writefile.out
--- a/src/testdir/test_alot.vim
+++ b/src/testdir/test_alot.vim
@@ -35,6 +35,7 @@ source test_statusline.vim
 source test_syn_attr.vim
 source test_tabline.vim
 source test_tabpage.vim
+source test_tagcase.vim
 source test_tagjump.vim
 source test_timers.vim
 source test_true_false.vim
deleted file mode 100644
--- a/src/testdir/test_tagcase.in
+++ /dev/null
@@ -1,57 +0,0 @@
-Tests for 'tagcase' option
-
-STARTTEST
-:so small.vim
-:lang mess C
-:/^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
deleted file mode 100644
--- a/src/testdir/test_tagcase.ok
+++ /dev/null
@@ -1,76 +0,0 @@
-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
new file mode 100644
--- /dev/null
+++ b/src/testdir/test_tagcase.vim
@@ -0,0 +1,73 @@
+" test 'tagcase' option
+
+func Test_tagcase()
+  call writefile(["Bar\tXtext\t3", "Foo\tXtext\t2", "foo\tXtext\t4"], 'Xtags')
+  set tags=Xtags
+  e Xtext
+
+  for &ic in [0, 1]
+    for &scs in [0, 1]
+      for &g:tc in ["followic", "ignore", "match", "followscs", "smart"]
+	for &l:tc in ["", "followic", "ignore", "match", "followscs", "smart"]
+	  let smart = 0
+	  if &l:tc != ''
+	    let tc = &l:tc
+	  else
+	    let tc = &g:tc
+	  endif
+	  if tc == 'followic'
+	    let ic = &ic
+	  elseif tc == 'ignore'
+	    let ic = 1
+	  elseif tc == 'followscs'
+	    let ic = &ic
+	    let smart = &scs
+	  elseif tc == 'smart'
+	    let ic = 1
+	    let smart = 1
+	  else
+	    let ic = 0
+	  endif
+	  if ic && smart
+	    call assert_equal(['foo', 'Foo'], map(taglist("^foo$"), {i, v -> v.name}))
+	    call assert_equal(['Foo'], map(taglist("^Foo$"), {i, v -> v.name}))
+	  elseif ic
+	    call assert_equal(['foo', 'Foo'], map(taglist("^foo$"), {i, v -> v.name}))
+	    call assert_equal(['Foo', 'foo'], map(taglist("^Foo$"), {i, v -> v.name}))
+	  else
+	    call assert_equal(['foo'], map(taglist("^foo$"), {i, v -> v.name}))
+	    call assert_equal(['Foo'], map(taglist("^Foo$"), {i, v -> v.name}))
+	  endif
+	endfor
+      endfor
+    endfor
+  endfor
+
+  call delete('Xtags')
+  set ic&
+  setg tc&
+  setl tc&
+  set scs&
+endfunc
+
+func Test_set_tagcase()
+  " Verify default values.
+  set ic&
+  setg tc&
+  setl tc&
+  call assert_equal(0, &ic)
+  call assert_equal('followic', &g:tc)
+  call assert_equal('followic', &l:tc)
+  call assert_equal('followic', &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.
+  setl tc=
+  call assert_fails('setg tc=', 'E474:')
+  call assert_fails('set tc=', 'E474:')
+
+  set ic&
+  setg tc&
+  setl tc&
+endfunc
--- a/src/version.c
+++ b/src/version.c
@@ -764,6 +764,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2230,
+/**/
     2229,
 /**/
     2228,