sammccall created this revision.

This allows us to limit the number of results we return and still allow them
to be surfaced by refining a query (https://reviews.llvm.org/D39852).

The initial algorithm is very conservative - it accepts a completion if the
filter is any case-insensitive sub-sequence. It does not attempt to rank items
based on match quality.


https://reviews.llvm.org/D39882

Files:
  clangd/ClangdUnit.cpp
  unittests/clangd/ClangdTests.cpp

Index: unittests/clangd/ClangdTests.cpp
===================================================================
--- unittests/clangd/ClangdTests.cpp
+++ unittests/clangd/ClangdTests.cpp
@@ -694,6 +694,61 @@
   }
 }
 
+TEST_F(ClangdCompletionTest, Filter) {
+  MockFSProvider FS;
+  MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
+  CDB.ExtraClangFlags.push_back("-xc++");
+  ErrorCheckingDiagConsumer DiagConsumer;
+  clangd::CodeCompleteOptions Opts;
+  ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
+                      Opts, EmptyLogger::getInstance());
+
+  auto FooCpp = getVirtualTestFilePath("foo.cpp");
+  FS.Files[FooCpp] = "";
+  FS.ExpectedFile = FooCpp;
+
+  const char *Body = R"cpp(
+    int Abracadabra;
+    int Alakazam;
+    struct S {
+      int FooBar;
+      int FooBaz;
+      int Qux;
+    };
+  )cpp";
+  auto Complete = [&](StringRef Query) {
+    StringWithPos Completion = parseTextMarker(
+        formatv("{0} int main() { {1}{{complete}} }", Body, Query).str(),
+        "complete");
+    Server.addDocument(FooCpp, Completion.Text);
+    return Server
+        .codeComplete(FooCpp, Completion.MarkerPos, StringRef(Completion.Text))
+        .get()
+        .Value;
+  };
+
+  auto Foba = Complete("S().Foba");
+  EXPECT_TRUE(ContainsItem(Foba, "FooBar"));
+  EXPECT_TRUE(ContainsItem(Foba, "FooBaz"));
+  EXPECT_FALSE(ContainsItem(Foba, "Qux"));
+
+  auto FR = Complete("S().FR");
+  EXPECT_TRUE(ContainsItem(FR, "FooBar"));
+  EXPECT_FALSE(ContainsItem(FR, "FooBaz"));
+  EXPECT_FALSE(ContainsItem(FR, "Qux"));
+
+  auto Op = Complete("S().opr");
+  EXPECT_TRUE(ContainsItem(Op, "operator="));
+
+  auto Aaa = Complete("aaa");
+  EXPECT_TRUE(ContainsItem(Aaa, "Abracadabra"));
+  EXPECT_TRUE(ContainsItem(Aaa, "Alakazam"));
+
+  auto UA = Complete("_a");
+  EXPECT_TRUE(ContainsItem(UA, "static_cast"));
+  EXPECT_FALSE(ContainsItem(UA, "Abracadabra"));
+}
+
 TEST_F(ClangdCompletionTest, CompletionOptions) {
   MockFSProvider FS;
   ErrorCheckingDiagConsumer DiagConsumer;
Index: clangd/ClangdUnit.cpp
===================================================================
--- clangd/ClangdUnit.cpp
+++ clangd/ClangdUnit.cpp
@@ -380,9 +380,12 @@
   void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
                                   CodeCompletionResult *Results,
                                   unsigned NumResults) override final {
+    StringRef Filter = S.getPreprocessor().getCodeCompletionFilter();
     Items.reserve(NumResults);
     for (unsigned I = 0; I < NumResults; ++I) {
       auto &Result = Results[I];
+      if (!Filter.empty() && !fuzzyMatch(S, Context, Filter, Result))
+        continue;
       const auto *CCS = Result.CreateCodeCompletionString(
           S, Context, *Allocator, CCTUInfo,
           CodeCompleteOpts.IncludeBriefComments);
@@ -397,6 +400,41 @@
   CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
 
 private:
+  bool fuzzyMatch(Sema &S, const CodeCompletionContext &CCCtx, StringRef Filter,
+                  CodeCompletionResult Result) {
+    switch (Result.Kind) {
+    case CodeCompletionResult::RK_Declaration:
+      if (auto *ID = Result.Declaration->getIdentifier())
+        return fuzzyMatch(Filter, ID->getName());
+      break;
+    case CodeCompletionResult::RK_Keyword:
+      return fuzzyMatch(Filter, Result.Keyword);
+    case CodeCompletionResult::RK_Macro:
+      return fuzzyMatch(Filter, Result.Macro->getName());
+    case CodeCompletionResult::RK_Pattern:
+      return fuzzyMatch(Filter, Result.Pattern->getTypedText());
+    }
+    auto *CCS = Result.CreateCodeCompletionString(
+        S, CCCtx, *Allocator, CCTUInfo, /*IncludeBriefComments=*/false);
+    return fuzzyMatch(Filter, CCS->getTypedText());
+  }
+
+  // Checks whether Target matches the Filter.
+  // Currently just requires a case-insensitive subsequence match.
+  // FIXME: make stricter and word-based: 'unique_ptr' should not match 'que'.
+  // FIXME: return a score to be incorporated into ranking.
+  static bool fuzzyMatch(StringRef Filter, StringRef Target) {
+    llvm::errs() << "match " << Target << " against " << Filter << "\n";
+    size_t TPos = 0;
+    for (char C : Filter) {
+      TPos = Target.find_lower(C, TPos);
+      if (TPos == StringRef::npos)
+        return false;
+    }
+    llvm::errs() << "yeah\n";
+    return true;
+  }
+
   CompletionItem
   ProcessCodeCompleteResult(const CodeCompletionResult &Result,
                             const CodeCompletionString &CCS) const {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to