Skip to content
This repository has been archived by the owner on Jul 30, 2020. It is now read-only.

More options for completion filtering #786

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@
[submodule "third_party/reproc"]
path = third_party/reproc
url = https://github.com/DaanDeMeyer/reproc.git
[submodule "third_party/lib_fts"]
path = third_party/lib_fts
url = https://github.com/forrestthewoods/lib_fts.git
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ target_include_directories(cquery PRIVATE
third_party/pugixml/src
third_party/rapidjson/include
third_party/sparsepp
third_party/lib_fts/code
)

### Install
Expand Down Expand Up @@ -304,6 +305,8 @@ target_sources(cquery PRIVATE
src/file_contents.cc
src/file_types.cc
src/fuzzy_match.cc
src/fts_match.cc
src/prefix_match.cc
src/iindexer.cc
src/import_manager.cc
src/import_pipeline.cc
Expand Down
10 changes: 10 additions & 0 deletions src/completion_matcher.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once

#include <string_view.h>

class CompletionMatcher {
public:
// virtual ~CompletionMatcher() = default; // don't know why but it crashed if I uncomment this like, investigating
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strange, but this should have a virtual dtor for correct behavior (also, the = default should probably go in a .cc file)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The = default part can behave differently if moved to the .cc file.
https://accu.org/index.php/journals/2379

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea whats happened but now it works fine. Probably some weird stuff on my side

virtual int Match(std::string_view text) = 0;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add some timing to see if the virtual function call is negatively impacting how long it takes to filter completion results?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll check it a bit later, no problem. I'm sure that virtual function call will not spend much more time than plain function call, especially comparing to all that fuzzy-matching stuff = )

virtual int MinScore() const = 0;
};
6 changes: 5 additions & 1 deletion src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ struct Config {
// For example, to hide all files in a /CACHE/ folder, use ".*/CACHE/.*"
std::vector<std::string> includeBlacklist;
std::vector<std::string> includeWhitelist;

//
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure to document all possible options here

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

std::string matcherType = "cqueryMatcher";
};
Completion completion;

Expand Down Expand Up @@ -261,7 +264,8 @@ MAKE_REFLECT_STRUCT(Config::Completion,
includeMaxPathSize,
includeSuffixWhitelist,
includeBlacklist,
includeWhitelist);
includeWhitelist,
matcherType);
MAKE_REFLECT_STRUCT(Config::Formatting, enabled)
MAKE_REFLECT_STRUCT(Config::Diagnostics,
blacklist,
Expand Down
23 changes: 23 additions & 0 deletions src/fts_match.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include "fts_match.h"

#define FTS_FUZZY_MATCH_IMPLEMENTATION 1
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
#include <fts_fuzzy_match.h>
#pragma clang diagnostic pop

FtsMatcher::FtsMatcher(std::string_view pattern) {
original_pattern = pattern;
}

int FtsMatcher::Match(std::string_view text) {
int result = 0;
if (fts::fuzzy_match(original_pattern.data(), text.data(), result)) {
return result;
}
return MinScore();
}

int FtsMatcher::MinScore() const {
return -100000;
}
13 changes: 13 additions & 0 deletions src/fts_match.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once

#include "completion_matcher.h"

class FtsMatcher : public CompletionMatcher {
public:
FtsMatcher(std::string_view pattern);
int Match(std::string_view text) override;
int MinScore() const override;

private:
std::string_view original_pattern;
};
9 changes: 9 additions & 0 deletions src/fuzzy_match.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <stdio.h>
#include <algorithm>
#include <vector>
#include "lex_utils.h"

enum CharClass { Other, Lower, Upper };
enum CharRole { None, Tail, Head };
Expand Down Expand Up @@ -73,6 +74,7 @@ int FuzzyMatcher::MatchScore(int i, int j, bool last) {
}

FuzzyMatcher::FuzzyMatcher(std::string_view pattern) {
original_pattern = pattern;
CalculateRoles(pattern, pat_role, &pat_set);
size_t n = 0;
for (size_t i = 0; i < pattern.size(); i++)
Expand All @@ -85,6 +87,9 @@ FuzzyMatcher::FuzzyMatcher(std::string_view pattern) {
}

int FuzzyMatcher::Match(std::string_view text) {
if (!CaseFoldingSubsequenceMatch(original_pattern, text).first) {
return MinScore();
}
int n = int(text.size());
if (n > kMaxText)
return kMinScore + 1;
Expand Down Expand Up @@ -123,6 +128,10 @@ int FuzzyMatcher::Match(std::string_view text) {
return ret;
}

int FuzzyMatcher::MinScore() const {
return kMinScore;
}

TEST_SUITE("fuzzy_match") {
bool Ranks(std::string_view pat, std::vector<const char*> texts) {
FuzzyMatcher fuzzy(pat);
Expand Down
7 changes: 5 additions & 2 deletions src/fuzzy_match.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

#include <limits.h>
#include <string>
#include "completion_matcher.h"

class FuzzyMatcher {
class FuzzyMatcher : public CompletionMatcher {
public:
constexpr static int kMaxPat = 100;
constexpr static int kMaxText = 200;
Expand All @@ -14,9 +15,11 @@ class FuzzyMatcher {
constexpr static int kMinScore = INT_MIN / 4;

FuzzyMatcher(std::string_view pattern);
int Match(std::string_view text);
int Match(std::string_view text) override;
int MinScore() const override;

private:
std::string_view original_pattern;
std::string pat;
std::string_view text;
int pat_set, text_set;
Expand Down
25 changes: 17 additions & 8 deletions src/messages/text_document_completion.cc
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
#include "clang_complete.h"
#include "code_complete_cache.h"
#include "fuzzy_match.h"
#include "fts_match.h"
#include "prefix_match.h"
#include "include_complete.h"
#include "message_handler.h"
#include "queue_manager.h"
#include "timer.h"
#include "working_files.h"
#include "config.h"

#include "lex_utils.h"

Expand Down Expand Up @@ -221,17 +224,23 @@ void FilterAndSortCompletionResponse(
item.filterText = item.label;
}

// Fuzzy match and remove awful candidates.
FuzzyMatcher fuzzy(complete_text);
// Match and remove awful candidates.
std::unique_ptr<CompletionMatcher> matcher;
if (g_config->completion.matcherType == "cqueryMatcher") {
matcher = std::make_unique<FuzzyMatcher>(complete_text);
} else if (g_config->completion.matcherType == "ftsMatcher") {
matcher = std::make_unique<FtsMatcher>(complete_text);
} else if (g_config->completion.matcherType == "caseSensitivePrefixMatcher") {
matcher = std::make_unique<PrefixMatcher>(complete_text, true);
} else if (g_config->completion.matcherType == "caseInsensitivePrefixMatcher") {
matcher = std::make_unique<PrefixMatcher>(complete_text, false);
}
for (auto& item : items) {
item.score_ =
CaseFoldingSubsequenceMatch(complete_text, *item.filterText).first
? fuzzy.Match(*item.filterText)
: FuzzyMatcher::kMinScore;
item.score_ = matcher->Match(*item.filterText);
}
items.erase(std::remove_if(items.begin(), items.end(),
[](const lsCompletionItem& item) {
return item.score_ <= FuzzyMatcher::kMinScore;
[&matcher](const lsCompletionItem& item) {
return item.score_ <= matcher->MinScore();
}),
items.end());
std::sort(items.begin(), items.end(),
Expand Down
39 changes: 39 additions & 0 deletions src/prefix_match.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include "prefix_match.h"

#include <algorithm>
#include <cctype>

namespace {
bool StartsWith(std::string_view text, std::string_view prefix) {
return text.find(prefix) == 0;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There may be a function in utils.h to do this

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. And also moved StartsWithIgnoreCase to utils

}

bool StartsWithIgnoreCase(std::string_view text, std::string_view prefix) {
auto min_length = std::min(text.size(), prefix.size());
if (min_length == 0) {
return false;
}
return std::equal(
text.begin(), text.begin() + min_length,
prefix.begin(), prefix.begin() + min_length,
[](char ch1, char ch2) { return std::toupper(ch1) == std::toupper(ch2); }
);
}
}

PrefixMatcher::PrefixMatcher(std::string_view pattern, bool case_sensitive) {
original_pattern = pattern;
this->case_sensitive = case_sensitive;
}

int PrefixMatcher::Match(std::string_view text) {
if (case_sensitive) {
return ::StartsWith(text, original_pattern) ? 1 : MinScore();
} else {
return ::StartsWithIgnoreCase(text, original_pattern) ? 1 : MinScore();
}
}

int PrefixMatcher::MinScore() const {
return -1;
}
14 changes: 14 additions & 0 deletions src/prefix_match.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include "completion_matcher.h"

class PrefixMatcher : public CompletionMatcher {
public:
PrefixMatcher(std::string_view pattern, bool case_sensitive);
int Match(std::string_view text) override;
int MinScore() const override;

private:
std::string_view original_pattern;
bool case_sensitive;
};
1 change: 1 addition & 0 deletions third_party/lib_fts
Submodule lib_fts added at 80f3f8