Improve command completion by taking into account quoting of arguments

This commit is contained in:
Maxime Coste 2015-09-20 11:19:10 +01:00
parent f59108072f
commit 574e337132

View File

@ -48,6 +48,7 @@ struct Token
enum class Type enum class Type
{ {
Raw, Raw,
RawQuoted,
RawEval, RawEval,
ShellExpand, ShellExpand,
RegisterExpand, RegisterExpand,
@ -188,7 +189,7 @@ template<bool throw_on_invalid>
Token::Type token_type(StringView type_name) Token::Type token_type(StringView type_name)
{ {
if (type_name == "") if (type_name == "")
return Token::Type::Raw; return Token::Type::RawQuoted;
else if (type_name == "sh") else if (type_name == "sh")
return Token::Type::ShellExpand; return Token::Type::ShellExpand;
else if (type_name == "reg") else if (type_name == "reg")
@ -200,7 +201,7 @@ Token::Type token_type(StringView type_name)
else if (throw_on_invalid) else if (throw_on_invalid)
throw unknown_expand{type_name}; throw unknown_expand{type_name};
else else
return Token::Type::Raw; return Token::Type::RawQuoted;
} }
void skip_blanks_and_comments(Reader& reader) void skip_blanks_and_comments(Reader& reader)
@ -295,7 +296,7 @@ TokenList parse(StringView line)
if (throw_on_unterminated and not reader) if (throw_on_unterminated and not reader)
throw parse_error{format("unterminated string {0}...{0}", c)}; throw parse_error{format("unterminated string {0}...{0}", c)};
result.emplace_back(c == '"' ? Token::Type::RawEval result.emplace_back(c == '"' ? Token::Type::RawEval
: Token::Type::Raw, : Token::Type::RawQuoted,
start, reader.pos, coord, std::move(token)); start, reader.pos, coord, std::move(token));
} }
else if (c == '%') else if (c == '%')
@ -346,6 +347,7 @@ String expand_token(const Token& token, const Context& context,
case Token::Type::RawEval: case Token::Type::RawEval:
return expand(content, context, shell_params, env_vars); return expand(content, context, shell_params, env_vars);
case Token::Type::Raw: case Token::Type::Raw:
case Token::Type::RawQuoted:
return content; return content;
default: kak_assert(false); default: kak_assert(false);
} }
@ -483,7 +485,8 @@ CommandInfo CommandManager::command_info(const Context& context, StringView comm
CommandInfo res; CommandInfo res;
if (cmd_idx == tokens.size() or if (cmd_idx == tokens.size() or
tokens[cmd_idx].type() != Token::Type::Raw) (tokens[cmd_idx].type() != Token::Type::Raw and
tokens[cmd_idx].type() != Token::Type::RawQuoted))
return res; return res;
auto cmd = find_command(context, tokens[cmd_idx].content()); auto cmd = find_command(context, tokens[cmd_idx].content());
@ -501,7 +504,9 @@ CommandInfo CommandManager::command_info(const Context& context, StringView comm
it != tokens.end() and it->type() != Token::Type::CommandSeparator; it != tokens.end() and it->type() != Token::Type::CommandSeparator;
++it) ++it)
{ {
if (it->type() == Token::Type::Raw or it->type() == Token::Type::RawEval) if (it->type() == Token::Type::Raw or
it->type() == Token::Type::RawQuoted or
it->type() == Token::Type::RawEval)
params.push_back(it->content()); params.push_back(it->content());
} }
String helpstr = cmd->second.helper(context, params); String helpstr = cmd->second.helper(context, params);
@ -554,7 +559,8 @@ Completions CommandManager::complete(const Context& context,
// command name completion // command name completion
if (tokens.empty() or if (tokens.empty() or
(tok_idx == cmd_idx and (tok_idx == tokens.size() or (tok_idx == cmd_idx and (tok_idx == tokens.size() or
tokens[tok_idx].type() == Token::Type::Raw))) tokens[tok_idx].type() == Token::Type::Raw or
tokens[tok_idx].type() == Token::Type::RawQuoted)))
{ {
const bool is_end_token = tok_idx == tokens.size(); const bool is_end_token = tok_idx == tokens.size();
ByteCount cmd_start = is_end_token ? cursor_pos ByteCount cmd_start = is_end_token ? cursor_pos
@ -586,7 +592,7 @@ Completions CommandManager::complete(const Context& context,
ByteCount cursor_pos_in_token = cursor_pos - start; ByteCount cursor_pos_in_token = cursor_pos - start;
const Token::Type type = tok_idx < tokens.size() ? const Token::Type type = tok_idx < tokens.size() ?
tokens[tok_idx].type() : Token::Type::Raw; tokens[tok_idx].type() : Token::Type::Raw;
switch (type) switch (type)
{ {
case Token::Type::OptionExpand: case Token::Type::OptionExpand:
@ -599,6 +605,8 @@ Completions CommandManager::complete(const Context& context,
cursor_pos_in_token), start); cursor_pos_in_token), start);
case Token::Type::Raw: case Token::Type::Raw:
case Token::Type::RawQuoted:
case Token::Type::RawEval:
{ {
if (tokens[cmd_idx].type() != Token::Type::Raw) if (tokens[cmd_idx].type() != Token::Type::Raw)
return Completions{}; return Completions{};
@ -615,14 +623,16 @@ Completions CommandManager::complete(const Context& context,
params.push_back(it->content()); params.push_back(it->content());
if (tok_idx == tokens.size()) if (tok_idx == tokens.size())
params.push_back(""); params.push_back("");
Completions completions = command_it->second.completer( Completions completions = offset_pos(command_it->second.completer(
context, flags, params, tok_idx - cmd_idx - 1, context, flags, params, tok_idx - cmd_idx - 1,
cursor_pos_in_token); cursor_pos_in_token), start);
completions.start += start;
completions.end += start;
for (auto& candidate : completions.candidates) if (type != Token::Type::RawQuoted)
candidate = escape(candidate, " \t;", '\\'); {
StringView to_escape = type == Token::Type::Raw ? "% \t;" : "%";
for (auto& candidate : completions.candidates)
candidate = escape(candidate, to_escape, '\\');
}
return completions; return completions;
} }