diff --git a/README.asciidoc b/README.asciidoc index 6ee322c63..8d4776107 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -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. * +incsearch+ _bool_: execute search as it is typed * +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 as candidates on filename completion (except if the text being completed already matches it). diff --git a/src/client.cc b/src/client.cc index a596a9616..d999816f5 100644 --- a/src/client.cc +++ b/src/client.cc @@ -267,7 +267,7 @@ public: display_line.insert(display_line.begin(), { "filter:"_str, get_color("Prompt") }); context().print_status(display_line); } - } + } String description() const override { @@ -410,9 +410,9 @@ public: const bool reverse = (key == Key::BackTab); CandidateList& candidates = m_completions.candidates; // 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())); if (candidates.empty()) return; @@ -451,13 +451,26 @@ public: // when we have only one completion candidate, make next tab complete // from the new content. if (candidates.size() == 1) - m_current_completion = -1; + candidates.clear(); } else { - context().ui().menu_hide(); - m_current_completion = -1; m_line_editor.handle_key(key); + m_current_completion = -1; + context().ui().menu_hide(); + + if (context().options()["autoshowcompl"].get()) 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(); m_callback(line, PromptEvent::Change, context()); diff --git a/src/command_manager.cc b/src/command_manager.cc index c62d75fa8..165a58f3a 100644 --- a/src/command_manager.cc +++ b/src/command_manager.cc @@ -312,7 +312,7 @@ void CommandManager::execute(const String& command_line, 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) { TokenPosList pos_info; @@ -356,20 +356,21 @@ Completions CommandManager::complete(const Context& context, return Completions(); 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); ByteCount cursor_pos_in_token = cursor_pos - start; std::vector params; for (auto token_it = tokens.begin()+1; token_it != tokens.end(); ++token_it) 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, cursor_pos_in_token); return result; } CandidateList PerArgumentCommandCompleter::operator()(const Context& context, + CompletionFlags flags, CommandParameters params, size_t token_to_complete, ByteCount pos_in_token) const @@ -382,7 +383,7 @@ CandidateList PerArgumentCommandCompleter::operator()(const Context& context, const String& argument = token_to_complete < params.size() ? 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); } } diff --git a/src/command_manager.hh b/src/command_manager.hh index 9aee1e0c2..849ea3a7b 100644 --- a/src/command_manager.hh +++ b/src/command_manager.hh @@ -18,6 +18,7 @@ struct Context; using CommandParameters = memoryview; using Command = std::function; using CommandCompleter = std::function; @@ -25,6 +26,7 @@ class PerArgumentCommandCompleter { public: using ArgumentCompleter = std::function; using ArgumentCompleterList = memoryview; @@ -32,6 +34,7 @@ public: : m_completers(completers.begin(), completers.end()) {} CandidateList operator()(const Context& context, + CompletionFlags flags, CommandParameters params, size_t token_to_complete, ByteCount pos_in_token) const; @@ -47,7 +50,7 @@ public: memoryview shell_params = {}, const EnvVarMap& env_vars = EnvVarMap{}); - Completions complete(const Context& context, + Completions complete(const Context& context, CompletionFlags flags, const String& command_line, ByteCount cursor_pos); bool command_defined(const String& command_name) const; diff --git a/src/commands.cc b/src/commands.cc index ce973b225..87743ef8c 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -405,7 +405,8 @@ void define_command(CommandParameters params, Context& context) CommandCompleter completer; 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) { 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")) { 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) { + if (flags == CompletionFlags::Fast) // no shell on fast completion + return CandidateList{}; EnvVarMap vars = { { "token_to_complete", to_string(token_to_complete) }, { "pos_in_token", to_string(pos_in_token) } @@ -743,8 +747,9 @@ void change_working_directory(CommandParameters params, Context&) template CommandCompleter group_rm_completer(GetRootGroup get_root_group) { - return [=](const Context& context, CommandParameters params, - size_t token_to_complete, ByteCount pos_in_token) { + return [=](const Context& context, CompletionFlags flags, + CommandParameters params, size_t token_to_complete, + ByteCount pos_in_token) { auto& root_group = get_root_group(context); const String& arg = token_to_complete < params.size() ? params[token_to_complete] : String(); @@ -759,8 +764,9 @@ CommandCompleter group_rm_completer(GetRootGroup get_root_group) template CommandCompleter group_add_completer(GetRootGroup get_root_group) { - return [=](const Context& context, CommandParameters params, - size_t token_to_complete, ByteCount pos_in_token) { + return [=](const Context& context, CompletionFlags flags, + CommandParameters params, size_t token_to_complete, + ByteCount pos_in_token) { auto& root_group = get_root_group(context); const String& arg = token_to_complete < params.size() ? params[token_to_complete] : String(); @@ -810,7 +816,7 @@ void register_commands() cm.register_commands({"nop"}, [](CommandParameters, Context&){}); 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(), cursor_pos); } }); cm.register_commands({ "e", "edit" }, edit, filename_completer); @@ -823,7 +829,7 @@ void register_commands() cm.register_command("wq!", write_and_quit); 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); } }); 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("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) { diff --git a/src/completion.hh b/src/completion.hh index c833e2b48..bd89ea4cb 100644 --- a/src/completion.hh +++ b/src/completion.hh @@ -26,10 +26,15 @@ struct Completions : start(start), end(end) {} }; -typedef std::function Completer; +enum class CompletionFlags +{ + None, + Fast +}; +using Completer = std::function; -inline Completions complete_nothing(const Context& context, +inline Completions complete_nothing(const Context& context, CompletionFlags, const String&, ByteCount cursor_pos) { return Completions(cursor_pos, cursor_pos); diff --git a/src/normal.cc b/src/normal.cc index 2ff1e0b03..226f554b2 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -249,7 +249,7 @@ void command(Context& context, int) { context.client().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) { if (event == PromptEvent::Validate) CommandManager::instance().execute(cmdline, context); diff --git a/src/option_manager.cc b/src/option_manager.cc index 65943061c..f472d3fa3 100644 --- a/src/option_manager.cc +++ b/src/option_manager.cc @@ -116,6 +116,7 @@ GlobalOptions::GlobalOptions() declare_option("complete_prefix", true); declare_option("incsearch", true); declare_option("autoinfo", true); + declare_option("autoshowcompl", true); declare_option("ignored_files", Regex{R"(^(\..*|.*\.(o|so|a))$)"}); declare_option("filetype", ""); declare_option>("completions", {});