ioeric updated this revision to Diff 125962.
ioeric added a comment.
Diff on https://reviews.llvm.org/D40897 instead origin/master!
Repository:
rCTE Clang Tools Extra
https://reviews.llvm.org/D40548
Files:
clangd/CMakeLists.txt
clangd/ClangdIndex.cpp
clangd/ClangdIndex.h
clangd/ClangdLSPServer.cpp
clangd/ClangdLSPServer.h
clangd/ClangdServer.cpp
clangd/ClangdServer.h
clangd/ClangdUnit.cpp
clangd/ClangdUnit.h
clangd/ClangdUnitStore.cpp
clangd/ClangdUnitStore.h
clangd/CodeComplete.cpp
clangd/CodeComplete.h
clangd/Symbol.cpp
clangd/Symbol.h
clangd/SymbolCompletionInfo.cpp
clangd/SymbolCompletionInfo.h
clangd/SymbolIndex.h
clangd/tool/ClangdMain.cpp
unittests/clangd/SymbolCollectorTests.cpp
Index: unittests/clangd/SymbolCollectorTests.cpp
===================================================================
--- unittests/clangd/SymbolCollectorTests.cpp
+++ unittests/clangd/SymbolCollectorTests.cpp
@@ -36,15 +36,15 @@
public:
SymbolIndexActionFactory() = default;
- clang::ASTFrontendAction *create() override {
+ clang::FrontendAction *create() override {
index::IndexingOptions IndexOpts;
IndexOpts.SystemSymbolFilter =
index::IndexingOptions::SystemSymbolFilterKind::All;
IndexOpts.IndexFunctionLocals = false;
Collector = std::make_shared<SymbolCollector>();
FrontendAction *Action =
index::createIndexingAction(Collector, IndexOpts, nullptr).release();
- return llvm::cast<ASTFrontendAction>(Action);
+ return Action;
}
std::shared_ptr<SymbolCollector> Collector;
Index: clangd/tool/ClangdMain.cpp
===================================================================
--- clangd/tool/ClangdMain.cpp
+++ clangd/tool/ClangdMain.cpp
@@ -90,6 +90,15 @@
"Trace internal events and timestamps in chrome://tracing JSON format"),
llvm::cl::init(""), llvm::cl::Hidden);
+static llvm::cl::opt<bool> EnableIndexBasedCompletion(
+ "enable-index-based-completion",
+ llvm::cl::desc(
+ "Enable index-based global code completion (experimental). Clangd will "
+ "use symbols built from ASTs of opened files and additional indexes "
+ "(e.g. offline built codebase-wide symbol table) to complete partial "
+ "symbols."),
+ llvm::cl::init(false));
+
int main(int argc, char *argv[]) {
llvm::cl::ParseCommandLineOptions(argc, argv, "clangd");
@@ -170,10 +179,15 @@
clangd::CodeCompleteOptions CCOpts;
CCOpts.EnableSnippets = EnableSnippets;
CCOpts.IncludeIneligibleResults = IncludeIneligibleResults;
+ if (EnableIndexBasedCompletion) {
+ // Disable sema code completion for qualified code completion and use global
+ // symbol index instead.
+ CCOpts.IncludeNamespaceLevelDecls = false;
+ }
// Initialize and run ClangdLSPServer.
ClangdLSPServer LSPServer(Out, WorkerThreadsCount, StorePreamblesInMemory,
- CCOpts, ResourceDirRef,
- CompileCommandsDirPath);
+ CCOpts, ResourceDirRef, CompileCommandsDirPath,
+ EnableIndexBasedCompletion, {});
constexpr int NoShutdownRequestErrorCode = 1;
llvm::set_thread_name("clangd.main");
return LSPServer.run(std::cin) ? 0 : NoShutdownRequestErrorCode;
Index: clangd/SymbolIndex.h
===================================================================
--- /dev/null
+++ clangd/SymbolIndex.h
@@ -0,0 +1,60 @@
+//===--- CompletionIndex.h - Index for code completion -----------*- C++-*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOLINDEX_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOLINDEX_H
+
+#include "SymbolCompletionInfo.h"
+#include "llvm/Support/Error.h"
+
+namespace clang {
+namespace clangd {
+
+struct CompletionRequest {
+ std::string Query;
+ size_t MaxCandidateCount = UINT_MAX;
+};
+
+/// \brief Signals for scoring completion candidates.
+struct ScoreSignals {
+ // FIXME: add score signals like cross-reference count.
+};
+
+struct CompletionSymbol {
+ ScoreSignals Signals;
+ float SymbolScore;
+
+ std::string USR;
+ index::SymbolKind Kind;
+ std::string QualifiedName;
+
+ SymbolCompletionInfo CompletionInfo;
+};
+
+struct CompletionResult {
+ std::vector<CompletionSymbol> Symbols;
+ bool AllMatched;
+};
+
+class SymbolIndex {
+public:
+ virtual ~SymbolIndex() = default;
+
+ virtual llvm::Expected<CompletionResult>
+ complete(const CompletionRequest &Req) const = 0;
+
+ // FIXME: add interfaces for more index use cases:
+ // - Symbol getSymbolInfo(llvm::StringRef USR);
+ // - getAllOccurrences(llvm::StringRef USR);
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOLINDEX_H
Index: clangd/SymbolCompletionInfo.h
===================================================================
--- /dev/null
+++ clangd/SymbolCompletionInfo.h
@@ -0,0 +1,31 @@
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOLCOMPLETIONINFO_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOLCOMPLETIONINFO_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Index/IndexSymbol.h"
+#include "clang/Lex/Preprocessor.h"
+
+namespace clang {
+namespace clangd {
+
+struct SymbolCompletionInfo {
+ static SymbolCompletionInfo create(ASTContext &Ctx, Preprocessor &PP,
+ const NamedDecl *ND);
+
+ SymbolCompletionInfo() = default;
+ /// Label that can be be displayed in the completion list.
+ std::string Label;
+ /// Symbol annotation and/or comment for the symbol declaration.
+ std::string Documentation;
+ /// Detail about the symbol like result type.
+ std::string Detail;
+ /// Function/method parameters. Useful for snippets.
+ std::vector<std::string> Params;
+};
+
+} // clangd
+} // clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOLCOMPLETIONINFO_H
Index: clangd/SymbolCompletionInfo.cpp
===================================================================
--- /dev/null
+++ clangd/SymbolCompletionInfo.cpp
@@ -0,0 +1,116 @@
+#include "SymbolCompletionInfo.h"
+#include "clang/Sema/CodeCompleteConsumer.h"
+#include "llvm/Support/YAMLTraits.h"
+#include <memory>
+
+namespace clang {
+namespace clangd {
+namespace {
+
+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,
+ SymbolCompletionInfo *Info) {
+ 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.
+ Info->Label += Chunk.Text;
+ break;
+ case CodeCompletionString::CK_ResultType:
+ Info->Detail = 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.
+ Info->Params.push_back(Chunk.Text);
+ LLVM_FALLTHROUGH;
+ default:
+ Info->Label += Chunk.Text;
+ break;
+ }
+ }
+}
+
+inline std::string
+joinNamespaces(const llvm::SmallVectorImpl<StringRef> &Namespaces) {
+ if (Namespaces.empty())
+ return "";
+ std::string Result = Namespaces.front();
+ for (auto I = Namespaces.begin() + 1, E = Namespaces.end(); I != E; ++I)
+ Result += ("::" + *I).str();
+ return Result;
+}
+
+// Given "a::b::c", returns {"a", "b", "c"}.
+llvm::SmallVector<llvm::StringRef, 4> splitSymbolName(llvm::StringRef Name) {
+ llvm::SmallVector<llvm::StringRef, 4> Splitted;
+ Name.split(Splitted, "::", /*MaxSplit=*/-1,
+ /*KeepEmpty=*/false);
+ return Splitted;
+}
+
+} // namespace
+
+SymbolCompletionInfo SymbolCompletionInfo::create(ASTContext &Ctx,
+ Preprocessor &PP,
+ const NamedDecl *ND) {
+ 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);
+ SymbolCompletionInfo Info;
+ Info.Documentation = getDocumentation(*CCS);
+
+ ProcessChunks(*CCS, &Info);
+ // Since symbol names in CCS labels are not qualified, we prepend a namespace
+ // qualfifier.
+ std::string QualifiedName = ND->getQualifiedNameAsString();
+ auto SplittedNames = splitSymbolName(QualifiedName);
+ if (SplittedNames.size() > 1) {
+ std::string LabelPrefix = joinNamespaces(SmallVector<llvm::StringRef, 4>(
+ SplittedNames.begin(), SplittedNames.end() - 1));
+ Info.Label = LabelPrefix + "::" + Info.Label;
+ }
+ return Info;
+}
+
+} // namespace clangd
+} // namespace clang
Index: clangd/Symbol.h
===================================================================
--- clangd/Symbol.h
+++ clangd/Symbol.h
@@ -10,6 +10,7 @@
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOL_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SYMBOL_H
+#include "SymbolCompletionInfo.h"
#include "clang/Index/IndexDataConsumer.h"
#include "clang/Index/IndexSymbol.h"
@@ -29,10 +30,6 @@
unsigned EndOffset;
};
-struct CodeCompletionInfo {
- // FIXME: add fields here.
-};
-
// The class presents a C++ symbol, e.g. class, function.
struct Symbol {
// The symbol identifier, using USR.
@@ -50,7 +47,7 @@
// * For classes, the canonical location is where they are defined.
SymbolLocation CanonicalLocation;
// Information for code completion.
- CodeCompletionInfo CompletionInfo;
+ SymbolCompletionInfo CompletionInfo;
bool operator<(const Symbol& S) const {
return Identifier < S.Identifier;
@@ -69,6 +66,11 @@
public:
SymbolCollector() = default;
+ void setPreprocessor(std::shared_ptr<Preprocessor> PP) override {
+ this->PP = std::move(PP);
+ }
+ void initialize(ASTContext &Ctx) override { ASTCtx = &Ctx; }
+
const std::set<Symbol> &getSymbols() const { return Symbols; }
bool
@@ -79,6 +81,8 @@
private:
std::set<Symbol> Symbols;
+ std::shared_ptr<Preprocessor> PP;
+ ASTContext *ASTCtx;
};
} // namespace clangd
Index: clangd/Symbol.cpp
===================================================================
--- clangd/Symbol.cpp
+++ clangd/Symbol.cpp
@@ -47,8 +47,13 @@
SymbolLocation Location = {SM.getFilename(D->getLocation()),
SM.getFileOffset(D->getLocStart()),
SM.getFileOffset(D->getLocEnd())};
+ assert(ASTCtx && "ASTContext must be set.");
+ assert(PP.get() && "Preprocessor must be set.");
+ SymbolCompletionInfo CompleteInfo =
+ SymbolCompletionInfo::create(*ASTCtx, *PP, ND);
Symbols.insert({std::move(ID), ND->getQualifiedNameAsString(),
- index::getSymbolInfo(D), std::move(Location)});
+ index::getSymbolInfo(D), std::move(Location),
+ std::move(CompleteInfo)});
}
return true;
Index: clangd/CodeComplete.h
===================================================================
--- clangd/CodeComplete.h
+++ clangd/CodeComplete.h
@@ -18,6 +18,7 @@
#include "Logger.h"
#include "Path.h"
#include "Protocol.h"
+#include "SymbolIndex.h"
#include "clang/Frontend/PrecompiledPreamble.h"
#include "clang/Sema/CodeCompleteOptions.h"
#include "clang/Tooling/CompilationDatabase.h"
@@ -46,9 +47,15 @@
/// Add globals to code completion results.
bool IncludeGlobals = true;
+ /// Add symbols in namespace context (including global namespace) to code
+ /// completion.
+ /// FIXME(ioeric): this only affects qualified-id code completion at this
+ /// point.
+ unsigned IncludeNamespaceLevelDecls = true;
+
/// Add brief comments to completion items, if available.
- /// FIXME(ibiryukov): it looks like turning this option on significantly slows
- /// down completion, investigate if it can be made faster.
+ /// FIXME(ibiryukov): it looks like turning this option on significantly
+ /// slows down completion, investigate if it can be made faster.
bool IncludeBriefComments = true;
/// Include results that are not legal completions in the current context.
@@ -61,13 +68,16 @@
};
/// Get code completions at a specified \p Pos in \p FileName.
+/// If `Index` is not nullptr, this will use the index for global code
+/// completion; otherwise, use sema code completion by default.
CompletionList codeComplete(PathRef FileName,
const tooling::CompileCommand &Command,
PrecompiledPreamble const *Preamble,
StringRef Contents, Position Pos,
IntrusiveRefCntPtr<vfs::FileSystem> VFS,
std::shared_ptr<PCHContainerOperations> PCHs,
- CodeCompleteOptions Opts, Logger &Logger);
+ CodeCompleteOptions Opts, Logger &Logger,
+ const SymbolIndex *Index);
/// Get signature help at a specified \p Pos in \p FileName.
SignatureHelp
Index: clangd/CodeComplete.cpp
===================================================================
--- clangd/CodeComplete.cpp
+++ clangd/CodeComplete.cpp
@@ -15,6 +15,7 @@
//===---------------------------------------------------------------------===//
#include "CodeComplete.h"
+#include "ClangdServer.h"
#include "Compiler.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
@@ -85,6 +86,53 @@
llvm_unreachable("Unhandled CodeCompletionResult::ResultKind.");
}
+CompletionItemKind getKindOfSymbol(index::SymbolKind Kind) {
+ using SK = index::SymbolKind;
+ switch (Kind){
+ case SK::Unknown:
+ return CompletionItemKind::Missing;
+ case SK::Module:
+ case SK::Namespace:
+ case SK::NamespaceAlias:
+ return CompletionItemKind::Module;
+ case SK::Macro:
+ return CompletionItemKind::Text;
+ case SK::Enum:
+ return CompletionItemKind::Enum;
+ case SK::Struct:
+ case SK::Class:
+ case SK::Protocol:
+ case SK::Extension:
+ case SK::Union:
+ return CompletionItemKind::Class;
+ case SK::TypeAlias:
+ case SK::Using:
+ return CompletionItemKind::Reference;
+ case SK::Function:
+ case SK::ConversionFunction:
+ return CompletionItemKind::Function;
+ case SK::Variable:
+ case SK::Parameter:
+ return CompletionItemKind::Variable;
+ case SK::Field:
+ return CompletionItemKind::Field;
+ case SK::EnumConstant:
+ return CompletionItemKind::Value;
+ case SK::InstanceMethod:
+ case SK::ClassMethod:
+ case SK::StaticMethod:
+ return CompletionItemKind::Method;
+ case SK::InstanceProperty:
+ case SK::ClassProperty:
+ case SK::StaticProperty:
+ return CompletionItemKind::Property;
+ case SK::Constructor:
+ case SK::Destructor:
+ return CompletionItemKind::Constructor;
+ }
+ llvm_unreachable("Unhandled clang::index::SymbolKind.");
+}
+
std::string escapeSnippet(const llvm::StringRef Text) {
std::string Result;
Result.reserve(Text.size()); // Assume '$', '}' and '\\' are rare.
@@ -163,6 +211,22 @@
return Result;
}
+// Produces an integer that sorts in the same order as F.
+// That is: a < b <==> encodeFloat(a) < encodeFloat(b).
+static uint32_t encodeFloat(float F) {
+ static_assert(std::numeric_limits<float>::is_iec559, "");
+ static_assert(sizeof(float) == sizeof(uint32_t), "");
+ constexpr uint32_t TopBit = ~(~uint32_t{0} >> 1);
+
+ // Get the bits of the float. Endianness is the same as for integers.
+ uint32_t U;
+ memcpy(&U, &F, sizeof(float));
+ // IEEE 754 floats compare like sign-magnitude integers.
+ if (U & TopBit) // Negative float.
+ return 0 - U; // Map onto the low half of integers, order reversed.
+ return U + TopBit; // Positive floats map onto the high half of integers.
+}
+
/// A scored code completion result.
/// It may be promoted to a CompletionItem if it's among the top-ranked results.
struct CompletionCandidate {
@@ -210,38 +274,110 @@
}
return Score;
}
+};
+
+CompletionItem
+completionSymbolToCompletionItem(const CompletionSymbol &Sym,
+ bool FullyQualified, SourceManager &SM,
+ SourceRange SpecifierRange) {
+ CompletionItem Item;
+ if (FullyQualified)
+ Item.label = "::";
+ Item.label += Sym.CompletionInfo.Label.empty() ? Sym.QualifiedName
+ : Sym.CompletionInfo.Label;
+ Item.kind = getKindOfSymbol(Sym.Kind);
+ Item.detail = Sym.CompletionInfo.Detail;
+ Item.documentation = Sym.CompletionInfo.Documentation;
+ Item.insertTextFormat = InsertTextFormat::PlainText;
+ Item.sortText = encodeFloat(Sym.SymbolScore);
+
+
+ TextEdit Edit;
+ Edit.newText =
+ FullyQualified ? ("::" + Sym.QualifiedName) : Sym.QualifiedName;
+ // Get the range to be replaced. We simply replace the entire range of
+ // specifier.
+ FileID FID = SM.getFileID(SpecifierRange.getBegin());
+ const auto *FE = SM.getFileEntryForID(FID);
+ llvm::MemoryBuffer *Buffer = SM.getMemoryBufferForFile(FE);
+ llvm::StringRef Code = Buffer->getBuffer();
+ Edit.range.start =
+ offsetToPosition(Code, SM.getFileOffset(SpecifierRange.getBegin()));
+ Edit.range.end =
+ offsetToPosition(Code, SM.getFileOffset(SpecifierRange.getEnd()));
+ Item.textEdit = std::move(Edit);
+ return Item;
+}
- // Produces an integer that sorts in the same order as F.
- // That is: a < b <==> encodeFloat(a) < encodeFloat(b).
- static uint32_t encodeFloat(float F) {
- static_assert(std::numeric_limits<float>::is_iec559, "");
- static_assert(sizeof(float) == sizeof(uint32_t), "");
- constexpr uint32_t TopBit = ~(~uint32_t{0} >> 1);
-
- // Get the bits of the float. Endianness is the same as for integers.
- uint32_t U;
- memcpy(&U, &F, sizeof(float));
- // IEEE 754 floats compare like sign-magnitude integers.
- if (U & TopBit) // Negative float.
- return 0 - U; // Map onto the low half of integers, order reversed.
- return U + TopBit; // Positive floats map onto the high half of integers.
+void qualifiedIdCompletionWithIndex(const SymbolIndex &Index, Sema &S,
+ const CXXScopeSpec &SS,
+ CompletionList *Items) {
+ std::string WrittenSS =
+ Lexer::getSourceText(CharSourceRange::getCharRange(SS.getRange()),
+ S.getSourceManager(), clang::LangOptions());
+ std::string RecognizedSpecifier;
+ if (SS.isValid()) {
+ DeclContext *DC = S.computeDeclContext(SS);
+ if (auto *NS = llvm::dyn_cast<NamespaceDecl>(DC)) {
+ RecognizedSpecifier = NS->getQualifiedNameAsString();
+ } else if (auto *TU = llvm::dyn_cast<TranslationUnitDecl>(DC)) {
+ RecognizedSpecifier = "::";
+ // Sema does not include the suffix "::" in the range of SS, so we add it
+ // back here.
+ WrittenSS = "::";
+ }
}
-};
+
+ CompletionRequest Req;
+ Req.Query = RecognizedSpecifier.empty() ? WrittenSS : RecognizedSpecifier;
+ auto Filter = S.getPreprocessor().getCodeCompletionFilter();
+ llvm::errs() << " Specifier: [" << Req.Query << "], "
+ << "Filter: [" << Filter << "], Written: [" << WrittenSS
+ << "]\n";
+ // FIXME: for now we simply cancatenate specifier with the typed filter. We
+ // might want to fix the specifier prefix if it is a recognized context (e.g.
+ // a known namespace in the AST).
+ if (!Filter.empty()) {
+ if (!Req.Query.empty())
+ Req.Query += "::";
+ Req.Query += Filter;
+ }
+
+ llvm::Expected<CompletionResult> Result = Index.complete(Req);
+ if (!Result) {
+ // FIXME(ioeric): use logger to output error message.
+ llvm::consumeError(Result.takeError());
+ // FIXME(ioeric): consider falling back to sema code completion.
+ return;
+ }
+ for (unsigned int i = 0; i < Result->Symbols.size(); ++i)
+ Items->items.push_back(completionSymbolToCompletionItem(
+ Result->Symbols[i], StringRef(WrittenSS).startswith("::"),
+ S.getSourceManager(), SS.getRange()));
+ Items->isIncomplete = true;
+}
class CompletionItemsCollector : public CodeCompleteConsumer {
public:
CompletionItemsCollector(const CodeCompleteOptions &CodeCompleteOpts,
- CompletionList &Items)
+ CompletionList &Items,
+ const SymbolIndex *Index = nullptr)
: CodeCompleteConsumer(CodeCompleteOpts.getClangCompleteOpts(),
/*OutputIsBinary=*/false),
ClangdOpts(CodeCompleteOpts), Items(Items),
Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
- CCTUInfo(Allocator) {}
+ CCTUInfo(Allocator), Index(Index) {}
void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
CodeCompletionResult *Results,
unsigned NumResults) override final {
StringRef Filter = S.getPreprocessor().getCodeCompletionFilter();
+ if (Index) {
+ if (auto OptSS = Context.getCXXScopeSpecifier()) {
+ // FIXME: output a warning to logger if there are results from sema.
+ return qualifiedIdCompletionWithIndex(*Index, S, **OptSS, &Items);
+ }
+ }
std::priority_queue<CompletionCandidate> Candidates;
for (unsigned I = 0; I < NumResults; ++I) {
auto &Result = Results[I];
@@ -336,7 +472,9 @@
CompletionList &Items;
std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
CodeCompletionTUInfo CCTUInfo;
-
+ // If set, use the index-based global code completion; otherwise, use sema
+ // code completion by default.
+ const SymbolIndex *Index;
}; // CompletionItemsCollector
bool isInformativeQualifierChunk(CodeCompletionString::Chunk const &Chunk) {
@@ -349,8 +487,9 @@
public:
PlainTextCompletionItemsCollector(const CodeCompleteOptions &CodeCompleteOpts,
- CompletionList &Items)
- : CompletionItemsCollector(CodeCompleteOpts, Items) {}
+ CompletionList &Items,
+ const SymbolIndex *Index)
+ : CompletionItemsCollector(CodeCompleteOpts, Items, Index) {}
private:
void ProcessChunks(const CodeCompletionString &CCS,
@@ -385,8 +524,9 @@
public:
SnippetCompletionItemsCollector(const CodeCompleteOptions &CodeCompleteOpts,
- CompletionList &Items)
- : CompletionItemsCollector(CodeCompleteOpts, Items) {}
+ CompletionList &Items,
+ const SymbolIndex *Index)
+ : CompletionItemsCollector(CodeCompleteOpts, Items, Index) {}
private:
void ProcessChunks(const CodeCompletionString &CCS,
@@ -655,6 +795,7 @@
Result.IncludeCodePatterns = EnableSnippets && IncludeCodePatterns;
Result.IncludeMacros = IncludeMacros;
Result.IncludeGlobals = IncludeGlobals;
+ Result.IncludeNamespaceLevelDecls = IncludeNamespaceLevelDecls;
Result.IncludeBriefComments = IncludeBriefComments;
return Result;
@@ -666,15 +807,16 @@
StringRef Contents, Position Pos,
IntrusiveRefCntPtr<vfs::FileSystem> VFS,
std::shared_ptr<PCHContainerOperations> PCHs,
- CodeCompleteOptions Opts, Logger &Logger) {
+ CodeCompleteOptions Opts, Logger &Logger,
+ const SymbolIndex *Index) {
CompletionList Results;
std::unique_ptr<CodeCompleteConsumer> Consumer;
if (Opts.EnableSnippets) {
- Consumer =
- llvm::make_unique<SnippetCompletionItemsCollector>(Opts, Results);
+ Consumer = llvm::make_unique<SnippetCompletionItemsCollector>(Opts, Results,
+ Index);
} else {
- Consumer =
- llvm::make_unique<PlainTextCompletionItemsCollector>(Opts, Results);
+ Consumer = llvm::make_unique<PlainTextCompletionItemsCollector>(
+ Opts, Results, Index);
}
invokeCodeComplete(std::move(Consumer), Opts.getClangCompleteOpts(), FileName,
Command, Preamble, Contents, Pos, std::move(VFS),
Index: clangd/ClangdUnitStore.h
===================================================================
--- clangd/ClangdUnitStore.h
+++ clangd/ClangdUnitStore.h
@@ -12,6 +12,7 @@
#include <mutex>
+#include "ClangdIndex.h"
#include "ClangdUnit.h"
#include "GlobalCompilationDatabase.h"
#include "Path.h"
@@ -25,11 +26,14 @@
/// Thread-safe mapping from FileNames to CppFile.
class CppFileCollection {
public:
+ explicit CppFileCollection(ASTIndexSourcer *IndexSourcer)
+ : IndexSourcer(IndexSourcer) {}
+
std::shared_ptr<CppFile>
getOrCreateFile(PathRef File, PathRef ResourceDir,
GlobalCompilationDatabase &CDB, bool StorePreamblesInMemory,
std::shared_ptr<PCHContainerOperations> PCHs,
- clangd::Logger &Logger) {
+ clangd::Logger &Logger, ASTIndexSourcer *IndexSourcer) {
std::lock_guard<std::mutex> Lock(Mutex);
auto It = OpenedFiles.find(File);
@@ -39,7 +43,8 @@
It = OpenedFiles
.try_emplace(File, CppFile::Create(File, std::move(Command),
StorePreamblesInMemory,
- std::move(PCHs), Logger))
+ std::move(PCHs), Logger,
+ IndexSourcer))
.first;
}
return It->second;
@@ -86,6 +91,7 @@
std::mutex Mutex;
llvm::StringMap<std::shared_ptr<CppFile>> OpenedFiles;
+ ASTIndexSourcer *IndexSourcer;
};
} // namespace clangd
} // namespace clang
Index: clangd/ClangdUnitStore.cpp
===================================================================
--- clangd/ClangdUnitStore.cpp
+++ clangd/ClangdUnitStore.cpp
@@ -23,6 +23,9 @@
std::shared_ptr<CppFile> Result = It->second;
OpenedFiles.erase(It);
+ if (IndexSourcer) {
+ IndexSourcer->remove(File);
+ }
return Result;
}
@@ -42,14 +45,15 @@
It = OpenedFiles
.try_emplace(File, CppFile::Create(File, std::move(NewCommand),
StorePreamblesInMemory,
- std::move(PCHs), Logger))
+ std::move(PCHs), Logger,
+ IndexSourcer))
.first;
} else if (!compileCommandsAreEqual(It->second->getCompileCommand(),
NewCommand)) {
Result.RemovedFile = std::move(It->second);
It->second =
CppFile::Create(File, std::move(NewCommand), StorePreamblesInMemory,
- std::move(PCHs), Logger);
+ std::move(PCHs), Logger, IndexSourcer);
}
Result.FileInCollection = It->second;
return Result;
Index: clangd/ClangdUnit.h
===================================================================
--- clangd/ClangdUnit.h
+++ clangd/ClangdUnit.h
@@ -10,6 +10,7 @@
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H
+#include "ClangdIndex.h"
#include "Function.h"
#include "Path.h"
#include "Protocol.h"
@@ -81,6 +82,7 @@
Preprocessor &getPreprocessor();
const Preprocessor &getPreprocessor() const;
+ std::shared_ptr<Preprocessor> getPreprocessorPtr();
/// This function returns all top-level decls, including those that come
/// from Preamble. Decls, coming from Preamble, have to be deserialized, so
@@ -146,12 +148,14 @@
static std::shared_ptr<CppFile>
Create(PathRef FileName, tooling::CompileCommand Command,
bool StorePreamblesInMemory,
- std::shared_ptr<PCHContainerOperations> PCHs, clangd::Logger &Logger);
+ std::shared_ptr<PCHContainerOperations> PCHs, clangd::Logger &Logger,
+ ASTIndexSourcer *IndexSourcer);
private:
CppFile(PathRef FileName, tooling::CompileCommand Command,
bool StorePreamblesInMemory,
- std::shared_ptr<PCHContainerOperations> PCHs, clangd::Logger &Logger);
+ std::shared_ptr<PCHContainerOperations> PCHs, clangd::Logger &Logger,
+ ASTIndexSourcer *IndexSourcer);
public:
CppFile(CppFile const &) = delete;
@@ -254,8 +258,9 @@
std::shared_ptr<PCHContainerOperations> PCHs;
/// Used for logging various messages.
clangd::Logger &Logger;
-};
+ ASTIndexSourcer *IndexSourcer;
+};
/// Get the beginning SourceLocation at a specified \p Pos.
SourceLocation getBeginningOfIdentifier(ParsedAST &Unit, const Position &Pos,
Index: clangd/ClangdUnit.cpp
===================================================================
--- clangd/ClangdUnit.cpp
+++ clangd/ClangdUnit.cpp
@@ -382,6 +382,10 @@
Preprocessor &ParsedAST::getPreprocessor() { return Clang->getPreprocessor(); }
+std::shared_ptr<Preprocessor> ParsedAST::getPreprocessorPtr() {
+ return Clang->getPreprocessorPtr();
+}
+
const Preprocessor &ParsedAST::getPreprocessor() const {
return Clang->getPreprocessor();
}
@@ -424,19 +428,20 @@
CppFile::Create(PathRef FileName, tooling::CompileCommand Command,
bool StorePreamblesInMemory,
std::shared_ptr<PCHContainerOperations> PCHs,
- clangd::Logger &Logger) {
- return std::shared_ptr<CppFile>(new CppFile(FileName, std::move(Command),
- StorePreamblesInMemory,
- std::move(PCHs), Logger));
+ clangd::Logger &Logger, ASTIndexSourcer *IndexSourcer) {
+ return std::shared_ptr<CppFile>(
+ new CppFile(FileName, std::move(Command), StorePreamblesInMemory,
+ std::move(PCHs), Logger, IndexSourcer));
}
CppFile::CppFile(PathRef FileName, tooling::CompileCommand Command,
bool StorePreamblesInMemory,
std::shared_ptr<PCHContainerOperations> PCHs,
- clangd::Logger &Logger)
+ clangd::Logger &Logger, ASTIndexSourcer *IndexSourcer)
: FileName(FileName), Command(std::move(Command)),
StorePreamblesInMemory(StorePreamblesInMemory), RebuildCounter(0),
- RebuildInProgress(false), PCHs(std::move(PCHs)), Logger(Logger) {
+ RebuildInProgress(false), PCHs(std::move(PCHs)), Logger(Logger),
+ IndexSourcer(IndexSourcer) {
Logger.log("Opened file " + FileName + " with command [" +
this->Command.Directory + "] " +
llvm::join(this->Command.CommandLine, " "));
@@ -639,6 +644,10 @@
if (NewAST) {
Diagnostics.insert(Diagnostics.end(), NewAST->getDiagnostics().begin(),
NewAST->getDiagnostics().end());
+ if (That->IndexSourcer)
+ That->IndexSourcer->update(That->FileName, NewAST->getASTContext(),
+ NewAST->getPreprocessorPtr(),
+ NewAST->getTopLevelDecls());
} else {
// Don't report even Preamble diagnostics if we coulnd't build AST.
Diagnostics.clear();
Index: clangd/ClangdServer.h
===================================================================
--- clangd/ClangdServer.h
+++ clangd/ClangdServer.h
@@ -10,6 +10,7 @@
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H
+#include "ClangdIndex.h"
#include "ClangdUnitStore.h"
#include "DraftStore.h"
#include "GlobalCompilationDatabase.h"
@@ -207,12 +208,15 @@
/// clangd are stored in-memory or on disk.
///
/// Various messages are logged using \p Logger.
- ClangdServer(GlobalCompilationDatabase &CDB,
- DiagnosticsConsumer &DiagConsumer,
- FileSystemProvider &FSProvider, unsigned AsyncThreadsCount,
- bool StorePreamblesInMemory,
- clangd::Logger &Logger,
- llvm::Optional<StringRef> ResourceDir = llvm::None);
+ ClangdServer(
+ GlobalCompilationDatabase &CDB, DiagnosticsConsumer &DiagConsumer,
+ FileSystemProvider &FSProvider, unsigned AsyncThreadsCount,
+ bool StorePreamblesInMemory, clangd::Logger &Logger,
+ bool EnableIndexBasedCodeCompletion = false,
+ std::vector<
+ std::pair<llvm::StringRef, CombinedSymbolIndex::WeightedIndex>>
+ AdditionalIndexes = {},
+ llvm::Optional<StringRef> ResourceDir = llvm::None);
/// Set the root path of the workspace.
void setRootPath(PathRef RootPath);
@@ -323,7 +327,10 @@
DiagnosticsConsumer &DiagConsumer;
FileSystemProvider &FSProvider;
DraftStore DraftMgr;
+
+ std::unique_ptr<ASTIndexSourcer> IndexSourcer;
CppFileCollection Units;
+
std::string ResourceDir;
// If set, this represents the workspace path.
llvm::Optional<std::string> RootPath;
@@ -339,6 +346,8 @@
// called before all other members to stop the worker thread that references
// ClangdServer
ClangdScheduler WorkScheduler;
+
+ std::unique_ptr<CombinedSymbolIndex> CombinedIndex;
};
} // namespace clangd
Index: clangd/ClangdServer.cpp
===================================================================
--- clangd/ClangdServer.cpp
+++ clangd/ClangdServer.cpp
@@ -169,18 +169,35 @@
Worker.join();
}
-ClangdServer::ClangdServer(GlobalCompilationDatabase &CDB,
- DiagnosticsConsumer &DiagConsumer,
- FileSystemProvider &FSProvider,
- unsigned AsyncThreadsCount,
- bool StorePreamblesInMemory, clangd::Logger &Logger,
- llvm::Optional<StringRef> ResourceDir)
+ClangdServer::ClangdServer(
+ GlobalCompilationDatabase &CDB, DiagnosticsConsumer &DiagConsumer,
+ FileSystemProvider &FSProvider, unsigned AsyncThreadsCount,
+ bool StorePreamblesInMemory, clangd::Logger &Logger,
+ bool EnableIndexBasedCodeCompletion,
+ std::vector<std::pair<llvm::StringRef, CombinedSymbolIndex::WeightedIndex>>
+ AdditionalIndexes,
+ llvm::Optional<StringRef> ResourceDir)
: Logger(Logger), CDB(CDB), DiagConsumer(DiagConsumer),
FSProvider(FSProvider),
+ IndexSourcer(EnableIndexBasedCodeCompletion ? new ASTIndexSourcer()
+ : nullptr),
+ Units(IndexSourcer.get()),
ResourceDir(ResourceDir ? ResourceDir->str() : getStandardResourceDir()),
PCHs(std::make_shared<PCHContainerOperations>()),
StorePreamblesInMemory(StorePreamblesInMemory),
- WorkScheduler(AsyncThreadsCount) {}
+ WorkScheduler(AsyncThreadsCount) {
+ if (EnableIndexBasedCodeCompletion) {
+ assert(IndexSourcer.get() && "IndexSourcer must be set when index-based "
+ "code completion is enabled.");
+ CombinedSymbolIndex::WeightedIndex WeightedASTIndex(
+ llvm::make_unique<SimpleSymbolIndex>(IndexSourcer.get()));
+ WeightedASTIndex.Weight = 10;
+ CombinedIndex.reset(new CombinedSymbolIndex());
+ CombinedIndex->addSymbolIndex("AST", std::move(WeightedASTIndex));
+ for (auto &Index : AdditionalIndexes)
+ CombinedIndex->addSymbolIndex(Index.first, std::move(Index.second));
+ }
+}
void ClangdServer::setRootPath(PathRef RootPath) {
std::string NewRootPath = llvm::sys::path::convert_to_slash(
@@ -193,8 +210,9 @@
DocVersion Version = DraftMgr.updateDraft(File, Contents);
auto TaggedFS = FSProvider.getTaggedFileSystem(File);
- std::shared_ptr<CppFile> Resources = Units.getOrCreateFile(
- File, ResourceDir, CDB, StorePreamblesInMemory, PCHs, Logger);
+ std::shared_ptr<CppFile> Resources =
+ Units.getOrCreateFile(File, ResourceDir, CDB, StorePreamblesInMemory,
+ PCHs, Logger, IndexSourcer.get());
return scheduleReparseAndDiags(File, VersionedDraft{Version, Contents.str()},
std::move(Resources), std::move(TaggedFS));
}
@@ -289,7 +307,7 @@
CompletionList Result = clangd::codeComplete(
File, Resources->getCompileCommand(),
Preamble ? &Preamble->Preamble : nullptr, Contents, Pos,
- TaggedFS.Value, PCHs, CodeCompleteOpts, Logger);
+ TaggedFS.Value, PCHs, CodeCompleteOpts, Logger, CombinedIndex.get());
Callback(make_tagged(std::move(Result), std::move(TaggedFS.Tag)));
};
Index: clangd/ClangdLSPServer.h
===================================================================
--- clangd/ClangdLSPServer.h
+++ clangd/ClangdLSPServer.h
@@ -30,11 +30,15 @@
/// If \p CompileCommandsDir has a value, compile_commands.json will be
/// loaded only from \p CompileCommandsDir. Otherwise, clangd will look
/// for compile_commands.json in all parent directories of each file.
- ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount,
- bool StorePreamblesInMemory,
- const clangd::CodeCompleteOptions &CCOpts,
- llvm::Optional<StringRef> ResourceDir,
- llvm::Optional<Path> CompileCommandsDir);
+ ClangdLSPServer(
+ JSONOutput &Out, unsigned AsyncThreadsCount, bool StorePreamblesInMemory,
+ const clangd::CodeCompleteOptions &CCOpts,
+ llvm::Optional<StringRef> ResourceDir,
+ llvm::Optional<Path> CompileCommandsDir,
+ bool EnableIndexBasedCodeCompletion,
+ std::vector<
+ std::pair<llvm::StringRef, CombinedSymbolIndex::WeightedIndex>>
+ AdditionalIndexes);
/// Run LSP server loop, receiving input for it from \p In. \p In must be
/// opened in binary mode. Output will be written using Out variable passed to
Index: clangd/ClangdLSPServer.cpp
===================================================================
--- clangd/ClangdLSPServer.cpp
+++ clangd/ClangdLSPServer.cpp
@@ -235,15 +235,19 @@
C.reply(Result ? URI::fromFile(*Result).uri : "");
}
-ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount,
- bool StorePreamblesInMemory,
- const clangd::CodeCompleteOptions &CCOpts,
- llvm::Optional<StringRef> ResourceDir,
- llvm::Optional<Path> CompileCommandsDir)
+ClangdLSPServer::ClangdLSPServer(
+ JSONOutput &Out, unsigned AsyncThreadsCount, bool StorePreamblesInMemory,
+ const clangd::CodeCompleteOptions &CCOpts,
+ llvm::Optional<StringRef> ResourceDir,
+ llvm::Optional<Path> CompileCommandsDir,
+ bool EnableIndexBasedCodeCompletion,
+ std::vector<std::pair<llvm::StringRef, CombinedSymbolIndex::WeightedIndex>>
+ AdditionalIndexes)
: Out(Out), CDB(/*Logger=*/Out, std::move(CompileCommandsDir)),
CCOpts(CCOpts), Server(CDB, /*DiagConsumer=*/*this, FSProvider,
AsyncThreadsCount, StorePreamblesInMemory,
- /*Logger=*/Out, ResourceDir) {}
+ /*Logger=*/Out, EnableIndexBasedCodeCompletion,
+ std::move(AdditionalIndexes), ResourceDir) {}
bool ClangdLSPServer::run(std::istream &In) {
assert(!IsDone && "Run was called before");
Index: clangd/ClangdIndex.h
===================================================================
--- /dev/null
+++ clangd/ClangdIndex.h
@@ -0,0 +1,111 @@
+//===--- ClangdIndex.h - Symbol indexes for clangd.---------------*- C++-*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDINDEX_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDINDEX_H
+
+#include "Path.h"
+#include "Symbol.h"
+#include "SymbolIndex.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Error.h"
+
+namespace clang {
+namespace clangd {
+
+/// \brief This combines multiple indexes. It is responsible for merging and
+/// (re-)scoring symbols from different indexes.
+class CombinedSymbolIndex : public SymbolIndex {
+public:
+ CombinedSymbolIndex() = default;
+
+ struct WeightedIndex {
+ explicit WeightedIndex(std::unique_ptr<SymbolIndex> Index)
+ : Index(std::move(Index)) {}
+
+ std::unique_ptr<SymbolIndex> Index;
+ double Weight;
+ };
+
+ void addSymbolIndex(llvm::StringRef Label, WeightedIndex Index);
+
+ llvm::Expected<CompletionResult>
+ complete(const CompletionRequest &Req) const override;
+
+private:
+ /// A list of <label, weighted index> pairs managed by the combined index,
+ /// sorted by weight - the highest comes first.
+ std::vector<std::pair<std::string, WeightedIndex>> Indexes;
+};
+
+/// \brief An interface for in-memory symbol sources that can be used to build
+/// indexes. All symbols should be able to fit into memory, e.g. AST symbol or
+// relatively small symbol table built offline.
+class InMemoryIndexSourcer {
+public:
+ virtual ~InMemoryIndexSourcer() = default;
+
+ /// \brief True if it is safe to take a reference of the symbol table.
+ virtual bool safeToReferenceSymbols() const = 0;
+
+ /// \brief Returns a copy of the symbol table.
+ virtual std::set<Symbol> symbols() const = 0;
+
+ /// \brief Returns a reference to the symbol table.
+ virtual const std::set<Symbol> &symbolsReference() const = 0;
+};
+
+/// \brief This sources all symbols for AST index.
+class ASTIndexSourcer : public InMemoryIndexSourcer {
+ public:
+ ASTIndexSourcer() = default;
+
+ /// \brief Updates symbols in the AST corresponding to `Path`.
+ /// \param TopLevelDecls Only symbols in these declarations will be
+ /// collected.
+ void update(PathRef Path, ASTContext &Context,
+ std::shared_ptr<Preprocessor> PP,
+ llvm::ArrayRef<const Decl *> TopLevelDecls);
+
+ /// \brief Removes all symbols corresponding to `Path`
+ void remove(PathRef Path);
+
+ /// \brief Since multiple threads can read and update symbols, a reference is
+ /// not safe.
+ bool safeToReferenceSymbols() const override { return false; }
+
+ std::set<Symbol> symbols() const override;
+
+ const std::set<Symbol> &symbolsReference() const override {
+ llvm_unreachable("ASTIndexSourcer does not support getting reference to "
+ "the symbol index.");
+ }
+
+ private:
+ llvm::StringMap<std::set<Symbol>> FileToSymbols;
+ mutable std::mutex Mutex;
+};
+
+/// \brief A simple SymbolIndex implementation which iterates over all symbols
+/// and performs simple substring matching.
+class SimpleSymbolIndex : public SymbolIndex {
+public:
+ SimpleSymbolIndex(InMemoryIndexSourcer *Sourcer) : Sourcer(Sourcer) {}
+
+ llvm::Expected<CompletionResult>
+ complete(const CompletionRequest &Req) const override;
+
+private:
+ InMemoryIndexSourcer *Sourcer;
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDINDEX_H
Index: clangd/ClangdIndex.cpp
===================================================================
--- /dev/null
+++ clangd/ClangdIndex.cpp
@@ -0,0 +1,127 @@
+//===--- ClangdIndex.cpp - Symbol indexes for clangd. ------------*- C++-*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===-------------------------------------------------------------------===//
+
+#include "ClangdIndex.h"
+#include "SymbolIndex.h"
+#include "clang/Index/IndexingAction.h"
+
+namespace clang {
+namespace clangd {
+
+void CombinedSymbolIndex::addSymbolIndex(
+ llvm::StringRef Label, CombinedSymbolIndex::WeightedIndex Index) {
+ // Keep the indexes sorted by weight.
+ auto I = Indexes.begin();
+ for (auto E = Indexes.end(); I != E; ++I) {
+ if (I->second.Weight < Index.Weight)
+ break;
+ }
+ Indexes.insert(I, std::make_pair(Label.str(), std::move(Index)));
+}
+
+llvm::Expected<CompletionResult>
+CombinedSymbolIndex::complete(const CompletionRequest &Req) const {
+ // Add results from indexes that have highest weight first and ignore
+ // duplications in indexes with lower weight.
+ std::set<std::string> AddedUSRs;
+ CompletionResult Result;
+ Result.AllMatched = true;
+ for (const auto &Pair : Indexes) {
+ const WeightedIndex &Index = Pair.second;
+ auto Res = Index.Index->complete(Req);
+ if (!Res) {
+ // FIXME: use logger.
+ llvm::errs() << "Failed to complete request " << Req.Query << " in index "
+ << Pair.first << "\n";
+ continue;
+ }
+ // FIXME: do something about `AllMatched`. If an index didn't match all
+ // candidates, we might need to keep an internal state for this request so
+ // that we could know which indexes to query when users ask for more
+ // results with the same request.
+ Result.AllMatched &= Res->AllMatched;
+
+ for (auto &Sym : Res->Symbols) {
+ if (AddedUSRs.find(Sym.USR) == AddedUSRs.end()) {
+ AddedUSRs.insert(Sym.USR);
+ // FIXME(ioeric): consider ranking signals.
+ Sym.SymbolScore = Index.Weight;
+ Result.Symbols.push_back(std::move(Sym));
+ }
+ }
+ }
+ return Result;
+}
+
+void ASTIndexSourcer::remove(PathRef Path) {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ FileToSymbols.erase(Path);
+}
+
+void ASTIndexSourcer::update(PathRef Path, ASTContext &Ctx,
+ std::shared_ptr<Preprocessor> PP,
+ llvm::ArrayRef<const Decl *> TopLevelDecls) {
+ auto Collector = std::make_shared<SymbolCollector>();
+ Collector->setPreprocessor(std::move(PP));
+ index::IndexingOptions IndexOpts;
+ IndexOpts.SystemSymbolFilter =
+ index::IndexingOptions::SystemSymbolFilterKind::All;
+ IndexOpts.IndexFunctionLocals = false;
+
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+
+ index::indexTopLevelDecls(Ctx, TopLevelDecls, Collector, IndexOpts);
+ FileToSymbols[Path.str()] = Collector->getSymbols();
+ }
+}
+
+std::set<Symbol> ASTIndexSourcer::symbols() const {
+ std::set<Symbol> Symbols;
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ for (const auto &FileAndSymbols : FileToSymbols)
+ Symbols.insert(FileAndSymbols.second.begin(),
+ FileAndSymbols.second.end());
+ }
+ return Symbols;
+}
+
+/// FIXME: improve the indexing and matching algorithm.
+/// FIXME: we might want to return only the next immediate segment of name. For
+/// example, given query "a::", only return symbols "a::*" but "a::*::*". For
+/// we simply do substring match on qualified names.
+llvm::Expected<CompletionResult>
+SimpleSymbolIndex::complete(const CompletionRequest &Req) const {
+ std::set<Symbol> CopiedSymbols;
+ if (!Sourcer->safeToReferenceSymbols())
+ CopiedSymbols = Sourcer->symbols();
+ const auto &SymbolsRef = Sourcer->safeToReferenceSymbols()
+ ? Sourcer->symbolsReference()
+ : CopiedSymbols;
+
+ std::string LoweredQuery = llvm::StringRef(Req.Query).lower();
+ CompletionResult Result;
+ for (const auto &Symbol : SymbolsRef) {
+ if (StringRef(StringRef(Symbol.QualifiedName).lower())
+ .contains(LoweredQuery)) {
+ CompletionSymbol CS;
+ CS.QualifiedName = Symbol.QualifiedName;
+ CS.USR = Symbol.Identifier;
+ CS.CompletionInfo = Symbol.CompletionInfo;
+ CS.Kind = Symbol.SymInfo.Kind;
+ Result.Symbols.push_back(std::move(CS));
+ }
+ }
+ Result.AllMatched = true;
+ return Result;
+}
+
+} // namespace clangd
+} // namespace clang
Index: clangd/CMakeLists.txt
===================================================================
--- clangd/CMakeLists.txt
+++ clangd/CMakeLists.txt
@@ -3,6 +3,7 @@
)
add_clang_library(clangDaemon
+ ClangdIndex.cpp
ClangdLSPServer.cpp
ClangdServer.cpp
ClangdUnit.cpp
@@ -18,6 +19,7 @@
Protocol.cpp
ProtocolHandlers.cpp
Symbol.cpp
+ SymbolCompletionInfo.cpp
Trace.cpp
LINK_LIBS
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits