diff --git a/complete.cpp b/complete.cpp index d38104dd5..4808c4dbd 100644 --- a/complete.cpp +++ b/complete.cpp @@ -1712,29 +1712,67 @@ bool completer_t::complete_variable(const wcstring &str, size_t start_offset) return res; } -/** - Search the specified string for the \$ sign. If found, try to - complete as an environment variable. - - \return 0 if unable to complete, 1 otherwise -*/ bool completer_t::try_complete_variable(const wcstring &str) { - size_t i = str.size(); - while (i--) + enum {e_unquoted, e_single_quoted, e_double_quoted} mode = e_unquoted; + const size_t len = str.size(); + + /* Get the position of the dollar heading a run of valid variable characters. -1 means none. */ + size_t variable_start = -1; + + for (size_t in_pos=0; in_poscomplete_variable(str, i+1); + /* This character cannot be in a variable, reset the dollar */ + variable_start = -1; } - if (!isalnum(c) && c != L'_') + + switch (c) { - return false; + case L'\\': + in_pos++; + break; + + case L'$': + if (mode == e_unquoted || mode == e_double_quoted) + { + variable_start = in_pos; + } + break; + + case L'\'': + if (mode == e_single_quoted) + { + mode = e_unquoted; + } + else if (mode == e_unquoted) + { + mode = e_single_quoted; + } + break; + + case L'"': + if (mode == e_double_quoted) + { + mode = e_unquoted; + } + else if (mode == e_unquoted) + { + mode = e_double_quoted; + } + break; } } - return false; + + /* Now complete if we have a variable start that's also not the last character */ + bool result = false; + if (variable_start != static_cast(-1) && variable_start + 1 < len) + { + result = this->complete_variable(str, variable_start + 1); + } + return result; } /** @@ -1822,7 +1860,7 @@ void complete(const wcstring &cmd_with_subcmds, std::vector &comps bool use_function = 1; bool use_builtin = 1; - // debug( 1, L"Complete '%ls'", cmd ); + //debug( 1, L"Complete '%ls'", cmd.c_str() ); const wchar_t *cmd_cstr = cmd.c_str(); const wchar_t *tok_begin = NULL, *prev_begin = NULL, *prev_end = NULL; @@ -1832,9 +1870,11 @@ void complete(const wcstring &cmd_with_subcmds, std::vector &comps If we are completing a variable name or a tilde expansion user name, we do that and return. No need for any other completions. */ - const wcstring current_token = tok_begin; + /* Get the quote type of the current token */ + + if (!done) { done = completer.try_complete_variable(current_token) || completer.try_complete_user(current_token); diff --git a/fish_tests.cpp b/fish_tests.cpp index 13e9d2fa7..984c740be 100644 --- a/fish_tests.cpp +++ b/fish_tests.cpp @@ -1535,7 +1535,14 @@ static void test_complete(void) complete(L"foobarbaz ", completions, COMPLETION_REQUEST_DEFAULT); do_test(completions.size() == 1); do_test(completions.at(0).completion == L"qux"); - + + /* Don't complete variable names in single quotes (#1023) */ + completions.clear(); + complete(L"echo '$Foo", completions, COMPLETION_REQUEST_DEFAULT); + do_test(completions.empty()); + completions.clear(); + complete(L"echo \\$Foo", completions, COMPLETION_REQUEST_DEFAULT); + do_test(completions.empty()); complete_set_variable_names(NULL); }