aboutsummaryrefslogtreecommitdiffhomepage
path: root/complete.cpp
diff options
context:
space:
mode:
authorGravatar ridiculousfish <corydoras@ridiculousfish.com>2013-10-12 18:17:03 -0700
committerGravatar ridiculousfish <corydoras@ridiculousfish.com>2013-10-12 18:17:03 -0700
commitddf98661e4f16f75bb3deea26ec0c1e3bc651263 (patch)
treed811a53831ba23bfb81254d61798ba459f93357c /complete.cpp
parentcbd8a27a6d1ec705032486851203f8c4d1b4f56f (diff)
Adopt new parser in tab completions
Diffstat (limited to 'complete.cpp')
-rw-r--r--complete.cpp302
1 files changed, 117 insertions, 185 deletions
diff --git a/complete.cpp b/complete.cpp
index 8df02b35..e2504205 100644
--- a/complete.cpp
+++ b/complete.cpp
@@ -44,6 +44,7 @@
#include "parser_keywords.h"
#include "wutil.h"
#include "path.h"
+#include "parse_tree.h"
/*
Completion description strings, mostly for different types of files, such as sockets, block devices, etc.
@@ -1363,7 +1364,9 @@ struct local_options_t
bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spopt, const wcstring &sstr, bool use_switches)
{
- const wchar_t * const cmd_orig = scmd_orig.c_str(), * const popt = spopt.c_str(), * const str = sstr.c_str();
+ const wchar_t * const cmd_orig = scmd_orig.c_str();
+ const wchar_t * const popt = spopt.c_str();
+ const wchar_t * const str = sstr.c_str();
bool use_common=1, use_files=1;
@@ -1790,231 +1793,160 @@ bool completer_t::try_complete_user(const wcstring &str)
return res;
}
-void complete(const wcstring &cmd, std::vector<completion_t> &comps, completion_request_flags_t flags, wcstring_list_t *commands_to_load)
+void complete(const wcstring &cmd_with_subcmds, std::vector<completion_t> &comps, completion_request_flags_t flags, wcstring_list_t *commands_to_load)
{
+ /* Determine the innermost subcommand */
+ const wchar_t *cmdsubst_begin, *cmdsubst_end;
+ parse_util_cmdsubst_extent(cmd_with_subcmds.c_str(), cmd_with_subcmds.size(), &cmdsubst_begin, &cmdsubst_end);
+ assert(cmdsubst_begin != NULL && cmdsubst_end != NULL && cmdsubst_end >= cmdsubst_begin);
+ const wcstring cmd = wcstring(cmdsubst_begin, cmdsubst_end - cmdsubst_begin);
+
/* Make our completer */
completer_t completer(cmd, flags);
- const wchar_t *tok_begin, *tok_end, *cmdsubst_begin, *cmdsubst_end, *prev_begin, *prev_end;
- wcstring current_token, prev_token;
wcstring current_command;
- int on_command=0;
- size_t pos;
+ const size_t pos = cmd.size();
bool done=false;
- int use_command = 1;
- int use_function = 1;
- int use_builtin = 1;
- int had_ddash = 0;
+ bool use_command = 1;
+ bool use_function = 1;
+ bool use_builtin = 1;
// debug( 1, L"Complete '%ls'", cmd );
- size_t cursor_pos = cmd.size();
-
const wchar_t *cmd_cstr = cmd.c_str();
- parse_util_cmdsubst_extent(cmd_cstr, cursor_pos, &cmdsubst_begin, &cmdsubst_end);
- parse_util_token_extent(cmd_cstr, cursor_pos, &tok_begin, &tok_end, &prev_begin, &prev_end);
-
- if (!cmdsubst_begin)
- done=1;
-
+ const wchar_t *tok_begin = NULL, *prev_begin = NULL, *prev_end = NULL;
+ parse_util_token_extent(cmd_cstr, cmd.size(), &tok_begin, NULL, &prev_begin, &prev_end);
/**
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;
if (!done)
{
- wcstring tmp = tok_begin;
- done = completer.try_complete_variable(tmp) || completer.try_complete_user(tmp);
+ done = completer.try_complete_variable(current_token) || completer.try_complete_user(current_token);
}
if (!done)
{
- pos = cursor_pos-(cmdsubst_begin-cmd_cstr);
-
- const wcstring buff = wcstring(cmdsubst_begin, cmdsubst_end-cmdsubst_begin);
-
- int had_cmd=0;
- int end_loop=0;
-
- tokenizer_t tok(buff.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS);
- while (tok_has_next(&tok) && !end_loop)
+ //const size_t prev_token_len = (prev_begin ? prev_end - prev_begin : 0);
+ //const wcstring prev_token(prev_begin, prev_token_len);
+
+ parse_node_tree_t tree;
+ parse_t::parse(cmd, parse_flag_continue_after_error | parse_flag_accept_incomplete_tokens, &tree, NULL);
+
+ /* Find the plain statement that contains the position */
+ const parse_node_t *plain_statement = tree.find_node_matching_source_location(symbol_plain_statement, pos, NULL);
+ if (plain_statement != NULL)
{
- switch (tok_last_type(&tok))
+ assert(plain_statement->has_source() && plain_statement->type == symbol_plain_statement);
+
+ /* Get the command node */
+ const parse_node_t *cmd_node = tree.get_child(*plain_statement, 0, parse_token_type_string);
+
+ /* Get the actual command string */
+ if (cmd_node != NULL)
+ current_command = cmd_node->get_source(cmd);
+
+ /* Check the decoration */
+ switch (tree.decoration_for_plain_statement(*plain_statement))
{
-
- case TOK_STRING:
+ case parse_statement_decoration_none:
+ use_command = true;
+ use_function = false;
+ use_builtin = false;
+ break;
+
+ case parse_statement_decoration_command:
+ use_command = true;
+ use_function = false;
+ use_builtin = false;
+ break;
+
+ case parse_statement_decoration_builtin:
+ use_command = false;
+ use_function = false;
+ use_builtin = true;
+ break;
+ }
+
+ if (cmd_node && cmd_node->location_in_or_at_end_of_source_range(pos))
+ {
+ /* Complete command filename */
+ completer.complete_cmd(current_token, use_function, use_builtin, use_command);
+ }
+ else
+ {
+ /* Get all the arguments */
+ const parse_node_tree_t::parse_node_list_t all_arguments = tree.find_nodes(*plain_statement, symbol_argument);
+
+ /* See whether we are in an argument. We may also be in a redirection, or nothing at all. */
+ size_t matching_arg_index = -1;
+ for (size_t i=0; i < all_arguments.size(); i++)
{
-
- const wcstring ncmd = tok_last(&tok);
- int is_ddash = (ncmd == L"--") && ((tok_get_pos(&tok)+2) < (long)pos);
-
- if (!had_cmd)
+ const parse_node_t *node = all_arguments.at(i);
+ if (node->location_in_or_at_end_of_source_range(pos))
{
-
- if (parser_keywords_is_subcommand(ncmd))
- {
- if (ncmd == L"builtin")
- {
- use_function = 0;
- use_command = 0;
- use_builtin = 1;
- }
- else if (ncmd == L"command")
- {
- use_command = 1;
- use_function = 0;
- use_builtin = 0;
- }
- break;
- }
-
-
- if (!is_ddash ||
- ((use_command && use_function && use_builtin)))
- {
- current_command = ncmd;
-
- size_t token_end = tok_get_pos(&tok) + ncmd.size();
-
- on_command = (pos <= token_end);
- had_cmd=1;
- }
-
+ matching_arg_index = i;
+ break;
}
- else
+ }
+
+ bool had_ddash = false;
+ wcstring current_argument, previous_argument;
+ if (matching_arg_index != (size_t)(-1))
+ {
+ /* Get the current argument and the previous argument, if we have one */
+ current_argument = all_arguments.at(matching_arg_index)->get_source(cmd);
+
+ if (matching_arg_index > 0)
+ previous_argument = all_arguments.at(matching_arg_index - 1)->get_source(cmd);
+
+ /* Check to see if we have a preceding double-dash */
+ for (size_t i=0; i < matching_arg_index; i++)
{
- if (is_ddash)
+ if (all_arguments.at(i)->get_source(cmd) == L"--")
{
- had_ddash = 1;
+ had_ddash = true;
+ break;
}
}
-
- break;
}
- case TOK_END:
- case TOK_PIPE:
- case TOK_BACKGROUND:
- {
- had_cmd=0;
- had_ddash = 0;
- use_command = 1;
- use_function = 1;
- use_builtin = 1;
- break;
- }
+ bool do_file = false;
- case TOK_ERROR:
- {
- end_loop=1;
- break;
- }
-
- default:
+ wcstring current_command_unescape = current_command;
+ wcstring previous_argument_unescape = previous_argument;
+ wcstring current_argument_unescape = current_argument;
+
+ if (unescape_string(current_command_unescape, 0) &&
+ unescape_string(previous_argument_unescape, 0) &&
+ unescape_string(current_argument_unescape, UNESCAPE_INCOMPLETE))
{
- break;
+ do_file = completer.complete_param(current_command_unescape,
+ previous_argument_unescape,
+ current_argument_unescape,
+ !had_ddash);
}
- }
-
- if (tok_get_pos(&tok) >= (long)pos)
- {
- end_loop=1;
- }
-
- tok_next(&tok);
-
- }
-
- /*
- Get the string to complete
- */
-
- current_token.assign(tok_begin, cursor_pos-(tok_begin-cmd_cstr));
- if (prev_begin)
- {
- prev_token.assign(prev_begin, prev_end - prev_begin);
- }
- else
- {
- prev_token.clear();
- }
+ /* If we have found no command specific completions at all, fall back to using file completions. */
+ if (completer.empty())
+ do_file = true;
-// debug( 0, L"on_command: %d, %ls %ls\n", on_command, current_command, current_token );
+ /* But if we are planning on loading commands, don't do file completions.
+ See https://github.com/fish-shell/fish-shell/issues/378 */
+ if (commands_to_load != NULL && completer.has_commands_to_load())
+ do_file = false;
- /*
- Check if we are using the 'command' or 'builtin' builtins
- _and_ we are writing a switch instead of a command. In that
- case, complete using the builtins completions, not using a
- subcommand.
- */
+ /* And if we're autosuggesting, and the token is empty, don't do file suggestions */
+ if ((flags & COMPLETION_REQUEST_AUTOSUGGESTION) && current_argument_unescape.empty())
+ do_file = false;
- if ((on_command || current_token == L"--") &&
- string_prefixes_string(L"-", current_token) &&
- !(use_command && use_function && use_builtin))
- {
- if (use_command == 0)
- current_command = L"builtin";
- else
- current_command = L"command";
-
- had_cmd = 1;
- on_command = 0;
- }
-
- /*
- Use command completions if in between commands
- */
- if (!had_cmd)
- {
- on_command=1;
- }
-
-
- if (on_command)
- {
- /* Complete command filename */
- completer.complete_cmd(current_token, use_function, use_builtin, use_command);
- }
- else
- {
- bool do_file = false;
-
- wcstring current_command_unescape = current_command;
- wcstring prev_token_unescape = prev_token;
- wcstring current_token_unescape = current_token;
-
- if (unescape_string(current_command_unescape, 0) &&
- unescape_string(prev_token_unescape, 0) &&
- unescape_string(current_token_unescape, UNESCAPE_INCOMPLETE))
- {
- do_file = completer.complete_param(current_command_unescape,
- prev_token_unescape,
- current_token_unescape,
- !had_ddash);
+ /* This function wants the unescaped string */
+ completer.complete_param_expand(current_token, do_file);
}
-
- /* If we have found no command specific completions at
- all, fall back to using file completions.
- */
- if (completer.empty())
- do_file = true;
-
- /* But if we are planning on loading commands, don't do file completions.
- See https://github.com/fish-shell/fish-shell/issues/378 */
- if (commands_to_load != NULL && completer.has_commands_to_load())
- do_file = false;
-
- /* And if we're autosuggesting, and the token is empty, don't do file suggestions */
- if ((flags & COMPLETION_REQUEST_AUTOSUGGESTION) && current_token_unescape.empty())
- do_file = false;
-
- /*
- This function wants the unescaped string
- */
- completer.complete_param_expand(current_token, do_file);
}
}