From c36eda70f9e39c8ab8f0172550e16064f12034c3 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Sat, 20 Jan 2018 13:15:43 -0800 Subject: [PATCH] Rename SubstringMatch to SubsequenceMatch and use it to pre-filter completion items Fix #321 --- src/lex_utils.cc | 64 +++++++++--------------- src/lex_utils.h | 3 +- src/messages/text_document_completion.cc | 21 +++++--- src/messages/workspace_symbol.cc | 2 +- 4 files changed, 41 insertions(+), 49 deletions(-) diff --git a/src/lex_utils.cc b/src/lex_utils.cc index 59955d31..29e6292a 100644 --- a/src/lex_utils.cc +++ b/src/lex_utils.cc @@ -188,31 +188,17 @@ std::string LexWordAroundPos(lsPosition position, const std::string& content) { return content.substr(start, end - start + 1); } -bool SubstringMatch(const std::string& search, const std::string& content) { - if (search.empty()) - return true; - - size_t search_index = 0; - char search_char = tolower(search[search_index]); - - size_t content_index = 0; - - while (true) { - char content_char = tolower(content[content_index]); - - if (content_char == search_char) { - search_index += 1; - if (search_index >= search.size()) - return true; - search_char = tolower(search[search_index]); - } - - content_index += 1; - if (content_index >= content.size()) +bool SubsequenceMatch(const std::string& search, const std::string& content) { + size_t j = 0; + for (size_t i = 0; i < search.size(); i++) { + char search_char = tolower(search[i]); + while (j < content.size() && tolower(content[j]) != search_char) + j++; + if (j == content.size()) return false; + j++; } - - return false; + return true; } TEST_SUITE("Offset") { @@ -239,32 +225,32 @@ TEST_SUITE("Offset") { TEST_SUITE("Substring") { TEST_CASE("match") { // Sanity. - REQUIRE(SubstringMatch("a", "aa")); - REQUIRE(SubstringMatch("aa", "aa")); + REQUIRE(SubsequenceMatch("a", "aa")); + REQUIRE(SubsequenceMatch("aa", "aa")); // Empty string matches anything. - REQUIRE(SubstringMatch("", "")); - REQUIRE(SubstringMatch("", "aa")); + REQUIRE(SubsequenceMatch("", "")); + REQUIRE(SubsequenceMatch("", "aa")); // Match in start/middle/end. - REQUIRE(SubstringMatch("a", "abbbb")); - REQUIRE(SubstringMatch("a", "bbabb")); - REQUIRE(SubstringMatch("a", "bbbba")); - REQUIRE(SubstringMatch("aa", "aabbb")); - REQUIRE(SubstringMatch("aa", "bbaab")); - REQUIRE(SubstringMatch("aa", "bbbaa")); + REQUIRE(SubsequenceMatch("a", "abbbb")); + REQUIRE(SubsequenceMatch("a", "bbabb")); + REQUIRE(SubsequenceMatch("a", "bbbba")); + REQUIRE(SubsequenceMatch("aa", "aabbb")); + REQUIRE(SubsequenceMatch("aa", "bbaab")); + REQUIRE(SubsequenceMatch("aa", "bbbaa")); // Capitalization. - REQUIRE(SubstringMatch("aa", "aA")); - REQUIRE(SubstringMatch("aa", "Aa")); - REQUIRE(SubstringMatch("aa", "AA")); + REQUIRE(SubsequenceMatch("aa", "aA")); + REQUIRE(SubsequenceMatch("aa", "Aa")); + REQUIRE(SubsequenceMatch("aa", "AA")); // Token skipping. - REQUIRE(SubstringMatch("ad", "abcd")); - REQUIRE(SubstringMatch("ad", "ABCD")); + REQUIRE(SubsequenceMatch("ad", "abcd")); + REQUIRE(SubsequenceMatch("ad", "ABCD")); // Ordering. - REQUIRE(!SubstringMatch("ad", "dcba")); + REQUIRE(!SubsequenceMatch("ad", "dcba")); } } diff --git a/src/lex_utils.h b/src/lex_utils.h index 89bbff05..69355ba4 100644 --- a/src/lex_utils.h +++ b/src/lex_utils.h @@ -24,4 +24,5 @@ void LexFunctionDeclaration(const std::string& buffer_content, std::string LexWordAroundPos(lsPosition position, const std::string& content); -bool SubstringMatch(const std::string& search, const std::string& content); \ No newline at end of file +// Case-insensitive subsequence matching. +bool SubsequenceMatch(const std::string& search, const std::string& content); diff --git a/src/messages/text_document_completion.cc b/src/messages/text_document_completion.cc index e947af39..030959ec 100644 --- a/src/messages/text_document_completion.cc +++ b/src/messages/text_document_completion.cc @@ -128,6 +128,13 @@ void FilterAndSortCompletionResponse( return; } + items.erase(std::remove_if(items.begin(), items.end(), + [&](const lsCompletionItem& item) { + return !SubsequenceMatch(complete_text, + item.label); + }), + items.end()); + // Find the appearance of |complete_text| in all candidates. bool found = false; for (auto& item : items) { @@ -187,15 +194,13 @@ void FilterAndSortCompletionResponse( // Find fuzzy matches if we haven't found all of the literal matches. if (filtered_result.size() < kMaxResultSize) { for (const auto& item : items) { - if (SubstringMatch(complete_text, item.label)) { - // Don't insert the same completion entry. - if (!inserted.insert(item.InsertedContent()).second) - continue; + // Don't insert the same completion entry. + if (!inserted.insert(item.InsertedContent()).second) + continue; - filtered_result.push_back(item); - if (filtered_result.size() >= kMaxResultSize) - break; - } + filtered_result.push_back(item); + if (filtered_result.size() >= kMaxResultSize) + break; } } diff --git a/src/messages/workspace_symbol.cc b/src/messages/workspace_symbol.cc index d61b7d93..03587256 100644 --- a/src/messages/workspace_symbol.cc +++ b/src/messages/workspace_symbol.cc @@ -225,7 +225,7 @@ struct WorkspaceSymbolHandler : BaseMessageHandler { query_without_space += c; for (int i = 0; i < db->short_names.size(); ++i) { - if (SubstringMatch(query_without_space, db->short_names[i])) { + if (SubsequenceMatch(query_without_space, db->short_names[i])) { // Do not show the same entry twice. if (!inserted_results.insert(db->detailed_names[i]).second) continue;