Rename SubstringMatch to SubsequenceMatch and use it to pre-filter completion items

Fix #321
This commit is contained in:
Fangrui Song 2018-01-20 13:15:43 -08:00
parent 2e0f14bef8
commit c36eda70f9
4 changed files with 41 additions and 49 deletions

View File

@ -188,31 +188,17 @@ std::string LexWordAroundPos(lsPosition position, const std::string& content) {
return content.substr(start, end - start + 1); return content.substr(start, end - start + 1);
} }
bool SubstringMatch(const std::string& search, const std::string& content) { bool SubsequenceMatch(const std::string& search, const std::string& content) {
if (search.empty()) size_t j = 0;
return true; for (size_t i = 0; i < search.size(); i++) {
char search_char = tolower(search[i]);
size_t search_index = 0; while (j < content.size() && tolower(content[j]) != search_char)
char search_char = tolower(search[search_index]); j++;
if (j == content.size())
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())
return false; return false;
j++;
} }
return true;
return false;
} }
TEST_SUITE("Offset") { TEST_SUITE("Offset") {
@ -239,32 +225,32 @@ TEST_SUITE("Offset") {
TEST_SUITE("Substring") { TEST_SUITE("Substring") {
TEST_CASE("match") { TEST_CASE("match") {
// Sanity. // Sanity.
REQUIRE(SubstringMatch("a", "aa")); REQUIRE(SubsequenceMatch("a", "aa"));
REQUIRE(SubstringMatch("aa", "aa")); REQUIRE(SubsequenceMatch("aa", "aa"));
// Empty string matches anything. // Empty string matches anything.
REQUIRE(SubstringMatch("", "")); REQUIRE(SubsequenceMatch("", ""));
REQUIRE(SubstringMatch("", "aa")); REQUIRE(SubsequenceMatch("", "aa"));
// Match in start/middle/end. // Match in start/middle/end.
REQUIRE(SubstringMatch("a", "abbbb")); REQUIRE(SubsequenceMatch("a", "abbbb"));
REQUIRE(SubstringMatch("a", "bbabb")); REQUIRE(SubsequenceMatch("a", "bbabb"));
REQUIRE(SubstringMatch("a", "bbbba")); REQUIRE(SubsequenceMatch("a", "bbbba"));
REQUIRE(SubstringMatch("aa", "aabbb")); REQUIRE(SubsequenceMatch("aa", "aabbb"));
REQUIRE(SubstringMatch("aa", "bbaab")); REQUIRE(SubsequenceMatch("aa", "bbaab"));
REQUIRE(SubstringMatch("aa", "bbbaa")); REQUIRE(SubsequenceMatch("aa", "bbbaa"));
// Capitalization. // Capitalization.
REQUIRE(SubstringMatch("aa", "aA")); REQUIRE(SubsequenceMatch("aa", "aA"));
REQUIRE(SubstringMatch("aa", "Aa")); REQUIRE(SubsequenceMatch("aa", "Aa"));
REQUIRE(SubstringMatch("aa", "AA")); REQUIRE(SubsequenceMatch("aa", "AA"));
// Token skipping. // Token skipping.
REQUIRE(SubstringMatch("ad", "abcd")); REQUIRE(SubsequenceMatch("ad", "abcd"));
REQUIRE(SubstringMatch("ad", "ABCD")); REQUIRE(SubsequenceMatch("ad", "ABCD"));
// Ordering. // Ordering.
REQUIRE(!SubstringMatch("ad", "dcba")); REQUIRE(!SubsequenceMatch("ad", "dcba"));
} }
} }

View File

@ -24,4 +24,5 @@ void LexFunctionDeclaration(const std::string& buffer_content,
std::string LexWordAroundPos(lsPosition position, const std::string& content); std::string LexWordAroundPos(lsPosition position, const std::string& content);
bool SubstringMatch(const std::string& search, const std::string& content); // Case-insensitive subsequence matching.
bool SubsequenceMatch(const std::string& search, const std::string& content);

View File

@ -128,6 +128,13 @@ void FilterAndSortCompletionResponse(
return; 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. // Find the appearance of |complete_text| in all candidates.
bool found = false; bool found = false;
for (auto& item : items) { for (auto& item : items) {
@ -187,15 +194,13 @@ void FilterAndSortCompletionResponse(
// Find fuzzy matches if we haven't found all of the literal matches. // Find fuzzy matches if we haven't found all of the literal matches.
if (filtered_result.size() < kMaxResultSize) { if (filtered_result.size() < kMaxResultSize) {
for (const auto& item : items) { for (const auto& item : items) {
if (SubstringMatch(complete_text, item.label)) { // Don't insert the same completion entry.
// Don't insert the same completion entry. if (!inserted.insert(item.InsertedContent()).second)
if (!inserted.insert(item.InsertedContent()).second) continue;
continue;
filtered_result.push_back(item); filtered_result.push_back(item);
if (filtered_result.size() >= kMaxResultSize) if (filtered_result.size() >= kMaxResultSize)
break; break;
}
} }
} }

View File

@ -225,7 +225,7 @@ struct WorkspaceSymbolHandler : BaseMessageHandler<Ipc_WorkspaceSymbol> {
query_without_space += c; query_without_space += c;
for (int i = 0; i < db->short_names.size(); ++i) { 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. // Do not show the same entry twice.
if (!inserted_results.insert(db->detailed_names[i]).second) if (!inserted_results.insert(db->detailed_names[i]).second)
continue; continue;