diff --git a/src/complete.cpp b/src/complete.cpp index 8e7fa63d7..2b4cf9963 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -1294,10 +1294,10 @@ void complete(const wcstring &cmd_with_subcmds, std::vector *out_c while (position_in_statement > 0 && cmd.at(position_in_statement - 1) == L' ') { position_in_statement--; } - const parse_node_t *plain_statement = tree.find_node_matching_source_location( - symbol_plain_statement, position_in_statement, NULL); - - if (plain_statement == NULL) { + auto plain_statement = tnode_t{ + &tree, tree.find_node_matching_source_location(symbol_plain_statement, + position_in_statement, NULL)}; + if (!plain_statement) { // Not part of a plain statement. This could be e.g. a for loop header, case expression, // etc. Do generic file completions (issue #1309). If we had to backtrack, it means // there was whitespace; don't do an autosuggestion in that case. Also don't do it if we @@ -1327,15 +1327,14 @@ void complete(const wcstring &cmd_with_subcmds, std::vector *out_c } completer.complete_param_expand(current_token, do_file); } else { - assert(plain_statement->has_source() && - plain_statement->type == symbol_plain_statement); + assert(plain_statement && plain_statement.has_source()); // Get the command node. - const parse_node_t *cmd_node = - tree.get_child(*plain_statement, 0, parse_token_type_string); + tnode_t cmd_node = plain_statement.child<0>(); + assert(cmd_node && cmd_node.has_source() && "Expected command node to be valid"); // Get the actual command string. - if (cmd_node) current_command = cmd_node->get_source(cmd); + current_command = cmd_node.get_source(cmd); // Check the decoration. switch (tree.decoration_for_plain_statement(*plain_statement)) { @@ -1363,7 +1362,7 @@ void complete(const wcstring &cmd_with_subcmds, std::vector *out_c } } - if (cmd_node && cmd_node->location_in_or_at_end_of_source_range(pos)) { + if (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, use_implicit_cd); @@ -1394,7 +1393,7 @@ void complete(const wcstring &cmd_with_subcmds, std::vector *out_c // of the argument, then the current argument is the matching one, and the // previous argument is the one before it. bool cursor_in_whitespace = - !plain_statement->location_in_or_at_end_of_source_range(pos); + !plain_statement.location_in_or_at_end_of_source_range(pos); if (cursor_in_whitespace) { current_argument = L""; previous_argument = matching_arg; @@ -1452,8 +1451,9 @@ void complete(const wcstring &cmd_with_subcmds, std::vector *out_c assert(wrap_chain.at(i) == current_command_unescape); } else if (!(flags & COMPLETION_REQUEST_AUTOSUGGESTION)) { wcstring faux_cmdline = cmd; - faux_cmdline.replace(cmd_node->source_start, - cmd_node->source_length, wrap_chain.at(i)); + faux_cmdline.replace(cmd_node.source_range()->start, + cmd_node.source_range()->length, + wrap_chain.at(i)); transient_cmd = make_unique( faux_cmdline); } diff --git a/src/parse_grammar.h b/src/parse_grammar.h index 5375a681b..c7a9fd2bf 100644 --- a/src/parse_grammar.h +++ b/src/parse_grammar.h @@ -116,6 +116,7 @@ struct alternative { #define DEF_ALT(T) struct T : public alternative #define ALT_BODY(T) \ BODY(T) \ + using type_tuple = std::tuple<>; \ static const production_element_t *resolve(const parse_token_t &, const parse_token_t &, \ parse_node_tag_t *); diff --git a/src/parse_tree.h b/src/parse_tree.h index aae621cfb..09729f007 100644 --- a/src/parse_tree.h +++ b/src/parse_tree.h @@ -256,6 +256,14 @@ class tnode_t { assert((!n || n->type == Type::token) && "node has wrong type"); } + /// Temporary conversion to parse_node_t to assist in migration. + /* implicit */ operator const parse_node_t &() const { + assert(nodeptr && "Empty tnode_t"); + return *nodeptr; + } + + /* implicit */ operator const parse_node_t *() const { return nodeptr; } + /// Return the underlying (type-erased) node. const parse_node_t *node() const { return nodeptr; } @@ -274,6 +282,10 @@ class tnode_t { return nodeptr->get_source(str); } + bool location_in_or_at_end_of_source_range(size_t loc) const { + return nodeptr && nodeptr->location_in_or_at_end_of_source_range(loc); + } + /// Type-safe access to a child at the given index. template tnode_t> child() const { @@ -282,6 +294,15 @@ class tnode_t { if (nodeptr) child = tree->get_child(*nodeptr, Index, child_type::token); return tnode_t{tree, child}; } + + /// Type-safe access to a node's parent. + /// If the parent exists and has type ParentType, return it. + /// Otherwise return a missing tnode. + template + tnode_t try_get_parent() const { + if (!nodeptr) return {}; + return {tree, tree->get_parent(*nodeptr, ParentType::token)}; + } }; /// The big entry point. Parse a string, attempting to produce a tree for the given goal type.