Add automatic completion display in prompt mode

Controlled by the autoshowcompl option
Completers now take a CompletionFlag parameter, used to specify
we want fast completion (tag completion can be slow, we do not
want to run it if not explicitely wanted by the user).
This commit is contained in:
Maxime Coste 2013-11-04 21:53:10 +00:00
parent 70e94cb00a
commit 3e1bb777ce
8 changed files with 55 additions and 24 deletions

View File

@ -394,6 +394,8 @@ Some options are built in kakoune, and can be used to control it's behaviour:
candidates exist, enable completion with common prefix. candidates exist, enable completion with common prefix.
* +incsearch+ _bool_: execute search as it is typed * +incsearch+ _bool_: execute search as it is typed
* +autoinfo+ _bool_: display automatic information box for certain commands. * +autoinfo+ _bool_: display automatic information box for certain commands.
* +autoshowcompl+ _bool_: automatically display possible completions when
editing a prompt.
* +ignored_files+ _regex_: filenames matching this regex wont be considered * +ignored_files+ _regex_: filenames matching this regex wont be considered
as candidates on filename completion (except if the text being completed as candidates on filename completion (except if the text being completed
already matches it). already matches it).

View File

@ -267,7 +267,7 @@ public:
display_line.insert(display_line.begin(), { "filter:"_str, get_color("Prompt") }); display_line.insert(display_line.begin(), { "filter:"_str, get_color("Prompt") });
context().print_status(display_line); context().print_status(display_line);
} }
} }
String description() const override String description() const override
{ {
@ -410,9 +410,9 @@ public:
const bool reverse = (key == Key::BackTab); const bool reverse = (key == Key::BackTab);
CandidateList& candidates = m_completions.candidates; CandidateList& candidates = m_completions.candidates;
// first try, we need to ask our completer for completions // first try, we need to ask our completer for completions
if (m_current_completion == -1) if (candidates.empty())
{ {
m_completions = m_completer(context(), line, m_completions = m_completer(context(), CompletionFlags::None, line,
line.byte_count_to(m_line_editor.cursor_pos())); line.byte_count_to(m_line_editor.cursor_pos()));
if (candidates.empty()) if (candidates.empty())
return; return;
@ -451,13 +451,26 @@ public:
// when we have only one completion candidate, make next tab complete // when we have only one completion candidate, make next tab complete
// from the new content. // from the new content.
if (candidates.size() == 1) if (candidates.size() == 1)
m_current_completion = -1; candidates.clear();
} }
else else
{ {
context().ui().menu_hide();
m_current_completion = -1;
m_line_editor.handle_key(key); m_line_editor.handle_key(key);
m_current_completion = -1;
context().ui().menu_hide();
if (context().options()["autoshowcompl"].get<bool>()) try
{
m_completions = m_completer(context(), CompletionFlags::Fast, line,
line.byte_count_to(m_line_editor.cursor_pos()));
CandidateList& candidates = m_completions.candidates;
if (not candidates.empty())
{
DisplayCoord menu_pos{ context().ui().dimensions().line, 0_char };
context().ui().menu_show(candidates, menu_pos, get_color("MenuForeground"),
get_color("MenuBackground"), MenuStyle::Prompt);
}
} catch (runtime_error&) {}
} }
display(); display();
m_callback(line, PromptEvent::Change, context()); m_callback(line, PromptEvent::Change, context());

View File

@ -312,7 +312,7 @@ void CommandManager::execute(const String& command_line,
execute_single_command(params, context); execute_single_command(params, context);
} }
Completions CommandManager::complete(const Context& context, Completions CommandManager::complete(const Context& context, CompletionFlags flags,
const String& command_line, ByteCount cursor_pos) const String& command_line, ByteCount cursor_pos)
{ {
TokenPosList pos_info; TokenPosList pos_info;
@ -356,20 +356,21 @@ Completions CommandManager::complete(const Context& context,
return Completions(); return Completions();
ByteCount start = token_to_complete < tokens.size() ? ByteCount start = token_to_complete < tokens.size() ?
pos_info[token_to_complete].first : cursor_pos; pos_info[token_to_complete].first : cursor_pos;
Completions result(start , cursor_pos); Completions result(start , cursor_pos);
ByteCount cursor_pos_in_token = cursor_pos - start; ByteCount cursor_pos_in_token = cursor_pos - start;
std::vector<String> params; std::vector<String> params;
for (auto token_it = tokens.begin()+1; token_it != tokens.end(); ++token_it) for (auto token_it = tokens.begin()+1; token_it != tokens.end(); ++token_it)
params.push_back(token_it->content()); params.push_back(token_it->content());
result.candidates = command_it->second.completer(context, params, result.candidates = command_it->second.completer(context, flags, params,
token_to_complete - 1, token_to_complete - 1,
cursor_pos_in_token); cursor_pos_in_token);
return result; return result;
} }
CandidateList PerArgumentCommandCompleter::operator()(const Context& context, CandidateList PerArgumentCommandCompleter::operator()(const Context& context,
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
@ -382,7 +383,7 @@ CandidateList PerArgumentCommandCompleter::operator()(const Context& context,
const String& argument = token_to_complete < params.size() ? const String& argument = token_to_complete < params.size() ?
params[token_to_complete] : String(); params[token_to_complete] : String();
return m_completers[token_to_complete](context, argument, pos_in_token); return m_completers[token_to_complete](context, flags, argument, pos_in_token);
} }
} }

View File

@ -18,6 +18,7 @@ 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<CandidateList (const Context& context,
CompletionFlags,
CommandParameters, CommandParameters,
size_t, ByteCount)>; size_t, ByteCount)>;
@ -25,6 +26,7 @@ class PerArgumentCommandCompleter
{ {
public: public:
using ArgumentCompleter = std::function<CandidateList (const Context&, using ArgumentCompleter = std::function<CandidateList (const Context&,
CompletionFlags flags,
const String&, ByteCount)>; const String&, ByteCount)>;
using ArgumentCompleterList = memoryview<ArgumentCompleter>; using ArgumentCompleterList = memoryview<ArgumentCompleter>;
@ -32,6 +34,7 @@ public:
: m_completers(completers.begin(), completers.end()) {} : m_completers(completers.begin(), completers.end()) {}
CandidateList operator()(const Context& context, CandidateList operator()(const Context& context,
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;
@ -47,7 +50,7 @@ public:
memoryview<String> shell_params = {}, memoryview<String> shell_params = {},
const EnvVarMap& env_vars = EnvVarMap{}); const EnvVarMap& env_vars = EnvVarMap{});
Completions complete(const Context& context, Completions complete(const Context& context, CompletionFlags flags,
const String& command_line, ByteCount cursor_pos); const String& command_line, ByteCount cursor_pos);
bool command_defined(const String& command_name) const; bool command_defined(const String& command_name) const;

View File

@ -405,7 +405,8 @@ void define_command(CommandParameters params, Context& context)
CommandCompleter completer; CommandCompleter completer;
if (parser.has_option("file-completion")) if (parser.has_option("file-completion"))
{ {
completer = [](const Context& context, CommandParameters params, completer = [](const Context& context, CompletionFlags flags,
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 = token_to_complete < params.size() ?
@ -416,9 +417,12 @@ void define_command(CommandParameters params, Context& context)
else if (parser.has_option("shell-completion")) else if (parser.has_option("shell-completion"))
{ {
String shell_cmd = parser.option_value("shell-completion"); String shell_cmd = parser.option_value("shell-completion");
completer = [=](const Context& context, CommandParameters params, completer = [=](const Context& context, CompletionFlags flags,
CommandParameters params,
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
return CandidateList{};
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) }
@ -743,8 +747,9 @@ void change_working_directory(CommandParameters params, Context&)
template<typename GetRootGroup> template<typename GetRootGroup>
CommandCompleter group_rm_completer(GetRootGroup get_root_group) CommandCompleter group_rm_completer(GetRootGroup get_root_group)
{ {
return [=](const Context& context, CommandParameters params, return [=](const Context& context, CompletionFlags flags,
size_t token_to_complete, ByteCount pos_in_token) { CommandParameters params, size_t token_to_complete,
ByteCount pos_in_token) {
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 = token_to_complete < params.size() ?
params[token_to_complete] : String(); params[token_to_complete] : String();
@ -759,8 +764,9 @@ CommandCompleter group_rm_completer(GetRootGroup get_root_group)
template<typename FactoryRegistry, typename GetRootGroup> template<typename FactoryRegistry, typename GetRootGroup>
CommandCompleter group_add_completer(GetRootGroup get_root_group) CommandCompleter group_add_completer(GetRootGroup get_root_group)
{ {
return [=](const Context& context, CommandParameters params, return [=](const Context& context, CompletionFlags flags,
size_t token_to_complete, ByteCount pos_in_token) { CommandParameters params, size_t token_to_complete,
ByteCount pos_in_token) {
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 = token_to_complete < params.size() ?
params[token_to_complete] : String(); params[token_to_complete] : String();
@ -810,7 +816,7 @@ void register_commands()
cm.register_commands({"nop"}, [](CommandParameters, Context&){}); cm.register_commands({"nop"}, [](CommandParameters, Context&){});
PerArgumentCommandCompleter filename_completer({ PerArgumentCommandCompleter filename_completer({
[](const Context& context, 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 complete_filename(prefix, context.options()["ignored_files"].get<Regex>(), cursor_pos); }
}); });
cm.register_commands({ "e", "edit" }, edit<false>, filename_completer); cm.register_commands({ "e", "edit" }, edit<false>, filename_completer);
@ -823,7 +829,7 @@ void register_commands()
cm.register_command("wq!", write_and_quit<true>); cm.register_command("wq!", write_and_quit<true>);
PerArgumentCommandCompleter buffer_completer({ PerArgumentCommandCompleter buffer_completer({
[](const Context& context, 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 BufferManager::instance().complete_buffername(prefix, cursor_pos); }
}); });
cm.register_commands({ "b", "buffer" }, show_buffer, buffer_completer); cm.register_commands({ "b", "buffer" }, show_buffer, buffer_completer);
@ -857,7 +863,7 @@ void register_commands()
cm.register_command("debug", write_debug_message); cm.register_command("debug", write_debug_message);
cm.register_command("set", set_option, cm.register_command("set", set_option,
[](const Context& context, 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)
{ {
if (token_to_complete == 0) if (token_to_complete == 0)
{ {

View File

@ -26,10 +26,15 @@ struct Completions
: start(start), end(end) {} : start(start), end(end) {}
}; };
typedef std::function<Completions (const Context&, enum class CompletionFlags
const String&, ByteCount)> Completer; {
None,
Fast
};
using Completer = std::function<Completions (const Context&, CompletionFlags,
const String&, ByteCount)>;
inline Completions complete_nothing(const Context& context, inline Completions complete_nothing(const Context& context, CompletionFlags,
const String&, ByteCount cursor_pos) const String&, ByteCount cursor_pos)
{ {
return Completions(cursor_pos, cursor_pos); return Completions(cursor_pos, cursor_pos);

View File

@ -249,7 +249,7 @@ void command(Context& context, int)
{ {
context.client().prompt( context.client().prompt(
":", get_color("Prompt"), ":", get_color("Prompt"),
std::bind(&CommandManager::complete, &CommandManager::instance(), _1, _2, _3), std::bind(&CommandManager::complete, &CommandManager::instance(), _1, _2, _3, _4),
[](const String& cmdline, PromptEvent event, Context& context) { [](const String& cmdline, PromptEvent event, Context& context) {
if (event == PromptEvent::Validate) if (event == PromptEvent::Validate)
CommandManager::instance().execute(cmdline, context); CommandManager::instance().execute(cmdline, context);

View File

@ -116,6 +116,7 @@ GlobalOptions::GlobalOptions()
declare_option<bool>("complete_prefix", true); declare_option<bool>("complete_prefix", true);
declare_option<bool>("incsearch", true); declare_option<bool>("incsearch", true);
declare_option<bool>("autoinfo", true); declare_option<bool>("autoinfo", true);
declare_option<bool>("autoshowcompl", true);
declare_option<Regex>("ignored_files", Regex{R"(^(\..*|.*\.(o|so|a))$)"}); declare_option<Regex>("ignored_files", Regex{R"(^(\..*|.*\.(o|so|a))$)"});
declare_option<String>("filetype", ""); declare_option<String>("filetype", "");
declare_option<std::vector<String>>("completions", {}); declare_option<std::vector<String>>("completions", {});