diff --git a/src/insert_completer.cc b/src/insert_completer.cc index a4ce0ba6..02b88856 100644 --- a/src/insert_completer.cc +++ b/src/insert_completer.cc @@ -95,11 +95,13 @@ InsertCompletion complete_word(const Buffer& buffer, ByteCoord cursor_pos) struct RankedMatchAndBuffer : RankedMatch { - RankedMatchAndBuffer(StringView w, int r = 0, const Buffer* b = nullptr) - : RankedMatch{w, r}, buffer{b} {} + RankedMatchAndBuffer(const RankedMatch& m, const Buffer* b = nullptr) + : RankedMatch{m}, buffer{b} {} - bool operator==(const RankedMatchAndBuffer& other) const { return word == other.word; } - bool operator<(const RankedMatchAndBuffer& other) const { return rank > other.rank; } + bool operator==(StringView other) const { return candidate() == other; } + + bool operator==(const RankedMatchAndBuffer& other) const { return RankedMatch::operator==(other); } + bool operator<(const RankedMatchAndBuffer& other) const { return RankedMatch::operator<(other);; } const Buffer* buffer; }; @@ -109,7 +111,7 @@ InsertCompletion complete_word(const Buffer& buffer, ByteCoord cursor_pos) auto& word_db = get_word_db(buf); auto bufmatches = word_db.find_matching(prefix); for (auto& m : bufmatches) - matches.push_back({ m.word, m.rank, &buf }); + matches.push_back({ m, &buf }); }; add_matches(buffer); @@ -127,23 +129,12 @@ InsertCompletion complete_word(const Buffer& buffer, ByteCoord cursor_pos) } } unordered_erase(matches, StringView{prefix}); - // Sort by word, favoring lowercase - std::sort(matches.begin(), matches.end(), - [](const RankedMatchAndBuffer& lhs, const RankedMatchAndBuffer& rhs) { - return std::lexicographical_compare( - lhs.word.begin(), lhs.word.end(), rhs.word.begin(), rhs.word.end(), - [](char a, char b) { - const bool low_a = islower(a), low_b = islower(b); - return low_a == low_b ? a < b : low_a; - }); - }); + std::sort(matches.begin(), matches.end()); matches.erase(std::unique(matches.begin(), matches.end()), matches.end()); - // Stable sort by rank to preserve by word sorting - std::stable_sort(matches.begin(), matches.end()); const auto longest = std::accumulate(matches.begin(), matches.end(), 0_char, [](const CharCount& lhs, const RankedMatchAndBuffer& rhs) - { return std::max(lhs, rhs.word.char_length()); }); + { return std::max(lhs, rhs.candidate().char_length()); }); InsertCompletion::CandidateList candidates; candidates.reserve(matches.size()); @@ -152,17 +143,15 @@ InsertCompletion complete_word(const Buffer& buffer, ByteCoord cursor_pos) DisplayLine menu_entry; if (m.buffer) { - const auto pad_len = longest + 1 - m.word.char_length(); - menu_entry.push_back(m.word.str()); + const auto pad_len = longest + 1 - m.candidate().char_length(); + menu_entry.push_back(m.candidate().str()); menu_entry.push_back(String{' ', pad_len}); menu_entry.push_back({ m.buffer->display_name(), get_face("MenuInfo") }); } else - menu_entry.push_back(m.word.str()); + menu_entry.push_back(m.candidate().str()); - menu_entry.push_back({ " " + to_string(m.rank), get_face("cyan") }); - - candidates.push_back({m.word.str(), "", std::move(menu_entry)}); + candidates.push_back({m.candidate().str(), "", std::move(menu_entry)}); } return { begin.coord(), cursor_pos, std::move(candidates), buffer.timestamp() }; diff --git a/src/ranked_match.cc b/src/ranked_match.cc index 0063b8ae..03cb2fcd 100644 --- a/src/ranked_match.cc +++ b/src/ranked_match.cc @@ -3,7 +3,7 @@ namespace Kakoune { -int match_rank(StringView candidate, StringView query) +static bool match_rank(StringView candidate, StringView query) { int rank = 0; auto it = candidate.begin(); @@ -37,4 +37,29 @@ int match_rank(StringView candidate, StringView query) return rank; } +RankedMatch::RankedMatch(StringView candidate, StringView query) +{ + if (candidate.empty() or query.empty()) + { + m_candidate = candidate; + return; + } + + m_match_rank = match_rank(candidate, query); +} + +bool RankedMatch::operator<(const RankedMatch& other) const +{ + if (m_match_rank == other.m_match_rank) + return std::lexicographical_compare( + m_candidate.begin(), m_candidate.end(), + other.m_candidate.begin(), other.m_candidate.end(), + [](char a, char b) { + const bool low_a = islower(a), low_b = islower(b); + return low_a == low_b ? a < b : low_a; + }); + + return m_match_rank < other.m_match_rank; +} + } diff --git a/src/ranked_match.hh b/src/ranked_match.hh index 2e248dc1..d6367453 100644 --- a/src/ranked_match.hh +++ b/src/ranked_match.hh @@ -9,12 +9,20 @@ namespace Kakoune struct RankedMatch { - StringView word; - int rank; -}; -using RankedMatchList = Vector; + RankedMatch(StringView candidate, StringView query); -int match_rank(StringView candidate, StringView query); + const StringView& candidate() const { return m_candidate; } + bool operator<(const RankedMatch& other) const; + bool operator==(const RankedMatch& other) const { return m_candidate == other.m_candidate; } + + explicit operator bool() const { return not m_candidate.empty(); } + +private: + StringView m_candidate; + int m_match_rank = 0; +}; + +using RankedMatchList = Vector; } diff --git a/src/word_db.cc b/src/word_db.cc index 80b59f0c..147287b5 100644 --- a/src/word_db.cc +++ b/src/word_db.cc @@ -160,7 +160,7 @@ RankedMatchList WordDB::find_matching(StringView query) { if (query.empty()) { - res.push_back({word.first, 1 }); + res.push_back(RankedMatch{word.first, query}); continue; } @@ -168,8 +168,9 @@ RankedMatchList WordDB::find_matching(StringView query) if (not matches(to_lower(letters), to_lower(word_letters)) or not matches(letters & upper_mask, word_letters & upper_mask)) continue; - if (int rank = match_rank(word.first, query)) - res.push_back({ word.first, rank }); + + if (RankedMatch match{word.first, query}) + res.push_back(match); } return res; @@ -178,14 +179,14 @@ RankedMatchList WordDB::find_matching(StringView query) UnitTest test_word_db{[]() { auto cmp_words = [](const RankedMatch& lhs, const RankedMatch& rhs) { - return lhs.word < rhs.word; + return lhs.candidate() < rhs.candidate(); }; auto eq = [](ArrayView lhs, const WordList& rhs) { return lhs.size() == rhs.size() and std::equal(lhs.begin(), lhs.end(), rhs.begin(), [](const RankedMatch& lhs, const StringView& rhs) { - return lhs.word == rhs; + return lhs.candidate() == rhs; }); };