Rework command completion to allow partial token completion
Implement hook completion. fixes #44
This commit is contained in:
parent
00c1523c6e
commit
37b4eacdc8
|
@ -376,7 +376,6 @@ Completions CommandManager::complete(const Context& context, CompletionFlags fla
|
||||||
|
|
||||||
ByteCount start = tok_idx < tokens.size() ?
|
ByteCount start = tok_idx < tokens.size() ?
|
||||||
tokens[tok_idx].begin() : cursor_pos;
|
tokens[tok_idx].begin() : cursor_pos;
|
||||||
Completions result(start , cursor_pos);
|
|
||||||
ByteCount cursor_pos_in_token = cursor_pos - start;
|
ByteCount cursor_pos_in_token = cursor_pos - start;
|
||||||
|
|
||||||
const Token::Type token_type = tok_idx < tokens.size() ?
|
const Token::Type token_type = tok_idx < tokens.size() ?
|
||||||
|
@ -384,15 +383,17 @@ Completions CommandManager::complete(const Context& context, CompletionFlags fla
|
||||||
switch (token_type)
|
switch (token_type)
|
||||||
{
|
{
|
||||||
case Token::Type::OptionExpand:
|
case Token::Type::OptionExpand:
|
||||||
|
{
|
||||||
|
Completions result(start , cursor_pos);
|
||||||
result.candidates = context.options().complete_option_name(tokens[tok_idx].content(), cursor_pos_in_token);
|
result.candidates = context.options().complete_option_name(tokens[tok_idx].content(), cursor_pos_in_token);
|
||||||
return result;
|
return result;
|
||||||
|
}
|
||||||
case Token::Type::ShellExpand:
|
case Token::Type::ShellExpand:
|
||||||
{
|
{
|
||||||
Completions shell_completion = shell_complete(context, flags, tokens[tok_idx].content(), cursor_pos_in_token);
|
Completions shell_completions = shell_complete(context, flags, tokens[tok_idx].content(), cursor_pos_in_token);
|
||||||
result.start = start + shell_completion.start;
|
shell_completions.start += start;
|
||||||
result.end = start + shell_completion.end;
|
shell_completions.end += start;
|
||||||
result.candidates = std::move(shell_completion.candidates);
|
return shell_completions;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
case Token::Type::Raw:
|
case Token::Type::Raw:
|
||||||
{
|
{
|
||||||
|
@ -410,10 +411,12 @@ Completions CommandManager::complete(const Context& context, CompletionFlags fla
|
||||||
params.push_back(token_it->content());
|
params.push_back(token_it->content());
|
||||||
if (tok_idx == tokens.size())
|
if (tok_idx == tokens.size())
|
||||||
params.push_back("");
|
params.push_back("");
|
||||||
result.candidates = command_it->second.completer(context, flags, params,
|
Completions completions = command_it->second.completer(context, flags, params,
|
||||||
tok_idx - cmd_idx - 1,
|
tok_idx - cmd_idx - 1,
|
||||||
cursor_pos_in_token);
|
cursor_pos_in_token);
|
||||||
return result;
|
completions.start += start;
|
||||||
|
completions.end += start;
|
||||||
|
return completions;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -421,14 +424,14 @@ Completions CommandManager::complete(const Context& context, CompletionFlags fla
|
||||||
return Completions{};
|
return Completions{};
|
||||||
}
|
}
|
||||||
|
|
||||||
CandidateList PerArgumentCommandCompleter::operator()(const Context& context,
|
Completions PerArgumentCommandCompleter::operator()(const Context& context,
|
||||||
CompletionFlags flags,
|
CompletionFlags flags,
|
||||||
CommandParameters params,
|
CommandParameters params,
|
||||||
size_t token_to_complete,
|
size_t token_to_complete,
|
||||||
ByteCount pos_in_token) const
|
ByteCount pos_in_token) const
|
||||||
{
|
{
|
||||||
if (token_to_complete >= m_completers.size())
|
if (token_to_complete >= m_completers.size())
|
||||||
return CandidateList();
|
return Completions{};
|
||||||
|
|
||||||
// it is possible to try to complete a new argument
|
// it is possible to try to complete a new argument
|
||||||
kak_assert(token_to_complete <= params.size());
|
kak_assert(token_to_complete <= params.size());
|
||||||
|
|
|
@ -17,10 +17,10 @@ namespace Kakoune
|
||||||
struct Context;
|
struct Context;
|
||||||
using CommandParameters = memoryview<String>;
|
using CommandParameters = memoryview<String>;
|
||||||
using Command = std::function<void (CommandParameters, Context& context)>;
|
using Command = std::function<void (CommandParameters, Context& context)>;
|
||||||
using CommandCompleter = std::function<CandidateList (const Context& context,
|
using CommandCompleter = std::function<Completions (const Context& context,
|
||||||
CompletionFlags,
|
CompletionFlags,
|
||||||
CommandParameters,
|
CommandParameters,
|
||||||
size_t, ByteCount)>;
|
size_t, ByteCount)>;
|
||||||
enum class CommandFlags
|
enum class CommandFlags
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
|
@ -38,7 +38,7 @@ constexpr bool operator&(CommandFlags lhs, CommandFlags rhs)
|
||||||
class PerArgumentCommandCompleter
|
class PerArgumentCommandCompleter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using ArgumentCompleter = std::function<CandidateList (const Context&,
|
using ArgumentCompleter = std::function<Completions (const Context&,
|
||||||
CompletionFlags flags,
|
CompletionFlags flags,
|
||||||
const String&, ByteCount)>;
|
const String&, ByteCount)>;
|
||||||
using ArgumentCompleterList = memoryview<ArgumentCompleter>;
|
using ArgumentCompleterList = memoryview<ArgumentCompleter>;
|
||||||
|
@ -46,11 +46,11 @@ public:
|
||||||
PerArgumentCommandCompleter(ArgumentCompleterList completers)
|
PerArgumentCommandCompleter(ArgumentCompleterList completers)
|
||||||
: m_completers(completers.begin(), completers.end()) {}
|
: m_completers(completers.begin(), completers.end()) {}
|
||||||
|
|
||||||
CandidateList operator()(const Context& context,
|
Completions operator()(const Context& context,
|
||||||
CompletionFlags flags,
|
CompletionFlags flags,
|
||||||
CommandParameters params,
|
CommandParameters params,
|
||||||
size_t token_to_complete,
|
size_t token_to_complete,
|
||||||
ByteCount pos_in_token) const;
|
ByteCount pos_in_token) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<ArgumentCompleter> m_completers;
|
std::vector<ArgumentCompleter> m_completers;
|
||||||
|
|
|
@ -384,9 +384,11 @@ void define_command(CommandParameters params, Context& context)
|
||||||
CommandParameters params,
|
CommandParameters params,
|
||||||
size_t token_to_complete, ByteCount pos_in_token)
|
size_t token_to_complete, ByteCount pos_in_token)
|
||||||
{
|
{
|
||||||
const String& prefix = token_to_complete < params.size() ?
|
const String& prefix = params[token_to_complete];
|
||||||
params[token_to_complete] : String();
|
auto& ignored_files = context.options()["ignored_files"].get<Regex>();
|
||||||
return complete_filename(prefix, context.options()["ignored_files"].get<Regex>(), pos_in_token);
|
return Completions{ 0_byte, prefix.length(),
|
||||||
|
complete_filename(prefix, ignored_files,
|
||||||
|
pos_in_token) };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (parser.has_option("shell-completion"))
|
else if (parser.has_option("shell-completion"))
|
||||||
|
@ -397,13 +399,13 @@ void define_command(CommandParameters params, Context& context)
|
||||||
size_t token_to_complete, ByteCount pos_in_token)
|
size_t token_to_complete, ByteCount pos_in_token)
|
||||||
{
|
{
|
||||||
if (flags == CompletionFlags::Fast) // no shell on fast completion
|
if (flags == CompletionFlags::Fast) // no shell on fast completion
|
||||||
return CandidateList{};
|
return Completions{};
|
||||||
EnvVarMap vars = {
|
EnvVarMap vars = {
|
||||||
{ "token_to_complete", to_string(token_to_complete) },
|
{ "token_to_complete", to_string(token_to_complete) },
|
||||||
{ "pos_in_token", to_string(pos_in_token) }
|
{ "pos_in_token", to_string(pos_in_token) }
|
||||||
};
|
};
|
||||||
String output = ShellManager::instance().eval(shell_cmd, context, params, vars);
|
String output = ShellManager::instance().eval(shell_cmd, context, params, vars);
|
||||||
return split(output, '\n');
|
return Completions{ 0_byte, params[token_to_complete].length(), split(output, '\n') };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
CommandManager::instance().register_command(cmd_name, cmd, flags, completer);
|
CommandManager::instance().register_command(cmd_name, cmd, flags, completer);
|
||||||
|
@ -750,15 +752,14 @@ CommandCompleter group_rm_completer(GetRootGroup get_root_group)
|
||||||
{
|
{
|
||||||
return [=](const Context& context, CompletionFlags flags,
|
return [=](const Context& context, CompletionFlags flags,
|
||||||
CommandParameters params, size_t token_to_complete,
|
CommandParameters params, size_t token_to_complete,
|
||||||
ByteCount pos_in_token) {
|
ByteCount pos_in_token) -> Completions {
|
||||||
auto& root_group = get_root_group(context);
|
auto& root_group = get_root_group(context);
|
||||||
const String& arg = token_to_complete < params.size() ?
|
const String& arg = params[token_to_complete];
|
||||||
params[token_to_complete] : String();
|
|
||||||
if (token_to_complete == 1 and params[0] == "-group")
|
if (token_to_complete == 1 and params[0] == "-group")
|
||||||
return root_group.complete_group_id(arg, pos_in_token);
|
return { 0_byte, arg.length(), root_group.complete_group_id(arg, pos_in_token) };
|
||||||
else if (token_to_complete == 2 and params[0] == "-group")
|
else if (token_to_complete == 2 and params[0] == "-group")
|
||||||
return root_group.get_group(params[1], '/').complete_id(arg, pos_in_token);
|
return { 0_byte, arg.length(), root_group.get_group(params[1], '/').complete_id(arg, pos_in_token) };
|
||||||
return root_group.complete_id(arg, pos_in_token);
|
return { 0_byte, arg.length(), root_group.complete_id(arg, pos_in_token) };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -767,15 +768,14 @@ CommandCompleter group_add_completer(GetRootGroup get_root_group)
|
||||||
{
|
{
|
||||||
return [=](const Context& context, CompletionFlags flags,
|
return [=](const Context& context, CompletionFlags flags,
|
||||||
CommandParameters params, size_t token_to_complete,
|
CommandParameters params, size_t token_to_complete,
|
||||||
ByteCount pos_in_token) {
|
ByteCount pos_in_token) -> Completions {
|
||||||
auto& root_group = get_root_group(context);
|
auto& root_group = get_root_group(context);
|
||||||
const String& arg = token_to_complete < params.size() ?
|
const String& arg = params[token_to_complete];
|
||||||
params[token_to_complete] : String();
|
|
||||||
if (token_to_complete == 1 and params[0] == "-group")
|
if (token_to_complete == 1 and params[0] == "-group")
|
||||||
return root_group.complete_group_id(arg, pos_in_token);
|
return { 0_byte, arg.length(), root_group.complete_group_id(arg, pos_in_token) };
|
||||||
else if (token_to_complete == 0 or (token_to_complete == 2 and params[0] == "-group"))
|
else if (token_to_complete == 0 or (token_to_complete == 2 and params[0] == "-group"))
|
||||||
return FactoryRegistry::instance().complete_name(arg, pos_in_token);
|
return { 0_byte, arg.length(), FactoryRegistry::instance().complete_name(arg, pos_in_token) };
|
||||||
return CandidateList();
|
return Completions{};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -810,6 +810,17 @@ void exec_keys(const KeyList& keys, Context& context)
|
||||||
context.input_handler().handle_key(key);
|
context.input_handler().handle_key(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CandidateList complete_scope(const String& prefix)
|
||||||
|
{
|
||||||
|
CandidateList res;
|
||||||
|
for (auto scope : { "global", "buffer", "window" })
|
||||||
|
{
|
||||||
|
if (prefix_match(scope, prefix))
|
||||||
|
res.emplace_back(scope);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
void register_commands()
|
void register_commands()
|
||||||
{
|
{
|
||||||
CommandManager& cm = CommandManager::instance();
|
CommandManager& cm = CommandManager::instance();
|
||||||
|
@ -818,7 +829,10 @@ void register_commands()
|
||||||
|
|
||||||
PerArgumentCommandCompleter filename_completer({
|
PerArgumentCommandCompleter filename_completer({
|
||||||
[](const Context& context, CompletionFlags flags, const String& prefix, ByteCount cursor_pos)
|
[](const Context& context, CompletionFlags flags, const String& prefix, ByteCount cursor_pos)
|
||||||
{ return complete_filename(prefix, context.options()["ignored_files"].get<Regex>(), cursor_pos); }
|
{ return Completions{ 0_byte, prefix.length(),
|
||||||
|
complete_filename(prefix,
|
||||||
|
context.options()["ignored_files"].get<Regex>(),
|
||||||
|
cursor_pos) }; }
|
||||||
});
|
});
|
||||||
cm.register_commands({ "edit", "e" }, edit<false>, CommandFlags::None, filename_completer);
|
cm.register_commands({ "edit", "e" }, edit<false>, CommandFlags::None, filename_completer);
|
||||||
cm.register_commands({ "edit!", "e!" }, edit<true>, CommandFlags::None, filename_completer);
|
cm.register_commands({ "edit!", "e!" }, edit<true>, CommandFlags::None, filename_completer);
|
||||||
|
@ -831,7 +845,8 @@ void register_commands()
|
||||||
|
|
||||||
PerArgumentCommandCompleter buffer_completer({
|
PerArgumentCommandCompleter buffer_completer({
|
||||||
[](const Context& context, CompletionFlags flags, const String& prefix, ByteCount cursor_pos)
|
[](const Context& context, CompletionFlags flags, const String& prefix, ByteCount cursor_pos)
|
||||||
{ return BufferManager::instance().complete_buffername(prefix, cursor_pos); }
|
{ return Completions{ 0_byte, prefix.length(),
|
||||||
|
BufferManager::instance().complete_buffername(prefix, cursor_pos) }; }
|
||||||
});
|
});
|
||||||
cm.register_commands({ "buffer", "b" }, show_buffer, CommandFlags::None, buffer_completer);
|
cm.register_commands({ "buffer", "b" }, show_buffer, CommandFlags::None, buffer_completer);
|
||||||
cm.register_commands({ "delbuf", "db" }, delete_buffer<false>, CommandFlags::None, buffer_completer);
|
cm.register_commands({ "delbuf", "db" }, delete_buffer<false>, CommandFlags::None, buffer_completer);
|
||||||
|
@ -843,7 +858,20 @@ void register_commands()
|
||||||
cm.register_commands({ "rmhl", "rh" }, rm_highlighter, CommandFlags::None, group_rm_completer(get_highlighters));
|
cm.register_commands({ "rmhl", "rh" }, rm_highlighter, CommandFlags::None, group_rm_completer(get_highlighters));
|
||||||
cm.register_commands({ "defhl", "dh" }, define_highlighter);
|
cm.register_commands({ "defhl", "dh" }, define_highlighter);
|
||||||
|
|
||||||
cm.register_command("hook", add_hook);
|
cm.register_command("hook", add_hook, CommandFlags::None,
|
||||||
|
[](const Context& context, CompletionFlags flags,
|
||||||
|
CommandParameters params, size_t token_to_complete, ByteCount pos_in_token)
|
||||||
|
{
|
||||||
|
if (token_to_complete == 0)
|
||||||
|
return Completions{ 0_byte, params[0].length(),
|
||||||
|
complete_scope(params[0].substr(0_byte, pos_in_token)) };
|
||||||
|
else if (token_to_complete == 3)
|
||||||
|
{
|
||||||
|
auto& cm = CommandManager::instance();
|
||||||
|
return cm.complete(context, flags, params[3], pos_in_token);
|
||||||
|
}
|
||||||
|
return Completions{};
|
||||||
|
});
|
||||||
cm.register_command("rmhooks", rm_hooks);
|
cm.register_command("rmhooks", rm_hooks);
|
||||||
|
|
||||||
cm.register_command("source", exec_commands_in_file, CommandFlags::None, filename_completer);
|
cm.register_command("source", exec_commands_in_file, CommandFlags::None, filename_completer);
|
||||||
|
@ -862,24 +890,20 @@ void register_commands()
|
||||||
cm.register_command("debug", write_debug_message);
|
cm.register_command("debug", write_debug_message);
|
||||||
|
|
||||||
cm.register_command("set", set_option, CommandFlags::None,
|
cm.register_command("set", set_option, CommandFlags::None,
|
||||||
[](const Context& context, CompletionFlags, CommandParameters params, size_t token_to_complete, ByteCount pos_in_token)
|
[](const Context& context, CompletionFlags,
|
||||||
|
CommandParameters params, size_t token_to_complete,
|
||||||
|
ByteCount pos_in_token) -> Completions
|
||||||
{
|
{
|
||||||
if (token_to_complete == 0)
|
if (token_to_complete == 0)
|
||||||
{
|
return { 0_byte, params[0].length(),
|
||||||
CandidateList res;
|
complete_scope(params[0].substr(0_byte, pos_in_token)) };
|
||||||
for (auto scope : { "global", "buffer", "window" })
|
|
||||||
{
|
|
||||||
if (params.size() == 0 or prefix_match(scope, params[0].substr(0_byte, pos_in_token)))
|
|
||||||
res.emplace_back(scope);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
else if (token_to_complete == 1)
|
else if (token_to_complete == 1)
|
||||||
{
|
{
|
||||||
OptionManager& options = get_options(params[0], context);
|
OptionManager& options = get_options(params[0], context);
|
||||||
return options.complete_option_name(params[1], pos_in_token);
|
return { 0_byte, params[1].length(),
|
||||||
|
options.complete_option_name(params[1], pos_in_token) };
|
||||||
}
|
}
|
||||||
return CandidateList{};
|
return Completions{};
|
||||||
} );
|
} );
|
||||||
|
|
||||||
cm.register_commands({ "colalias", "ca" }, define_color_alias);
|
cm.register_commands({ "colalias", "ca" }, define_color_alias);
|
||||||
|
|
|
@ -25,6 +25,9 @@ struct Completions
|
||||||
|
|
||||||
Completions(ByteCount start, ByteCount end)
|
Completions(ByteCount start, ByteCount end)
|
||||||
: start(start), end(end) {}
|
: start(start), end(end) {}
|
||||||
|
|
||||||
|
Completions(ByteCount start, ByteCount end, CandidateList candidates)
|
||||||
|
: start(start), end(end), candidates(std::move(candidates)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class CompletionFlags
|
enum class CompletionFlags
|
||||||
|
|
Loading…
Reference in New Issue
Block a user