ioeric created this revision.
ioeric added reviewers: hokein, sammccall.
Herald added subscribers: cfe-commits, ilya-biryukov, klimek.

Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D41345

Files:
  clangd/ClangdUnit.cpp
  clangd/ClangdUnit.h
  clangd/index/FileIndex.cpp
  clangd/index/Index.h
  clangd/index/SymbolCollector.cpp
  clangd/index/SymbolCollector.h
  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,12 +25,17 @@
 #include <memory>
 #include <string>
 
+using testing::AllOf;
 using testing::Eq;
 using testing::Field;
 using testing::UnorderedElementsAre;
 
 // GMock helpers for matching Symbol.
 MATCHER_P(QName, Name, "") { return arg.second.QualifiedName == Name; }
+MATCHER_P(Labeled, Label, "") { return arg.second.CompletionLabel == Label; }
+MATCHER_P(Detail, D, "") { return arg.second.CompletionDetail == D; }
+MATCHER_P(Doc, D, "") { return arg.second.Documentation == D; }
+MATCHER_P(Params, P, "") { return arg.second.Params == P; }
 
 namespace clang {
 namespace clangd {
@@ -107,6 +111,23 @@
                                             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."),
+                Params(std::vector<std::string>({"int x", "double y"})))));
+}
+
 TEST_F(SymbolCollectorTest, YAMLConversions) {
   const std::string YAML1 = R"(
 ---
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
@@ -14,6 +14,7 @@
 #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"
 
@@ -56,6 +57,79 @@
   }
   return AbsolutePath.str();
 }
+
+std::string getDocumentation(const CodeCompletionString &CCS) {
+  // Things like __attribute__((nonnull(1,3))) and [[noreturn]]. Present this
+  // information in the documentation field.
+  std::string Result;
+  const unsigned AnnotationCount = CCS.getAnnotationCount();
+  if (AnnotationCount > 0) {
+    Result += "Annotation";
+    if (AnnotationCount == 1) {
+      Result += ": ";
+    } else /* AnnotationCount > 1 */ {
+      Result += "s: ";
+    }
+    for (unsigned I = 0; I < AnnotationCount; ++I) {
+      Result += CCS.getAnnotation(I);
+      Result.push_back(I == AnnotationCount - 1 ? '\n' : ' ');
+    }
+  }
+  // Add brief documentation (if there is any).
+  if (CCS.getBriefComment() != nullptr) {
+    if (!Result.empty()) {
+      // This means we previously added annotations. Add an extra newline
+      // character to make the annotations stand out.
+      Result.push_back('\n');
+    }
+    Result += CCS.getBriefComment();
+  }
+  return Result;
+}
+
+void ProcessChunks(const CodeCompletionString &CCS, Symbol *Sym) {
+  for (const auto &Chunk : CCS) {
+    // Informative qualifier chunks only clutter completion results, skip
+    // them.
+    if (Chunk.Kind == CodeCompletionString::CK_Informative &&
+        StringRef(Chunk.Text).endswith("::"))
+      continue;
+
+    switch (Chunk.Kind) {
+    case CodeCompletionString::CK_TypedText:
+      // There's always exactly one CK_TypedText chunk.
+      Sym->CompletionLabel += Chunk.Text;
+      break;
+    case CodeCompletionString::CK_ResultType:
+      Sym->CompletionDetail = Chunk.Text;
+      break;
+    case CodeCompletionString::CK_Optional:
+      break;
+    case CodeCompletionString::CK_Placeholder:
+      // A string that acts as a placeholder for, e.g., a function call
+      // argument.
+      Sym->Params.push_back(Chunk.Text);
+      LLVM_FALLTHROUGH;
+    default:
+      Sym->CompletionLabel += Chunk.Text;
+      break;
+    }
+  }
+}
+
+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->Documentation = getDocumentation(*CCS);
+
+  ProcessChunks(*CCS, Sym);
+}
+
 } // namespace
 
 // Always return true to continue indexing.
@@ -86,8 +160,14 @@
     SymbolLocation Location = {
         makeAbsolutePath(SM, SM.getFilename(D->getLocation())),
         SM.getFileOffset(D->getLocStart()), SM.getFileOffset(D->getLocEnd())};
-    Symbols.insert({std::move(ID), ND->getQualifiedNameAsString(),
-                    index::getSymbolInfo(D), std::move(Location)});
+    Symbol Sym;
+    Sym.ID = std::move(ID);
+    Sym.QualifiedName = ND->getQualifiedNameAsString();
+    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
@@ -89,10 +89,20 @@
   //     (not a definition), which is usually declared in ".h" file.
   SymbolLocation CanonicalDeclaration;
 
+  // Documentation including comment for the symbol declaration.
+  std::string Documentation;
+  // A brief description of the symbol that can be displayed in the completion
+  // candidate list. For example, "a::b::Foo(X x, Y y) const" is a labal for
+  // a function.
+  std::string CompletionLabel;
+  // Detail about the symbol. For example, the result type of a function.
+  std::string CompletionDetail;
+  // The placeholder text for function parameters in order.
+  std::vector<std::string> Params;
+
   // 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/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
@@ -569,6 +569,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