Fix completion past explicitely closed tokens

This fixes an issue where completion would still be provided after
the closing character of a token, which could then get frustrating
combined with auto-insertion of completions.

For example, inserting `%{<newline>}` for a command-completed token
(such as the commands for a hook) would still trigger completion right
after the `}` and that completion would get auto-inserted **replacing**
that closing `}`.
This commit is contained in:
Maxime Coste 2021-05-18 20:46:55 +10:00
parent ead12e11bd
commit 0cecea6593
2 changed files with 24 additions and 19 deletions

View File

@ -295,7 +295,7 @@ Token parse_percent_token(Reader& reader, bool throw_on_unterminated)
coord.line, coord.column, type_name, coord.line, coord.column, type_name,
opening_delimiter, closing_delimiter)}; opening_delimiter, closing_delimiter)};
return {type, start - str_beg, coord, std::move(quoted.content)}; return {type, start - str_beg, coord, std::move(quoted.content), quoted.terminated};
} }
else else
{ {
@ -306,7 +306,7 @@ Token parse_percent_token(Reader& reader, bool throw_on_unterminated)
coord.line, coord.column, type_name, coord.line, coord.column, type_name,
opening_delimiter, opening_delimiter)}; opening_delimiter, opening_delimiter)};
return {type, start - str_beg, coord, std::move(quoted.content)}; return {type, start - str_beg, coord, std::move(quoted.content), quoted.terminated};
} }
} }
@ -404,7 +404,8 @@ Optional<Token> CommandParser::read_token(bool throw_on_unterminated)
throw parse_error{format("unterminated string {0}...{0}", c)}; throw parse_error{format("unterminated string {0}...{0}", c)};
return Token{c == '"' ? Token::Type::RawEval return Token{c == '"' ? Token::Type::RawEval
: Token::Type::RawQuoted, : Token::Type::RawQuoted,
start - line.begin(), coord, std::move(quoted.content)}; start - line.begin(), coord, std::move(quoted.content),
quoted.terminated};
} }
else if (c == '%') else if (c == '%')
{ {
@ -676,6 +677,9 @@ Completions CommandManager::complete(const Context& context,
kak_assert(not tokens.empty()); kak_assert(not tokens.empty());
const auto& token = tokens.back(); const auto& token = tokens.back();
if (token.terminated) // do not complete past explicit token close
return Completions{};
auto requote = [](Completions completions, Token::Type token_type) { auto requote = [](Completions completions, Token::Type token_type) {
if (completions.flags & Completions::Flags::Quoted) if (completions.flags & Completions::Flags::Quoted)
return completions; return completions;
@ -700,13 +704,13 @@ Completions CommandManager::complete(const Context& context,
}; };
const ByteCount start = token.pos; const ByteCount start = token.pos;
const ByteCount cursor_pos_in_token = cursor_pos - start; const ByteCount pos_in_token = cursor_pos - start;
// command name completion // command name completion
if (tokens.size() == 1 and (token.type == Token::Type::Raw or if (tokens.size() == 1 and (token.type == Token::Type::Raw or
token.type == Token::Type::RawQuoted)) token.type == Token::Type::RawQuoted))
{ {
StringView query = command_line.substr(start, cursor_pos_in_token); StringView query = command_line.substr(start, pos_in_token);
return offset_pos(requote(complete_command_name(context, query), token.type), start); return offset_pos(requote(complete_command_name(context, query), token.type), start);
} }
@ -715,27 +719,27 @@ Completions CommandManager::complete(const Context& context,
case Token::Type::RegisterExpand: case Token::Type::RegisterExpand:
return {start , cursor_pos, return {start , cursor_pos,
RegisterManager::instance().complete_register_name( RegisterManager::instance().complete_register_name(
token.content, cursor_pos_in_token) }; token.content, pos_in_token) };
case Token::Type::OptionExpand: case Token::Type::OptionExpand:
return {start , cursor_pos, return {start , cursor_pos,
GlobalScope::instance().option_registry().complete_option_name( GlobalScope::instance().option_registry().complete_option_name(
token.content, cursor_pos_in_token) }; token.content, pos_in_token) };
case Token::Type::ShellExpand: case Token::Type::ShellExpand:
return offset_pos(shell_complete(context, flags, token.content, return offset_pos(shell_complete(context, flags, token.content,
cursor_pos_in_token), start); pos_in_token), start);
case Token::Type::ValExpand: case Token::Type::ValExpand:
return {start , cursor_pos, return {start , cursor_pos,
ShellManager::instance().complete_env_var( ShellManager::instance().complete_env_var(
token.content, cursor_pos_in_token) }; token.content, pos_in_token) };
case Token::Type::FileExpand: case Token::Type::FileExpand:
{ {
const auto& ignored_files = context.options()["ignored_files"].get<Regex>(); const auto& ignored_files = context.options()["ignored_files"].get<Regex>();
return {start , cursor_pos, complete_filename( return {start , cursor_pos, complete_filename(
token.content, ignored_files, cursor_pos_in_token, FilenameFlags::Expand) }; token.content, ignored_files, pos_in_token, FilenameFlags::Expand) };
} }
case Token::Type::Raw: case Token::Type::Raw:
@ -752,23 +756,23 @@ Completions CommandManager::complete(const Context& context,
if (command_it == m_commands.end()) if (command_it == m_commands.end())
return Completions{}; return Completions{};
auto& command = command_it->value;
if (token.content.substr(0_byte, 1_byte) == "-") if (token.content.substr(0_byte, 1_byte) == "-")
{ {
auto switches = Kakoune::complete(token.content.substr(1_byte), cursor_pos_in_token, auto switches = Kakoune::complete(token.content.substr(1_byte), pos_in_token,
command_it->value.param_desc.switches | command.param_desc.switches |
transform(&SwitchMap::Item::key)); transform(&SwitchMap::Item::key));
if (not switches.empty()) if (not switches.empty())
return Completions{start+1, cursor_pos, std::move(switches)}; return Completions{start+1, cursor_pos, std::move(switches)};
} }
if (not command_it->value.completer) if (not command.completer)
return Completions{}; return Completions{};
Vector<String> params; auto params = tokens | skip(1) | transform(&Token::content) | gather<Vector>();
for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) auto index = params.size() - 1;
params.push_back(it->content);
return offset_pos(requote(command_it->value.completer( return offset_pos(requote(command.completer(context, flags, params, index, pos_in_token), token.type), start);
context, flags, params, tokens.size() - 2,
cursor_pos_in_token), token.type), start);
} }
case Token::Type::RawEval: case Token::Type::RawEval:
default: default:

View File

@ -59,6 +59,7 @@ struct Token
ByteCount pos; ByteCount pos;
BufferCoord coord; BufferCoord coord;
String content; String content;
bool terminated = false;
}; };
struct Reader struct Reader