On Sat, Feb 21, 2009 at 10:38 PM, Dominique Pelle <dominique.pe...@gmail.com> wrote: > I'm trying your patch on Linux x86 (from Vim-7.2.108). > I notice something broken. > > I do: > > $ mkdir foobar/ > $ vim -u NONE > set nocompatible > set wildmode=longest,list > set wildmenu > set path=. > > If I type: > > :find foo<tab> > > I would expect to complete up to foobar/ (it does > that without patch) but after patch it does not complete > the directory name anymore.
Thanks for trying it out and reporting the problem. It happens during the 'uniquefying' process. To be specific it's the find_previous_pathsep function. The foobar/ name gets uniquefied as an empty string due the ending slash. During fixing this I (carefully) read :he 'path' and noticed that the completion should also be applied to :tabfind, and it should not be applied when the path is a fullpath or starts with a dot. Attached patch correct the mentioned problem(s). nazri --~--~---------~--~----~------------~-------~--~----~ You received this message from the "vim_dev" maillist. For more information, visit http://www.vim.org/maillist.php -~----------~----~----~----~------~----~------~--~---
Index: ex_docmd.c =================================================================== --- ex_docmd.c (revision 1365) +++ ex_docmd.c (working copy) @@ -3422,6 +3422,11 @@ */ switch (ea.cmdidx) { + case CMD_find: + case CMD_sfind: + case CMD_tabfind: + xp->xp_context = EXPAND_FILES_IN_PATH; + break; case CMD_cd: case CMD_chdir: case CMD_lcd: Index: ex_getln.c =================================================================== --- ex_getln.c (revision 1365) +++ ex_getln.c (working copy) @@ -4063,6 +4063,7 @@ char_u *tail; if (context != EXPAND_FILES + && context != EXPAND_FILES_IN_PATH && context != EXPAND_SHELLCMD && context != EXPAND_DIRECTORIES) { @@ -4377,7 +4378,9 @@ if (options & WILD_SILENT) flags |= EW_SILENT; - if (xp->xp_context == EXPAND_FILES || xp->xp_context == EXPAND_DIRECTORIES) + if (xp->xp_context == EXPAND_FILES + || xp->xp_context == EXPAND_DIRECTORIES + || xp->xp_context == EXPAND_FILES_IN_PATH) { /* * Expand file or directory names. @@ -4407,6 +4410,8 @@ if (xp->xp_context == EXPAND_FILES) flags |= EW_FILE; + else if (xp->xp_context == EXPAND_FILES_IN_PATH) + flags |= (EW_FILE | EW_PATH); else flags = (flags | EW_DIR) & ~EW_FILE; ret = expand_wildcards(1, &pat, num_file, file, flags); Index: misc1.c =================================================================== --- misc1.c (revision 1365) +++ misc1.c (working copy) @@ -9097,7 +9097,254 @@ } #endif +#if defined(FEAT_SEARCHPATH) +/* + * Expand the files matching pattern, starting from the given path, save + * the matches in their equivalent fullpath. + */ + static int +expand_to_fullpath(path, gap, pattern, flags) + char_u *path; + garray_T *gap; + char_u *pattern; + int flags; /* EW_* flags */ +{ + int i; + int c; + int old_len; + int new_len; + char_u **files; + char_u *fullpath; + + old_len = gap->ga_len; + c = mch_expandpath(gap, pattern, flags); + + files = (gap->ga_data != NULL) ? (char_u **)gap->ga_data : (char_u **)""; + new_len = gap->ga_len; + + for(i = old_len; i < new_len; i++) + { + fullpath = concat_fnames(path, files[i], TRUE); + vim_free(files[i]); + files[i] = fullpath; + } + return c; +} + /* + * Moves psep to the previous path separator in path, starting from the + * end of path. Returns FAIL is psep ends up at the beginning of path. + */ + static int +find_previous_pathsep(path, psep) + char_u *path; + char_u **psep; +{ + /* + * As we're looking for the previous path separator, skip the current + * separator. + */ + if (vim_ispathsep(**psep)) + (*psep)--; + + while (*psep >= path && !vim_ispathsep(**psep)) + (*psep)--; + + if (*psep != path && vim_ispathsep(**psep)) + return OK; + + return FAIL; +} + +/* + * Returns TRUE if maybe_unique is unique wrt other_paths in gap. maybe_unique + * is the end portion of ((char_u **)gap->ga_data)[i]. + */ + static int +is_unique(maybe_unique, gap, i) + char_u *maybe_unique; + garray_T *gap; + int i; +{ + int j; + int candidate_len; + int other_path_len; + char_u *rival; + char_u **other_paths; + + other_paths = (gap->ga_data != NULL) ? (char_u **)gap->ga_data : (char_u **)""; + + for (j = 0; j < gap->ga_len && !got_int; j++) + { + /* Don't compare it with itself */ + if(j == i) + continue; + + candidate_len = STRLEN(maybe_unique); + other_path_len = STRLEN(other_paths[j]); + + if(other_path_len < candidate_len) + /* It's different, */ + continue; + + rival = other_paths[j] + other_path_len - candidate_len; + + if (fnamecmp(maybe_unique, rival) == 0) + return FALSE; + } + + return TRUE; +} + +/* + * Sorts, removes duplicates and modifies all the fullpath names in gap so that + * they are unique with respect to each other. Beware, this is at least O(n^2) + * wrt gap->ga_len. + */ + static void +uniquefy_paths(gap) + garray_T *gap; +{ + int i; + int path_len; + char_u *pathsep_p; + char_u *path; + char_u **fnames = (char_u **) gap->ga_data; + + int j; + int sort_again = 0; + + /* Remove duplicate entries */ + sort_strings(fnames, gap->ga_len); + for (i = 0; i < gap->ga_len - 1; i++) + if (fnamecmp(fnames[i], fnames[i+1]) == 0) + { + vim_free(fnames[i]); + for (j = i+1; j < gap->ga_len; j++) + fnames[j-1] = fnames[j]; + gap->ga_len--; + i--; + } + + for (i = 0; i < gap->ga_len; i++) + { + path = fnames[i]; + path_len = STRLEN(path); + + /* We start at the end of the path */ + pathsep_p = path + path_len - 1; + + while (find_previous_pathsep(path, &pathsep_p)) + if (is_unique(pathsep_p, gap, i)) + { + sort_again = 1; + mch_memmove(path, pathsep_p + 1, STRLEN(pathsep_p)); + break; + } + } + + if (sort_again) + sort_strings(fnames, gap->ga_len); +} + +/* + * Calls mch_expandpath for each 'path' values for the given pattern and stores + * the result in gap. Returns the total number of match. + */ + static int +expand_in_path(gap, pattern, flags) + garray_T *gap; + char_u *pattern; + int flags; /* EW_* flags */ +{ + /* + * For each expandable 'path' option values we get the list of all + * directories and expand the file names in each the directories. + */ + int c = 0; + char_u *cwd = NULL; + char_u *cwd_orig = NULL; + char_u *path_opt; + char_u *path_option = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; + + if ((cwd_orig = alloc((int)(MAXPATHL))) == NULL || + (cwd = alloc((int)(MAXPATHL))) == NULL || + (path_opt = alloc((int)(MAXPATHL))) == NULL) + return 0; /* FIXME Give emsg instead? */ + + if (getcwd((char *)cwd_orig, MAXPATHL) == NULL) + /* FIXME The directory may have been deleted, is it ok to ignore it? */ + ; + + /* + * Search for matching files in each 'path' options. + * TODO Detect circular paths (symbolic links to path) + */ + for(;;) + { + int i; + int dir_count; + garray_T ga_dirs; + + path_opt[0] = 0; + copy_option_part(&path_option, path_opt, MAXPATHL, " ;,"); + + STRCPY(cwd, cwd_orig); + + if (mch_isFullName(path_opt)) + STRCPY(cwd, path_opt); + else + { + char_u *fullpath; + + fullpath = concat_fnames(cwd_orig, path_opt, TRUE); + if (fullpath == NULL) + continue; /* FIXME Give emsg too? */ + + STRNCPY(cwd, fullpath, MAXPATHL); + vim_free(fullpath); + } + + ga_init2(&ga_dirs, (int)sizeof(char_u *), 30); + dir_count = mch_expandpath(&ga_dirs, cwd, EW_DIR|EW_SILENT); + + if (dir_count == 0) + { + if (mch_chdir(cwd) == 0) + c += expand_to_fullpath(cwd, gap, pattern, flags); + } + else + { + /* Add files from each expanded path. */ + int i; + char_u **tmpdirs; + tmpdirs = (ga_dirs.ga_data != NULL) ? (char_u **)ga_dirs.ga_data + : (char_u **)""; + for (i = 0; i < dir_count && !got_int ; i++) + { + /* FIXME Skip symbolic links to avoid cycle */ + if (mch_chdir(tmpdirs[i]) == 0) + c += expand_to_fullpath(tmpdirs[i], gap, pattern, flags); + } + } + ga_clear_strings(&ga_dirs); + + if (path_option == NULL || *path_option == NUL) + break; + } + + if (mch_chdir(cwd_orig) != 0) + /* FIXME Is it safe to ignore this? */ ; + + vim_free(path_opt); + vim_free(cwd); + vim_free(cwd_orig); + + return c; +} +#endif + +/* * Generic wildcard expansion code. * * Characters in "pat" that should not be expanded must be preceded with a @@ -9205,7 +9452,14 @@ * when EW_NOTFOUND is given. */ if (mch_has_exp_wildcard(p)) - add_pat = mch_expandpath(&ga, p, flags); + { +#if defined(FEAT_SEARCHPATH) + if (*p != '.' && !vim_ispathsep(*p) && flags & EW_PATH) + add_pat = expand_in_path(&ga, p, flags); + else +#endif + add_pat = mch_expandpath(&ga, p, flags); + } } if (add_pat == -1 || (add_pat == 0 && (flags & EW_NOTFOUND))) @@ -9228,6 +9482,9 @@ vim_free(p); } + if (flags & EW_PATH) + uniquefy_paths(&ga); + *num_file = ga.ga_len; *file = (ga.ga_data != NULL) ? (char_u **)ga.ga_data : (char_u **)""; Index: vim.h =================================================================== --- vim.h (revision 1365) +++ vim.h (working copy) @@ -708,6 +708,7 @@ #define EXPAND_USER_DEFINED 30 #define EXPAND_USER_LIST 31 #define EXPAND_SHELLCMD 32 +#define EXPAND_FILES_IN_PATH 33 /* Values for exmode_active (0 is no exmode) */ #define EXMODE_NORMAL 1 @@ -739,6 +740,7 @@ #define EW_KEEPALL 0x10 /* keep all matches */ #define EW_SILENT 0x20 /* don't print "1 returned" from shell */ #define EW_EXEC 0x40 /* executable files */ +#define EW_PATH 0x80 /* search in 'path' too */ /* Note: mostly EW_NOTFOUND and EW_SILENT are mutually exclusive: EW_NOTFOUND * is used when executing commands and EW_SILENT for interactive expanding. */