From 37254d5e11f9d3271630577746c2fb0ca820cacc Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Wed, 21 Dec 2022 11:56:42 +0100 Subject: [PATCH] Support optional priority in completions options Closes #1709 This also allows supporting LSP's sortText (to a reasonable extent). --- src/insert_completer.cc | 6 +++--- src/insert_completer.hh | 2 +- src/option_types.cc | 5 +++++ src/option_types.hh | 47 +++++++++++++++++++++++++++++++++++++---- src/ranges.hh | 21 ++++++++++++------ 5 files changed, 66 insertions(+), 15 deletions(-) diff --git a/src/insert_completer.cc b/src/insert_completer.cc index 3b83568f6c..7098ce71b7 100644 --- a/src/insert_completer.cc +++ b/src/insert_completer.cc @@ -301,10 +301,10 @@ InsertCompletion complete_option(const SelectionList& sels, for (auto& candidate : opt.list) { - if (RankedMatchAndInfo match{std::get<0>(candidate), query}) + auto& [completion, on_select, menu, priority] = candidate; + if (RankedMatchAndInfo match{completion, query, priority}) { - match.on_select = std::get<1>(candidate); - auto& menu = std::get<2>(candidate); + match.on_select = on_select; match.menu_entry = not menu.empty() ? parse_display_line(expand_tabs(menu, tabstop, column), faces) : DisplayLine{String{}, {}}; diff --git a/src/insert_completer.hh b/src/insert_completer.hh index 269368d50d..fe2ae15f1b 100644 --- a/src/insert_completer.hh +++ b/src/insert_completer.hh @@ -45,7 +45,7 @@ inline StringView option_type_name(Meta::Type) return "completer"; } -using CompletionCandidate = std::tuple; +using CompletionCandidate = std::tuple>; using CompletionList = PrefixedList; inline StringView option_type_name(Meta::Type) diff --git a/src/option_types.cc b/src/option_types.cc index a1eeb4186a..e32df3b739 100644 --- a/src/option_types.cc +++ b/src/option_types.cc @@ -19,6 +19,11 @@ UnitTest test_option_parsing{[]{ check(Vector{10, 20, 30}, {"10", "20", "30"}); check(HashMap{{"foo", 10}, {"b=r", 20}, {"b:z", 30}}, {"foo=10", "b\\=r=20", "b:z=30"}); check(DebugFlags::Keys | DebugFlags::Hooks, {"hooks|keys"}); + check(std::tuple, Optional>(1, 2, 3), {"1|2|3"}); + std::tuple, Optional> tupleWithNullOptionals{1, {}, {}}; + check(tupleWithNullOptionals, {"1||"}); + // Can also parse if tuple separators are missing. + kak_assert(option_from_strings(Meta::Type{}, {"1"}) == tupleWithNullOptionals); }}; } diff --git a/src/option_types.hh b/src/option_types.hh index 40d367dfb4..7b650e0be5 100644 --- a/src/option_types.hh +++ b/src/option_types.hh @@ -79,6 +79,22 @@ inline Codepoint option_from_string(Meta::Type, StringView str) } constexpr StringView option_type_name(Meta::Type) { return "codepoint"; } +template +String option_to_string(const Optional& opt, Quoting quoting) +{ + if (not opt) + return ""; + return option_to_string(*opt, quoting); +} + +template +Optional option_from_string(Meta::Type>, StringView str) +{ + if (str.empty()) + return {}; + return option_from_string(Meta::Type{}, str); +} + template Vector option_to_strings(const Vector& opt) { @@ -225,8 +241,32 @@ String option_to_string(const std::tuple& opt, Quoting quoting) return option_to_string_impl(quoting, opt, std::make_index_sequence()); } +template +struct IsOptionalImpl : std::false_type{}; +template +struct IsOptionalImpl> : std::true_type{}; + +template +struct CountTrailingOptionals {}; +template<> +struct CountTrailingOptionals<> +{ + static constexpr bool all_optional = true; + static constexpr size_t value = 0; +}; +template +struct CountTrailingOptionals +{ + static constexpr bool all_optional = IsOptionalImpl::value + and CountTrailingOptionals::all_optional; + static_assert(not IsOptionalImpl::value or all_optional, + "non-optional fields cannot follow optional types"); + static constexpr size_t value = all_optional ? 1 + sizeof...(Tail) : CountTrailingOptionals::value; +}; + template -std::tuple option_from_string_impl(Meta::Type>, StringView str, +std::tuple option_from_string_impl(Meta::Type>, + StringView str, std::index_sequence) { struct error : runtime_error @@ -237,15 +277,14 @@ std::tuple option_from_string_impl(Meta::Type>, S }; auto elems = str | split(tuple_separator, '\\') | transform(unescape) - | static_gather(); + | static_gather::value>(); return std::tuple{option_from_string(Meta::Type{}, elems[I])...}; } template std::tuple option_from_string(Meta::Type>, StringView str) { - return option_from_string_impl(Meta::Type>{}, str, - std::make_index_sequence()); + return option_from_string_impl(Meta::Type>{}, str, std::make_index_sequence()); } template diff --git a/src/ranges.hh b/src/ranges.hh index 9c766e062f..bf9c1cd13d 100644 --- a/src/ranges.hh +++ b/src/ranges.hh @@ -648,34 +648,41 @@ auto gather() }}; } -template +template auto elements() { return ViewFactory{[=] (auto&& range) { using std::begin; using std::end; + using ElementType = std::remove_cvref_t; auto it = begin(range), end_it = end(range); auto elem = [&](size_t index) { - if (it == end_it) throw ExceptionType{index}; + static_assert(sizeof...(Indexes) >= optional_elements); + if (it == end_it) + { + if (index >= sizeof...(Indexes) - optional_elements) + return ElementType{}; + throw ExceptionType{index}; + } return *it++; }; // Note that initializer lists elements are guaranteed to be sequenced - Array, sizeof...(Indexes)> res{{elem(Indexes)...}}; + Array res{{elem(Indexes)...}}; if (throw_on_extra_elements and it != end_it) throw ExceptionType{sizeof...(Indexes)}; return res; }}; } -template +template auto static_gather_impl(std::index_sequence) { - return elements(); + return elements(); } -template +template auto static_gather() { - return static_gather_impl(std::make_index_sequence()); + return static_gather_impl(std::make_index_sequence()); } }