changeset 2429:7ce8b24450dc vim73

Improvements for ":find" completion. (Nazri Ramliy)
author Bram Moolenaar <bram@vim.org>
date Wed, 28 Jul 2010 22:29:10 +0200
parents 33148c37f3c9
children 55b20270a4ce
files runtime/doc/editing.txt runtime/doc/todo.txt src/ex_getln.c src/misc1.c
diffstat 4 files changed, 259 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/editing.txt
+++ b/runtime/doc/editing.txt
@@ -1,4 +1,4 @@
-*editing.txt*   For Vim version 7.3c.  Last change: 2010 Jul 21
+*editing.txt*   For Vim version 7.3c.  Last change: 2010 Jul 28
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -1622,10 +1622,13 @@ 3) Combined up/downward search:
 
    In the above example you might want to set path to: >
 	:set path=**,/u/user_x/**
-<   This searches: >
-	/u/user_x/work/release/**
-	/u/user_x/**
-<   This searches the same directories, but in a different order.
+<  This searches:
+	/u/user_x/work/release/** ~
+	/u/user_x/** ~
+   This searches the same directories, but in a different order.
 
+   Note that completion for ":find", ":sfind", and ":tabfind" commands do not
+   currently work with 'path' items that contain a url or use the double star
+   (/usr/**2) or upward search (;) notations. >
 
  vim:tw=78:ts=8:ft=help:norl:
--- a/runtime/doc/todo.txt
+++ b/runtime/doc/todo.txt
@@ -30,23 +30,21 @@ be worked on, but only if you sponsor Vi
 							*known-bugs*
 -------------------- Known bugs and current work -----------------------
 
-Patch for :find completion. (Nazri Ramliy, 2010 Jul 27, and leak fix)
-And patch for Windows, Jul 28.
-And fix for patch, Jul 28.
+Patch for :filetype completion. (Dominique Pelle, Jul 28)
 
 Windows 7: "Open with..." menu starts Vim without a file.
 
 Windows 7: installing Vim again doesn't find the previously installed Vim.
 
+Move more common code from if_python.c and if_python3.c to if_py_both.h
+
+Add filetype completion to user commands. (Christian Brabandt, 2010 Jul 26)
+But call it "filetype" instead of "syntax"?
+
 ftplugin/mupad.vim should not source AppendMatchGroup.vim, it should use an
 autoload function.
 Same for indent/GenericIndent.vim
 
-Move more common code from if_python.c and if_python3.c to if_py_both.h
-
-Add filetype completion to user commands. (Christian Brabandt, 2010 Jul 26)
-But call it "filetype" instead of "syntax".
-
 Before release 7.3:
 - Rename vim73 branch to default (hints: Xavier de Gaye, 2010 May 23)
 
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -5033,6 +5033,12 @@ globpath(path, file, expand_options)
     {
 	/* Copy one item of the path to buf[] and concatenate the file name. */
 	copy_option_part(&path, buf, MAXPATHL, ",");
+	if (path_with_url(buf))
+	    continue;
+	/*
+	 * FIXME: should we proactively skip 'path' with limiter (/usr/ **N)
+	 * and upward search (;) notations, just like we did with url above?
+	 */
 	if (STRLEN(buf) + STRLEN(file) + 2 < MAXPATHL)
 	{
 	    add_pathsep(buf);
--- a/src/misc1.c
+++ b/src/misc1.c
@@ -9239,6 +9239,8 @@ unix_expandpath(gap, path, wildoff, flag
 static int find_previous_pathsep __ARGS((char_u *path, char_u **psep));
 static int is_unique __ARGS((char_u *maybe_unique, garray_T *gap, int i));
 static void remove_duplicates __ARGS((garray_T *gap));
+static void expand_path_option __ARGS((char_u *curdir, garray_T	*gap));
+static char_u *get_path_cutoff __ARGS((char_u *fname, garray_T *gap));
 static void uniquefy_paths __ARGS((garray_T *gap, char_u *pattern));
 static int expand_in_path __ARGS((garray_T *gap, char_u	*pattern, int flags));
 
@@ -9267,8 +9269,8 @@ find_previous_pathsep(path, psep)
 }
 
 /*
- * 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].
+ * 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)
@@ -9279,32 +9281,24 @@ is_unique(maybe_unique, gap, 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 **)"";
+    char_u  **other_paths = (char_u **)gap->ga_data;
 
     for (j = 0; j < gap->ga_len && !got_int; j++)
     {
 	ui_breakcheck();
-	/* don't compare it with itself */
 	if (j == i)
-	    continue;
+	    continue;  /* don't compare it with itself */
 
 	candidate_len = (int)STRLEN(maybe_unique);
 	other_path_len = (int)STRLEN(other_paths[j]);
-
 	if (other_path_len < candidate_len)
 	    continue;  /* it's different */
 
-	rival = other_paths[j] + other_path_len - candidate_len;
-
-	if (fnamecmp(maybe_unique, rival) == 0)
-	    return FALSE;
-    }
-
-    return TRUE;
+	if (fnamecmp(maybe_unique, gettail(other_paths[j])) == 0)
+	    return FALSE;  /* match */
+    }
+
+    return TRUE;  /* no match found */
 }
 
 /*
@@ -9331,6 +9325,101 @@ remove_duplicates(gap)
 }
 
 /*
+ * Split the 'path' option to a an array of strings as garray_T.  Relative
+ * paths are expanded to their equivalent fullpath.  This includes the "."
+ * (relative to current buffer directory) and empty path (relative to current
+ * directory) notations.
+ *
+ * TODO: handle upward search (;) and path limiter (**N) notations by
+ * expanding each into their equivalent path(s).
+ */
+    static void
+expand_path_option(curdir, gap)
+    char_u	*curdir;
+    garray_T	*gap;
+{
+    char_u	*path_option = *curbuf->b_p_path == NUL
+						  ? p_path : curbuf->b_p_path;
+    char_u	*buf;
+
+    ga_init2(gap, (int)sizeof(char_u *), 1);
+
+    if ((buf = alloc((int)(MAXPATHL))) == NULL)
+	return;
+
+    while (*path_option != NUL)
+    {
+	copy_option_part(&path_option, buf, MAXPATHL, " ,");
+
+	if (STRCMP(buf, ".") == 0) /* relative to current buffer */
+	{
+	    if (curbuf->b_ffname == NULL)
+		continue;
+	    STRCPY(buf, curbuf->b_ffname);
+	    *gettail(buf) = NUL;
+	}
+	else if (buf[0] == NUL) /* relative to current directory */
+	    STRCPY(buf, curdir);
+	else if (!mch_isFullName(buf))
+	{
+	    /* Expand relative path to their full path equivalent */
+	    int curdir_len = STRLEN(curdir);
+	    int buf_len = STRLEN(buf);
+
+	    if (curdir_len + buf_len + 3 > MAXPATHL)
+		continue;
+	    STRMOVE(buf + curdir_len + 1, buf);
+	    STRCPY(buf, curdir);
+	    add_pathsep(buf);
+	    STRMOVE(buf + curdir_len, buf + curdir_len + 1);
+	}
+
+	addfile(gap, buf, EW_NOTFOUND|EW_DIR|EW_FILE);
+    }
+
+    vim_free(buf);
+}
+
+/*
+ * Returns a pointer to the file or directory name in fname that matches the
+ * longest path in gap, or NULL if there is no match. For example:
+ *
+ *    path: /foo/bar/baz
+ *   fname: /foo/bar/baz/quux.txt
+ * returns:              ^this
+ */
+    static char_u *
+get_path_cutoff(fname, gap)
+    char_u *fname;
+    garray_T *gap;
+{
+    int	    i;
+    int	    maxlen = 0;
+    char_u  **path_part = (char_u **)gap->ga_data;
+    char_u  *cutoff = NULL;
+
+    for (i = 0; i < gap->ga_len; i++)
+    {
+	int j = 0;
+
+	while (fname[j] == path_part[i][j] && fname[j] != NUL
+						   && path_part[i][j] != NUL)
+	    j++;
+	if (j > maxlen)
+	{
+	    maxlen = j;
+	    cutoff = &fname[j];
+	}
+    }
+
+    /* Skip to the file or directory name */
+    while (cutoff != NULL && vim_ispathsep(*cutoff) && *cutoff != NUL)
+	mb_ptr_adv(cutoff);
+
+    return cutoff;
+}
+
+/*
  * Sorts, removes duplicates and modifies all the fullpath names in gap so that
  * they are unique with respect to each other while conserving the part that
  * matches the pattern. Beware, this is at least O(n^2) wrt gap->ga_len.
@@ -9342,13 +9431,14 @@ uniquefy_paths(gap, pattern)
 {
     int		i;
     int		len;
-    char_u	*pathsep_p;
-    char_u	*path;
-    char_u	**fnames = (char_u **) gap->ga_data;
+    char_u	**fnames = (char_u **)gap->ga_data;
     int		sort_again = 0;
     char_u	*pat;
     char_u      *file_pattern;
+    char_u	*curdir = NULL;
+    int		len_curdir = 0;
     regmatch_T	regmatch;
+    garray_T	path_ga;
 
     sort_strings(fnames, gap->ga_len);
     remove_duplicates(gap);
@@ -9360,8 +9450,10 @@ uniquefy_paths(gap, pattern)
      */
     len = (int)STRLEN(pattern);
     file_pattern = alloc(len + 2);
+    if (file_pattern == NULL)
+	return;
     file_pattern[0] = '*';
-    file_pattern[1] = '\0';
+    file_pattern[1] = NUL;
     STRCAT(file_pattern, pattern);
     pat = file_pat_to_reg_pat(file_pattern, NULL, NULL, TRUE);
     vim_free(file_pattern);
@@ -9374,25 +9466,95 @@ uniquefy_paths(gap, pattern)
     if (regmatch.regprog == NULL)
 	return;
 
+    if ((curdir = alloc((int)(MAXPATHL))) == NULL)
+	return;
+    mch_dirname(curdir, MAXPATHL);
+    len_curdir = STRLEN(curdir);
+
+    expand_path_option(curdir, &path_ga);
+
     for (i = 0; i < gap->ga_len; i++)
     {
-	path = fnames[i];
+	char_u	    *path = fnames[i];
+	int	    is_in_curdir;
+	char_u	    *dir_end = gettail(path);
+
 	len = (int)STRLEN(path);
-
-	/* 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, gap, i))
-	    {
-		sort_again = 1;
-		mch_memmove(path, pathsep_p + 1, STRLEN(pathsep_p));
-		break;
-	    }
-    }
-
+	while (dir_end > path && !vim_ispathsep(*dir_end))
+	    mb_ptr_back(path, dir_end);
+	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?
+	 */
+	if (is_in_curdir)
+	{
+	    char_u *rel_path;
+	    char_u *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;
+	    }
+
+	    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 = 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;
+		}
+	}
+    }
+
+theend:
+    vim_free(curdir);
+    ga_clear_strings(&path_ga);
     vim_free(regmatch.regprog);
+
     if (sort_again)
     {
 	sort_strings(fnames, gap->ga_len);
@@ -9412,23 +9574,53 @@ expand_in_path(gap, pattern, flags)
     int		flags;		/* EW_* flags */
 {
     int		c = 0;
-    char_u	*path_option = *curbuf->b_p_path == NUL
-						  ? p_path : curbuf->b_p_path;
-    char_u	*files;
+    char_u	*files = NULL;
     char_u	*s;	/* start */
     char_u	*e;	/* end */
-
-    files = globpath(path_option, pattern, 0);
+    char_u	*paths = NULL;
+    char_u	**path_list;
+    char_u	*curdir;
+    garray_T	path_ga;
+    int		i;
+
+    if ((curdir = alloc((int)(MAXPATHL))) == NULL)
+	return 0;
+    mch_dirname(curdir, MAXPATHL);
+
+    expand_path_option(curdir, &path_ga);
+    vim_free(curdir);
+    path_list = (char_u **)(path_ga.ga_data);
+    for (i = 0; i < path_ga.ga_len; i++)
+    {
+	if (paths == NULL)
+	{
+	    if ((paths = alloc((int)(STRLEN(path_list[i]) + 1))) == NULL)
+		return 0;
+	    STRCPY(paths, path_list[i]);
+	}
+	else
+	{
+	    if ((paths = realloc(paths, (int)(STRLEN(paths)
+					  + STRLEN(path_list[i]) + 2))) == NULL)
+		return 0;
+	    STRCAT(paths, ",");
+	    STRCAT(paths, path_list[i]);
+	}
+    }
+
+    files = globpath(paths, pattern, 0);
+    vim_free(paths);
+
     if (files == NULL)
 	return 0;
 
     /* Copy each path in files into gap */
     s = e = files;
-    while (*s != '\0')
-    {
-	while (*e != '\n' && *e != '\0')
+    while (*s != NUL)
+    {
+	while (*e != '\n' && *e != NUL)
 	    e++;
-	if (*e == '\0')
+	if (*e == NUL)
 	{
 	    addfile(gap, s, flags);
 	    break;
@@ -9436,7 +9628,7 @@ expand_in_path(gap, pattern, flags)
 	else
 	{
 	    /* *e is '\n' */
-	    *e = '\0';
+	    *e = NUL;
 	    addfile(gap, s, flags);
 	    e++;
 	    s = e;
@@ -9817,7 +10009,7 @@ get_cmd_output(cmd, infile, flags)
 	buffer = NULL;
     }
     else
-	buffer[len] = '\0';	/* make sure the buffer is terminated */
+	buffer[len] = NUL;	/* make sure the buffer is terminated */
 
 done:
     vim_free(tempname);