ioeric updated this revision to Diff 127730.
ioeric marked 4 inline comments as done.
ioeric added a comment.

- Merge with origin/master
- Fixed an error in merge
- Make documentation etc optional in symbols
- Merge remote-tracking branch 'origin/master' into symbol
- Merge branch 'index-completion' into symbol
- Address review comments; merge with https://reviews.llvm.org/D41450.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D41345

Files:
  clangd/ClangdUnit.cpp
  clangd/ClangdUnit.h
  clangd/CodeComplete.cpp
  clangd/index/FileIndex.cpp
  clangd/index/Index.h
  clangd/index/SymbolCollector.cpp
  clangd/index/SymbolCollector.h
  unittests/clangd/CodeCompleteTests.cpp
  unittests/clangd/SymbolCollectorTests.cpp

Index: unittests/clangd/SymbolCollectorTests.cpp
===================================================================
--- unittests/clangd/SymbolCollectorTests.cpp
+++ unittests/clangd/SymbolCollectorTests.cpp
@@ -9,7 +9,6 @@
 
 #include "index/SymbolCollector.h"
 #include "index/SymbolYAML.h"
-#include "clang/Index/IndexingAction.h"
 #include "clang/Basic/FileManager.h"
 #include "clang/Basic/FileSystemOptions.h"
 #include "clang/Basic/VirtualFileSystem.h"
@@ -26,11 +25,25 @@
 #include <memory>
 #include <string>
 
+using testing::AllOf;
 using testing::Eq;
 using testing::Field;
 using testing::UnorderedElementsAre;
 
 // GMock helpers for matching Symbol.
+MATCHER_P(Labeled, Label, "") { return arg.second.CompletionLabel == Label; }
+MATCHER_P(Detail, D, "") {
+  return arg.second.Detail && arg.second.Detail->CompletionDetail == D;
+}
+MATCHER_P(Doc, D, "") {
+  return arg.second.Detail && arg.second.Detail->Documentation == D;
+}
+MATCHER_P(Plain, Text, "") {
+  return arg.second.CompletionPlainInsertText == Text;
+}
+MATCHER_P(Snippet, S, "") {
+  return arg.second.CompletionSnippetInsertText == S;
+}
 MATCHER_P(QName, Name, "") {
   return (arg.second.Scope + (arg.second.Scope.empty() ? "" : "::") +
           arg.second.Name) == Name;
@@ -110,6 +123,38 @@
                                             QName("f1"), QName("f2")));
 }
 
+TEST_F(SymbolCollectorTest, SymbolWithDocumentation) {
+  const std::string Main = R"(
+    namespace nx {
+    /// Foo comment.
+    int ff(int x, double y) { return 0; }
+    }
+  )";
+  runSymbolCollector(/*Header=*/"", Main);
+  EXPECT_THAT(Symbols,
+              UnorderedElementsAre(QName("nx"),
+                                   AllOf(QName("nx::ff"),
+                                         Labeled("ff(int x, double y)"),
+                                         Detail("int"), Doc("Foo comment."))));
+}
+
+TEST_F(SymbolCollectorTest, PlainAndSnippet) {
+  const std::string Main = R"(
+    namespace nx {
+    void f() {}
+    int ff(int x, double y) { return 0; }
+    }
+  )";
+  runSymbolCollector(/*Header=*/"", Main);
+  EXPECT_THAT(
+      Symbols,
+      UnorderedElementsAre(
+          QName("nx"),
+          AllOf(QName("nx::f"), Labeled("f()"), Plain("f()"), Snippet("")),
+          AllOf(QName("nx::ff"), Labeled("ff(int x, double y)"), Plain("ff"),
+                Snippet("ff(${1:int x}, ${2:double y})"))));
+}
+
 TEST_F(SymbolCollectorTest, YAMLConversions) {
   const std::string YAML1 = R"(
 ---
Index: unittests/clangd/CodeCompleteTests.cpp
===================================================================
--- unittests/clangd/CodeCompleteTests.cpp
+++ unittests/clangd/CodeCompleteTests.cpp
@@ -86,6 +86,8 @@
 MATCHER_P(Labeled, Label, "") { return arg.label == Label; }
 MATCHER_P(Kind, K, "") { return arg.kind == K; }
 MATCHER_P(Filter, F, "") { return arg.filterText == F; }
+MATCHER_P(Doc, D, "") { return arg.documentation == D; }
+MATCHER_P(Detail, D, "") { return arg.detail == D; }
 MATCHER_P(PlainText, Text, "") {
   return arg.insertTextFormat == clangd::InsertTextFormat::PlainText &&
          arg.insertText == Text;
@@ -480,6 +482,7 @@
       Sym.Name = QName.substr(Pos + 2);
       Sym.Scope = QName.substr(0, Pos);
     }
+    Sym.CompletionPlainInsertText = Sym.Name;
     Sym.SymInfo.Kind = Pair.second;
     Snap->Slab.insert(std::move(Sym));
   }
@@ -530,7 +533,7 @@
       void f() { ns::x^ }
   )cpp",
                              Opts);
-  EXPECT_THAT(Results.items, Contains(AllOf(Named("XYZ"), Filter("x"))));
+  EXPECT_THAT(Results.items, Contains(Named("XYZ")));
   EXPECT_THAT(Results.items, Not(Has("foo")));
 }
 
@@ -568,13 +571,17 @@
 
   Server
       .addDocument(Context::empty(), getVirtualTestFilePath("foo.cpp"), R"cpp(
-      namespace ns { class XYZ {}; void foo() {} }
+      namespace ns { class XYZ {}; void foo(int x) {} }
   )cpp")
       .wait();
 
   auto File = getVirtualTestFilePath("bar.cpp");
   auto Test = parseTextMarker(R"cpp(
-      namespace ns { class XXX {}; void fooooo() {} }
+      namespace ns {
+      class XXX {};
+      /// Doooc
+      void fooooo() {}
+      }
       void f() { ns::^ }
   )cpp");
   Server.addDocument(Context::empty(), File, Test.Text).wait();
@@ -587,7 +594,9 @@
   EXPECT_THAT(Results.items, Has("XYZ", CompletionItemKind::Class));
   EXPECT_THAT(Results.items, Has("foo", CompletionItemKind::Function));
   EXPECT_THAT(Results.items, Has("XXX", CompletionItemKind::Class));
-  EXPECT_THAT(Results.items, Has("fooooo", CompletionItemKind::Function));
+  EXPECT_THAT(Results.items, Contains(AllOf(Named("fooooo()"), Filter("fooooo"),
+                                            Kind(CompletionItemKind::Function),
+                                            Doc("Doooc"), Detail("void"))));
 }
 
 } // namespace
Index: clangd/index/SymbolCollector.h
===================================================================
--- clangd/index/SymbolCollector.h
+++ clangd/index/SymbolCollector.h
@@ -23,6 +23,12 @@
 public:
   SymbolCollector() = default;
 
+  void initialize(ASTContext &Ctx) override { ASTCtx = &Ctx; }
+
+  void setPreprocessor(std::shared_ptr<Preprocessor> PP) override {
+    this->PP = std::move(PP);
+  }
+
   bool
   handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles,
                       ArrayRef<index::SymbolRelation> Relations, FileID FID,
@@ -36,6 +42,8 @@
 private:
   // All Symbols collected from the AST.
   SymbolSlab Symbols;
+  ASTContext *ASTCtx;
+  std::shared_ptr<Preprocessor> PP;
 };
 
 } // namespace clangd
Index: clangd/index/SymbolCollector.cpp
===================================================================
--- clangd/index/SymbolCollector.cpp
+++ clangd/index/SymbolCollector.cpp
@@ -8,12 +8,14 @@
 //===----------------------------------------------------------------------===//
 
 #include "SymbolCollector.h"
+#include "../CompletionString.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Index/IndexSymbol.h"
 #include "clang/Index/USRGeneration.h"
+#include "clang/Sema/CodeCompleteConsumer.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Path.h"
 
@@ -57,7 +59,30 @@
   return AbsolutePath.str();
 }
 
-// Split a qualified symbol name into scope and unqualified name, e.g. given
+void addSymbolCompletionInfo(ASTContext &Ctx, Preprocessor &PP,
+                             const NamedDecl *ND, Symbol *Sym) {
+  CodeCompletionResult SymbolCompletion(ND, 0);
+  auto Allocator = std::make_shared<GlobalCodeCompletionAllocator>();
+  CodeCompletionTUInfo TUInfo(Allocator);
+  const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
+      Ctx, PP, CodeCompletionContext::CCC_Name, *Allocator, TUInfo,
+      /*IncludeBriefComments*/ true);
+  Sym->CompletionFilterText = getFilterText(*CCS);
+  bool IsSnippet = false;
+  auto LabelAndSnippetText = getLabelAndInsertText(*CCS, &IsSnippet);
+  Sym->CompletionLabel = LabelAndSnippetText.first;
+  if (IsSnippet) {
+    Sym->CompletionSnippetInsertText = LabelAndSnippetText.second;
+    Sym->CompletionPlainInsertText = getLabelAndInsertText(*CCS).second;
+  } else {
+    Sym->CompletionPlainInsertText = LabelAndSnippetText.second;
+  }
+
+  Sym->Detail.emplace();
+  Sym->Detail->Documentation = getDocumentation(*CCS);
+  Sym->Detail->CompletionDetail = getDetail(*CCS);
+}
+
 // "a::b::c", return {"a::b", "c"}. Scope is empty if it doesn't exist.
 std::pair<llvm::StringRef, llvm::StringRef>
 splitQualifiedName(llvm::StringRef QName) {
@@ -98,10 +123,19 @@
     SymbolLocation Location = {
         makeAbsolutePath(SM, SM.getFilename(D->getLocation())),
         SM.getFileOffset(D->getLocStart()), SM.getFileOffset(D->getLocEnd())};
+    Symbol Sym;
+    Sym.ID = std::move(ID);
+
     std::string QName = ND->getQualifiedNameAsString();
     auto ScopeAndName = splitQualifiedName(QName);
-    Symbols.insert({std::move(ID), ScopeAndName.second, ScopeAndName.first,
-                    index::getSymbolInfo(D), std::move(Location)});
+    Sym.Name = ScopeAndName.second;
+    Sym.Scope = ScopeAndName.first;
+
+    Sym.SymInfo = index::getSymbolInfo(D);
+    Sym.CanonicalDeclaration = std::move(Location);
+    assert(ASTCtx && PP.get() && "ASTContext and Preprocessor must be set.");
+    addSymbolCompletionInfo(*ASTCtx, *PP, ND, &Sym);
+    Symbols.insert(std::move(Sym));
   }
 
   return true;
Index: clangd/index/Index.h
===================================================================
--- clangd/index/Index.h
+++ clangd/index/Index.h
@@ -14,6 +14,7 @@
 #include "clang/Index/IndexSymbol.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/StringExtras.h"
 #include <array>
 #include <string>
@@ -92,10 +93,37 @@
   //     (not a definition), which is usually declared in ".h" file.
   SymbolLocation CanonicalDeclaration;
 
+  /// A brief description of the symbol that can be displayed in the completion
+  /// candidate list. For example, "Foo(X x, Y y) const" is a labal for a
+  /// function.
+  std::string CompletionLabel;
+  /// The piece of text that the user is expected to type to match the
+  /// code-completion string, typically a keyword or the name of a declarator or
+  /// macro.
+  std::string CompletionFilterText;
+  /// What to insert when completing this symbol (plain text version).
+  std::string CompletionPlainInsertText;
+  /// What to insert when completing this symbol (snippet version).
+  std::string CompletionSnippetInsertText;
+
+  /// Optional symbol details that are not required to be set. For example, an
+  /// index fuzzy match can return a large number of symbol candidates, and it
+  /// is preferable to send only core symbol information in the batched results
+  /// and have clients resolve full symbol information for a specific candidate
+  /// if needed.
+  struct Details {
+    // Documentation including comment for the symbol declaration.
+    std::string Documentation;
+    // This is what goes into the LSP detail field in a completion item. For
+    // example, the result type of a function.
+    std::string CompletionDetail;
+  };
+
+  llvm::Optional<Details> Detail;
+
   // FIXME: add definition location of the symbol.
   // FIXME: add all occurrences support.
   // FIXME: add extra fields for index scoring signals.
-  // FIXME: add code completion information.
 };
 
 // A symbol container that stores a set of symbols. The container will maintain
Index: clangd/index/FileIndex.cpp
===================================================================
--- clangd/index/FileIndex.cpp
+++ clangd/index/FileIndex.cpp
@@ -17,8 +17,10 @@
 
 /// Retrieves namespace and class level symbols in \p Decls.
 std::unique_ptr<SymbolSlab> indexAST(ASTContext &Ctx,
+                                     std::shared_ptr<Preprocessor> PP,
                                      llvm::ArrayRef<const Decl *> Decls) {
   auto Collector = std::make_shared<SymbolCollector>();
+  Collector->setPreprocessor(std::move(PP));
   index::IndexingOptions IndexOpts;
   IndexOpts.SystemSymbolFilter =
       index::IndexingOptions::SystemSymbolFilterKind::All;
@@ -67,7 +69,8 @@
   if (!AST) {
     FSymbols.update(Path, nullptr);
   } else {
-    auto Slab = indexAST(AST->getASTContext(), AST->getTopLevelDecls());
+    auto Slab = indexAST(AST->getASTContext(), AST->getPreprocessorPtr(),
+                         AST->getTopLevelDecls());
     FSymbols.update(Path, std::move(Slab));
   }
   auto Symbols = FSymbols.allSymbols();
Index: clangd/CodeComplete.cpp
===================================================================
--- clangd/CodeComplete.cpp
+++ clangd/CodeComplete.cpp
@@ -559,16 +559,21 @@
   Item.kind = toCompletionItemKind(Sym.SymInfo.Kind);
   Item.label = Sym.Name;
   // FIXME(ioeric): support inserting/replacing scope qualifiers.
-  Item.insertText = Sym.Name;
+
   // FIXME(ioeric): support snippets.
+  Item.insertText = Sym.CompletionPlainInsertText;
   Item.insertTextFormat = InsertTextFormat::PlainText;
-  Item.filterText = Filter;
+  Item.filterText = Sym.CompletionFilterText;
 
   // FIXME(ioeric): sort symbols appropriately.
   Item.sortText = "";
 
   // FIXME(ioeric): use more symbol information (e.g. documentation, label) to
   // populate the completion item.
+  if (Sym.Detail) {
+    Item.documentation = Sym.Detail->Documentation;
+    Item.detail = Sym.Detail->CompletionDetail;
+  }
 
   return Item;
 }
Index: clangd/ClangdUnit.h
===================================================================
--- clangd/ClangdUnit.h
+++ clangd/ClangdUnit.h
@@ -79,6 +79,7 @@
   const ASTContext &getASTContext() const;
 
   Preprocessor &getPreprocessor();
+  std::shared_ptr<Preprocessor> getPreprocessorPtr();
   const Preprocessor &getPreprocessor() const;
 
   /// This function returns all top-level decls, including those that come
Index: clangd/ClangdUnit.cpp
===================================================================
--- clangd/ClangdUnit.cpp
+++ clangd/ClangdUnit.cpp
@@ -316,6 +316,10 @@
 
 Preprocessor &ParsedAST::getPreprocessor() { return Clang->getPreprocessor(); }
 
+std::shared_ptr<Preprocessor> ParsedAST::getPreprocessorPtr() {
+  return Clang->getPreprocessorPtr();
+}
+
 const Preprocessor &ParsedAST::getPreprocessor() const {
   return Clang->getPreprocessor();
 }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to