summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2010-08-12 21:50:51 +0200
committerBram Moolenaar <Bram@vim.org>2010-08-12 21:50:51 +0200
commit0be992e3476ff9df8b7d565042432970f56d7b7e (patch)
treec043d06bbf15669c9c9b1bcb297dcfd87b948a4f
parentd7b734a49364df78c2d246efa1bb6cd1e7595474 (diff)
downloadvim-0be992e3476ff9df8b7d565042432970f56d7b7e.zip
Improvements for :find completion.
-rw-r--r--runtime/doc/todo.txt4
-rw-r--r--src/misc1.c173
-rw-r--r--src/testdir/test73.in95
-rw-r--r--src/testdir/test73.ok6
4 files changed, 182 insertions, 96 deletions
diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt
index 94dcfdede..642f52fc1 100644
--- a/runtime/doc/todo.txt
+++ b/runtime/doc/todo.txt
@@ -1,4 +1,4 @@
-*todo.txt* For Vim version 7.3f. Last change: 2010 Aug 10
+*todo.txt* For Vim version 7.3f. Last change: 2010 Aug 12
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -32,6 +32,8 @@ be worked on, but only if you sponsor Vim development. See |sponsor|.
'cursorline' stops too early in a help file, caused by conceal feature.
+Have a close look at :find completion, anything that could be wrong?
+
Test 73 fails on MS-Windows when compiled with DJGPP.
:find completion with 'path' set to "./**" results in full path for
"./subdir/file", should shorten to start with "./".
diff --git a/src/misc1.c b/src/misc1.c
index 400ecf158..432d36cbe 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,8 @@ 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 */
}
@@ -9301,8 +9303,6 @@ expand_path_option(curdir, gap)
char_u *buf;
char_u *p;
- ga_init2(gap, (int)sizeof(char_u *), 1);
-
if ((buf = alloc((int)MAXPATHL)) == NULL)
return;
@@ -9357,7 +9357,7 @@ expand_path_option(curdir, gap)
*
* path: /foo/bar/baz
* fname: /foo/bar/baz/quux.txt
- * returns: ^this
+ * returns: ^this
*/
static char_u *
get_path_cutoff(fname, gap)
@@ -9413,15 +9413,21 @@ uniquefy_paths(gap, pattern)
int i;
int len;
char_u **fnames = (char_u **)gap->ga_data;
- int sort_again = 0;
+ int sort_again = FALSE;
char_u *pat;
char_u *file_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);
+ if (gap->ga_len == 0)
+ return;
+
+ ga_init2(&path_ga, (int)sizeof(char_u *), 1);
/*
* We need to prepend a '*' at the beginning of file_pattern so that the
@@ -9447,17 +9453,21 @@ uniquefy_paths(gap, pattern)
return;
if ((curdir = alloc((int)(MAXPATHL))) == NULL)
- return;
+ goto theend;
mch_dirname(curdir, MAXPATHL);
expand_path_option(curdir, &path_ga);
+ in_curdir = (char_u **)alloc(gap->ga_len * sizeof(char_u *));
+ if (in_curdir == NULL)
+ goto theend;
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;
+ char_u *pathsep_p;
+ char_u *path_cutoff;
len = (int)STRLEN(path);
while (dir_end > path &&
@@ -9468,78 +9478,30 @@ uniquefy_paths(gap, pattern)
#endif
)
mb_ptr_back(path, dir_end);
- is_in_curdir = STRNCMP(curdir, path, dir_end - path) == 0
+ is_in_curdir = fnamencmp(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 */
+ 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(&regmatch, 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 = TRUE;
+ 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(&regmatch, 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))
{
/*
@@ -9548,11 +9510,11 @@ uniquefy_paths(gap, pattern)
* 1. It is under the current directory.
* 2. The result is actually shorter than the original.
*
- * Before curdir After
- * /foo/bar/file.txt /foo/bar ./file.txt
- * c:\foo\bar\file.txt c:\foo\bar .\file.txt
- * /file.txt / /file.txt
- * c:\file.txt c:\ .\file.txt
+ * Before curdir After
+ * /foo/bar/file.txt /foo/bar ./file.txt
+ * c:\foo\bar\file.txt c:\foo\bar .\file.txt
+ * /file.txt / /file.txt
+ * c:\file.txt c:\ .\file.txt
*/
short_name = shorten_fname(path, curdir);
if (short_name != NULL && short_name > path + 1)
@@ -9564,8 +9526,68 @@ 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);
+ continue;
+ }
+
+ rel_path = alloc((int)(STRLEN(short_name) + STRLEN(PATHSEPSTR) + 2));
+ if (rel_path == NULL)
+ 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);
+ goto theend;
+ }
+ }
+
+ STRCPY(fnames[i], rel_path);
+ vim_free(rel_path);
+ sort_again = TRUE;
+ }
+
theend:
vim_free(curdir);
+ if (in_curdir != NULL)
+ {
+ for (i = 0; i < gap->ga_len; i++)
+ vim_free(in_curdir[i]);
+ vim_free(in_curdir);
+ }
ga_clear_strings(&path_ga);
vim_free(regmatch.regprog);
@@ -9598,6 +9620,7 @@ expand_in_path(gap, pattern, flags)
return 0;
mch_dirname(curdir, MAXPATHL);
+ ga_init2(&path_ga, (int)sizeof(char_u *), 1);
expand_path_option(curdir, &path_ga);
vim_free(curdir);
if (path_ga.ga_len == 0)
diff --git a/src/testdir/test73.in b/src/testdir/test73.in
index cb2604e68..41865908f 100644
--- a/src/testdir/test73.in
+++ b/src/testdir/test73.in
@@ -6,7 +6,8 @@ STARTTEST
:" delete the Xfind directory during cleanup
:"
:" This will cause a few errors, do it silently.
-:set nocp viminfo+=nviminfo visualbell
+:set visualbell
+:set nocp viminfo+=nviminfo
:"
:function! DeleteDirectory(dir)
: if has("win16") || has("win32") || has("win64") || has("dos16") || has("dos32")
@@ -20,32 +21,33 @@ STARTTEST
:call DeleteDirectory("Xfind")
:new
:let cwd=getcwd()
-:!mkdir Xfind
+:let test_out = cwd . '/test.out'
+:silent !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.
-:!mkdir in
+:" We shouldn't find any file at this point, test.out must be empty.
+:silent !mkdir in
:cd in
-:!mkdir path
+:silent !mkdir path
:exec "cd " . cwd
:e Xfind/file.txt
SHoly Grail:w
@@ -57,40 +59,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
+:silent !mkdir foo
+:cd foo
+:silent !mkdir x
+:silent !mkdir y
+:cd x
+:silent !mkdir bar
+:cd ..
+:cd y
+:silent !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 cd787f23a..a54e5f447 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