diff --git a/src/command_manager.cc b/src/command_manager.cc index be7b3c72..c7de0c71 100644 --- a/src/command_manager.cc +++ b/src/command_manager.cc @@ -336,6 +336,35 @@ void CommandManager::execute(const String& command_line, execute_single_command(params, context); } +std::pair CommandManager::command_info(const String& command_line) const +{ + TokenList tokens = parse(command_line); + size_t cmd_idx = 0; + for (size_t i = 0; i < tokens.size(); ++i) + { + if (tokens[i].type() == Token::Type::CommandSeparator) + cmd_idx = i+1; + } + + std::pair res; + if (cmd_idx == tokens.size() or tokens[cmd_idx].type() != Token::Type::Raw) + return res; + + auto cmd = find_command(tokens[cmd_idx].content()); + if (cmd == m_commands.end()) + return res; + + res.first = cmd->first; + auto& opts = cmd->second.param_desc.options; + if (not opts.empty()) + { + res.second += "Flags:\n"; + res.second += generate_flags_doc(opts); + } + + return res; +} + Completions CommandManager::complete(const Context& context, CompletionFlags flags, const String& command_line, ByteCount cursor_pos) { diff --git a/src/command_manager.hh b/src/command_manager.hh index d758aa6c..09a4e222 100644 --- a/src/command_manager.hh +++ b/src/command_manager.hh @@ -67,6 +67,8 @@ public: Completions complete(const Context& context, CompletionFlags flags, const String& command_line, ByteCount cursor_pos); + std::pair command_info(const String& command_line) const; + bool command_defined(const String& command_name) const; void register_command(String command_name, Command command, diff --git a/src/commands.cc b/src/commands.cc index 046e8f13..d9e47a80 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -85,7 +85,8 @@ Buffer* open_fifo(const String& name , const String& filename, Context& context) } static const ParameterDesc edit_params{ - OptionMap{ { "scratch", false }, { "fifo", true } }, + OptionMap{ { "scratch", { false, "create a scratch buffer, not linked to a file" } }, + { "fifo", { true, "create a buffer reading its content from a named fifo" } } }, ParameterDesc::Flags::None, 1, 3 }; @@ -242,7 +243,8 @@ void define_highlighter(const ParametersParser& parser, Context& context) } static const ParameterDesc add_highlighter_params{ - OptionMap{ { "group", true }, { "def-group", true } }, + OptionMap{ { "group", { true, "add highlighter to named group" } }, + { "def-group", { true, "add highlighter to reusable defined group" } } }, ParameterDesc::Flags::None, 1 }; @@ -275,7 +277,8 @@ void add_highlighter(const ParametersParser& parser, Context& context) } static const ParameterDesc rm_highlighter_params{ - OptionMap{ { "group", true } }, ParameterDesc::Flags::None, 1, 1 + OptionMap{ { "group", { true, "remove highlighter from given group" } } }, + ParameterDesc::Flags::None, 1, 1 }; void rm_highlighter(const ParametersParser& parser, Context& context) @@ -300,7 +303,7 @@ static HookManager& get_hook_manager(const String& scope, Context& context) } static const ParameterDesc add_hook_params{ - OptionMap{ { "id", true } }, ParameterDesc::Flags::None, 4, 4 + OptionMap{ { "id", { true, "set hook id" } } }, ParameterDesc::Flags::None, 4, 4 }; void add_hook(const ParametersParser& parser, Context& context) @@ -347,12 +350,12 @@ std::vector params_to_shell(const ParametersParser& parser) } static const ParameterDesc define_command_params{ - OptionMap{ { "env-params", false }, - { "shell-params", false }, - { "allow-override", false }, - { "file-completion", false }, - { "hidden", false }, - { "shell-completion", true } }, + OptionMap{ { "env-params", { false, "pass parameters as env variables param0..paramN" } }, + { "shell-params", { false, "pass parameters to each shell escape as $0..$N" } }, + { "allow-override", { false, "allow overriding existing command" } }, + { "file-completion", { false, "complete parameters using filename completion" } }, + { "hidden", { false, "do not display the command as completion candidate" } }, + { "shell-completion", { true, "complete the parameters using the given shell-script" } } }, ParameterDesc::Flags::None, 2, 2 }; @@ -431,7 +434,7 @@ void define_command(const ParametersParser& parser, Context& context) } static const ParameterDesc echo_message_params{ - { { "color", true } }, + OptionMap{ { "color", { true, "set message color" } } }, ParameterDesc::Flags::OptionsOnlyAtStart }; @@ -495,7 +498,7 @@ static OptionManager& get_options(const String& scope, const Context& context) static const ParameterDesc set_option_params{ - { { "add", false } }, + OptionMap{ { "add", { false, "add to option rather than replacing it" } } }, ParameterDesc::Flags::OptionsOnlyAtStart, 3, 3 }; @@ -510,7 +513,7 @@ void set_option(const ParametersParser& parser, Context& context) } static const ParameterDesc declare_option_params{ - { { "hidden", false } }, + OptionMap{ { "hidden", { false, "do not display option name when completing" } } }, ParameterDesc::Flags::OptionsOnlyAtStart, 2, 3 }; @@ -585,8 +588,10 @@ void map_key(const ParametersParser& parser, Context& context) } const ParameterDesc context_wrap_params = { - { { "client", true }, { "try-client", true }, - { "draft", false }, { "itersel", false } }, + OptionMap{ { "client", { true, "run in given client context" } }, + { "try-client", { true, "run in given client context if it exists, or else in the current one" } }, + { "draft", { false, "run in a disposable context" } }, + { "itersel", { false, "run once for each selection with that selection as the only one" } } }, ParameterDesc::Flags::OptionsOnlyAtStart, 1 }; @@ -661,8 +666,10 @@ void eval_string(const ParametersParser& parser, Context& context) }); } -static const ParameterDesc menu_params{ { { "auto-single", false }, - { "select-cmds", false } } }; +static const ParameterDesc menu_params{ + OptionMap{ { "auto-single", { false, "instantly validate if only one item is available" } }, + { "select-cmds", { false, "each item specify an additional command to run when selected" } } } +}; void menu(const ParametersParser& parser, Context& context) { @@ -700,7 +707,8 @@ void menu(const ParametersParser& parser, Context& context) } static const ParameterDesc info_params{ - { { "anchor", true }, { "title", true } }, + OptionMap{ { "anchor", { true, "set info anchoring (left, right, or cursor)" } }, + { "title", { true, "set info title" } } }, ParameterDesc::Flags::None, 0, 1 }; diff --git a/src/main.cc b/src/main.cc index 154e2819..b99cb5dc 100644 --- a/src/main.cc +++ b/src/main.cc @@ -231,16 +231,8 @@ int run_client(const String& session, const String& init_command) return 0; } -int kakoune(memoryview params) +int kakoune(const ParametersParser& parser) { - const ParameterDesc param_desc{ - OptionMap{ { "c", true }, - { "e", true }, - { "n", false }, - { "s", true }, - { "d", false } } - }; - ParametersParser parser(params, param_desc); String init_command; if (parser.has_option("e")) init_command = parser.option_value("e"); @@ -356,31 +348,34 @@ int kakoune(memoryview params) int main(int argc, char* argv[]) { + setlocale(LC_ALL, ""); + + signal(SIGSEGV, signal_handler); + signal(SIGFPE, signal_handler); + signal(SIGQUIT, signal_handler); + signal(SIGTERM, signal_handler); + + std::vector params; + for (size_t i = 1; i < argc; ++i) + params.push_back(argv[i]); + + const ParameterDesc param_desc{ + OptionMap{ { "c", { true, "connect to given session" } }, + { "e", { true, "execute argument on initialisation" } }, + { "n", { false, "do not source kakrc files on startup" } }, + { "s", { true, "set session name" } }, + { "d", { false, "run as a headless session (requires -s)" } } } + }; try { - setlocale(LC_ALL, ""); - - signal(SIGSEGV, signal_handler); - signal(SIGFPE, signal_handler); - signal(SIGQUIT, signal_handler); - signal(SIGTERM, signal_handler); - - std::vector params; - for (size_t i = 1; i < argc; ++i) - params.push_back(argv[i]); - - kakoune(params); + kakoune(ParametersParser(params, param_desc)); } catch (Kakoune::parameter_error& error) { printf("Error: %s\n" "Valid options:\n" - " -e : execute commands on initialisation\n" - " -c : connect to the given session\n" - " -s : set session name\n" - " -d: run as a headless session (requires -s)\n" - " -n: do not source kakrc files on startup\n", - error.what()); + "%s", + error.what(), generate_flags_doc(param_desc.options).c_str()); return -1; } catch (Kakoune::exception& error) diff --git a/src/normal.cc b/src/normal.cc index 2295239d..34863615 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -418,6 +418,19 @@ void command(Context& context, int) ":", get_color("Prompt"), std::bind(&CommandManager::complete, &CommandManager::instance(), _1, _2, _3, _4), [](const String& cmdline, PromptEvent event, Context& context) { + if (context.has_ui()) + { + context.ui().info_hide(); + if (event == PromptEvent::Change) + { + auto info = CommandManager::instance().command_info(cmdline); + ColorPair col = get_color("Information"); + DisplayCoord pos = context.window().dimensions(); + pos.column -= 1; + if (not info.first.empty() and not info.second.empty()) + context.ui().info_show(info.first, info.second, pos , col, MenuStyle::Prompt); + } + } if (event == PromptEvent::Validate) CommandManager::instance().execute(cmdline, context); }); diff --git a/src/parameters_parser.cc b/src/parameters_parser.cc index eb49521f..b50e8414 100644 --- a/src/parameters_parser.cc +++ b/src/parameters_parser.cc @@ -3,6 +3,14 @@ namespace Kakoune { +String generate_flags_doc(const OptionMap& opts) +{ + String res; + for (auto& opt : opts) + res += " -" + opt.first + (opt.second.takes_arg ? " : " : ": ") + opt.second.description + "\n"; + return res; +} + ParametersParser::ParametersParser(ParameterList params, const ParameterDesc& desc) : m_params(params), @@ -19,7 +27,7 @@ ParametersParser::ParametersParser(ParameterList params, if (it == m_desc.options.end()) throw unknown_option(params[i]); - if (it->second) + if (it->second.takes_arg) { ++i; if (i == params.size() or params[i][0] == '-') @@ -57,7 +65,7 @@ const String& ParametersParser::option_value(const String& name) const #ifdef KAK_DEBUG auto it = m_desc.options.find(name); kak_assert(it != m_desc.options.end()); - kak_assert(it->second == true); + kak_assert(it->second.takes_arg); #endif for (size_t i = 0; i < m_params.size(); ++i) diff --git a/src/parameters_parser.hh b/src/parameters_parser.hh index 6b82bd8d..643a01f4 100644 --- a/src/parameters_parser.hh +++ b/src/parameters_parser.hh @@ -34,7 +34,15 @@ struct wrong_argument_count : public parameter_error wrong_argument_count() : parameter_error("wrong argument count") {} }; -using OptionMap = std::unordered_map; +struct OptionDesc +{ + bool takes_arg; + String description; +}; + +using OptionMap = std::unordered_map; + +String generate_flags_doc(const OptionMap& opts); struct ParameterDesc {