This is a regression introduced by my own patch previously (7ce8b24450dc: Improvements for ":find" completion. (Nazri Ramliy)).
Attached patch fixes the regression and add tests to test73 to catch the bug. The test runs fine on unix, but fails on msvc vim. I'll investigate it later. Sorry I'm in a bit of a hurry I'll look into cleaning up the patch and making the test successfull on msvc vim soon. nazri. -- 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
commit ec6ce759849bc269de959210f1c6712198ce0b91 Author: Nazri Ramliy <ayieh...@gmail.com> Date: Thu Aug 12 13:42:22 2010 +0800 find completion bugfix: Correctly uniquefy foo/a/bar/file.txt and foo/b/bar/file.txt diff --git a/src/misc1.c b/src/misc1.c index 400ecf1..3380288 100644 --- a/src/misc1.c +++ b/src/misc1.c @@ -9263,6 +9263,7 @@ is_unique(maybe_unique, gap, i) int candidate_len; int other_path_len; char_u **other_paths = (char_u **)gap->ga_data; + char_u *rival; for (j = 0; j < gap->ga_len && !got_int; j++) { @@ -9275,7 +9276,9 @@ is_unique(maybe_unique, gap, i) if (other_path_len < candidate_len) continue; /* it's different */ - if (fnamecmp(maybe_unique, gettail(other_paths[j])) == 0) + rival = other_paths[j] + other_path_len - candidate_len; + + if (fnamecmp(maybe_unique, rival) == 0) return FALSE; /* match */ } @@ -9419,6 +9422,8 @@ uniquefy_paths(gap, pattern) char_u *curdir = NULL; regmatch_T regmatch; garray_T path_ga; + char_u **in_curdir = NULL; + char_u *short_name; sort_strings(fnames, gap->ga_len); remove_duplicates(gap); @@ -9452,12 +9457,17 @@ uniquefy_paths(gap, pattern) expand_path_option(curdir, &path_ga); + if (gap->ga_len > 0) + in_curdir = (char_u **)alloc(gap->ga_len * sizeof(char_u *)); + + if (in_curdir == NULL) + return; + for (i = 0; i < gap->ga_len; i++) { char_u *path = fnames[i]; int is_in_curdir; char_u *dir_end = gettail(path); - char_u *short_name; len = (int)STRLEN(path); while (dir_end > path && @@ -9471,75 +9481,28 @@ uniquefy_paths(gap, pattern) is_in_curdir = STRNCMP(curdir, path, dir_end - path) == 0 && curdir[dir_end - path] == NUL; - /* - * If the file is in the current directory, - * and it is not unique, - * reduce it to ./{filename} - * FIXME ^ Is this portable? - * - * Note: If the full filename is /curdir/foo/bar/{filename}, we don't - * want to shorten it to ./foo/bar/{filename} yet because 'path' might - * contain ". / * *", in which case the shortened filename could be - * shorter than ./foo/bar/{filename}. - */ if (is_in_curdir) - { - char_u *rel_path; - - short_name = shorten_fname(path, curdir); - if (short_name == NULL) - short_name = path; - if (is_unique(short_name, gap, i)) - { - STRMOVE(path, short_name); - continue; - } + in_curdir[i] = vim_strsave(path); + else + in_curdir[i] = NULL; - rel_path = alloc((int)(STRLEN(short_name) - + STRLEN(PATHSEPSTR) + 2)); - if (rel_path == NULL) - goto theend; + /* Shorten the filename while maintaining its uniqueness */ + char_u *pathsep_p; + char_u *path_cutoff = get_path_cutoff(path, &path_ga); - /* FIXME Is "." a portable way of denoting the current directory? */ - STRCPY(rel_path, "."); - add_pathsep(rel_path); - STRCAT(rel_path, short_name); + /* we start at the end of the path */ + pathsep_p = path + len - 1; - if (len < (int)STRLEN(rel_path)) + while (find_previous_pathsep(path, &pathsep_p)) + if (vim_regexec(®match, pathsep_p + 1, (colnr_T)0) + && is_unique(pathsep_p + 1, gap, i) + && path_cutoff != NULL && pathsep_p + 1 >= path_cutoff) { - vim_free(fnames[i]); - fnames[i] = alloc((int)(STRLEN(rel_path) + 1)); - if (fnames[i] == NULL) - { - vim_free(rel_path); - goto theend; - } + sort_again = 1; + mch_memmove(path, pathsep_p + 1, STRLEN(pathsep_p)); + break; } - STRCPY(fnames[i], rel_path); - vim_free(rel_path); - sort_again = 1; - } - else - { - /* Shorten the filename while maintaining its uniqueness */ - char_u *pathsep_p; - char_u *path_cutoff = get_path_cutoff(path, &path_ga); - - /* we start at the end of the path */ - pathsep_p = path + len - 1; - - while (find_previous_pathsep(path, &pathsep_p)) - if (vim_regexec(®match, pathsep_p + 1, (colnr_T)0) - && is_unique(pathsep_p + 1, gap, i) - && path_cutoff != NULL && pathsep_p + 1 >= path_cutoff) - { - sort_again = 1; - mch_memmove(path, pathsep_p + 1, STRLEN(pathsep_p)); - break; - } - } - if (mch_isFullName(path)) { /* @@ -9564,8 +9527,71 @@ uniquefy_paths(gap, pattern) } } + /* Shorten filenames in /in/current/directory/{filename} */ + for (i = 0; i < gap->ga_len; i++) + { + char_u *rel_path; + char_u *path = in_curdir[i]; + + if (path == NULL) + continue; + /* + * If the file is in the current directory, + * and it is not unique, + * reduce it to ./{filename} + * FIXME ^ Is this portable? + * else reduce it to {filename} + * + * Note: If the full filename is /curdir/foo/bar/{filename}, we don't + * want to shorten it to ./foo/bar/{filename} yet because 'path' might + * contain ". / * *", in which case the shortened filename could be + * shorter than ./foo/bar/{filename}. + */ + + short_name = shorten_fname(path, curdir); + if (short_name == NULL) + short_name = path; + if (is_unique(short_name, gap, i)) + { + STRCPY(fnames[i], short_name); + vim_free(path); + continue; + } + + rel_path = alloc((int)(STRLEN(short_name) + STRLEN(PATHSEPSTR) + 2)); + if (rel_path == NULL) + { + vim_free(path); + goto theend; + } + + /* FIXME Is "." a portable way of denoting the current directory? */ + STRCPY(rel_path, "."); + add_pathsep(rel_path); + STRCAT(rel_path, short_name); + + if (len < (int)STRLEN(rel_path)) + { + vim_free(fnames[i]); + fnames[i] = alloc((int)(STRLEN(rel_path) + 1)); + if (fnames[i] == NULL) + { + vim_free(rel_path); + vim_free(path); + goto theend; + } + } + + STRCPY(fnames[i], rel_path); + vim_free(rel_path); + vim_free(path); + sort_again = 1; + } + + theend: vim_free(curdir); + vim_free(in_curdir); ga_clear_strings(&path_ga); vim_free(regmatch.regprog); diff --git a/src/testdir/test73.in b/src/testdir/test73.in index cb2604e..8d28c22 100644 --- a/src/testdir/test73.in +++ b/src/testdir/test73.in @@ -20,29 +20,30 @@ STARTTEST :call DeleteDirectory("Xfind") :new :let cwd=getcwd() +:let test_out = cwd . '/test.out' :!mkdir Xfind :cd Xfind :set path= :find -:w! ../test.out +:exec "w! " . test_out :close :new :set path=. :find -:w >>../test.out +:exec "w >>" . test_out :close :new :set path=.,, :find -:w >>../test.out +:exec "w >>" . test_out :close :new :set path=./** :find -:w >>../test.out +:exec "w >>" . test_out :close :new -:" We shouldn't find any file at this point, ../test.out must be empty. +:" We shouldn't find any file at this point, test.out must be empty. :!mkdir in :cd in :!mkdir path @@ -57,40 +58,93 @@ SAnother Holy Grail:w SE.T.:w :set path=Xfind/** :find file -:w >> test.out +:exec "w >>" . test_out :find file -:w >>test.out +:exec "w >>" . test_out :find file -:w >>test.out +:exec "w >>" . test_out :" Rerun the previous three find completions, using fullpath in 'path' :exec "set path=" . cwd . "/Xfind/**" :find file -:w >> test.out +:exec "w >>" . test_out :find file -:w >>test.out +:exec "w >>" . test_out :find file -:w >>test.out +:exec "w >>" . test_out :" Same steps again, using relative and fullpath items that point to the same :" recursive location. :" This is to test that there are no duplicates in the completion list. :exec "set path+=Xfind/**" :find file -:w >> test.out +:exec "w >>" . test_out :find file -:w >>test.out +:exec "w >>" . test_out :find file -:w >>test.out +:exec "w >>" . test_out :find file :" Test find completion for directory of current buffer, which at this point :" is Xfind/in/file.txt. :set path=. :find st -:w >> test.out +:exec "w >>" . test_out :" Test find completion for empty path item ",," which is the current directory :cd Xfind :set path=,, :find f -:w >> ../test.out +:exec "w >>" . test_out +:" Test shortening of +:" +:" foo/x/bar/voyager.txt +:" foo/y/bar/voyager.txt +:" +:" When current directory is above foo/ they should be shortened to (in order +:" of appearance): +:" +:" x/bar/voyager.txt +:" y/bar/voyager.txt +:!mkdir foo +:cd foo +:!mkdir x +:!mkdir y +:cd x +:!mkdir bar +:cd .. +:cd y +:!mkdir bar +:cd .. +:cd .. +:" We should now be in the Xfind directory +:e foo/x/bar/voyager.txt +SVoyager 1:w +:e foo/y/bar/voyager.txt +SVoyager 2:w +:exec "set path=" . cwd . "/Xfind/**" +:find voyager +:exec "w >>" . test_out +:find voyager +:exec "w >>" . test_out +:" +:" When current directory is .../foo/y/bar they should be shortened to (in +:" order of appearance): +:" +:" ./voyager.txt +:" x/bar/voyager.txt +:cd foo +:cd y +:cd bar +:find voyager +:exec "w >> " . test_out +:find voyager +:exec "w >> " . test_out +:" Check the opposite too: +:cd .. +:cd .. +:cd x +:cd bar +:find voyager +:exec "w >> " . test_out +:find voyager +:exec "w >> " . test_out :cd .. :q :call DeleteDirectory("Xfind") diff --git a/src/testdir/test73.ok b/src/testdir/test73.ok index cd787f2..a54e5f4 100644 --- a/src/testdir/test73.ok +++ b/src/testdir/test73.ok @@ -9,3 +9,9 @@ Jimmy Hoffa E.T. Another Holy Grail Holy Grail +Voyager 1 +Voyager 2 +Voyager 2 +Voyager 1 +Voyager 1 +Voyager 2