aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/expand.cpp23
-rw-r--r--src/fish_tests.cpp1
-rw-r--r--src/wildcard.cpp91
3 files changed, 88 insertions, 27 deletions
diff --git a/src/expand.cpp b/src/expand.cpp
index 6f93ac35..42b5fbad 100644
--- a/src/expand.cpp
+++ b/src/expand.cpp
@@ -1777,29 +1777,6 @@ static expand_error_t expand_stage_home_and_pid(const wcstring &input, std::vect
}
return EXPAND_OK;
}
-#if 0
-if (string_prefixes_string(L"./", path))
-{
- /* Ignore the CDPATH in this case; just use the working directory */
- directories.push_back(working_directory);
-}
-else
-{
- /* Get the CDPATH */
- env_var_t cdpath = env_get_string(L"CDPATH");
- if (cdpath.missing_or_empty())
- cdpath = L".";
-
- /* Tokenize it into directories */
- wcstokenizer tokenizer(cdpath, ARRAY_SEP_STR);
- wcstring next_path;
- while (tokenizer.next(next_path))
- {
- /* Ensure that we use the working directory for relative cdpaths like "." */
- directories.push_back(apply_working_directory(next_path, working_directory));
- }
-}
-#endif
static expand_error_t expand_stage_wildcards(const wcstring &input, std::vector<completion_t> *out, expand_flags_t flags, parse_error_list_t *errors)
{
diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp
index d5b28341..cd1c0b60 100644
--- a/src/fish_tests.cpp
+++ b/src/fish_tests.cpp
@@ -2429,6 +2429,7 @@ static void test_autosuggest_suggest_special()
if (system("mkdir -p '/tmp/autosuggest_test/~hahaha/path1/path2/'")) err(L"mkdir failed");
perform_one_autosuggestion_cd_test(L"cd ~haha", vars, L"ha/path1/path2/", __LINE__);
+ perform_one_autosuggestion_cd_test(L"cd ~hahaha/", vars, L"path1/path2/", __LINE__);
if (chdir_set_pwd(saved_wd)) err(L"chdir failed");
if (system("rm -Rf '/tmp/autosuggest_test/'")) err(L"rm failed");
diff --git a/src/wildcard.cpp b/src/wildcard.cpp
index 671a3dd0..6ba4b1a2 100644
--- a/src/wildcard.cpp
+++ b/src/wildcard.cpp
@@ -617,9 +617,8 @@ static bool wildcard_test_flags_then_complete(const wcstring &filepath,
}
/* Compute the description */
- bool wants_desc = !(expand_flags & EXPAND_NO_DESCRIPTIONS);
wcstring desc;
- if (wants_desc)
+ if (!(expand_flags & EXPAND_NO_DESCRIPTIONS))
{
desc = file_get_desc(filepath, lstat_res, lstat_buf, stat_res, stat_buf, stat_errno);
@@ -631,7 +630,7 @@ static bool wildcard_test_flags_then_complete(const wcstring &filepath,
}
}
- /* Append a / if this is a directory */
+ /* Append a / if this is a directory. Note this requirement may be the only reason we have to call stat() in some cases. */
if (is_directory)
{
return wildcard_complete(filename + L'/', wc, desc.c_str(), NULL, out, expand_flags, COMPLETE_NO_SPACE);
@@ -714,12 +713,76 @@ class wildcard_expander_t
}
}
+ /* Given a start point as an absolute path, for any directory that has exactly one non-hidden entity in it which is itself a directory, return that. The result is a relative path. For example, if start_point is '/usr' we may return 'local/bin/'.
+
+ The result does not have a leading slash, but does have a trailing slash if non-empty. */
+ wcstring descend_unique_hierarchy(const wcstring &start_point)
+ {
+ assert(! start_point.empty() && start_point.at(0) == L'/');
+
+ wcstring unique_hierarchy;
+ wcstring abs_unique_hierarchy = start_point;
+
+ bool stop_descent = false;
+ DIR *dir;
+ while (!stop_descent && (dir = wopendir(abs_unique_hierarchy)))
+ {
+ /* We keep track of the single unique_entry entry. If we get more than one, it's not unique and we stop the descent. */
+ wcstring unique_entry;
+
+ bool child_is_dir;
+ wcstring child_entry;
+ while (wreaddir_resolving(dir, abs_unique_hierarchy, child_entry, &child_is_dir))
+ {
+ if (child_entry.empty() || child_entry.at(0) == L'.')
+ {
+ /* Either hidden, or . and .. entries. Skip them. */
+ continue;
+ }
+ else if (child_is_dir && unique_entry.empty())
+ {
+ /* First candidate */
+ unique_entry = child_entry;
+ }
+ else
+ {
+ /* We either have two or more candidates, or the child is not a directory. We're done. */
+ stop_descent = true;
+ break;
+ }
+ }
+
+ /* We stop if we got two or more entries; also stop if we got zero. */
+ if (unique_entry.empty())
+ {
+ stop_descent = true;
+ }
+
+ if (! stop_descent)
+ {
+ /* We have an entry in the unique hierarchy! */
+ append_path_component(unique_hierarchy, unique_entry);
+ unique_hierarchy.push_back(L'/');
+
+ append_path_component(abs_unique_hierarchy, unique_entry);
+ abs_unique_hierarchy.push_back(L'/');
+ }
+ closedir(dir);
+ }
+ return unique_hierarchy;
+ }
+
+
void try_add_completion_result(const wcstring &filepath, const wcstring &filename, const wcstring &wildcard)
{
/* This function is only for the completions case */
assert(this->flags & EXPAND_FOR_COMPLETIONS);
+
+ wcstring abs_path = this->prefix;
+ append_path_component(abs_path, filepath);
+
size_t before = this->resolved_completions->size();
- if (wildcard_test_flags_then_complete(filepath, filename, wildcard.c_str(), this->flags, this->resolved_completions))
+ if (wildcard_test_flags_then_complete(abs_path, filename, wildcard.c_str(), this->flags, this->resolved_completions))
{
/* Hack. We added this completion result based on the last component of the wildcard.
Prepend all prior components of the wildcard to each completion that replaces its token. */
@@ -735,6 +798,21 @@ class wildcard_expander_t
c.prepend_token_prefix(wc_base);
c.prepend_token_prefix(this->original_base);
}
+
+ /* Hack. Implement EXPAND_SPECIAL_CD by descending the deepest unique hierarchy we can, and then appending any components to each new result. */
+ if (flags & EXPAND_SPECIAL_CD)
+ {
+ wcstring unique_hierarchy = this->descend_unique_hierarchy(abs_path);
+ if (! unique_hierarchy.empty())
+ {
+ for (size_t i=before; i < after; i++)
+ {
+ completion_t &c = this->resolved_completions->at(i);
+ c.completion.append(unique_hierarchy);
+ }
+ }
+ }
+
this->did_add = true;
}
}
@@ -1069,6 +1147,11 @@ int wildcard_expand_string(const wcstring &wc, const wcstring &working_directory
assert(output != NULL);
/* Fuzzy matching only if we're doing completions */
assert((flags & (EXPAND_FUZZY_MATCH | EXPAND_FOR_COMPLETIONS)) != EXPAND_FUZZY_MATCH);
+
+ /* EXPAND_SPECIAL_CD requires DIRECTORIES_ONLY and EXPAND_FOR_COMPLETIONS and EXPAND_NO_DESCRIPTIONS */
+ assert(!(flags & EXPAND_SPECIAL_CD) ||
+ ((flags & DIRECTORIES_ONLY) && (flags & EXPAND_FOR_COMPLETIONS) && (flags & EXPAND_NO_DESCRIPTIONS)));
+
/* Hackish fix for 1631. We are about to call c_str(), which will produce a string truncated at any embedded nulls. We could fix this by passing around the size, etc. However embedded nulls are never allowed in a filename, so we just check for them and return 0 (no matches) if there is an embedded null. */
if (wc.find(L'\0') != wcstring::npos)
{