[clangd] Add more symbol information for code completion.
Reviewers: hokein, sammccall
Reviewed By: sammccall
Subscribers: klimek, ilya-biryukov, cfe-commits
Differential Revision: https://reviews.llvm.org/D41345
llvm-svn: 322097
diff --git a/clang-tools-extra/clangd/ClangdUnit.cpp b/clang-tools-extra/clangd/ClangdUnit.cpp
index 82ca109..202f968 100644
--- a/clang-tools-extra/clangd/ClangdUnit.cpp
+++ b/clang-tools-extra/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();
}
diff --git a/clang-tools-extra/clangd/ClangdUnit.h b/clang-tools-extra/clangd/ClangdUnit.h
index 9ec115d..c4cfe9f 100644
--- a/clang-tools-extra/clangd/ClangdUnit.h
+++ b/clang-tools-extra/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
diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp
index 93588b4..ee70902 100644
--- a/clang-tools-extra/clangd/CodeComplete.cpp
+++ b/clang-tools-extra/clangd/CodeComplete.cpp
@@ -556,16 +556,19 @@
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 = Sym.Name;
// 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;
}
diff --git a/clang-tools-extra/clangd/index/FileIndex.cpp b/clang-tools-extra/clangd/index/FileIndex.cpp
index cc8487a..150a013 100644
--- a/clang-tools-extra/clangd/index/FileIndex.cpp
+++ b/clang-tools-extra/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();
diff --git a/clang-tools-extra/clangd/index/Index.cpp b/clang-tools-extra/clangd/index/Index.cpp
index 6d26dc8..7f59864 100644
--- a/clang-tools-extra/clangd/index/Index.cpp
+++ b/clang-tools-extra/clangd/index/Index.cpp
@@ -55,6 +55,23 @@
Intern(S.Name);
Intern(S.Scope);
Intern(S.CanonicalDeclaration.FilePath);
+
+ Intern(S.CompletionLabel);
+ Intern(S.CompletionFilterText);
+ Intern(S.CompletionPlainInsertText);
+ Intern(S.CompletionSnippetInsertText);
+
+ if (S.Detail) {
+ // Copy values of StringRefs into arena.
+ auto *Detail = Arena.Allocate<Symbol::Details>();
+ Detail->Documentation = S.Detail->Documentation;
+ Detail->CompletionDetail = S.Detail->CompletionDetail;
+ S.Detail = Detail;
+
+ // Intern the actual strings.
+ Intern(S.Detail->Documentation);
+ Intern(S.Detail->CompletionDetail);
+ }
}
void SymbolSlab::Builder::insert(const Symbol &S) {
diff --git a/clang-tools-extra/clangd/index/Index.h b/clang-tools-extra/clangd/index/Index.h
index 5d4dff4..df945c2 100644
--- a/clang-tools-extra/clangd/index/Index.h
+++ b/clang-tools-extra/clangd/index/Index.h
@@ -15,6 +15,7 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringExtras.h"
#include <array>
#include <string>
@@ -126,10 +127,39 @@
// (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.
+ llvm::StringRef 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.
+ llvm::StringRef CompletionFilterText;
+ /// What to insert when completing this symbol (plain text version).
+ llvm::StringRef CompletionPlainInsertText;
+ /// What to insert when completing this symbol (snippet version). This is
+ /// empty if it is the same as the plain insert text above.
+ llvm::StringRef 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.
+ llvm::StringRef Documentation;
+ // This is what goes into the LSP detail field in a completion item. For
+ // example, the result type of a function.
+ llvm::StringRef CompletionDetail;
+ };
+
+ // Optional details of the symbol.
+ Details *Detail = nullptr;
+
// 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.
};
// An immutable symbol container that stores a set of symbols.
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index 291ee59..3b9046e 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -8,8 +8,7 @@
//===----------------------------------------------------------------------===//
#include "SymbolCollector.h"
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/Decl.h"
+#include "../CodeCompletionStrings.h"
#include "clang/AST/DeclCXX.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Index/IndexSymbol.h"
@@ -56,7 +55,6 @@
return AbsolutePath.str();
}
-// Split a qualified symbol name into scope and unqualified name, e.g. given
// "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) {
@@ -69,6 +67,13 @@
} // namespace
+void SymbolCollector::initialize(ASTContext &Ctx) {
+ ASTCtx = &Ctx;
+ CompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>();
+ CompletionTUInfo =
+ llvm::make_unique<CodeCompletionTUInfo>(CompletionAllocator);
+}
+
// Always return true to continue indexing.
bool SymbolCollector::handleDeclOccurence(
const Decl *D, index::SymbolRoleSet Roles,
@@ -79,6 +84,8 @@
Roles & static_cast<unsigned>(index::SymbolRole::Definition)))
return true;
+ assert(CompletionAllocator && CompletionTUInfo);
+
if (const NamedDecl *ND = llvm::dyn_cast<NamedDecl>(D)) {
// FIXME: figure out a way to handle internal linkage symbols (e.g. static
// variables, function) defined in the .cc files. Also we skip the symbols
@@ -113,6 +120,35 @@
S.Name = ScopeAndName.second;
S.SymInfo = index::getSymbolInfo(D);
S.CanonicalDeclaration = Location;
+
+ // Add completion info.
+ assert(ASTCtx && PP.get() && "ASTContext and Preprocessor must be set.");
+ CodeCompletionResult SymbolCompletion(ND, 0);
+ const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
+ *ASTCtx, *PP, CodeCompletionContext::CCC_Name, *CompletionAllocator,
+ *CompletionTUInfo,
+ /*IncludeBriefComments*/ true);
+ std::string Label;
+ std::string SnippetInsertText;
+ std::string IgnoredLabel;
+ std::string PlainInsertText;
+ getLabelAndInsertText(*CCS, &Label, &SnippetInsertText,
+ /*EnableSnippets=*/true);
+ getLabelAndInsertText(*CCS, &IgnoredLabel, &PlainInsertText,
+ /*EnableSnippets=*/false);
+ std::string FilterText = getFilterText(*CCS);
+ std::string Documentation = getDocumentation(*CCS);
+ std::string CompletionDetail = getDetail(*CCS);
+
+ S.CompletionFilterText = FilterText;
+ S.CompletionLabel = Label;
+ S.CompletionPlainInsertText = PlainInsertText;
+ S.CompletionSnippetInsertText = SnippetInsertText;
+ Symbol::Details Detail;
+ Detail.Documentation = Documentation;
+ Detail.CompletionDetail = CompletionDetail;
+ S.Detail = &Detail;
+
Symbols.insert(S);
}
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.h b/clang-tools-extra/clangd/index/SymbolCollector.h
index 55840b5..b954bac 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.h
+++ b/clang-tools-extra/clangd/index/SymbolCollector.h
@@ -8,8 +8,11 @@
//===----------------------------------------------------------------------===//
#include "Index.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
#include "clang/Index/IndexDataConsumer.h"
#include "clang/Index/IndexSymbol.h"
+#include "clang/Sema/CodeCompleteConsumer.h"
namespace clang {
namespace clangd {
@@ -21,7 +24,11 @@
// changed.
class SymbolCollector : public index::IndexDataConsumer {
public:
- SymbolCollector() = default;
+ void initialize(ASTContext &Ctx) override;
+
+ void setPreprocessor(std::shared_ptr<Preprocessor> PP) override {
+ this->PP = std::move(PP);
+ }
bool
handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles,
@@ -34,6 +41,10 @@
private:
// All Symbols collected from the AST.
SymbolSlab::Builder Symbols;
+ ASTContext *ASTCtx;
+ std::shared_ptr<Preprocessor> PP;
+ std::shared_ptr<GlobalCodeCompletionAllocator> CompletionAllocator;
+ std::unique_ptr<CodeCompletionTUInfo> CompletionTUInfo;
};
} // namespace clangd
diff --git a/clang-tools-extra/clangd/index/SymbolYAML.cpp b/clang-tools-extra/clangd/index/SymbolYAML.cpp
index ab922da..295b220 100644
--- a/clang-tools-extra/clangd/index/SymbolYAML.cpp
+++ b/clang-tools-extra/clangd/index/SymbolYAML.cpp
@@ -9,6 +9,7 @@
#include "SymbolYAML.h"
#include "Index.h"
+#include "llvm/ADT/Optional.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/YAMLTraits.h"
@@ -59,15 +60,39 @@
}
};
-template<> struct MappingTraits<Symbol> {
+template <>
+struct MappingTraits<Symbol::Details *> {
+ static void mapping(IO &io, Symbol::Details *&Detail) {
+ if (!io.outputting()) {
+ assert(io.getContext() && "Expecting an arena (as context) to allocate "
+ "data for new symbols.");
+ Detail = static_cast<llvm::BumpPtrAllocator *>(io.getContext())
+ ->Allocate<Symbol::Details>();
+ } else if (!Detail) {
+ // Detail is optional in outputting.
+ return;
+ }
+ assert(Detail);
+ io.mapOptional("Documentation", Detail->Documentation);
+ io.mapOptional("CompletionDetail", Detail->CompletionDetail);
+ }
+};
+
+template <> struct MappingTraits<Symbol> {
static void mapping(IO &IO, Symbol &Sym) {
- MappingNormalization<NormalizedSymbolID, SymbolID> NSymbolID(
- IO, Sym.ID);
+ MappingNormalization<NormalizedSymbolID, SymbolID> NSymbolID(IO, Sym.ID);
IO.mapRequired("ID", NSymbolID->HexString);
IO.mapRequired("Name", Sym.Name);
IO.mapRequired("Scope", Sym.Scope);
IO.mapRequired("SymInfo", Sym.SymInfo);
IO.mapRequired("CanonicalDeclaration", Sym.CanonicalDeclaration);
+ IO.mapRequired("CompletionLabel", Sym.CompletionLabel);
+ IO.mapRequired("CompletionFilterText", Sym.CompletionFilterText);
+ IO.mapRequired("CompletionPlainInsertText", Sym.CompletionPlainInsertText);
+
+ IO.mapOptional("CompletionSnippetInsertText",
+ Sym.CompletionSnippetInsertText);
+ IO.mapOptional("Detail", Sym.Detail);
}
};
@@ -124,11 +149,14 @@
namespace clangd {
SymbolSlab SymbolFromYAML(llvm::StringRef YAMLContent) {
+ // Store data of pointer fields (excl. `StringRef`) like `Detail`.
+ llvm::BumpPtrAllocator Arena;
+ llvm::yaml::Input Yin(YAMLContent, &Arena);
std::vector<Symbol> S;
- llvm::yaml::Input Yin(YAMLContent);
Yin >> S;
+
SymbolSlab::Builder Syms;
- for (auto& Sym : S)
+ for (auto &Sym : S)
Syms.insert(Sym);
return std::move(Syms).build();
}
@@ -138,7 +166,7 @@
llvm::raw_string_ostream OS(Str);
llvm::yaml::Output Yout(OS);
for (Symbol S : Symbols) // copy: Yout<< requires mutability.
- Yout<< S;
+ Yout << S;
return OS.str();
}