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);
}
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"));
}
}

View File

@ -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);
// Case-insensitive subsequence matching.
bool SubsequenceMatch(const std::string& search, const std::string& content);

View File

@ -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,7 +194,6 @@ 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;
@ -197,7 +203,6 @@ void FilterAndSortCompletionResponse(
break;
}
}
}
items = filtered_result;
}

View File

@ -225,7 +225,7 @@ struct WorkspaceSymbolHandler : BaseMessageHandler<Ipc_WorkspaceSymbol> {
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;