# HG changeset patch # User Christian Brabandt # Date 1471705207 -7200 # Node ID bb00c661b3a4af133e91f65694a921abe6e116d0 # Parent 4c5014d765647de32beb81dcc852eae96964aed4 commit https://github.com/vim/vim/commit/66e29d7112e437b2b50efe1f82c7e892736d23e4 Author: Bram Moolenaar 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. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt --- 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) diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt --- 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 diff --git a/src/Makefile b/src/Makefile --- 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 \ diff --git a/src/option.h b/src/option.h --- 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' */ diff --git a/src/proto/search.pro b/src/proto/search.pro --- 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); diff --git a/src/search.c b/src/search.c --- 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 diff --git a/src/tag.c b/src/tag.c --- 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; diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak --- 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 diff --git a/src/testdir/test_alot.vim b/src/testdir/test_alot.vim --- 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 diff --git a/src/testdir/test_tagcase.in b/src/testdir/test_tagcase.in 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 but that the global setting -:" does not. The first of these (setting the local value to ) 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 diff --git a/src/testdir/test_tagcase.ok b/src/testdir/test_tagcase.ok 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 diff --git a/src/testdir/test_tagcase.vim b/src/testdir/test_tagcase.vim 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 but that the global setting + " does not. The first of these (setting the local value to ) 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 diff --git a/src/version.c b/src/version.c --- 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,