From 4f2584a091f34cdb3eb0dda116709ef95fc34732 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Sun, 18 Oct 2015 16:55:21 +0100 Subject: [PATCH] Experiment with ranked word completion depending on word boundaries --- src/insert_completer.cc | 42 ++++++++++++------------- src/word_db.cc | 69 +++++++++++++++++++++++++++++++++++++++++ src/word_db.hh | 9 ++++++ 3 files changed, 99 insertions(+), 21 deletions(-) diff --git a/src/insert_completer.cc b/src/insert_completer.cc index 5b3b1224..8a9779a0 100644 --- a/src/insert_completer.cc +++ b/src/insert_completer.cc @@ -72,7 +72,7 @@ WordDB& get_word_db(const Buffer& buffer) return cache_val.as(); } -template +template InsertCompletion complete_word(const Buffer& buffer, ByteCoord cursor_pos) { auto pos = buffer.iterator_at(cursor_pos); @@ -93,23 +93,23 @@ InsertCompletion complete_word(const Buffer& buffer, ByteCoord cursor_pos) String current_word{begin, end}; - struct MatchAndBuffer { - MatchAndBuffer(StringView m, const Buffer* b = nullptr) : match(m), buffer(b) {} + struct RankedWordAndBuffer : WordDB::RankedWord + { + RankedWordAndBuffer(StringView w, int r = 0, const Buffer* b = nullptr) + : WordDB::RankedWord{w, r}, buffer{b} {} - bool operator==(const MatchAndBuffer& other) const { return match == other.match; } - bool operator<(const MatchAndBuffer& other) const { return match < other.match; } + bool operator==(const RankedWordAndBuffer& other) const { return word == other.word; } + bool operator<(const RankedWordAndBuffer& other) const { return rank > other.rank; } - StringView match; const Buffer* buffer; }; - Vector matches; + Vector matches; auto add_matches = [&](const Buffer& buf) { auto& word_db = get_word_db(buf); - auto bufmatches = word_db.find_matching( - prefix, subseq ? subsequence_match : prefix_match); + auto bufmatches = word_db.find_matching(prefix); for (auto& m : bufmatches) - matches.push_back({ m, &buf }); + matches.push_back({ m.word, m.rank, &buf }); }; add_matches(buffer); @@ -131,8 +131,8 @@ InsertCompletion complete_word(const Buffer& buffer, ByteCoord cursor_pos) matches.erase(std::unique(matches.begin(), matches.end()), matches.end()); const auto longest = std::accumulate(matches.begin(), matches.end(), 0_char, - [](const CharCount& lhs, const MatchAndBuffer& rhs) - { return std::max(lhs, rhs.match.char_length()); }); + [](const CharCount& lhs, const RankedWordAndBuffer& rhs) + { return std::max(lhs, rhs.word.char_length()); }); InsertCompletion::CandidateList candidates; candidates.reserve(matches.size()); @@ -141,15 +141,17 @@ InsertCompletion complete_word(const Buffer& buffer, ByteCoord cursor_pos) DisplayLine menu_entry; if (m.buffer) { - const auto pad_len = longest + 1 - m.match.char_length(); - menu_entry.push_back(m.match.str()); + const auto pad_len = longest + 1 - m.word.char_length(); + menu_entry.push_back(m.word.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.match.str()); + menu_entry.push_back(m.word.str()); - candidates.push_back({m.match.str(), "", std::move(menu_entry)}); + menu_entry.push_back({ " " + to_string(m.rank), get_face("cyan") }); + + candidates.push_back({m.word.str(), "", std::move(menu_entry)}); } return { begin.coord(), cursor_pos, std::move(candidates), buffer.timestamp() }; @@ -419,13 +421,11 @@ bool InsertCompleter::setup_ifn() return true; if (completer.mode == InsertCompleterDesc::Word and *completer.param == "buffer" and - (try_complete(complete_word) or - try_complete(complete_word))) + try_complete(complete_word)) return true; if (completer.mode == InsertCompleterDesc::Word and *completer.param == "all" and - (try_complete(complete_word) or - try_complete(complete_word))) + try_complete(complete_word)) return true; } return false; @@ -504,7 +504,7 @@ void InsertCompleter::explicit_file_complete() void InsertCompleter::explicit_word_complete() { - try_complete(complete_word); + try_complete(complete_word); } void InsertCompleter::explicit_line_complete() diff --git a/src/word_db.cc b/src/word_db.cc index f15ea380..addb9fab 100644 --- a/src/word_db.cc +++ b/src/word_db.cc @@ -27,6 +27,13 @@ UsedLetters used_letters(StringView str) return res; } +constexpr UsedLetters upper_mask = 0xFFFFFFC000000; + +UsedLetters to_lower(UsedLetters letters) +{ + return ((letters & upper_mask) >> 26) | (letters & (~upper_mask)); +} + static WordDB::WordList get_words(const SharedString& content) { WordDB::WordList res; @@ -136,6 +143,63 @@ int WordDB::get_word_occurences(StringView word) const return 0; } +WordDB::RankedWordList WordDB::find_matching(StringView query) +{ + auto match_rank = [](StringView candidate, StringView query) + { + int rank = 0; + auto it = candidate.begin(); + char prev = 0; + for (auto c : query) + { + if (it == candidate.end()) + return 0; + + const bool islow = islower(c); + auto eq_c = [islow, c](char ch) { return islow ? tolower(ch) == c : ch == c; }; + + if (eq_c(*it)) // improve rank on contiguous + ++rank; + + while (!eq_c(*it)) + { + prev = *it; + if (++it == candidate.end()) + return 0; + } + // Improve rank on word boundaries + if (prev == 0 or prev == '_' or + (islower(prev) and isupper(*it))) + rank += 5; + + prev = c; + ++rank; + ++it; + } + return rank; + }; + + auto matches = [](UsedLetters query, UsedLetters letters) + { + return (query & letters) == query; + }; + + update_db(); + const UsedLetters letters = used_letters(query); + RankedWordList res; + for (auto&& word : m_words) + { + UsedLetters word_letters = word.second.letters; + 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 }); + } + + return res; +} + UnitTest test_word_db{[]() { Buffer buffer("test", Buffer::Flags::None, @@ -160,4 +224,9 @@ UnitTest test_word_db{[]() kak_assert(res == WordDB::WordList{ "allo" COMMA "mutch" COMMA "retchou" COMMA "tchou" }); }}; +UnitTest test_used_letters{[]() +{ + kak_assert(used_letters("abcd") == to_lower(used_letters("abcdABCD"))); +}}; + } diff --git a/src/word_db.hh b/src/word_db.hh index ad0dbadc..4854e881 100644 --- a/src/word_db.hh +++ b/src/word_db.hh @@ -38,6 +38,15 @@ public: return res; } + struct RankedWord + { + StringView word; + int rank; + }; + using RankedWordList = Vector; + + RankedWordList find_matching(StringView str); + int get_word_occurences(StringView word) const; private: void update_db();