Introduce Menu completion flags to auto select best candidate
This commit is contained in:
parent
4916471029
commit
c972dfd2d7
|
@ -726,7 +726,7 @@ Completions CommandManager::complete(const Context& context,
|
||||||
context, flags, params, tokens.size() - 2,
|
context, flags, params, tokens.size() - 2,
|
||||||
cursor_pos_in_token), start);
|
cursor_pos_in_token), start);
|
||||||
|
|
||||||
if (not completions.quoted and token.type == Token::Type::Raw)
|
if (not (completions.flags & Completions::Flags::Quoted) and token.type == Token::Type::Raw)
|
||||||
{
|
{
|
||||||
for (auto& c : completions.candidates)
|
for (auto& c : completions.candidates)
|
||||||
c = (not c.empty() and contains("%'\"", c[0]) ? "\\" : "") + escape(c, "; \t", '\\');
|
c = (not c.empty() and contains("%'\"", c[0]) ? "\\" : "") + escape(c, "; \t", '\\');
|
||||||
|
|
|
@ -102,12 +102,30 @@ make_completer(Completers&&... completers)
|
||||||
return {std::forward<Completers>(completers)...};
|
return {std::forward<Completers>(completers)...};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Completer>
|
||||||
|
auto add_flags(Completer&& completer, Completions::Flags completions_flags)
|
||||||
|
{
|
||||||
|
return [=](const Context& context, CompletionFlags flags, const String& prefix, ByteCount cursor_pos) {
|
||||||
|
Completions res = completer(context, flags, prefix, cursor_pos);
|
||||||
|
res.flags |= completions_flags;
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Completer>
|
||||||
|
auto menu(Completer&& completer)
|
||||||
|
{
|
||||||
|
return add_flags(std::forward<Completer>(completer), Completions::Flags::Menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool menu>
|
||||||
auto filename_completer = make_completer(
|
auto filename_completer = make_completer(
|
||||||
[](const Context& context, CompletionFlags flags, const String& prefix, ByteCount cursor_pos)
|
[](const Context& context, CompletionFlags flags, const String& prefix, ByteCount cursor_pos)
|
||||||
{ return Completions{ 0_byte, cursor_pos,
|
{ return Completions{ 0_byte, cursor_pos,
|
||||||
complete_filename(prefix,
|
complete_filename(prefix,
|
||||||
context.options()["ignored_files"].get<Regex>(),
|
context.options()["ignored_files"].get<Regex>(),
|
||||||
cursor_pos, FilenameFlags::Expand) }; });
|
cursor_pos, FilenameFlags::Expand),
|
||||||
|
menu ? Completions::Flags::Menu : Completions::Flags::None}; });
|
||||||
|
|
||||||
template<bool ignore_current = false>
|
template<bool ignore_current = false>
|
||||||
static Completions complete_buffer_name(const Context& context, CompletionFlags flags,
|
static Completions complete_buffer_name(const Context& context, CompletionFlags flags,
|
||||||
|
@ -165,9 +183,6 @@ auto make_single_word_completer(std::function<String (const Context&)> func)
|
||||||
return { 0_byte, cursor_pos, complete(prefix, cursor_pos, candidate) }; });
|
return { 0_byte, cursor_pos, complete(prefix, cursor_pos, candidate) }; });
|
||||||
}
|
}
|
||||||
|
|
||||||
auto buffer_completer = make_completer(complete_buffer_name<false>);
|
|
||||||
auto other_buffer_completer = make_completer(complete_buffer_name<true>);
|
|
||||||
|
|
||||||
const ParameterDesc no_params{ {}, ParameterDesc::Flags::None, 0, 0 };
|
const ParameterDesc no_params{ {}, ParameterDesc::Flags::None, 0, 0 };
|
||||||
const ParameterDesc single_param{ {}, ParameterDesc::Flags::None, 1, 1 };
|
const ParameterDesc single_param{ {}, ParameterDesc::Flags::None, 1, 1 };
|
||||||
const ParameterDesc single_optional_param{ {}, ParameterDesc::Flags::None, 0, 1 };
|
const ParameterDesc single_optional_param{ {}, ParameterDesc::Flags::None, 0, 1 };
|
||||||
|
@ -190,7 +205,9 @@ static Completions complete_command_name(const Context& context, CompletionFlags
|
||||||
|
|
||||||
struct ShellScriptCompleter
|
struct ShellScriptCompleter
|
||||||
{
|
{
|
||||||
ShellScriptCompleter(String shell_script) : m_shell_script{std::move(shell_script)} {}
|
ShellScriptCompleter(String shell_script,
|
||||||
|
Completions::Flags flags = Completions::Flags::None)
|
||||||
|
: m_shell_script{std::move(shell_script)}, m_flags(flags) {}
|
||||||
|
|
||||||
Completions operator()(const Context& context, CompletionFlags flags,
|
Completions operator()(const Context& context, CompletionFlags flags,
|
||||||
CommandParameters params, size_t token_to_complete,
|
CommandParameters params, size_t token_to_complete,
|
||||||
|
@ -211,15 +228,18 @@ struct ShellScriptCompleter
|
||||||
for (auto&& candidate : output | split<StringView>('\n'))
|
for (auto&& candidate : output | split<StringView>('\n'))
|
||||||
candidates.push_back(candidate.str());
|
candidates.push_back(candidate.str());
|
||||||
|
|
||||||
return {0_byte, pos_in_token, std::move(candidates)};
|
return {0_byte, pos_in_token, std::move(candidates), m_flags};
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
String m_shell_script;
|
String m_shell_script;
|
||||||
|
Completions::Flags m_flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ShellCandidatesCompleter
|
struct ShellCandidatesCompleter
|
||||||
{
|
{
|
||||||
ShellCandidatesCompleter(String shell_script) : m_shell_script{std::move(shell_script)} {}
|
ShellCandidatesCompleter(String shell_script,
|
||||||
|
Completions::Flags flags = Completions::Flags::None)
|
||||||
|
: m_shell_script{std::move(shell_script)}, m_flags(flags) {}
|
||||||
|
|
||||||
Completions operator()(const Context& context, CompletionFlags flags,
|
Completions operator()(const Context& context, CompletionFlags flags,
|
||||||
CommandParameters params, size_t token_to_complete,
|
CommandParameters params, size_t token_to_complete,
|
||||||
|
@ -263,13 +283,14 @@ struct ShellCandidatesCompleter
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
return Completions{ 0_byte, pos_in_token, std::move(res) };
|
return Completions{0_byte, pos_in_token, std::move(res), m_flags};
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
String m_shell_script;
|
String m_shell_script;
|
||||||
Vector<std::pair<String, UsedLetters>, MemoryDomain::Completion> m_candidates;
|
Vector<std::pair<String, UsedLetters>, MemoryDomain::Completion> m_candidates;
|
||||||
int m_token = -1;
|
int m_token = -1;
|
||||||
|
Completions::Flags m_flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Completer>
|
template<typename Completer>
|
||||||
|
@ -421,7 +442,7 @@ const CommandDesc edit_cmd = {
|
||||||
edit_params,
|
edit_params,
|
||||||
CommandFlags::None,
|
CommandFlags::None,
|
||||||
CommandHelper{},
|
CommandHelper{},
|
||||||
filename_completer,
|
filename_completer<false>,
|
||||||
edit<false>
|
edit<false>
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -433,7 +454,7 @@ const CommandDesc force_edit_cmd = {
|
||||||
edit_params,
|
edit_params,
|
||||||
CommandFlags::None,
|
CommandFlags::None,
|
||||||
CommandHelper{},
|
CommandHelper{},
|
||||||
filename_completer,
|
filename_completer<false>,
|
||||||
edit<true>
|
edit<true>
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -480,7 +501,7 @@ const CommandDesc write_cmd = {
|
||||||
write_params,
|
write_params,
|
||||||
CommandFlags::None,
|
CommandFlags::None,
|
||||||
CommandHelper{},
|
CommandHelper{},
|
||||||
filename_completer,
|
filename_completer<false>,
|
||||||
write_buffer,
|
write_buffer,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -492,7 +513,7 @@ const CommandDesc force_write_cmd = {
|
||||||
write_params,
|
write_params,
|
||||||
CommandFlags::None,
|
CommandFlags::None,
|
||||||
CommandHelper{},
|
CommandHelper{},
|
||||||
filename_completer,
|
filename_completer<false>,
|
||||||
write_buffer<true>,
|
write_buffer<true>,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -688,7 +709,7 @@ const CommandDesc buffer_cmd = {
|
||||||
single_param,
|
single_param,
|
||||||
CommandFlags::None,
|
CommandFlags::None,
|
||||||
CommandHelper{},
|
CommandHelper{},
|
||||||
other_buffer_completer,
|
make_completer(menu(complete_buffer_name<true>)),
|
||||||
[](const ParametersParser& parser, Context& context, const ShellContext&)
|
[](const ParametersParser& parser, Context& context, const ShellContext&)
|
||||||
{
|
{
|
||||||
Buffer& buffer = BufferManager::instance().get_buffer(parser[0]);
|
Buffer& buffer = BufferManager::instance().get_buffer(parser[0]);
|
||||||
|
@ -775,7 +796,7 @@ const CommandDesc delete_buffer_cmd = {
|
||||||
single_optional_param,
|
single_optional_param,
|
||||||
CommandFlags::None,
|
CommandFlags::None,
|
||||||
CommandHelper{},
|
CommandHelper{},
|
||||||
buffer_completer,
|
make_completer(menu(complete_buffer_name<false>)),
|
||||||
delete_buffer<false>
|
delete_buffer<false>
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -787,7 +808,7 @@ const CommandDesc force_delete_buffer_cmd = {
|
||||||
single_optional_param,
|
single_optional_param,
|
||||||
CommandFlags::None,
|
CommandFlags::None,
|
||||||
CommandHelper{},
|
CommandHelper{},
|
||||||
buffer_completer,
|
make_completer(menu(complete_buffer_name<false>)),
|
||||||
delete_buffer<true>
|
delete_buffer<true>
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1037,6 +1058,9 @@ void define_command(const ParametersParser& parser, Context& context, const Shel
|
||||||
if (parser.get_switch("hidden"))
|
if (parser.get_switch("hidden"))
|
||||||
flags = CommandFlags::Hidden;
|
flags = CommandFlags::Hidden;
|
||||||
|
|
||||||
|
const Completions::Flags completions_flags = parser.get_switch("menu") ?
|
||||||
|
Completions::Flags::Menu : Completions::Flags::None;
|
||||||
|
|
||||||
const String& commands = parser[1];
|
const String& commands = parser[1];
|
||||||
CommandFunc cmd;
|
CommandFunc cmd;
|
||||||
ParameterDesc desc;
|
ParameterDesc desc;
|
||||||
|
@ -1073,45 +1097,48 @@ void define_command(const ParametersParser& parser, Context& context, const Shel
|
||||||
CommandCompleter completer;
|
CommandCompleter completer;
|
||||||
if (parser.get_switch("file-completion"))
|
if (parser.get_switch("file-completion"))
|
||||||
{
|
{
|
||||||
completer = [](const Context& context, CompletionFlags flags,
|
completer = [=](const Context& context, CompletionFlags flags,
|
||||||
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 = params[token_to_complete];
|
const String& prefix = params[token_to_complete];
|
||||||
auto& ignored_files = context.options()["ignored_files"].get<Regex>();
|
auto& ignored_files = context.options()["ignored_files"].get<Regex>();
|
||||||
return Completions{ 0_byte, pos_in_token,
|
return Completions{0_byte, pos_in_token,
|
||||||
complete_filename(prefix, ignored_files,
|
complete_filename(prefix, ignored_files,
|
||||||
pos_in_token, FilenameFlags::Expand) };
|
pos_in_token, FilenameFlags::Expand),
|
||||||
|
completions_flags};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (parser.get_switch("client-completion"))
|
else if (parser.get_switch("client-completion"))
|
||||||
{
|
{
|
||||||
completer = [](const Context& context, CompletionFlags flags,
|
completer = [=](const Context& context, CompletionFlags flags,
|
||||||
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 = params[token_to_complete];
|
const String& prefix = params[token_to_complete];
|
||||||
auto& cm = ClientManager::instance();
|
auto& cm = ClientManager::instance();
|
||||||
return Completions{ 0_byte, pos_in_token,
|
return Completions{0_byte, pos_in_token,
|
||||||
cm.complete_client_name(prefix, pos_in_token) };
|
cm.complete_client_name(prefix, pos_in_token),
|
||||||
|
completions_flags};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (parser.get_switch("buffer-completion"))
|
else if (parser.get_switch("buffer-completion"))
|
||||||
{
|
{
|
||||||
completer = [](const Context& context, CompletionFlags flags,
|
completer = [=](const Context& context, CompletionFlags flags,
|
||||||
CommandParameters params,
|
CommandParameters params,
|
||||||
size_t token_to_complete, ByteCount pos_in_token)
|
size_t token_to_complete, ByteCount pos_in_token)
|
||||||
{
|
{
|
||||||
return complete_buffer_name(context, flags, params[token_to_complete], pos_in_token);
|
return add_flags(complete_buffer_name, completions_flags)(
|
||||||
|
context, flags, params[token_to_complete], pos_in_token);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (auto shell_script = parser.get_switch("shell-script-completion"))
|
else if (auto shell_script = parser.get_switch("shell-script-completion"))
|
||||||
{
|
{
|
||||||
completer = ShellScriptCompleter{shell_script->str()};
|
completer = ShellScriptCompleter{shell_script->str(), completions_flags};
|
||||||
}
|
}
|
||||||
else if (auto shell_script = parser.get_switch("shell-script-candidates"))
|
else if (auto shell_script = parser.get_switch("shell-script-candidates"))
|
||||||
{
|
{
|
||||||
completer = ShellCandidatesCompleter{shell_script->str()};
|
completer = ShellCandidatesCompleter{shell_script->str(), completions_flags};
|
||||||
}
|
}
|
||||||
else if (parser.get_switch("command-completion"))
|
else if (parser.get_switch("command-completion"))
|
||||||
{
|
{
|
||||||
|
@ -1125,11 +1152,12 @@ void define_command(const ParametersParser& parser, Context& context, const Shel
|
||||||
}
|
}
|
||||||
else if (parser.get_switch("shell-completion"))
|
else if (parser.get_switch("shell-completion"))
|
||||||
{
|
{
|
||||||
completer = [](const Context& context, CompletionFlags flags,
|
completer = [=](const Context& context, CompletionFlags flags,
|
||||||
CommandParameters params,
|
CommandParameters params,
|
||||||
size_t token_to_complete, ByteCount pos_in_token)
|
size_t token_to_complete, ByteCount pos_in_token)
|
||||||
{
|
{
|
||||||
return shell_complete(context, flags, params[token_to_complete], pos_in_token);
|
return add_flags(shell_complete, completions_flags)(
|
||||||
|
context, flags, params[token_to_complete], pos_in_token);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1148,6 +1176,7 @@ const CommandDesc define_command_cmd = {
|
||||||
{ "override", { false, "allow overriding an existing command" } },
|
{ "override", { false, "allow overriding an existing command" } },
|
||||||
{ "hidden", { false, "do not display the command in completion candidates" } },
|
{ "hidden", { false, "do not display the command in completion candidates" } },
|
||||||
{ "docstring", { true, "define the documentation string for command" } },
|
{ "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" } },
|
{ "file-completion", { false, "complete parameters using filename completion" } },
|
||||||
{ "client-completion", { false, "complete parameters using client name completion" } },
|
{ "client-completion", { false, "complete parameters using client name completion" } },
|
||||||
{ "buffer-completion", { false, "complete parameters using buffer name completion" } },
|
{ "buffer-completion", { false, "complete parameters using buffer name completion" } },
|
||||||
|
@ -1368,7 +1397,7 @@ const CommandDesc source_cmd = {
|
||||||
ParameterDesc{ {}, ParameterDesc::Flags::None, 1, (size_t)-1 },
|
ParameterDesc{ {}, ParameterDesc::Flags::None, 1, (size_t)-1 },
|
||||||
CommandFlags::None,
|
CommandFlags::None,
|
||||||
CommandHelper{},
|
CommandHelper{},
|
||||||
filename_completer,
|
filename_completer<true>,
|
||||||
[](const ParametersParser& parser, Context& context, const ShellContext&)
|
[](const ParametersParser& parser, Context& context, const ShellContext&)
|
||||||
{
|
{
|
||||||
const DebugFlags debug_flags = context.options()["debug"].get<DebugFlags>();
|
const DebugFlags debug_flags = context.options()["debug"].get<DebugFlags>();
|
||||||
|
@ -1447,7 +1476,9 @@ const CommandDesc set_option_cmd = {
|
||||||
GlobalScope::instance().option_registry().option_exists(params[start + 1]))
|
GlobalScope::instance().option_registry().option_exists(params[start + 1]))
|
||||||
{
|
{
|
||||||
OptionManager& options = get_scope(params[start], context).options();
|
OptionManager& options = get_scope(params[start], context).options();
|
||||||
return { 0_byte, params[start + 2].length(), { options[params[start + 1]].get_as_string(Quoting::Kakoune) }, true };
|
return {0_byte, params[start + 2].length(),
|
||||||
|
{options[params[start + 1]].get_as_string(Quoting::Kakoune)},
|
||||||
|
Completions::Flags::Quoted};
|
||||||
}
|
}
|
||||||
return Completions{};
|
return Completions{};
|
||||||
},
|
},
|
||||||
|
|
|
@ -18,10 +18,19 @@ using CandidateList = Vector<String, MemoryDomain::Completion>;
|
||||||
|
|
||||||
struct Completions
|
struct Completions
|
||||||
{
|
{
|
||||||
|
enum class Flags
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Quoted = 0b1,
|
||||||
|
Menu = 0b10
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr friend bool with_bit_ops(Meta::Type<Flags>) { return true; }
|
||||||
|
|
||||||
CandidateList candidates;
|
CandidateList candidates;
|
||||||
ByteCount start;
|
ByteCount start;
|
||||||
ByteCount end;
|
ByteCount end;
|
||||||
bool quoted = false;
|
Flags flags = Flags::None;
|
||||||
|
|
||||||
Completions()
|
Completions()
|
||||||
: start(0), end(0) {}
|
: start(0), end(0) {}
|
||||||
|
@ -29,8 +38,8 @@ 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, bool quoted = false)
|
Completions(ByteCount start, ByteCount end, CandidateList candidates, Flags flags = Flags::None)
|
||||||
: candidates(std::move(candidates)), start(start), end(end), quoted{quoted} {}
|
: candidates(std::move(candidates)), start(start), end(end), flags{flags} {}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class CompletionFlags
|
enum class CompletionFlags
|
||||||
|
@ -53,8 +62,8 @@ Completions shell_complete(const Context& context, CompletionFlags,
|
||||||
|
|
||||||
inline Completions offset_pos(Completions completion, ByteCount offset)
|
inline Completions offset_pos(Completions completion, ByteCount offset)
|
||||||
{
|
{
|
||||||
return { completion.start + offset, completion.end + offset,
|
return {completion.start + offset, completion.end + offset,
|
||||||
std::move(completion.candidates), completion.quoted };
|
std::move(completion.candidates), completion.flags};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
|
|
|
@ -780,6 +780,15 @@ public:
|
||||||
|
|
||||||
if (key == Key::Return)
|
if (key == Key::Return)
|
||||||
{
|
{
|
||||||
|
if ((m_completions.flags & Completions::Flags::Menu) and
|
||||||
|
m_current_completion == -1 and
|
||||||
|
not m_completions.candidates.empty())
|
||||||
|
{
|
||||||
|
const String& completion = m_completions.candidates.front();
|
||||||
|
m_line_editor.insert_from(line.char_count_to(m_completions.start),
|
||||||
|
completion);
|
||||||
|
}
|
||||||
|
|
||||||
if (not context().history_disabled())
|
if (not context().history_disabled())
|
||||||
history_push(history, line);
|
history_push(history, line);
|
||||||
context().print_status(DisplayLine{});
|
context().print_status(DisplayLine{});
|
||||||
|
@ -1001,6 +1010,7 @@ private:
|
||||||
const String& line = m_line_editor.line();
|
const String& line = m_line_editor.line();
|
||||||
m_completions = m_completer(context(), flags, line,
|
m_completions = m_completer(context(), flags, line,
|
||||||
line.byte_count_to(m_line_editor.cursor_pos()));
|
line.byte_count_to(m_line_editor.cursor_pos()));
|
||||||
|
const bool menu = (bool)(m_completions.flags & Completions::Flags::Menu);
|
||||||
if (context().has_client())
|
if (context().has_client())
|
||||||
{
|
{
|
||||||
if (m_completions.candidates.empty())
|
if (m_completions.candidates.empty())
|
||||||
|
@ -1013,8 +1023,11 @@ private:
|
||||||
const auto menu_style = (m_flags & PromptFlags::Search) ? MenuStyle::Search : MenuStyle::Prompt;
|
const auto menu_style = (m_flags & PromptFlags::Search) ? MenuStyle::Search : MenuStyle::Prompt;
|
||||||
context().client().menu_show(items, {}, menu_style);
|
context().client().menu_show(items, {}, menu_style);
|
||||||
|
|
||||||
|
if (menu)
|
||||||
|
context().client().menu_select(0);
|
||||||
|
|
||||||
auto prefix = line.substr(m_completions.start, m_completions.end - m_completions.start);
|
auto prefix = line.substr(m_completions.start, m_completions.end - m_completions.start);
|
||||||
if (not contains(m_completions.candidates, prefix))
|
if (not menu and not contains(m_completions.candidates, prefix))
|
||||||
{
|
{
|
||||||
m_current_completion = m_completions.candidates.size();
|
m_current_completion = m_completions.candidates.size();
|
||||||
m_completions.candidates.push_back(prefix.str());
|
m_completions.candidates.push_back(prefix.str());
|
||||||
|
|
Loading…
Reference in New Issue
Block a user