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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits