Merge remote-tracking branch 'krobelus/complete-using-param-spec'
This commit is contained in:
commit
3b0d91c9c7
|
@ -812,24 +812,39 @@ Completions CommandManager::complete(const Context& context,
|
|||
|
||||
auto& command = command_it->value;
|
||||
|
||||
const bool has_switches = not command.param_desc.switches.empty();
|
||||
auto is_switch = [=](StringView s) { return has_switches and s.substr(0_byte, 1_byte) == "-"; };
|
||||
auto raw_params = tokens | skip(1) | transform(&Token::content) | gather<Vector<String>>();
|
||||
ParametersParser parser{raw_params, command.param_desc, true};
|
||||
|
||||
if (is_switch(token.content)
|
||||
and not contains(tokens | drop(1) | transform(&Token::content), "--"))
|
||||
switch (parser.state())
|
||||
{
|
||||
auto switches = Kakoune::complete(token.content.substr(1_byte), pos_in_token,
|
||||
concatenated(command.param_desc.switches
|
||||
| transform(&SwitchMap::Item::key),
|
||||
ConstArrayView<String>{"-"}));
|
||||
return switches.empty()
|
||||
? Completions{}
|
||||
: Completions{start+1, cursor_pos, std::move(switches), Completions::Flags::Menu};
|
||||
case ParametersParser::State::Switch:
|
||||
{
|
||||
auto switches = Kakoune::complete(token.content.substr(1_byte), pos_in_token,
|
||||
concatenated(command.param_desc.switches
|
||||
| transform(&SwitchMap::Item::key)
|
||||
| filter([&](const auto& key) {
|
||||
return not parser.get_switch(key);
|
||||
}),
|
||||
ConstArrayView<String>{"-"}));
|
||||
return switches.empty()
|
||||
? Completions{}
|
||||
: Completions{start+1, cursor_pos, std::move(switches), Completions::Flags::Menu};
|
||||
}
|
||||
case ParametersParser::State::SwitchArgument:
|
||||
{
|
||||
const auto& switch_desc = command.param_desc.switches.get(raw_params.at(raw_params.size() - 2).substr(1_byte));
|
||||
if (not *switch_desc.arg_completer)
|
||||
return Completions{};
|
||||
return offset_pos(requote((*switch_desc.arg_completer)(context, flags, raw_params.back(), pos_in_token), token.type), start);
|
||||
}
|
||||
case ParametersParser::State::Positional:
|
||||
break;
|
||||
}
|
||||
|
||||
if (not command.completer)
|
||||
return Completions{};
|
||||
|
||||
auto params = tokens | skip(1) | transform(&Token::content) | filter(std::not_fn(is_switch)) | gather<Vector>();
|
||||
Vector<String> params{parser.begin(), parser.end()};
|
||||
auto index = params.size() - 1;
|
||||
|
||||
return offset_pos(requote(command.completer(context, flags, params, index, pos_in_token), token.type), start);
|
||||
|
|
173
src/commands.cc
173
src/commands.cc
|
@ -128,6 +128,27 @@ auto filename_completer = make_completer(
|
|||
cursor_pos, FilenameFlags::Expand),
|
||||
menu ? Completions::Flags::Menu : Completions::Flags::None}; });
|
||||
|
||||
template<bool menu>
|
||||
auto filename_arg_completer =
|
||||
[](const Context& context, CompletionFlags flags, StringView prefix, ByteCount cursor_pos) -> Completions
|
||||
{ return { 0_byte, cursor_pos,
|
||||
complete_filename(prefix,
|
||||
context.options()["ignored_files"].get<Regex>(),
|
||||
cursor_pos, FilenameFlags::OnlyDirectories),
|
||||
menu ? Completions::Flags::Menu : Completions::Flags::None }; };
|
||||
|
||||
auto client_arg_completer =
|
||||
[](const Context& context, CompletionFlags flags, StringView prefix, ByteCount cursor_pos) -> Completions
|
||||
{ return { 0_byte, cursor_pos,
|
||||
ClientManager::instance().complete_client_name(prefix, cursor_pos),
|
||||
Completions::Flags::Menu }; };
|
||||
|
||||
auto arg_completer = [](auto candidates) -> PromptCompleter {
|
||||
return [=](const Context& context, CompletionFlags flags, StringView prefix, ByteCount cursor_pos) -> Completions {
|
||||
return Completions{ 0_byte, cursor_pos, complete(prefix, cursor_pos, candidates), Completions::Flags::Menu };
|
||||
};
|
||||
};
|
||||
|
||||
template<bool ignore_current = false>
|
||||
static Completions complete_buffer_name(const Context& context, CompletionFlags flags,
|
||||
StringView prefix, ByteCount cursor_pos)
|
||||
|
@ -444,12 +465,12 @@ void edit(const ParametersParser& parser, Context& context, const ShellContext&)
|
|||
}
|
||||
|
||||
ParameterDesc edit_params{
|
||||
{ { "existing", { false, "fail if the file does not exist, do not open a new file" } },
|
||||
{ "scratch", { false, "create a scratch buffer, not linked to a file" } },
|
||||
{ "debug", { false, "create buffer as debug output" } },
|
||||
{ "fifo", { true, "create a buffer reading its content from a named fifo" } },
|
||||
{ "readonly", { false, "create a buffer in readonly mode" } },
|
||||
{ "scroll", { false, "place the initial cursor so that the fifo will scroll to show new data" } } },
|
||||
{ { "existing", { {}, "fail if the file does not exist, do not open a new file" } },
|
||||
{ "scratch", { {}, "create a scratch buffer, not linked to a file" } },
|
||||
{ "debug", { {}, "create buffer as debug output" } },
|
||||
{ "fifo", { {filename_arg_completer<true>}, "create a buffer reading its content from a named fifo" } },
|
||||
{ "readonly", { {}, "create a buffer in readonly mode" } },
|
||||
{ "scroll", { {}, "place the initial cursor so that the fifo will scroll to show new data" } } },
|
||||
ParameterDesc::Flags::None, 0, 3
|
||||
};
|
||||
const CommandDesc edit_cmd = {
|
||||
|
@ -477,17 +498,17 @@ const CommandDesc force_edit_cmd = {
|
|||
|
||||
const ParameterDesc write_params = {
|
||||
{
|
||||
{ "sync", { false, "force the synchronization of the file onto the filesystem" } },
|
||||
{ "method", { true, "explicit writemethod (replace|overwrite)" } },
|
||||
{ "force", { false, "Allow overwriting existing file with explicit filename" } }
|
||||
{ "sync", { {}, "force the synchronization of the file onto the filesystem" } },
|
||||
{ "method", { {arg_completer(Array{"replace", "overwrite"})}, "explicit writemethod (replace|overwrite)" } },
|
||||
{ "force", { {}, "Allow overwriting existing file with explicit filename" } }
|
||||
},
|
||||
ParameterDesc::Flags::None, 0, 1
|
||||
};
|
||||
|
||||
const ParameterDesc write_params_except_force = {
|
||||
{
|
||||
{ "sync", { false, "force the synchronization of the file onto the filesystem" } },
|
||||
{ "method", { true, "explicit writemethod (replace|overwrite)" } },
|
||||
{ "sync", { {}, "force the synchronization of the file onto the filesystem" } },
|
||||
{ "method", { {arg_completer(Array{"replace", "overwrite"})}, "explicit writemethod (replace|overwrite)" } },
|
||||
},
|
||||
ParameterDesc::Flags::None, 0, 1
|
||||
};
|
||||
|
@ -872,8 +893,8 @@ const CommandDesc rename_buffer_cmd = {
|
|||
"rename-buffer <name>: change current buffer name",
|
||||
ParameterDesc{
|
||||
{
|
||||
{ "scratch", { false, "convert a file buffer to a scratch buffer" } },
|
||||
{ "file", { false, "convert a scratch buffer to a file buffer" } }
|
||||
{ "scratch", { {}, "convert a file buffer to a scratch buffer" } },
|
||||
{ "file", { {}, "convert a scratch buffer to a file buffer" } }
|
||||
},
|
||||
ParameterDesc::Flags::None, 1, 1
|
||||
},
|
||||
|
@ -997,7 +1018,7 @@ const CommandDesc add_highlighter_cmd = {
|
|||
" <path> is a '/' delimited path or the parent highlighter, starting with either\n"
|
||||
" 'global', 'buffer', 'window' or 'shared', if <name> is empty, it will be autogenerated",
|
||||
ParameterDesc{
|
||||
{ { "override", { false, "replace existing highlighter with same path if it exists" } }, },
|
||||
{ { "override", { {}, "replace existing highlighter with same path if it exists" } }, },
|
||||
ParameterDesc::Flags::SwitchesOnlyAtStart, 2
|
||||
},
|
||||
CommandFlags::None,
|
||||
|
@ -1097,9 +1118,9 @@ const CommandDesc add_hook_cmd = {
|
|||
" (and any window for that buffer)\n"
|
||||
" * window: hook is executed only for the current window\n",
|
||||
ParameterDesc{
|
||||
{ { "group", { true, "set hook group, see remove-hooks" } },
|
||||
{ "always", { false, "run hook even if hooks are disabled" } },
|
||||
{ "once", { false, "run the hook only once" } } },
|
||||
{ { "group", { ArgCompleter{}, "set hook group, see remove-hooks" } },
|
||||
{ "always", { {}, "run hook even if hooks are disabled" } },
|
||||
{ "once", { {}, "run the hook only once" } } },
|
||||
ParameterDesc::Flags::None, 4, 4
|
||||
},
|
||||
CommandFlags::None,
|
||||
|
@ -1335,19 +1356,19 @@ const CommandDesc define_command_cmd = {
|
|||
"def",
|
||||
"define-command [<switches>] <name> <cmds>: define a command <name> executing <cmds>",
|
||||
ParameterDesc{
|
||||
{ { "params", { true, "take parameters, accessible to each shell escape as $0..$N\n"
|
||||
{ { "params", { ArgCompleter{}, "take parameters, accessible to each shell escape as $0..$N\n"
|
||||
"parameter should take the form <count> or <min>..<max> (both omittable)" } },
|
||||
{ "override", { false, "allow overriding an existing command" } },
|
||||
{ "hidden", { false, "do not display the command in completion candidates" } },
|
||||
{ "docstring", { true, "define the documentation string for command" } },
|
||||
{ "menu", { false, "treat completions as the only valid inputs" } },
|
||||
{ "file-completion", { false, "complete parameters using filename completion" } },
|
||||
{ "client-completion", { false, "complete parameters using client name completion" } },
|
||||
{ "buffer-completion", { false, "complete parameters using buffer name completion" } },
|
||||
{ "command-completion", { false, "complete parameters using kakoune command completion" } },
|
||||
{ "shell-completion", { false, "complete parameters using shell command completion" } },
|
||||
{ "shell-script-completion", { true, "complete parameters using the given shell-script" } },
|
||||
{ "shell-script-candidates", { true, "get the parameter candidates using the given shell-script" } } },
|
||||
{ "override", { {}, "allow overriding an existing command" } },
|
||||
{ "hidden", { {}, "do not display the command in completion candidates" } },
|
||||
{ "docstring", { ArgCompleter{}, "define the documentation string for command" } },
|
||||
{ "menu", { {}, "treat completions as the only valid inputs" } },
|
||||
{ "file-completion", { {}, "complete parameters using filename completion" } },
|
||||
{ "client-completion", { {}, "complete parameters using client name completion" } },
|
||||
{ "buffer-completion", { {}, "complete parameters using buffer name completion" } },
|
||||
{ "command-completion", { {}, "complete parameters using kakoune command completion" } },
|
||||
{ "shell-completion", { {}, "complete parameters using shell command completion" } },
|
||||
{ "shell-script-completion", { ArgCompleter{}, "complete parameters using the given shell-script" } },
|
||||
{ "shell-script-candidates", { ArgCompleter{}, "get the parameter candidates using the given shell-script" } } },
|
||||
ParameterDesc::Flags::None,
|
||||
2, 2
|
||||
},
|
||||
|
@ -1408,7 +1429,7 @@ const CommandDesc complete_command_cmd = {
|
|||
"complete-command [<switches>] <name> <type> [<param>]\n"
|
||||
"define command completion",
|
||||
ParameterDesc{
|
||||
{ { "menu", { false, "treat completions as the only valid inputs" } }, },
|
||||
{ { "menu", { {}, "treat completions as the only valid inputs" } }, },
|
||||
ParameterDesc::Flags::None, 2, 3},
|
||||
CommandFlags::None,
|
||||
CommandHelper{},
|
||||
|
@ -1426,11 +1447,11 @@ const CommandDesc echo_cmd = {
|
|||
nullptr,
|
||||
"echo <params>...: display given parameters in the status line",
|
||||
ParameterDesc{
|
||||
{ { "markup", { false, "parse markup" } },
|
||||
{ "quoting", { true, "quote each argument separately using the given style (raw|kakoune|shell)" } },
|
||||
{ "to-file", { true, "echo contents to given filename" } },
|
||||
{ "to-shell-script", { true, "pipe contents to given shell script" } },
|
||||
{ "debug", { false, "write to debug buffer instead of status line" } } },
|
||||
{ { "markup", { {}, "parse markup" } },
|
||||
{ "quoting", { {arg_completer(Array{"raw", "kakoune", "shell"})}, "quote each argument separately using the given style (raw|kakoune|shell)" } },
|
||||
{ "to-file", { {filename_arg_completer<false>}, "echo contents to given filename" } },
|
||||
{ "to-shell-script", { ArgCompleter{}, "pipe contents to given shell script" } },
|
||||
{ "debug", { {}, "write to debug buffer instead of status line" } } },
|
||||
ParameterDesc::Flags::SwitchesOnlyAtStart
|
||||
},
|
||||
CommandFlags::None,
|
||||
|
@ -1666,8 +1687,8 @@ const CommandDesc set_option_cmd = {
|
|||
"<scope> can be global, buffer, window, or current which refers to the narrowest "
|
||||
"scope the option is set in",
|
||||
ParameterDesc{
|
||||
{ { "add", { false, "add to option rather than replacing it" } },
|
||||
{ "remove", { false, "remove from option rather than replacing it" } } },
|
||||
{ { "add", { {}, "add to option rather than replacing it" } },
|
||||
{ "remove", { {}, "remove from option rather than replacing it" } } },
|
||||
ParameterDesc::Flags::SwitchesOnlyAtStart, 2, (size_t)-1
|
||||
},
|
||||
CommandFlags::None,
|
||||
|
@ -1776,8 +1797,8 @@ const CommandDesc declare_option_cmd = {
|
|||
" range-specs: list of range specs\n"
|
||||
" str-to-str-map: map from strings to strings\n",
|
||||
ParameterDesc{
|
||||
{ { "hidden", { false, "do not display option name when completing" } },
|
||||
{ "docstring", { true, "specify option description" } } },
|
||||
{ { "hidden", { {}, "do not display option name when completing" } },
|
||||
{ "docstring", { ArgCompleter{}, "specify option description" } } },
|
||||
ParameterDesc::Flags::SwitchesOnlyAtStart, 2, (size_t)-1
|
||||
},
|
||||
CommandFlags::None,
|
||||
|
@ -1862,7 +1883,7 @@ const CommandDesc map_key_cmd = {
|
|||
nullptr,
|
||||
"map [<switches>] <scope> <mode> <key> <keys>: map <key> to <keys> in given <mode> in <scope>",
|
||||
ParameterDesc{
|
||||
{ { "docstring", { true, "specify mapping description" } } },
|
||||
{ { "docstring", { ArgCompleter{}, "specify mapping description" } } },
|
||||
ParameterDesc::Flags::None, 4, 4
|
||||
},
|
||||
CommandFlags::None,
|
||||
|
@ -1920,11 +1941,11 @@ template<size_t... P>
|
|||
ParameterDesc make_context_wrap_params_impl(Array<HashItem<String, SwitchDesc>, sizeof...(P)>&& additional_params,
|
||||
std::index_sequence<P...>)
|
||||
{
|
||||
return { { { "client", { true, "run in given client context" } },
|
||||
{ "try-client", { true, "run in given client context if it exists, or else in the current one" } },
|
||||
{ "buffer", { true, "run in a disposable context for each given buffer in the comma separated list argument" } },
|
||||
{ "draft", { false, "run in a disposable context" } },
|
||||
{ "itersel", { false, "run once for each selection with that selection as the only one" } },
|
||||
return { { { "client", { {client_arg_completer}, "run in given client context" } },
|
||||
{ "try-client", { {client_arg_completer}, "run in given client context if it exists, or else in the current one" } },
|
||||
{ "buffer", { {complete_buffer_name<false>}, "run in a disposable context for each given buffer in the comma separated list argument" } },
|
||||
{ "draft", { {}, "run in a disposable context" } },
|
||||
{ "itersel", { {}, "run once for each selection with that selection as the only one" } },
|
||||
std::move(additional_params[P])...},
|
||||
ParameterDesc::Flags::SwitchesOnlyAtStart, 1
|
||||
};
|
||||
|
@ -2089,9 +2110,9 @@ const CommandDesc execute_keys_cmd = {
|
|||
"exec",
|
||||
"execute-keys [<switches>] <keys>: execute given keys as if entered by user",
|
||||
make_context_wrap_params<3>({{
|
||||
{"save-regs", {true, "restore all given registers after execution (default: '/\"|^@:')"}},
|
||||
{"with-maps", {false, "use user defined key mapping when executing keys"}},
|
||||
{"with-hooks", {false, "trigger hooks while executing keys"}}
|
||||
{"save-regs", {ArgCompleter{}, "restore all given registers after execution (default: '/\"|^@:')"}},
|
||||
{"with-maps", {{}, "use user defined key mapping when executing keys"}},
|
||||
{"with-hooks", {{}, "trigger hooks while executing keys"}}
|
||||
}}),
|
||||
CommandFlags::None,
|
||||
CommandHelper{},
|
||||
|
@ -2113,9 +2134,9 @@ const CommandDesc evaluate_commands_cmd = {
|
|||
"eval",
|
||||
"evaluate-commands [<switches>] <commands>...: execute commands as if entered by user",
|
||||
make_context_wrap_params<3>({{
|
||||
{"save-regs", {true, "restore all given registers after execution (default: '')"}},
|
||||
{"no-hooks", { false, "disable hooks while executing commands" }},
|
||||
{"verbatim", { false, "do not reparse argument" }}
|
||||
{"save-regs", {ArgCompleter{}, "restore all given registers after execution (default: '')"}},
|
||||
{"no-hooks", { {}, "disable hooks while executing commands" }},
|
||||
{"verbatim", { {}, "do not reparse argument" }}
|
||||
}}),
|
||||
CommandFlags::None,
|
||||
CommandHelper{},
|
||||
|
@ -2151,18 +2172,18 @@ const CommandDesc prompt_cmd = {
|
|||
"prompt [<switches>] <prompt> <command>: prompt the user to enter a text string "
|
||||
"and then executes <command>, entered text is available in the 'text' value",
|
||||
ParameterDesc{
|
||||
{ { "init", { true, "set initial prompt content" } },
|
||||
{ "password", { false, "Do not display entered text and clear reg after command" } },
|
||||
{ "menu", { false, "treat completions as the only valid inputs" } },
|
||||
{ "file-completion", { false, "use file completion for prompt" } },
|
||||
{ "client-completion", { false, "use client completion for prompt" } },
|
||||
{ "buffer-completion", { false, "use buffer completion for prompt" } },
|
||||
{ "command-completion", { false, "use command completion for prompt" } },
|
||||
{ "shell-completion", { false, "use shell command completion for prompt" } },
|
||||
{ "shell-script-completion", { true, "use shell command completion for prompt" } },
|
||||
{ "shell-script-candidates", { true, "use shell command completion for prompt" } },
|
||||
{ "on-change", { true, "command to execute whenever the prompt changes" } },
|
||||
{ "on-abort", { true, "command to execute whenever the prompt is canceled" } } },
|
||||
{ { "init", { ArgCompleter{}, "set initial prompt content" } },
|
||||
{ "password", { {}, "Do not display entered text and clear reg after command" } },
|
||||
{ "menu", { {}, "treat completions as the only valid inputs" } },
|
||||
{ "file-completion", { {}, "use file completion for prompt" } },
|
||||
{ "client-completion", { {}, "use client completion for prompt" } },
|
||||
{ "buffer-completion", { {}, "use buffer completion for prompt" } },
|
||||
{ "command-completion", { {}, "use command completion for prompt" } },
|
||||
{ "shell-completion", { {}, "use shell command completion for prompt" } },
|
||||
{ "shell-script-completion", { ArgCompleter{}, "use shell command completion for prompt" } },
|
||||
{ "shell-script-candidates", { ArgCompleter{}, "use shell command completion for prompt" } },
|
||||
{ "on-change", { ArgCompleter{}, "command to execute whenever the prompt changes" } },
|
||||
{ "on-abort", { ArgCompleter{}, "command to execute whenever the prompt is canceled" } } },
|
||||
ParameterDesc::Flags::None, 2, 2
|
||||
},
|
||||
CommandFlags::None,
|
||||
|
@ -2226,9 +2247,9 @@ const CommandDesc menu_cmd = {
|
|||
"menu [<switches>] <name1> <commands1> <name2> <commands2>...: display a "
|
||||
"menu and execute commands for the selected item",
|
||||
ParameterDesc{
|
||||
{ { "auto-single", { false, "instantly validate if only one item is available" } },
|
||||
{ "select-cmds", { false, "each item specify an additional command to run when selected" } },
|
||||
{ "markup", { false, "parse menu entries as markup text" } } }
|
||||
{ { "auto-single", { {}, "instantly validate if only one item is available" } },
|
||||
{ "select-cmds", { {}, "each item specify an additional command to run when selected" } },
|
||||
{ "markup", { {}, "parse menu entries as markup text" } } }
|
||||
},
|
||||
CommandFlags::None,
|
||||
CommandHelper{},
|
||||
|
@ -2285,7 +2306,7 @@ const CommandDesc on_key_cmd = {
|
|||
"on-key [<switches>] <command>: wait for next user key and then execute <command>, "
|
||||
"with key available in the `key` value",
|
||||
ParameterDesc{
|
||||
{ { "mode-name", { true, "set mode name to use" } } },
|
||||
{ { "mode-name", { ArgCompleter{}, "set mode name to use" } } },
|
||||
ParameterDesc::Flags::None, 1, 1
|
||||
},
|
||||
CommandFlags::None,
|
||||
|
@ -2312,10 +2333,10 @@ const CommandDesc info_cmd = {
|
|||
nullptr,
|
||||
"info [<switches>] <text>: display an info box containing <text>",
|
||||
ParameterDesc{
|
||||
{ { "anchor", { true, "set info anchoring <line>.<column>" } },
|
||||
{ "style", { true, "set info style (above, below, menu, modal)" } },
|
||||
{ "markup", { false, "parse markup" } },
|
||||
{ "title", { true, "set info title" } } },
|
||||
{ { "anchor", { ArgCompleter{}, "set info anchoring <line>.<column>" } },
|
||||
{ "style", { {arg_completer(Array{"above", "below", "menu", "modal"})}, "set info style (above, below, menu, modal)" } },
|
||||
{ "markup", { {}, "parse markup" } },
|
||||
{ "title", { ArgCompleter{}, "set info title" } } },
|
||||
ParameterDesc::Flags::None, 0, 1
|
||||
},
|
||||
CommandFlags::None,
|
||||
|
@ -2518,9 +2539,9 @@ const CommandDesc select_cmd = {
|
|||
"\n"
|
||||
"selection_desc format is <anchor_line>.<anchor_column>,<cursor_line>.<cursor_column>",
|
||||
ParameterDesc{{
|
||||
{"timestamp", {true, "specify buffer timestamp at which those selections are valid"}},
|
||||
{"codepoint", {false, "columns are specified in codepoints, not bytes"}},
|
||||
{"display-column", {false, "columns are specified in display columns, not bytes"}}
|
||||
{"timestamp", {ArgCompleter{}, "specify buffer timestamp at which those selections are valid"}},
|
||||
{"codepoint", {{}, "columns are specified in codepoints, not bytes"}},
|
||||
{"display-column", {{}, "columns are specified in display columns, not bytes"}}
|
||||
},
|
||||
ParameterDesc::Flags::SwitchesOnlyAtStart, 1
|
||||
},
|
||||
|
@ -2642,7 +2663,7 @@ const CommandDesc enter_user_mode_cmd = {
|
|||
nullptr,
|
||||
"enter-user-mode [<switches>] <name>: enable <name> keymap mode for next key",
|
||||
ParameterDesc{
|
||||
{ { "lock", { false, "stay in mode until <esc> is pressed" } } },
|
||||
{ { "lock", { {}, "stay in mode until <esc> is pressed" } } },
|
||||
ParameterDesc::Flags::None, 1, 1
|
||||
},
|
||||
CommandFlags::None,
|
||||
|
@ -2672,7 +2693,7 @@ const CommandDesc provide_module_cmd = {
|
|||
nullptr,
|
||||
"provide-module [<switches>] <name> <cmds>: declares a module <name> provided by <cmds>",
|
||||
ParameterDesc{
|
||||
{ { "override", { false, "allow overriding an existing module" } } },
|
||||
{ { "override", { {}, "allow overriding an existing module" } } },
|
||||
ParameterDesc::Flags::None,
|
||||
2, 2
|
||||
},
|
||||
|
|
|
@ -678,10 +678,10 @@ const HighlighterDesc wrap_desc = {
|
|||
"Parameters: [-word] [-indent] [-width <max_width>] [-marker <marker_text>]\n"
|
||||
"Wrap lines to window width",
|
||||
{ {
|
||||
{ "word", { false, "wrap at word boundaries instead of codepoint boundaries" } },
|
||||
{ "indent", { false, "preserve line indentation of the wrapped line" } },
|
||||
{ "width", { true, "wrap at the given column instead of the window's width" } },
|
||||
{ "marker", { true, "insert the given text at the beginning of the wrapped line" } }, },
|
||||
{ "word", { {}, "wrap at word boundaries instead of codepoint boundaries" } },
|
||||
{ "indent", { {}, "preserve line indentation of the wrapped line" } },
|
||||
{ "width", { ArgCompleter{}, "wrap at the given column instead of the window's width" } },
|
||||
{ "marker", { ArgCompleter{}, "insert the given text at the beginning of the wrapped line" } }, },
|
||||
ParameterDesc::Flags::None, 0, 0
|
||||
}
|
||||
};
|
||||
|
@ -1047,11 +1047,11 @@ const HighlighterDesc show_whitespace_desc = {
|
|||
"Parameters: [-tab <separator>] [-tabpad <separator>] [-lf <separator>] [-spc <separator>] [-nbsp <separator>]\n"
|
||||
"Display whitespaces using symbols",
|
||||
{ {
|
||||
{ "tab", { true, "replace tabulations with the given character" } },
|
||||
{ "tabpad", { true, "append as many of the given character as is necessary to honor `tabstop`" } },
|
||||
{ "spc", { true, "replace spaces with the given character" } },
|
||||
{ "lf", { true, "replace line feeds with the given character" } },
|
||||
{ "nbsp", { true, "replace non-breakable spaces with the given character" } } },
|
||||
{ "tab", { ArgCompleter{}, "replace tabulations with the given character" } },
|
||||
{ "tabpad", { ArgCompleter{}, "append as many of the given character as is necessary to honor `tabstop`" } },
|
||||
{ "spc", { ArgCompleter{}, "replace spaces with the given character" } },
|
||||
{ "lf", { ArgCompleter{}, "replace line feeds with the given character" } },
|
||||
{ "nbsp", { ArgCompleter{}, "replace non-breakable spaces with the given character" } } },
|
||||
ParameterDesc::Flags::None, 0, 0
|
||||
}
|
||||
};
|
||||
|
@ -1131,11 +1131,11 @@ const HighlighterDesc line_numbers_desc = {
|
|||
"Parameters: [-relative] [-hlcursor] [-separators <separator|separator:cursor|cursor:up:down>] [-min-digits <cols>]\n"
|
||||
"Display line numbers",
|
||||
{ {
|
||||
{ "relative", { false, "show line numbers relative to the main cursor line" } },
|
||||
{ "separator", { true, "string to separate the line numbers column from the rest of the buffer (default '|')" } },
|
||||
{ "cursor-separator", { true, "identical to -separator but applies only to the line of the cursor (default is the same value passed to -separator)" } },
|
||||
{ "min-digits", { true, "use at least the given number of columns to display line numbers (default 2)" } },
|
||||
{ "hlcursor", { false, "highlight the cursor line with a separate face" } } },
|
||||
{ "relative", { {}, "show line numbers relative to the main cursor line" } },
|
||||
{ "separator", { ArgCompleter{}, "string to separate the line numbers column from the rest of the buffer (default '|')" } },
|
||||
{ "cursor-separator", { ArgCompleter{}, "identical to -separator but applies only to the line of the cursor (default is the same value passed to -separator)" } },
|
||||
{ "min-digits", { ArgCompleter{}, "use at least the given number of columns to display line numbers (default 2)" } },
|
||||
{ "hlcursor", { {}, "highlight the cursor line with a separate face" } } },
|
||||
ParameterDesc::Flags::None, 0, 0
|
||||
}
|
||||
};
|
||||
|
@ -1777,9 +1777,9 @@ const HighlighterDesc higlighter_group_desc = {
|
|||
"Parameters: [-passes <passes>]\n"
|
||||
"Creates a group that can contain other highlighters",
|
||||
{ {
|
||||
{ "passes", { true, "flags(colorize|move|wrap) "
|
||||
"kind of highlighters can be put in the group "
|
||||
"(default colorize)" } } },
|
||||
{ "passes", { ArgCompleter{}, "flags(colorize|move|wrap) "
|
||||
"kind of highlighters can be put in the group "
|
||||
"(default colorize)" } } },
|
||||
ParameterDesc::Flags::SwitchesOnlyAtStart, 0, 0
|
||||
}
|
||||
};
|
||||
|
@ -1795,9 +1795,9 @@ const HighlighterDesc ref_desc = {
|
|||
"Parameters: [-passes <passes>] <path>\n"
|
||||
"Reference the highlighter at <path> in shared highlighters",
|
||||
{ {
|
||||
{ "passes", { true, "flags(colorize|move|wrap) "
|
||||
"kind of highlighters that can be referenced "
|
||||
"(default colorize)" } } },
|
||||
{ "passes", { ArgCompleter{}, "flags(colorize|move|wrap) "
|
||||
"kind of highlighters that can be referenced "
|
||||
"(default colorize)" } } },
|
||||
ParameterDesc::Flags::SwitchesOnlyAtStart, 1, 1
|
||||
}
|
||||
};
|
||||
|
@ -1885,8 +1885,8 @@ const HighlighterDesc region_desc = {
|
|||
"highlighter as defined by <type> and eventual <params>...\n"
|
||||
"The region starts at <begin> match and ends at the first <end>",
|
||||
{ {
|
||||
{ "match-capture", { false, "only consider region ending/recurse delimiters whose first capture group match the region beginning delimiter" } },
|
||||
{ "recurse", { true, "make the region end on the first ending delimiter that does not close the given parameter" } } },
|
||||
{ "match-capture", { {}, "only consider region ending/recurse delimiters whose first capture group match the region beginning delimiter" } },
|
||||
{ "recurse", { ArgCompleter{}, "make the region end on the first ending delimiter that does not close the given parameter" } } },
|
||||
ParameterDesc::Flags::SwitchesOnlyAtStart | ParameterDesc::Flags::IgnoreUnknownSwitches,
|
||||
3
|
||||
}
|
||||
|
|
|
@ -941,7 +941,7 @@ public:
|
|||
{
|
||||
on_next_key_with_autoinfo(context(), "explicit-completion", KeymapMode::None,
|
||||
[this](Key key, Context&) {
|
||||
m_explicit_completer = PromptCompleter{};
|
||||
m_explicit_completer = ArgCompleter{};
|
||||
|
||||
if (key.key == 'f')
|
||||
use_explicit_completer([](const Context& context, StringView token) {
|
||||
|
@ -968,7 +968,7 @@ public:
|
|||
}
|
||||
else if (key == ctrl('o'))
|
||||
{
|
||||
m_explicit_completer = PromptCompleter{};
|
||||
m_explicit_completer = ArgCompleter{};
|
||||
m_auto_complete = not m_auto_complete;
|
||||
|
||||
if (m_auto_complete)
|
||||
|
|
34
src/main.cc
34
src/main.cc
|
@ -1071,23 +1071,23 @@ int main(int argc, char* argv[])
|
|||
set_signal_handler(SIGTTOU, SIG_IGN);
|
||||
|
||||
const ParameterDesc param_desc{
|
||||
SwitchMap{ { "c", { true, "connect to given session" } },
|
||||
{ "e", { true, "execute argument on client initialisation" } },
|
||||
{ "E", { true, "execute argument on server initialisation" } },
|
||||
{ "n", { false, "do not source kakrc files on startup" } },
|
||||
{ "s", { true, "set session name" } },
|
||||
{ "d", { false, "run as a headless session (requires -s)" } },
|
||||
{ "p", { true, "just send stdin as commands to the given session" } },
|
||||
{ "f", { true, "filter: for each file, select the entire buffer and execute the given keys" } },
|
||||
{ "i", { true, "backup the files on which a filter is applied using the given suffix" } },
|
||||
{ "q", { false, "in filter mode, be quiet about errors applying keys" } },
|
||||
{ "ui", { true, "set the type of user interface to use (terminal, dummy, or json)" } },
|
||||
{ "l", { false, "list existing sessions" } },
|
||||
{ "clear", { false, "clear dead sessions" } },
|
||||
{ "debug", { true, "initial debug option value" } },
|
||||
{ "version", { false, "display kakoune version and exit" } },
|
||||
{ "ro", { false, "readonly mode" } },
|
||||
{ "help", { false, "display a help message and quit" } } }
|
||||
SwitchMap{ { "c", { ArgCompleter{}, "connect to given session" } },
|
||||
{ "e", { ArgCompleter{}, "execute argument on client initialisation" } },
|
||||
{ "E", { ArgCompleter{}, "execute argument on server initialisation" } },
|
||||
{ "n", { {}, "do not source kakrc files on startup" } },
|
||||
{ "s", { ArgCompleter{}, "set session name" } },
|
||||
{ "d", { {}, "run as a headless session (requires -s)" } },
|
||||
{ "p", { ArgCompleter{}, "just send stdin as commands to the given session" } },
|
||||
{ "f", { ArgCompleter{}, "filter: for each file, select the entire buffer and execute the given keys" } },
|
||||
{ "i", { ArgCompleter{}, "backup the files on which a filter is applied using the given suffix" } },
|
||||
{ "q", { {}, "in filter mode, be quiet about errors applying keys" } },
|
||||
{ "ui", { ArgCompleter{}, "set the type of user interface to use (terminal, dummy, or json)" } },
|
||||
{ "l", { {}, "list existing sessions" } },
|
||||
{ "clear", { {}, "clear dead sessions" } },
|
||||
{ "debug", { ArgCompleter{}, "initial debug option value" } },
|
||||
{ "version", { {}, "display kakoune version and exit" } },
|
||||
{ "ro", { {}, "readonly mode" } },
|
||||
{ "help", { {}, "display a help message and quit" } } }
|
||||
};
|
||||
|
||||
try
|
||||
|
|
|
@ -11,21 +11,21 @@ String generate_switches_doc(const SwitchMap& switches)
|
|||
if (switches.empty())
|
||||
return res;
|
||||
|
||||
auto switch_len = [](auto& sw) { return sw.key.column_length() + (sw.value.takes_arg ? 5 : 0); };
|
||||
auto switch_len = [](auto& sw) { return sw.key.column_length() + (sw.value.arg_completer ? 5 : 0); };
|
||||
auto switches_len = switches | transform(switch_len);
|
||||
const ColumnCount maxlen = *std::max_element(switches_len.begin(), switches_len.end());
|
||||
|
||||
for (auto& sw : switches) {
|
||||
res += format("-{} {}{}{}\n",
|
||||
sw.key,
|
||||
sw.value.takes_arg ? "<arg>" : "",
|
||||
sw.value.arg_completer ? "<arg>" : "",
|
||||
String{' ', maxlen - switch_len(sw) + 1},
|
||||
sw.value.description);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
ParametersParser::ParametersParser(ParameterList params, const ParameterDesc& desc)
|
||||
ParametersParser::ParametersParser(ParameterList params, const ParameterDesc& desc, bool ignore_errors)
|
||||
: m_params(params)
|
||||
{
|
||||
const bool switches_only_at_start = desc.flags & ParameterDesc::Flags::SwitchesOnlyAtStart;
|
||||
|
@ -36,11 +36,16 @@ ParametersParser::ParametersParser(ParameterList params, const ParameterDesc& de
|
|||
for (size_t i = 0; i < params.size(); ++i)
|
||||
{
|
||||
if (not only_pos and not ignore_unknown_switches and params[i] == "--")
|
||||
{
|
||||
m_state = State::Switch;
|
||||
only_pos = true;
|
||||
}
|
||||
else if (not only_pos and not params[i].empty() and params[i][0_byte] == '-')
|
||||
{
|
||||
StringView switch_name = params[i].substr(1_byte);
|
||||
auto it = desc.switches.find(switch_name);
|
||||
m_state = it == desc.switches.end() and ignore_unknown_switches ?
|
||||
State::Positional : State::Switch;
|
||||
if (it == desc.switches.end())
|
||||
{
|
||||
if (ignore_unknown_switches)
|
||||
|
@ -50,28 +55,43 @@ ParametersParser::ParametersParser(ParameterList params, const ParameterDesc& de
|
|||
only_pos = true;
|
||||
continue;
|
||||
}
|
||||
if (ignore_errors)
|
||||
continue;
|
||||
throw unknown_option(params[i]);
|
||||
}
|
||||
|
||||
auto switch_index = it - desc.switches.begin();
|
||||
if (switch_seen[switch_index])
|
||||
{
|
||||
if (ignore_errors)
|
||||
continue;
|
||||
throw runtime_error{format("switch '-{}' specified more than once", it->key)};
|
||||
}
|
||||
switch_seen[switch_index] = true;
|
||||
|
||||
if (it->value.takes_arg and ++i == params.size())
|
||||
throw missing_option_value(it->key);
|
||||
if (it->value.arg_completer)
|
||||
{
|
||||
if (++i == params.size())
|
||||
{
|
||||
if (ignore_errors)
|
||||
continue;
|
||||
throw missing_option_value(it->key);
|
||||
}
|
||||
m_state = State::SwitchArgument;
|
||||
}
|
||||
|
||||
m_switches[switch_name.str()] = it->value.takes_arg ? params[i] : StringView{};
|
||||
m_switches[switch_name.str()] = it->value.arg_completer ? params[i] : StringView{};
|
||||
}
|
||||
else // positional
|
||||
{
|
||||
m_state = State::Positional;
|
||||
if (switches_only_at_start)
|
||||
only_pos = true;
|
||||
m_positional_indices.push_back(i);
|
||||
}
|
||||
}
|
||||
size_t count = m_positional_indices.size();
|
||||
if (count > desc.max_positionals or count < desc.min_positionals)
|
||||
if (not ignore_errors and (count > desc.max_positionals or count < desc.min_positionals))
|
||||
throw wrong_argument_count();
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include "string.hh"
|
||||
#include "string_utils.hh"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace Kakoune
|
||||
{
|
||||
|
||||
|
@ -37,9 +39,15 @@ struct wrong_argument_count : public parameter_error
|
|||
wrong_argument_count() : parameter_error("wrong argument count") {}
|
||||
};
|
||||
|
||||
class Context;
|
||||
struct Completions;
|
||||
enum class CompletionFlags;
|
||||
using ArgCompleter = std::function<Completions (const Context&, CompletionFlags,
|
||||
StringView, ByteCount)>;
|
||||
|
||||
struct SwitchDesc
|
||||
{
|
||||
bool takes_arg;
|
||||
Optional<ArgCompleter> arg_completer;
|
||||
String description;
|
||||
};
|
||||
|
||||
|
@ -74,7 +82,13 @@ struct ParametersParser
|
|||
// the options defines named options, if they map to true, then
|
||||
// they are understood as string options, else they are understood as
|
||||
// boolean option.
|
||||
ParametersParser(ParameterList params, const ParameterDesc& desc);
|
||||
ParametersParser(ParameterList params, const ParameterDesc& desc, bool ignore_errors = false);
|
||||
|
||||
enum class State {
|
||||
Switch,
|
||||
SwitchArgument,
|
||||
Positional,
|
||||
};
|
||||
|
||||
// Return a valid optional if the switch was given, with
|
||||
// a non empty StringView value if the switch took an argument.
|
||||
|
@ -132,10 +146,13 @@ struct ParametersParser
|
|||
iterator begin() const { return iterator(*this, 0); }
|
||||
iterator end() const { return iterator(*this, m_positional_indices.size()); }
|
||||
|
||||
State state() const { return *m_state; }
|
||||
|
||||
private:
|
||||
ParameterList m_params;
|
||||
Vector<size_t, MemoryDomain::Commands> m_positional_indices;
|
||||
HashMap<String, StringView> m_switches;
|
||||
Optional<State> m_state;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user