Auto-insert best completion on space for menu completions

The menu flag signifies that only the completions are valid arguments,
hence it makes sense to auto insert the best one on space.

Because full match is always considered the best match in completion
ranking, this should always have a reasonable behaviour.

This makes it harder to enter a hidden command, but completion can
always be disabled via <c-o> or by quoting in those rare cases.
This commit is contained in:
Maxime Coste 2020-10-19 21:01:18 +11:00
parent 2cd323b314
commit 65587d1eee
3 changed files with 48 additions and 25 deletions

View File

@ -7,6 +7,9 @@ released versions.
* `set-option -remove` support for substracting/removing from option values * `set-option -remove` support for substracting/removing from option values
* Menu completions such as command name completion are now auto-inserted on
space
== Kakoune 2020.09.01 == Kakoune 2020.09.01
* The `repl` and `send-text` aliases have been renamed respectively into * The `repl` and `send-text` aliases have been renamed respectively into

View File

@ -628,10 +628,9 @@ Completions CommandManager::complete_command_name(const Context& context, String
| transform(&CommandMap::Item::key); | transform(&CommandMap::Item::key);
auto aliases = context.aliases().flatten_aliases() auto aliases = context.aliases().flatten_aliases()
| transform(&HashItem<String, String>::key) | transform(&HashItem<String, String>::key);
| filter([](auto& alias) { return alias.length() > 3; });
return {0, query.length(), Kakoune::complete(query, query.length(), concatenated(commands, aliases))}; return {0, query.length(), Kakoune::complete(query, query.length(), concatenated(commands, aliases)), Completions::Flags::Menu};
} }
Completions CommandManager::complete_module_name(StringView query) const Completions CommandManager::complete_module_name(StringView query) const
@ -672,18 +671,43 @@ 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();
auto requote = [](Completions completions, Token::Type token_type) {
if (completions.flags & Completions::Flags::Quoted)
return completions;
if (token_type == Token::Type::Raw)
{
for (auto& c : completions.candidates)
{
if (c.substr(0_byte, 1_byte) == "%" or any_of(c, [](auto i) { return contains("; \t'\"", i); }))
c = quote(c);
}
}
else if (token_type == Token::Type::RawQuoted)
{
kak_assert(completions.start > 0);
--completions.start;
completions.flags |= Completions::Flags::Quoted;
for (auto& c : completions.candidates)
c = quote(c);
}
else
kak_assert(false);
return completions;
};
const ByteCount start = token.pos;
const ByteCount cursor_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))
{ {
auto cmd_start = token.pos; StringView query = command_line.substr(start, cursor_pos_in_token);
StringView query = command_line.substr(cmd_start, cursor_pos - cmd_start); return requote(offset_pos(complete_command_name(context, query), start), token.type);
return offset_pos(complete_command_name(context, query), cmd_start);
} }
const ByteCount start = token.pos;
const ByteCount cursor_pos_in_token = cursor_pos - start;
switch (token.type) switch (token.type)
{ {
case Token::Type::RegisterExpand: case Token::Type::RegisterExpand:
@ -714,11 +738,7 @@ Completions CommandManager::complete(const Context& context,
case Token::Type::Raw: case Token::Type::Raw:
case Token::Type::RawQuoted: case Token::Type::RawQuoted:
case Token::Type::RawEval:
{ {
if (token.type != Token::Type::Raw and token.type != Token::Type::RawQuoted)
return Completions{};
StringView command_name = tokens.front().content; StringView command_name = tokens.front().content;
if (command_name != m_last_complete_command) if (command_name != m_last_complete_command)
{ {
@ -744,20 +764,11 @@ Completions CommandManager::complete(const Context& context,
Vector<String> params; Vector<String> params;
for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) for (auto it = tokens.begin() + 1; it != tokens.end(); ++it)
params.push_back(it->content); params.push_back(it->content);
Completions completions = offset_pos(command_it->value.completer( return requote(offset_pos(command_it->value.completer(
context, flags, params, tokens.size() - 2, context, flags, params, tokens.size() - 2,
cursor_pos_in_token), start); cursor_pos_in_token), start), token.type);
if (not (completions.flags & Completions::Flags::Quoted) and token.type == Token::Type::Raw)
{
for (auto& c : completions.candidates)
c = (not c.empty() and c[0] == '%') or
any_of(c, [](auto i) { return contains("; \t'\"", i); }) ?
format("'{}'", replace(c, "'", "''")) : c;
}
return completions;
} }
case Token::Type::RawEval:
default: default:
break; break;
} }

View File

@ -991,6 +991,15 @@ public:
} }
else else
{ {
if (key == ' ' and
(m_completions.flags & Completions::Flags::Menu) and
not (m_completions.flags & Completions::Flags::Quoted) and
m_current_completion == -1 and not m_completions.candidates.empty())
{
m_line_editor.insert_from(line.char_count_to(m_completions.start),
m_completions.candidates.front());
}
m_line_editor.handle_key(key); m_line_editor.handle_key(key);
clear_completions(); clear_completions();
m_refresh_completion_pending = true; m_refresh_completion_pending = true;