summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang-tools-extra/clangd/ASTManager.cpp440
-rw-r--r--clang-tools-extra/clangd/ASTManager.h162
-rw-r--r--clang-tools-extra/clangd/CMakeLists.txt7
-rw-r--r--clang-tools-extra/clangd/ClangdLSPServer.cpp100
-rw-r--r--clang-tools-extra/clangd/ClangdLSPServer.h79
-rw-r--r--clang-tools-extra/clangd/ClangdMain.cpp28
-rw-r--r--clang-tools-extra/clangd/ClangdServer.cpp149
-rw-r--r--clang-tools-extra/clangd/ClangdServer.h138
-rw-r--r--clang-tools-extra/clangd/ClangdUnit.cpp224
-rw-r--r--clang-tools-extra/clangd/ClangdUnit.h63
-rw-r--r--clang-tools-extra/clangd/ClangdUnitStore.cpp34
-rw-r--r--clang-tools-extra/clangd/ClangdUnitStore.h93
-rw-r--r--clang-tools-extra/clangd/DocumentStore.h86
-rw-r--r--clang-tools-extra/clangd/DraftStore.cpp48
-rw-r--r--clang-tools-extra/clangd/DraftStore.h61
-rw-r--r--clang-tools-extra/clangd/GlobalCompilationDatabase.cpp65
-rw-r--r--clang-tools-extra/clangd/GlobalCompilationDatabase.h59
-rw-r--r--clang-tools-extra/clangd/Path.h29
-rw-r--r--clang-tools-extra/clangd/ProtocolHandlers.cpp23
-rw-r--r--clang-tools-extra/clangd/ProtocolHandlers.h48
20 files changed, 737 insertions, 1199 deletions
diff --git a/clang-tools-extra/clangd/ASTManager.cpp b/clang-tools-extra/clangd/ASTManager.cpp
new file mode 100644
index 00000000000..04cbf582bf0
--- /dev/null
+++ b/clang-tools-extra/clangd/ASTManager.cpp
@@ -0,0 +1,440 @@
+//===--- ASTManager.cpp - Clang AST manager -------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTManager.h"
+#include "JSONRPCDispatcher.h"
+#include "Protocol.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/Path.h"
+#include <mutex>
+#include <thread>
+using namespace clang;
+using namespace clangd;
+
+void DocData::setAST(std::unique_ptr<ASTUnit> AST) {
+ this->AST = std::move(AST);
+}
+
+ASTUnit *DocData::getAST() const { return AST.get(); }
+
+void DocData::cacheFixIts(DiagnosticToReplacementMap FixIts) {
+ this->FixIts = std::move(FixIts);
+}
+
+std::vector<clang::tooling::Replacement>
+DocData::getFixIts(const clangd::Diagnostic &D) const {
+ auto it = FixIts.find(D);
+ if (it != FixIts.end())
+ return it->second;
+ return {};
+}
+
+ASTManagerRequest::ASTManagerRequest(ASTManagerRequestType Type,
+ std::string File,
+ DocVersion Version)
+ : Type(Type), File(File), Version(Version) {}
+
+/// Retrieve a copy of the contents of every file in the store, for feeding into
+/// ASTUnit.
+static std::vector<ASTUnit::RemappedFile>
+getRemappedFiles(const DocumentStore &Docs) {
+ // FIXME: Use VFS instead. This would allow us to get rid of the chdir below.
+ std::vector<ASTUnit::RemappedFile> RemappedFiles;
+ for (const auto &P : Docs.getAllDocuments()) {
+ StringRef FileName = P.first;
+ RemappedFiles.push_back(ASTUnit::RemappedFile(
+ FileName,
+ llvm::MemoryBuffer::getMemBufferCopy(P.second, FileName).release()));
+ }
+ return RemappedFiles;
+}
+
+/// Convert from clang diagnostic level to LSP severity.
+static int getSeverity(DiagnosticsEngine::Level L) {
+ switch (L) {
+ case DiagnosticsEngine::Remark:
+ return 4;
+ case DiagnosticsEngine::Note:
+ return 3;
+ case DiagnosticsEngine::Warning:
+ return 2;
+ case DiagnosticsEngine::Fatal:
+ case DiagnosticsEngine::Error:
+ return 1;
+ case DiagnosticsEngine::Ignored:
+ return 0;
+ }
+ llvm_unreachable("Unknown diagnostic level!");
+}
+
+static CompletionItemKind getKind(CXCursorKind K) {
+ switch (K) {
+ case CXCursor_MacroInstantiation:
+ case CXCursor_MacroDefinition:
+ return CompletionItemKind::Text;
+ case CXCursor_CXXMethod:
+ return CompletionItemKind::Method;
+ case CXCursor_FunctionDecl:
+ case CXCursor_FunctionTemplate:
+ return CompletionItemKind::Function;
+ case CXCursor_Constructor:
+ case CXCursor_Destructor:
+ return CompletionItemKind::Constructor;
+ case CXCursor_FieldDecl:
+ return CompletionItemKind::Field;
+ case CXCursor_VarDecl:
+ case CXCursor_ParmDecl:
+ return CompletionItemKind::Variable;
+ case CXCursor_ClassDecl:
+ case CXCursor_StructDecl:
+ case CXCursor_UnionDecl:
+ case CXCursor_ClassTemplate:
+ case CXCursor_ClassTemplatePartialSpecialization:
+ return CompletionItemKind::Class;
+ case CXCursor_Namespace:
+ case CXCursor_NamespaceAlias:
+ case CXCursor_NamespaceRef:
+ return CompletionItemKind::Module;
+ case CXCursor_EnumConstantDecl:
+ return CompletionItemKind::Value;
+ case CXCursor_EnumDecl:
+ return CompletionItemKind::Enum;
+ case CXCursor_TypeAliasDecl:
+ case CXCursor_TypeAliasTemplateDecl:
+ case CXCursor_TypedefDecl:
+ case CXCursor_MemberRef:
+ case CXCursor_TypeRef:
+ return CompletionItemKind::Reference;
+ default:
+ return CompletionItemKind::Missing;
+ }
+}
+
+ASTManager::ASTManager(JSONOutput &Output, DocumentStore &Store,
+ bool RunSynchronously)
+ : Output(Output), Store(Store), RunSynchronously(RunSynchronously),
+ PCHs(std::make_shared<PCHContainerOperations>()),
+ ClangWorker([this]() { runWorker(); }) {}
+
+void ASTManager::runWorker() {
+ while (true) {
+ ASTManagerRequest Request;
+
+ // Pick request from the queue
+ {
+ std::unique_lock<std::mutex> Lock(RequestLock);
+ // Wait for more requests.
+ ClangRequestCV.wait(Lock,
+ [this] { return !RequestQueue.empty() || Done; });
+ if (Done)
+ return;
+ assert(!RequestQueue.empty() && "RequestQueue was empty");
+
+ Request = std::move(RequestQueue.back());
+ RequestQueue.pop_back();
+
+ // Skip outdated requests
+ if (Request.Version != DocVersions.find(Request.File)->second) {
+ Output.log("Version for " + Twine(Request.File) +
+ " in request is outdated, skipping request\n");
+ continue;
+ }
+ } // unlock RequestLock
+
+ handleRequest(Request.Type, Request.File);
+ }
+}
+
+void ASTManager::queueOrRun(ASTManagerRequestType RequestType, StringRef File) {
+ if (RunSynchronously) {
+ handleRequest(RequestType, File);
+ return;
+ }
+
+ std::lock_guard<std::mutex> Guard(RequestLock);
+ // We increment the version of the added document immediately and schedule
+ // the requested operation to be run on a worker thread
+ DocVersion version = ++DocVersions[File];
+ RequestQueue.push_back(ASTManagerRequest(RequestType, File, version));
+ ClangRequestCV.notify_one();
+}
+
+void ASTManager::handleRequest(ASTManagerRequestType RequestType,
+ StringRef File) {
+ switch (RequestType) {
+ case ASTManagerRequestType::ParseAndPublishDiagnostics:
+ parseFileAndPublishDiagnostics(File);
+ break;
+ case ASTManagerRequestType::RemoveDocData: {
+ std::lock_guard<std::mutex> Lock(ClangObjectLock);
+ auto DocDataIt = DocDatas.find(File);
+ // We could get the remove request before parsing for the document is
+ // started, just do nothing in that case, parsing request will be discarded
+ // because it has a lower version value
+ if (DocDataIt == DocDatas.end())
+ return;
+ DocDatas.erase(DocDataIt);
+ break;
+ } // unlock ClangObjectLock
+ }
+}
+
+void ASTManager::parseFileAndPublishDiagnostics(StringRef File) {
+ std::unique_lock<std::mutex> ClangObjectLockGuard(ClangObjectLock);
+
+ auto &DocData = DocDatas[File];
+ ASTUnit *Unit = DocData.getAST();
+ if (!Unit) {
+ auto newAST = createASTUnitForFile(File, this->Store);
+ Unit = newAST.get();
+
+ DocData.setAST(std::move(newAST));
+ } else {
+ // Do a reparse if this wasn't the first parse.
+ // FIXME: This might have the wrong working directory if it changed in the
+ // meantime.
+ Unit->Reparse(PCHs, getRemappedFiles(this->Store));
+ }
+
+ if (!Unit)
+ return;
+
+ // Send the diagnotics to the editor.
+ // FIXME: If the diagnostic comes from a different file, do we want to
+ // show them all? Right now we drop everything not coming from the
+ // main file.
+ std::string Diagnostics;
+ DocData::DiagnosticToReplacementMap LocalFixIts; // Temporary storage
+ for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(),
+ DEnd = Unit->stored_diag_end();
+ D != DEnd; ++D) {
+ if (!D->getLocation().isValid() ||
+ !D->getLocation().getManager().isInMainFile(D->getLocation()))
+ continue;
+ Position P;
+ P.line = D->getLocation().getSpellingLineNumber() - 1;
+ P.character = D->getLocation().getSpellingColumnNumber();
+ Range R = {P, P};
+ Diagnostics +=
+ R"({"range":)" + Range::unparse(R) +
+ R"(,"severity":)" + std::to_string(getSeverity(D->getLevel())) +
+ R"(,"message":")" + llvm::yaml::escape(D->getMessage()) +
+ R"("},)";
+
+ // We convert to Replacements to become independent of the SourceManager.
+ clangd::Diagnostic Diag = {R, getSeverity(D->getLevel()), D->getMessage()};
+ auto &FixItsForDiagnostic = LocalFixIts[Diag];
+ for (const FixItHint &Fix : D->getFixIts()) {
+ FixItsForDiagnostic.push_back(clang::tooling::Replacement(
+ Unit->getSourceManager(), Fix.RemoveRange, Fix.CodeToInsert));
+ }
+ }
+
+ // Put FixIts into place.
+ DocData.cacheFixIts(std::move(LocalFixIts));
+
+ ClangObjectLockGuard.unlock();
+ // No accesses to clang objects are allowed after this point.
+
+ // Publish diagnostics.
+ if (!Diagnostics.empty())
+ Diagnostics.pop_back(); // Drop trailing comma.
+ Output.writeMessage(
+ R"({"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":")" +
+ URI::fromFile(File).uri + R"(","diagnostics":[)" + Diagnostics + R"(]}})");
+}
+
+ASTManager::~ASTManager() {
+ {
+ std::lock_guard<std::mutex> Guard(RequestLock);
+ // Wake up the clang worker thread, then exit.
+ Done = true;
+ ClangRequestCV.notify_one();
+ } // unlock DocDataLock
+ ClangWorker.join();
+}
+
+void ASTManager::onDocumentAdd(StringRef File) {
+ queueOrRun(ASTManagerRequestType::ParseAndPublishDiagnostics, File);
+}
+
+void ASTManager::onDocumentRemove(StringRef File) {
+ queueOrRun(ASTManagerRequestType::RemoveDocData, File);
+}
+
+tooling::CompilationDatabase *
+ASTManager::getOrCreateCompilationDatabaseForFile(StringRef File) {
+ namespace path = llvm::sys::path;
+
+ assert((path::is_absolute(File, path::Style::posix) ||
+ path::is_absolute(File, path::Style::windows)) &&
+ "path must be absolute");
+
+ for (auto Path = path::parent_path(File); !Path.empty();
+ Path = path::parent_path(Path)) {
+
+ auto CachedIt = CompilationDatabases.find(Path);
+ if (CachedIt != CompilationDatabases.end())
+ return CachedIt->second.get();
+ std::string Error;
+ auto CDB = tooling::CompilationDatabase::loadFromDirectory(Path, Error);
+ if (!CDB) {
+ if (!Error.empty()) {
+ Output.log("Error when trying to load compilation database from " +
+ Twine(Path) + ": " + Twine(Error) + "\n");
+ }
+ continue;
+ }
+
+ // TODO(ibiryukov): Invalidate cached compilation databases on changes
+ auto result = CDB.get();
+ CompilationDatabases.insert(std::make_pair(Path, std::move(CDB)));
+ return result;
+ }
+
+ Output.log("Failed to find compilation database for " + Twine(File) + "\n");
+ return nullptr;
+}
+
+std::unique_ptr<clang::ASTUnit>
+ASTManager::createASTUnitForFile(StringRef File, const DocumentStore &Docs) {
+ tooling::CompilationDatabase *CDB =
+ getOrCreateCompilationDatabaseForFile(File);
+
+ std::vector<tooling::CompileCommand> Commands;
+
+ if (CDB) {
+ Commands = CDB->getCompileCommands(File);
+ // chdir. This is thread hostile.
+ if (!Commands.empty())
+ llvm::sys::fs::set_current_path(Commands.front().Directory);
+ }
+ if (Commands.empty()) {
+ // Add a fake command line if we know nothing.
+ Commands.push_back(tooling::CompileCommand(
+ llvm::sys::path::parent_path(File), llvm::sys::path::filename(File),
+ {"clang", "-fsyntax-only", File.str()}, ""));
+ }
+
+ // Inject the resource dir.
+ // FIXME: Don't overwrite it if it's already there.
+ static int Dummy; // Just an address in this process.
+ std::string ResourceDir =
+ CompilerInvocation::GetResourcesPath("clangd", (void *)&Dummy);
+ Commands.front().CommandLine.push_back("-resource-dir=" + ResourceDir);
+
+ IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
+ CompilerInstance::createDiagnostics(new DiagnosticOptions);
+
+ std::vector<const char *> ArgStrs;
+ for (const auto &S : Commands.front().CommandLine)
+ ArgStrs.push_back(S.c_str());
+
+ auto ArgP = &*ArgStrs.begin();
+ return std::unique_ptr<clang::ASTUnit>(ASTUnit::LoadFromCommandLine(
+ ArgP, ArgP + ArgStrs.size(), PCHs, Diags, ResourceDir,
+ /*OnlyLocalDecls=*/false, /*CaptureDiagnostics=*/true,
+ getRemappedFiles(Docs),
+ /*RemappedFilesKeepOriginalName=*/true,
+ /*PrecompilePreambleAfterNParses=*/1, /*TUKind=*/TU_Complete,
+ /*CacheCodeCompletionResults=*/true,
+ /*IncludeBriefCommentsInCodeCompletion=*/true,
+ /*AllowPCHWithCompilerErrors=*/true));
+}
+
+std::vector<clang::tooling::Replacement>
+ASTManager::getFixIts(StringRef File, const clangd::Diagnostic &D) {
+ // TODO(ibiryukov): the FixIts should be available immediately
+ // even when parsing is being run on a worker thread
+ std::lock_guard<std::mutex> Guard(ClangObjectLock);
+ return DocDatas[File].getFixIts(D);
+}
+
+namespace {
+class CompletionItemsCollector : public CodeCompleteConsumer {
+ std::vector<CompletionItem> *Items;
+ std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
+ CodeCompletionTUInfo CCTUInfo;
+
+public:
+ CompletionItemsCollector(std::vector<CompletionItem> *Items,
+ const CodeCompleteOptions &CodeCompleteOpts)
+ : CodeCompleteConsumer(CodeCompleteOpts, /*OutputIsBinary=*/false),
+ Items(Items),
+ Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
+ CCTUInfo(Allocator) {}
+
+ void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
+ CodeCompletionResult *Results,
+ unsigned NumResults) override {
+ for (unsigned I = 0; I != NumResults; ++I) {
+ CodeCompletionResult &Result = Results[I];
+ CodeCompletionString *CCS = Result.CreateCodeCompletionString(
+ S, Context, *Allocator, CCTUInfo,
+ CodeCompleteOpts.IncludeBriefComments);
+ if (CCS) {
+ CompletionItem Item;
+ assert(CCS->getTypedText());
+ Item.label = CCS->getTypedText();
+ Item.kind = getKind(Result.CursorKind);
+ if (CCS->getBriefComment())
+ Item.documentation = CCS->getBriefComment();
+ Items->push_back(std::move(Item));
+ }
+ }
+ }
+
+ GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }
+
+ CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
+};
+
+} // namespace
+
+std::vector<CompletionItem>
+ASTManager::codeComplete(StringRef File, unsigned Line, unsigned Column) {
+ CodeCompleteOptions CCO;
+ CCO.IncludeBriefComments = 1;
+ // This is where code completion stores dirty buffers. Need to free after
+ // completion.
+ SmallVector<const llvm::MemoryBuffer *, 4> OwnedBuffers;
+ SmallVector<StoredDiagnostic, 4> StoredDiagnostics;
+ IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine(
+ new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions));
+ std::vector<CompletionItem> Items;
+ CompletionItemsCollector Collector(&Items, CCO);
+
+ std::lock_guard<std::mutex> Guard(ClangObjectLock);
+ auto &DocData = DocDatas[File];
+ auto Unit = DocData.getAST();
+ if (!Unit) {
+ auto newAST = createASTUnitForFile(File, this->Store);
+ Unit = newAST.get();
+ DocData.setAST(std::move(newAST));
+ }
+ if (!Unit)
+ return {};
+ IntrusiveRefCntPtr<SourceManager> SourceMgr(
+ new SourceManager(*DiagEngine, Unit->getFileManager()));
+ // CodeComplete seems to require fresh LangOptions.
+ LangOptions LangOpts = Unit->getLangOpts();
+ // The language server protocol uses zero-based line and column numbers.
+ // The clang code completion uses one-based numbers.
+ Unit->CodeComplete(File, Line + 1, Column + 1, getRemappedFiles(this->Store),
+ CCO.IncludeMacros, CCO.IncludeCodePatterns,
+ CCO.IncludeBriefComments, Collector, PCHs, *DiagEngine,
+ LangOpts, *SourceMgr, Unit->getFileManager(),
+ StoredDiagnostics, OwnedBuffers);
+ for (const llvm::MemoryBuffer *Buffer : OwnedBuffers)
+ delete Buffer;
+ return Items;
+}
diff --git a/clang-tools-extra/clangd/ASTManager.h b/clang-tools-extra/clangd/ASTManager.h
new file mode 100644
index 00000000000..e213822a845
--- /dev/null
+++ b/clang-tools-extra/clangd/ASTManager.h
@@ -0,0 +1,162 @@
+//===--- ASTManager.h - Clang AST manager -----------------------*- 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_ASTMANAGER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_ASTMANAGER_H
+
+#include "DocumentStore.h"
+#include "JSONRPCDispatcher.h"
+#include "Protocol.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include <condition_variable>
+#include <deque>
+#include <thread>
+
+namespace clang {
+class ASTUnit;
+class DiagnosticsEngine;
+class PCHContainerOperations;
+namespace tooling {
+class CompilationDatabase;
+} // namespace tooling
+
+namespace clangd {
+
+/// Using 'unsigned' here to avoid undefined behaviour on overflow.
+typedef unsigned DocVersion;
+
+/// Stores ASTUnit and FixIts map for an opened document.
+class DocData {
+public:
+ typedef std::map<clangd::Diagnostic, std::vector<clang::tooling::Replacement>>
+ DiagnosticToReplacementMap;
+
+public:
+ void setAST(std::unique_ptr<ASTUnit> AST);
+ ASTUnit *getAST() const;
+
+ void cacheFixIts(DiagnosticToReplacementMap FixIts);
+ std::vector<clang::tooling::Replacement>
+ getFixIts(const clangd::Diagnostic &D) const;
+
+private:
+ std::unique_ptr<ASTUnit> AST;
+ DiagnosticToReplacementMap FixIts;
+};
+
+enum class ASTManagerRequestType { ParseAndPublishDiagnostics, RemoveDocData };
+
+/// A request to the worker thread
+class ASTManagerRequest {
+public:
+ ASTManagerRequest() = default;
+ ASTManagerRequest(ASTManagerRequestType Type, std::string File,
+ DocVersion Version);
+
+ ASTManagerRequestType Type;
+ std::string File;
+ DocVersion Version;
+};
+
+class ASTManager : public DocumentStoreListener {
+public:
+ ASTManager(JSONOutput &Output, DocumentStore &Store, bool RunSynchronously);
+ ~ASTManager() override;
+
+ void onDocumentAdd(StringRef File) override;
+ void onDocumentRemove(StringRef File) override;
+
+ /// Get code completions at a specified \p Line and \p Column in \p File.
+ ///
+ /// This function is thread-safe and returns completion items that own the
+ /// data they contain.
+ std::vector<CompletionItem> codeComplete(StringRef File, unsigned Line,
+ unsigned Column);
+
+ /// Get the fixes associated with a certain diagnostic in a specified file as
+ /// replacements.
+ ///
+ /// This function is thread-safe. It returns a copy to avoid handing out
+ /// references to unguarded data.
+ std::vector<clang::tooling::Replacement>
+ getFixIts(StringRef File, const clangd::Diagnostic &D);
+
+ DocumentStore &getStore() const { return Store; }
+
+private:
+ JSONOutput &Output;
+ DocumentStore &Store;
+
+ // Set to true if requests should block instead of being processed
+ // asynchronously.
+ bool RunSynchronously;
+
+ /// Loads a compilation database for File. May return nullptr if it fails. The
+ /// database is cached for subsequent accesses.
+ clang::tooling::CompilationDatabase *
+ getOrCreateCompilationDatabaseForFile(StringRef File);
+ // Creates a new ASTUnit for the document at File.
+ // FIXME: This calls chdir internally, which is thread unsafe.
+ std::unique_ptr<clang::ASTUnit>
+ createASTUnitForFile(StringRef File, const DocumentStore &Docs);
+
+ /// If RunSynchronously is false, queues the request to be run on the worker
+ /// thread.
+ /// If RunSynchronously is true, runs the request handler immediately on the
+ /// main thread.
+ void queueOrRun(ASTManagerRequestType RequestType, StringRef File);
+
+ void runWorker();
+ void handleRequest(ASTManagerRequestType RequestType, StringRef File);
+
+ /// Parses files and publishes diagnostics.
+ /// This function is called on the worker thread in asynchronous mode and
+ /// on the main thread in synchronous mode.
+ void parseFileAndPublishDiagnostics(StringRef File);
+
+ /// Caches compilation databases loaded from directories(keys are directories).
+ llvm::StringMap<std::unique_ptr<clang::tooling::CompilationDatabase>>
+ CompilationDatabases;
+
+ /// Clang objects.
+ /// A map from filenames to DocData structures that store ASTUnit and Fixits for
+ /// the files. The ASTUnits are used for generating diagnostics and fix-it-s
+ /// asynchronously by the worker thread and synchronously for code completion.
+ llvm::StringMap<DocData> DocDatas;
+ std::shared_ptr<clang::PCHContainerOperations> PCHs;
+ /// A lock for access to the DocDatas, CompilationDatabases and PCHs.
+ std::mutex ClangObjectLock;
+
+ /// Stores latest versions of the tracked documents to discard outdated requests.
+ /// Guarded by RequestLock.
+ /// TODO(ibiryukov): the entries are neved deleted from this map.
+ llvm::StringMap<DocVersion> DocVersions;
+
+ /// A LIFO queue of requests. Note that requests are discarded if the `version`
+ /// field is not equal to the one stored inside DocVersions.
+ /// TODO(krasimir): code completion should always have priority over parsing
+ /// for diagnostics.
+ std::deque<ASTManagerRequest> RequestQueue;
+ /// Setting Done to true will make the worker thread terminate.
+ bool Done = false;
+ /// Condition variable to wake up the worker thread.
+ std::condition_variable ClangRequestCV;
+ /// Lock for accesses to RequestQueue, DocVersions and Done.
+ std::mutex RequestLock;
+
+ /// We run parsing on a separate thread. This thread looks into RequestQueue to
+ /// find requests to handle and terminates when Done is set to true.
+ std::thread ClangWorker;
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif
diff --git a/clang-tools-extra/clangd/CMakeLists.txt b/clang-tools-extra/clangd/CMakeLists.txt
index 42ce778b8bf..170fe204306 100644
--- a/clang-tools-extra/clangd/CMakeLists.txt
+++ b/clang-tools-extra/clangd/CMakeLists.txt
@@ -1,11 +1,6 @@
add_clang_executable(clangd
- ClangdLSPServer.cpp
+ ASTManager.cpp
ClangdMain.cpp
- ClangdServer.cpp
- ClangdUnit.cpp
- ClangdUnitStore.cpp
- DraftStore.cpp
- GlobalCompilationDatabase.cpp
JSONRPCDispatcher.cpp
Protocol.cpp
ProtocolHandlers.cpp
diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp
deleted file mode 100644
index 2a2e0bf971f..00000000000
--- a/clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-//===--- ClangdLSPServer.cpp - LSP server ------------------------*- C++-*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===---------------------------------------------------------------------===//
-
-#include "ClangdLSPServer.h"
-#include "JSONRPCDispatcher.h"
-
-using namespace clang::clangd;
-using namespace clang;
-
-class ClangdLSPServer::LSPDiagnosticsConsumer : public DiagnosticsConsumer {
-public:
- LSPDiagnosticsConsumer(ClangdLSPServer &Server) : Server(Server) {}
-
- virtual void onDiagnosticsReady(PathRef File,
- std::vector<DiagWithFixIts> Diagnostics) {
- Server.consumeDiagnostics(File, Diagnostics);
- }
-
-private:
- ClangdLSPServer &Server;
-};
-
-ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, bool RunSynchronously)
- : Out(Out),
- Server(llvm::make_unique<DirectoryBasedGlobalCompilationDatabase>(),
- llvm::make_unique<LSPDiagnosticsConsumer>(*this),
- RunSynchronously) {}
-
-void ClangdLSPServer::openDocument(StringRef File, StringRef Contents) {
- Server.addDocument(File, Contents);
-}
-
-void ClangdLSPServer::closeDocument(StringRef File) {
- Server.removeDocument(File);
-}
-
-std::vector<CompletionItem> ClangdLSPServer::codeComplete(PathRef File,
- Position Pos) {
- return Server.codeComplete(File, Pos);
-}
-
-std::vector<clang::tooling::Replacement>
-ClangdLSPServer::getFixIts(StringRef File, const clangd::Diagnostic &D) {
- std::lock_guard<std::mutex> Lock(FixItsMutex);
- auto DiagToFixItsIter = FixItsMap.find(File);
- if (DiagToFixItsIter == FixItsMap.end())
- return {};
-
- const auto &DiagToFixItsMap = DiagToFixItsIter->second;
- auto FixItsIter = DiagToFixItsMap.find(D);
- if (FixItsIter == DiagToFixItsMap.end())
- return {};
-
- return FixItsIter->second;
-}
-
-std::string ClangdLSPServer::getDocument(PathRef File) {
- return Server.getDocument(File);
-}
-
-void ClangdLSPServer::consumeDiagnostics(
- PathRef File, std::vector<DiagWithFixIts> Diagnostics) {
- std::string DiagnosticsJSON;
-
- DiagnosticToReplacementMap LocalFixIts; // Temporary storage
- for (auto &DiagWithFixes : Diagnostics) {
- auto Diag = DiagWithFixes.Diag;
- DiagnosticsJSON +=
- R"({"range":)" + Range::unparse(Diag.range) +
- R"(,"severity":)" + std::to_string(Diag.severity) +
- R"(,"message":")" + llvm::yaml::escape(Diag.message) +
- R"("},)";
-
- // We convert to Replacements to become independent of the SourceManager.
- auto &FixItsForDiagnostic = LocalFixIts[Diag];
- std::copy(DiagWithFixes.FixIts.begin(), DiagWithFixes.FixIts.end(),
- std::back_inserter(FixItsForDiagnostic));
- }
-
- // Cache FixIts
- {
- // FIXME(ibiryukov): should be deleted when documents are removed
- std::lock_guard<std::mutex> Lock(FixItsMutex);
- FixItsMap[File] = LocalFixIts;
- }
-
- // Publish diagnostics.
- if (!DiagnosticsJSON.empty())
- DiagnosticsJSON.pop_back(); // Drop trailing comma.
- Out.writeMessage(
- R"({"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":")" +
- URI::fromFile(File).uri + R"(","diagnostics":[)" + DiagnosticsJSON +
- R"(]}})");
-}
diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h
deleted file mode 100644
index 371bc748d0d..00000000000
--- a/clang-tools-extra/clangd/ClangdLSPServer.h
+++ /dev/null
@@ -1,79 +0,0 @@
-//===--- ClangdLSPServer.h - LSP server --------------------------*- 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_CLANGDLSPSERVER_H
-#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H
-
-#include "ClangdServer.h"
-#include "Path.h"
-#include "Protocol.h"
-#include "clang/Tooling/Core/Replacement.h"
-
-namespace clang {
-namespace clangd {
-
-class JSONOutput;
-
-/// This class serves as an intermediate layer of LSP server implementation,
-/// glueing the JSON LSP protocol layer and ClangdServer together. It doesn't
-/// directly handle input from LSP client.
-/// Most methods are synchronous and return their result directly, but
-/// diagnostics are provided asynchronously when ready via
-/// JSONOutput::writeMessage.
-class ClangdLSPServer {
-public:
- ClangdLSPServer(JSONOutput &Out, bool RunSynchronously);
-
- /// Update the document text for \p File with \p Contents, schedule update of
- /// diagnostics. Out.writeMessage will called to push diagnostics to LSP
- /// client asynchronously when they are ready.
- void openDocument(PathRef File, StringRef Contents);
- /// Stop tracking the document for \p File.
- void closeDocument(PathRef File);
-
- /// Run code completion synchronously.
- std::vector<CompletionItem> codeComplete(PathRef File, Position Pos);
-
- /// Get the fixes associated with a certain diagnostic in a specified file as
- /// replacements.
- ///
- /// This function is thread-safe. It returns a copy to avoid handing out
- /// references to unguarded data.
- std::vector<clang::tooling::Replacement>
- getFixIts(StringRef File, const clangd::Diagnostic &D);
-
- /// Get the current document contents stored for \p File.
- /// FIXME(ibiryukov): This function is here to allow implementation of
- /// formatCode from ProtocolHandlers.cpp. We should move formatCode to
- /// ClangdServer class and remove this function from public interface.
- std::string getDocument(PathRef File);
-
-private:
- class LSPDiagnosticsConsumer;
-
- /// Function that will be called on a separate thread when diagnostics are
- /// ready. Sends the Dianostics to LSP client via Out.writeMessage and caches
- /// corresponding fixits in the FixItsMap.
- void consumeDiagnostics(PathRef File,
- std::vector<DiagWithFixIts> Diagnostics);
-
- JSONOutput &Out;
- ClangdServer Server;
-
- std::mutex FixItsMutex;
- typedef std::map<clangd::Diagnostic, std::vector<clang::tooling::Replacement>>
- DiagnosticToReplacementMap;
- /// Caches FixIts per file and diagnostics
- llvm::StringMap<DiagnosticToReplacementMap> FixItsMap;
-};
-
-} // namespace clangd
-} // namespace clang
-
-#endif
diff --git a/clang-tools-extra/clangd/ClangdMain.cpp b/clang-tools-extra/clangd/ClangdMain.cpp
index 44f866567e9..c6e16a7c368 100644
--- a/clang-tools-extra/clangd/ClangdMain.cpp
+++ b/clang-tools-extra/clangd/ClangdMain.cpp
@@ -7,19 +7,15 @@
//
//===----------------------------------------------------------------------===//
+#include "ASTManager.h"
+#include "DocumentStore.h"
#include "JSONRPCDispatcher.h"
-#include "ClangdLSPServer.h"
-#include "Protocol.h"
#include "ProtocolHandlers.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Program.h"
-
#include <iostream>
-#include <memory>
#include <string>
-
-using namespace clang;
using namespace clang::clangd;
static llvm::cl::opt<bool>
@@ -38,7 +34,9 @@ int main(int argc, char *argv[]) {
// Set up a document store and intialize all the method handlers for JSONRPC
// dispatching.
- ClangdLSPServer LSPServer(Out, RunSynchronously);
+ DocumentStore Store;
+ ASTManager AST(Out, Store, RunSynchronously);
+ Store.addListener(&AST);
JSONRPCDispatcher Dispatcher(llvm::make_unique<Handler>(Out));
Dispatcher.registerHandler("initialize",
llvm::make_unique<InitializeHandler>(Out));
@@ -47,26 +45,26 @@ int main(int argc, char *argv[]) {
Dispatcher.registerHandler("shutdown", std::move(ShutdownPtr));
Dispatcher.registerHandler(
"textDocument/didOpen",
- llvm::make_unique<TextDocumentDidOpenHandler>(Out, LSPServer));
+ llvm::make_unique<TextDocumentDidOpenHandler>(Out, Store));
Dispatcher.registerHandler(
"textDocument/didClose",
- llvm::make_unique<TextDocumentDidCloseHandler>(Out, LSPServer));
+ llvm::make_unique<TextDocumentDidCloseHandler>(Out, Store));
Dispatcher.registerHandler(
"textDocument/didChange",
- llvm::make_unique<TextDocumentDidChangeHandler>(Out, LSPServer));
+ llvm::make_unique<TextDocumentDidChangeHandler>(Out, Store));
Dispatcher.registerHandler(
"textDocument/rangeFormatting",
- llvm::make_unique<TextDocumentRangeFormattingHandler>(Out, LSPServer));
+ llvm::make_unique<TextDocumentRangeFormattingHandler>(Out, Store));
Dispatcher.registerHandler(
"textDocument/onTypeFormatting",
- llvm::make_unique<TextDocumentOnTypeFormattingHandler>(Out, LSPServer));
+ llvm::make_unique<TextDocumentOnTypeFormattingHandler>(Out, Store));
Dispatcher.registerHandler(
"textDocument/formatting",
- llvm::make_unique<TextDocumentFormattingHandler>(Out, LSPServer));
+ llvm::make_unique<TextDocumentFormattingHandler>(Out, Store));
Dispatcher.registerHandler("textDocument/codeAction",
- llvm::make_unique<CodeActionHandler>(Out, LSPServer));
+ llvm::make_unique<CodeActionHandler>(Out, AST));
Dispatcher.registerHandler("textDocument/completion",
- llvm::make_unique<CompletionHandler>(Out, LSPServer));
+ llvm::make_unique<CompletionHandler>(Out, AST));
while (std::cin.good()) {
// A Language Server Protocol message starts with a HTTP header, delimited
diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp
deleted file mode 100644
index 89e935a6f1a..00000000000
--- a/clang-tools-extra/clangd/ClangdServer.cpp
+++ /dev/null
@@ -1,149 +0,0 @@
-//===--- ClangdServer.cpp - Main clangd server code --------------*- C++-*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===-------------------------------------------------------------------===//
-
-#include "ClangdServer.h"
-#include "clang/Frontend/ASTUnit.h"
-#include "clang/Frontend/CompilerInstance.h"
-#include "clang/Frontend/CompilerInvocation.h"
-#include "clang/Tooling/CompilationDatabase.h"
-#include "llvm/Support/FileSystem.h"
-
-using namespace clang::clangd;
-
-WorkerRequest::WorkerRequest(WorkerRequestKind Kind, Path File,
- DocVersion Version)
- : Kind(Kind), File(File), Version(Version) {}
-
-ClangdScheduler::ClangdScheduler(ClangdServer &Server, bool RunSynchronously)
- : RunSynchronously(RunSynchronously) {
- if (RunSynchronously) {
- // Don't start the worker thread if we're running synchronously
- return;
- }
-
- // Initialize Worker in ctor body, rather than init list to avoid potentially
- // using not-yet-initialized members
- Worker = std::thread([&Server, this]() {
- while (true) {
- WorkerRequest Request;
-
- // Pick request from the queue
- {
- std::unique_lock<std::mutex> Lock(Mutex);
- // Wait for more requests.
- RequestCV.wait(Lock, [this] { return !RequestQueue.empty() || Done; });
- if (Done)
- return;
-
- assert(!RequestQueue.empty() && "RequestQueue was empty");
-
- Request = std::move(RequestQueue.back());
- RequestQueue.pop_back();
-
- // Skip outdated requests
- if (Request.Version != Server.DraftMgr.getVersion(Request.File)) {
- // FIXME(ibiryukov): Logging
- // Output.log("Version for " + Twine(Request.File) +
- // " in request is outdated, skipping request\n");
- continue;
- }
- } // unlock Mutex
-
- Server.handleRequest(std::move(Request));
- }
- });
-}
-
-ClangdScheduler::~ClangdScheduler() {
- if (RunSynchronously)
- return; // no worker thread is running in that case
-
- {
- std::lock_guard<std::mutex> Lock(Mutex);
- // Wake up the worker thread
- Done = true;
- RequestCV.notify_one();
- } // unlock Mutex
- Worker.join();
-}
-
-void ClangdScheduler::enqueue(ClangdServer &Server, WorkerRequest Request) {
- if (RunSynchronously) {
- Server.handleRequest(Request);
- return;
- }
-
- std::lock_guard<std::mutex> Lock(Mutex);
- RequestQueue.push_back(Request);
- RequestCV.notify_one();
-}
-
-ClangdServer::ClangdServer(std::unique_ptr<GlobalCompilationDatabase> CDB,
- std::unique_ptr<DiagnosticsConsumer> DiagConsumer,
- bool RunSynchronously)
- : CDB(std::move(CDB)), DiagConsumer(std::move(DiagConsumer)),
- PCHs(std::make_shared<PCHContainerOperations>()),
- WorkScheduler(*this, RunSynchronously) {}
-
-void ClangdServer::addDocument(PathRef File, StringRef Contents) {
- DocVersion NewVersion = DraftMgr.updateDraft(File, Contents);
- WorkScheduler.enqueue(
- *this, WorkerRequest(WorkerRequestKind::ParseAndPublishDiagnostics, File,
- NewVersion));
-}
-
-void ClangdServer::removeDocument(PathRef File) {
- auto NewVersion = DraftMgr.removeDraft(File);
- WorkScheduler.enqueue(
- *this, WorkerRequest(WorkerRequestKind::RemoveDocData, File, NewVersion));
-}
-
-std::vector<CompletionItem> ClangdServer::codeComplete(PathRef File,
- Position Pos) {
- auto FileContents = DraftMgr.getDraft(File);
- assert(FileContents.Draft && "codeComplete is called for non-added document");
-
- std::vector<CompletionItem> Result;
- Units.runOnUnitWithoutReparse(
- File, *FileContents.Draft, *CDB, PCHs, [&](ClangdUnit &Unit) {
- Result = Unit.codeComplete(*FileContents.Draft, Pos);
- });
- return Result;
-}
-
-std::string ClangdServer::getDocument(PathRef File) {
- auto draft = DraftMgr.getDraft(File);
- assert(draft.Draft && "File is not tracked, cannot get contents");
- return *draft.Draft;
-}
-
-void ClangdServer::handleRequest(WorkerRequest Request) {
- switch (Request.Kind) {
- case WorkerRequestKind::ParseAndPublishDiagnostics: {
- auto FileContents = DraftMgr.getDraft(Request.File);
- if (FileContents.Version != Request.Version)
- return; // This request is outdated, do nothing
-
- assert(FileContents.Draft &&
- "No contents inside a file that was scheduled for reparse");
- Units.runOnUnit(Request.File, *FileContents.Draft, *CDB, PCHs,
- [&](ClangdUnit const &Unit) {
- DiagConsumer->onDiagnosticsReady(
- Request.File, Unit.getLocalDiagnostics());
- });
- break;
- }
- case WorkerRequestKind::RemoveDocData:
- if (Request.Version != DraftMgr.getVersion(Request.File))
- return; // This request is outdated, do nothing
-
- Units.removeUnitIfPresent(Request.File);
- break;
- }
-}
diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h
deleted file mode 100644
index 72b6bd57540..00000000000
--- a/clang-tools-extra/clangd/ClangdServer.h
+++ /dev/null
@@ -1,138 +0,0 @@
-//===--- ClangdServer.h - Main clangd server code ----------------*- 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_CLANGDSERVER_H
-#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H
-
-#include "ClangdUnitStore.h"
-#include "DraftStore.h"
-#include "GlobalCompilationDatabase.h"
-#include "clang/Frontend/ASTUnit.h"
-#include "clang/Tooling/CompilationDatabase.h"
-#include "clang/Tooling/Core/Replacement.h"
-#include "llvm/ADT/IntrusiveRefCntPtr.h"
-#include "llvm/ADT/Optional.h"
-#include "llvm/ADT/StringRef.h"
-
-#include "ClangdUnit.h"
-#include "Protocol.h"
-
-#include <condition_variable>
-#include <mutex>
-#include <string>
-#include <thread>
-#include <utility>
-
-namespace clang {
-class PCHContainerOperations;
-
-namespace clangd {
-
-class DiagnosticsConsumer {
-public:
- virtual ~DiagnosticsConsumer() = default;
-
- /// Called by ClangdServer when \p Diagnostics for \p File are ready.
- virtual void onDiagnosticsReady(PathRef File,
- std::vector<DiagWithFixIts> Diagnostics) = 0;
-};
-
-enum class WorkerRequestKind { ParseAndPublishDiagnostics, RemoveDocData };
-
-/// A request to the worker thread
-class WorkerRequest {
-public:
- WorkerRequest() = default;
- WorkerRequest(WorkerRequestKind Kind, Path File, DocVersion Version);
-
- WorkerRequestKind Kind;
- Path File;
- DocVersion Version;
-};
-
-class ClangdServer;
-
-/// Handles running WorkerRequests of ClangdServer on a separate threads.
-/// Currently runs only one worker thread.
-class ClangdScheduler {
-public:
- ClangdScheduler(ClangdServer &Server, bool RunSynchronously);
- ~ClangdScheduler();
-
- /// Enqueue WorkerRequest to be run on a worker thread
- void enqueue(ClangdServer &Server, WorkerRequest Request);
-
-private:
- bool RunSynchronously;
- std::mutex Mutex;
- /// We run some tasks on a separate thread(parsing, ClangdUnit cleanup).
- /// This thread looks into RequestQueue to find requests to handle and
- /// terminates when Done is set to true.
- std::thread Worker;
- /// Setting Done to true will make the worker thread terminate.
- bool Done = false;
- /// A LIFO queue of requests. Note that requests are discarded if the
- /// `version` field is not equal to the one stored inside DraftStore.
- /// FIXME(krasimir): code completion should always have priority over parsing
- /// for diagnostics.
- std::deque<WorkerRequest> RequestQueue;
- /// Condition variable to wake up the worker thread.
- std::condition_variable RequestCV;
-};
-
-/// Provides API to manage ASTs for a collection of C++ files and request
-/// various language features(currently, only codeCompletion and async
-/// diagnostics for tracked files).
-class ClangdServer {
-public:
- ClangdServer(std::unique_ptr<GlobalCompilationDatabase> CDB,
- std::unique_ptr<DiagnosticsConsumer> DiagConsumer,
- bool RunSynchronously);
-
- /// Add a \p File to the list of tracked C++ files or update the contents if
- /// \p File is already tracked. Also schedules parsing of the AST for it on a
- /// separate thread. When the parsing is complete, DiagConsumer passed in
- /// constructor will receive onDiagnosticsReady callback.
- void addDocument(PathRef File, StringRef Contents);
-
- /// Remove \p File from list of tracked files, schedule a request to free
- /// resources associated with it.
- void removeDocument(PathRef File);
-
- /// Run code completion for \p File at \p Pos.
- std::vector<CompletionItem> codeComplete(PathRef File, Position Pos);
-
- /// Gets current document contents for \p File. \p File must point to a
- /// currently tracked file.
- /// FIXME(ibiryukov): This function is here to allow implementation of
- /// formatCode from ProtocolHandlers.cpp. We should move formatCode to this
- /// class and remove this function from public interface.
- std::string getDocument(PathRef File);
-
-private:
- friend class ClangdScheduler;
-
- /// This function is called on a worker thread.
- void handleRequest(WorkerRequest Request);
-
- std::unique_ptr<GlobalCompilationDatabase> CDB;
- std::unique_ptr<DiagnosticsConsumer> DiagConsumer;
- DraftStore DraftMgr;
- ClangdUnitStore Units;
- std::shared_ptr<PCHContainerOperations> PCHs;
- // WorkScheduler has to be the last member, because its destructor has to be
- // called before all other members to stop the worker thread that references
- // ClangdServer
- ClangdScheduler WorkScheduler;
-};
-
-} // namespace clangd
-} // namespace clang
-
-#endif
diff --git a/clang-tools-extra/clangd/ClangdUnit.cpp b/clang-tools-extra/clangd/ClangdUnit.cpp
deleted file mode 100644
index ff1ce9b6304..00000000000
--- a/clang-tools-extra/clangd/ClangdUnit.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-//===--- ClangdUnit.cpp -----------------------------------------*- C++-*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===---------------------------------------------------------------------===//
-
-#include "ClangdUnit.h"
-#include "clang/Frontend/ASTUnit.h"
-#include "clang/Frontend/CompilerInstance.h"
-#include "clang/Frontend/CompilerInvocation.h"
-#include "clang/Tooling/CompilationDatabase.h"
-
-using namespace clang::clangd;
-using namespace clang;
-
-ClangdUnit::ClangdUnit(PathRef FileName, StringRef Contents,
- std::shared_ptr<PCHContainerOperations> PCHs,
- std::vector<tooling::CompileCommand> Commands)
- : FileName(FileName), PCHs(PCHs) {
- assert(!Commands.empty() && "No compile commands provided");
-
- // Inject the resource dir.
- // FIXME: Don't overwrite it if it's already there.
- static int Dummy; // Just an address in this process.
- std::string ResourceDir =
- CompilerInvocation::GetResourcesPath("clangd", (void *)&Dummy);
- Commands.front().CommandLine.push_back("-resource-dir=" + ResourceDir);
-
- IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
- CompilerInstance::createDiagnostics(new DiagnosticOptions);
-
- std::vector<const char *> ArgStrs;
- for (const auto &S : Commands.front().CommandLine)
- ArgStrs.push_back(S.c_str());
-
- ASTUnit::RemappedFile RemappedSource(
- FileName,
- llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
-
- auto ArgP = &*ArgStrs.begin();
- Unit = std::unique_ptr<ASTUnit>(ASTUnit::LoadFromCommandLine(
- ArgP, ArgP + ArgStrs.size(), PCHs, Diags, ResourceDir,
- /*OnlyLocalDecls=*/false, /*CaptureDiagnostics=*/true, RemappedSource,
- /*RemappedFilesKeepOriginalName=*/true,
- /*PrecompilePreambleAfterNParses=*/1, /*TUKind=*/TU_Complete,
- /*CacheCodeCompletionResults=*/true,
- /*IncludeBriefCommentsInCodeCompletion=*/true,
- /*AllowPCHWithCompilerErrors=*/true));
-}
-
-void ClangdUnit::reparse(StringRef Contents) {
- // Do a reparse if this wasn't the first parse.
- // FIXME: This might have the wrong working directory if it changed in the
- // meantime.
- ASTUnit::RemappedFile RemappedSource(
- FileName,
- llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
-
- Unit->Reparse(PCHs, RemappedSource);
-}
-
-namespace {
-
-CompletionItemKind getKind(CXCursorKind K) {
- switch (K) {
- case CXCursor_MacroInstantiation:
- case CXCursor_MacroDefinition:
- return CompletionItemKind::Text;
- case CXCursor_CXXMethod:
- return CompletionItemKind::Method;
- case CXCursor_FunctionDecl:
- case CXCursor_FunctionTemplate:
- return CompletionItemKind::Function;
- case CXCursor_Constructor:
- case CXCursor_Destructor:
- return CompletionItemKind::Constructor;
- case CXCursor_FieldDecl:
- return CompletionItemKind::Field;
- case CXCursor_VarDecl:
- case CXCursor_ParmDecl:
- return CompletionItemKind::Variable;
- case CXCursor_ClassDecl:
- case CXCursor_StructDecl:
- case CXCursor_UnionDecl:
- case CXCursor_ClassTemplate:
- case CXCursor_ClassTemplatePartialSpecialization:
- return CompletionItemKind::Class;
- case CXCursor_Namespace:
- case CXCursor_NamespaceAlias:
- case CXCursor_NamespaceRef:
- return CompletionItemKind::Module;
- case CXCursor_EnumConstantDecl:
- return CompletionItemKind::Value;
- case CXCursor_EnumDecl:
- return CompletionItemKind::Enum;
- case CXCursor_TypeAliasDecl:
- case CXCursor_TypeAliasTemplateDecl:
- case CXCursor_TypedefDecl:
- case CXCursor_MemberRef:
- case CXCursor_TypeRef:
- return CompletionItemKind::Reference;
- default:
- return CompletionItemKind::Missing;
- }
-}
-
-class CompletionItemsCollector : public CodeCompleteConsumer {
- std::vector<CompletionItem> *Items;
- std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
- CodeCompletionTUInfo CCTUInfo;
-
-public:
- CompletionItemsCollector(std::vector<CompletionItem> *Items,
- const CodeCompleteOptions &CodeCompleteOpts)
- : CodeCompleteConsumer(CodeCompleteOpts, /*OutputIsBinary=*/false),
- Items(Items),
- Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
- CCTUInfo(Allocator) {}
-
- void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
- CodeCompletionResult *Results,
- unsigned NumResults) override {
- for (unsigned I = 0; I != NumResults; ++I) {
- CodeCompletionResult &Result = Results[I];
- CodeCompletionString *CCS = Result.CreateCodeCompletionString(
- S, Context, *Allocator, CCTUInfo,
- CodeCompleteOpts.IncludeBriefComments);
- if (CCS) {
- CompletionItem Item;
- assert(CCS->getTypedText());
- Item.label = CCS->getTypedText();
- Item.kind = getKind(Result.CursorKind);
- if (CCS->getBriefComment())
- Item.documentation = CCS->getBriefComment();
- Items->push_back(std::move(Item));
- }
- }
- }
-
- GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }
-
- CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
-};
-} // namespace
-
-std::vector<CompletionItem> ClangdUnit::codeComplete(StringRef Contents,
- Position Pos) {
- CodeCompleteOptions CCO;
- CCO.IncludeBriefComments = 1;
- // This is where code completion stores dirty buffers. Need to free after
- // completion.
- SmallVector<const llvm::MemoryBuffer *, 4> OwnedBuffers;
- SmallVector<StoredDiagnostic, 4> StoredDiagnostics;
- IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine(
- new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions));
- std::vector<CompletionItem> Items;
- CompletionItemsCollector Collector(&Items, CCO);
-
- ASTUnit::RemappedFile RemappedSource(
- FileName,
- llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
-
- IntrusiveRefCntPtr<SourceManager> SourceMgr(
- new SourceManager(*DiagEngine, Unit->getFileManager()));
- // CodeComplete seems to require fresh LangOptions.
- LangOptions LangOpts = Unit->getLangOpts();
- // The language server protocol uses zero-based line and column numbers.
- // The clang code completion uses one-based numbers.
- Unit->CodeComplete(FileName, Pos.line + 1, Pos.character + 1, RemappedSource,
- CCO.IncludeMacros, CCO.IncludeCodePatterns,
- CCO.IncludeBriefComments, Collector, PCHs, *DiagEngine,
- LangOpts, *SourceMgr, Unit->getFileManager(),
- StoredDiagnostics, OwnedBuffers);
- for (const llvm::MemoryBuffer *Buffer : OwnedBuffers)
- delete Buffer;
- return Items;
-}
-
-namespace {
-/// Convert from clang diagnostic level to LSP severity.
-static int getSeverity(DiagnosticsEngine::Level L) {
- switch (L) {
- case DiagnosticsEngine::Remark:
- return 4;
- case DiagnosticsEngine::Note:
- return 3;
- case DiagnosticsEngine::Warning:
- return 2;
- case DiagnosticsEngine::Fatal:
- case DiagnosticsEngine::Error:
- return 1;
- case DiagnosticsEngine::Ignored:
- return 0;
- }
- llvm_unreachable("Unknown diagnostic level!");
-}
-} // namespace
-
-std::vector<DiagWithFixIts> ClangdUnit::getLocalDiagnostics() const {
- std::vector<DiagWithFixIts> Result;
- for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(),
- DEnd = Unit->stored_diag_end();
- D != DEnd; ++D) {
- if (!D->getLocation().isValid() ||
- !D->getLocation().getManager().isInMainFile(D->getLocation()))
- continue;
- Position P;
- P.line = D->getLocation().getSpellingLineNumber() - 1;
- P.character = D->getLocation().getSpellingColumnNumber();
- Range R = {P, P};
- clangd::Diagnostic Diag = {R, getSeverity(D->getLevel()), D->getMessage()};
-
- llvm::SmallVector<tooling::Replacement, 1> FixItsForDiagnostic;
- for (const FixItHint &Fix : D->getFixIts()) {
- FixItsForDiagnostic.push_back(clang::tooling::Replacement(
- Unit->getSourceManager(), Fix.RemoveRange, Fix.CodeToInsert));
- }
- Result.push_back({Diag, std::move(FixItsForDiagnostic)});
- }
- return Result;
-}
diff --git a/clang-tools-extra/clangd/ClangdUnit.h b/clang-tools-extra/clangd/ClangdUnit.h
deleted file mode 100644
index ca8f003b09f..00000000000
--- a/clang-tools-extra/clangd/ClangdUnit.h
+++ /dev/null
@@ -1,63 +0,0 @@
-//===--- ClangdUnit.h -------------------------------------------*- 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_CLANGDUNIT_H
-#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H
-
-#include "Protocol.h"
-#include "Path.h"
-#include "clang/Frontend/ASTUnit.h"
-#include "clang/Tooling/Core/Replacement.h"
-#include <memory>
-
-namespace clang {
-class ASTUnit;
-class PCHContainerOperations;
-
-namespace tooling {
-struct CompileCommand;
-}
-
-namespace clangd {
-
-/// A diagnostic with its FixIts.
-struct DiagWithFixIts {
- clangd::Diagnostic Diag;
- llvm::SmallVector<tooling::Replacement, 1> FixIts;
-};
-
-/// Stores parsed C++ AST and provides implementations of all operations clangd
-/// would want to perform on parsed C++ files.
-class ClangdUnit {
-public:
- ClangdUnit(PathRef FileName, StringRef Contents,
- std::shared_ptr<PCHContainerOperations> PCHs,
- std::vector<tooling::CompileCommand> Commands);
-
- /// Reparse with new contents.
- void reparse(StringRef Contents);
-
- /// Get code completions at a specified \p Line and \p Column in \p File.
- ///
- /// This function is thread-safe and returns completion items that own the
- /// data they contain.
- std::vector<CompletionItem> codeComplete(StringRef Contents, Position Pos);
- /// Returns diagnostics and corresponding FixIts for each diagnostic that are
- /// located in the current file.
- std::vector<DiagWithFixIts> getLocalDiagnostics() const;
-
-private:
- Path FileName;
- std::unique_ptr<ASTUnit> Unit;
- std::shared_ptr<PCHContainerOperations> PCHs;
-};
-
-} // namespace clangd
-} // namespace clang
-#endif
diff --git a/clang-tools-extra/clangd/ClangdUnitStore.cpp b/clang-tools-extra/clangd/ClangdUnitStore.cpp
deleted file mode 100644
index 56e6f547a8d..00000000000
--- a/clang-tools-extra/clangd/ClangdUnitStore.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-//===--- ClangdUnitStore.cpp - A ClangdUnits container -----------*-C++-*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "ClangdUnitStore.h"
-#include "llvm/Support/Path.h"
-
-using namespace clang::clangd;
-using namespace clang;
-
-void ClangdUnitStore::removeUnitIfPresent(PathRef File) {
- std::lock_guard<std::mutex> Lock(Mutex);
-
- auto It = OpenedFiles.find(File);
- if (It == OpenedFiles.end())
- return;
- OpenedFiles.erase(It);
-}
-
-std::vector<tooling::CompileCommand> ClangdUnitStore::getCompileCommands(GlobalCompilationDatabase &CDB, PathRef File) {
- std::vector<tooling::CompileCommand> Commands = CDB.getCompileCommands(File);
- if (Commands.empty()) {
- // Add a fake command line if we know nothing.
- Commands.push_back(tooling::CompileCommand(
- llvm::sys::path::parent_path(File), llvm::sys::path::filename(File),
- {"clang", "-fsyntax-only", File.str()}, ""));
- }
- return Commands;
-}
diff --git a/clang-tools-extra/clangd/ClangdUnitStore.h b/clang-tools-extra/clangd/ClangdUnitStore.h
deleted file mode 100644
index fdd7124df57..00000000000
--- a/clang-tools-extra/clangd/ClangdUnitStore.h
+++ /dev/null
@@ -1,93 +0,0 @@
-//===--- ClangdUnitStore.h - A ClangdUnits container -------------*-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_CLANGDUNITSTORE_H
-#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNITSTORE_H
-
-#include <mutex>
-
-#include "ClangdUnit.h"
-#include "GlobalCompilationDatabase.h"
-#include "Path.h"
-#include "clang/Tooling/CompilationDatabase.h"
-
-namespace clang {
-namespace clangd {
-
-/// Thread-safe collection of ASTs built for specific files. Provides
-/// synchronized access to ASTs.
-class ClangdUnitStore {
-public:
- /// Run specified \p Action on the ClangdUnit for \p File.
- /// If the file is not present in ClangdUnitStore, a new ClangdUnit will be
- /// created from the \p FileContents. If the file is already present in the
- /// store, ClangdUnit::reparse will be called with the new contents before
- /// running \p Action.
- template <class Func>
- void runOnUnit(PathRef File, StringRef FileContents,
- GlobalCompilationDatabase &CDB,
- std::shared_ptr<PCHContainerOperations> PCHs, Func Action) {
- runOnUnitImpl(File, FileContents, CDB, PCHs, /*ReparseBeforeAction=*/true,
- std::forward<Func>(Action));
- }
-
- /// Run specified \p Action on the ClangdUnit for \p File.
- /// If the file is not present in ClangdUnitStore, a new ClangdUnit will be
- /// created from the \p FileContents. If the file is already present in the
- /// store, the \p Action will be run directly on it.
- template <class Func>
- void runOnUnitWithoutReparse(PathRef File, StringRef FileContents,
- GlobalCompilationDatabase &CDB,
- std::shared_ptr<PCHContainerOperations> PCHs,
- Func Action) {
- runOnUnitImpl(File, FileContents, CDB, PCHs, /*ReparseBeforeAction=*/false,
- std::forward<Func>(Action));
- }
-
- /// Remove ClangdUnit for \p File, if any
- void removeUnitIfPresent(PathRef File);
-
-private:
- /// Run specified \p Action on the ClangdUnit for \p File.
- template <class Func>
- void runOnUnitImpl(PathRef File, StringRef FileContents,
- GlobalCompilationDatabase &CDB,
- std::shared_ptr<PCHContainerOperations> PCHs,
- bool ReparseBeforeAction, Func Action) {
- std::lock_guard<std::mutex> Lock(Mutex);
-
- auto Commands = getCompileCommands(CDB, File);
- assert(!Commands.empty() &&
- "getCompileCommands should add default command");
- // chdir. This is thread hostile.
- // FIXME(ibiryukov): get rid of this
- llvm::sys::fs::set_current_path(Commands.front().Directory);
-
- auto It = OpenedFiles.find(File);
- if (It == OpenedFiles.end()) {
- It = OpenedFiles
- .insert(std::make_pair(
- File, ClangdUnit(File, FileContents, PCHs, Commands)))
- .first;
- } else if (ReparseBeforeAction) {
- It->second.reparse(FileContents);
- }
- return Action(It->second);
- }
-
- std::vector<tooling::CompileCommand>
- getCompileCommands(GlobalCompilationDatabase &CDB, PathRef File);
-
- std::mutex Mutex;
- llvm::StringMap<ClangdUnit> OpenedFiles;
-};
-} // namespace clangd
-} // namespace clang
-
-#endif
diff --git a/clang-tools-extra/clangd/DocumentStore.h b/clang-tools-extra/clangd/DocumentStore.h
new file mode 100644
index 00000000000..4d428935bf5
--- /dev/null
+++ b/clang-tools-extra/clangd/DocumentStore.h
@@ -0,0 +1,86 @@
+//===--- DocumentStore.h - File contents container --------------*- 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_DOCUMENTSTORE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_DOCUMENTSTORE_H
+
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/StringMap.h"
+#include <mutex>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace clangd {
+class DocumentStore;
+
+struct DocumentStoreListener {
+ virtual ~DocumentStoreListener() = default;
+ virtual void onDocumentAdd(StringRef File) {}
+ virtual void onDocumentRemove(StringRef File) {}
+};
+
+/// A container for files opened in a workspace, addressed by File. The contents
+/// are owned by the DocumentStore.
+class DocumentStore {
+public:
+ /// Add a document to the store. Overwrites existing contents.
+ void addDocument(StringRef File, StringRef Text) {
+ {
+ std::lock_guard<std::mutex> Guard(DocsMutex);
+ Docs[File] = Text;
+ }
+ for (const auto &Listener : Listeners)
+ Listener->onDocumentAdd(File);
+ }
+ /// Delete a document from the store.
+ void removeDocument(StringRef File) {
+ {
+ std::lock_guard<std::mutex> Guard(DocsMutex);
+ Docs.erase(File);
+ }
+ for (const auto &Listener : Listeners)
+ Listener->onDocumentRemove(File);
+ }
+ /// Retrieve a document from the store. Empty string if it's unknown.
+ ///
+ /// This function is thread-safe. It returns a copy to avoid handing out
+ /// references to unguarded data.
+ std::string getDocument(StringRef File) const {
+ // FIXME: This could be a reader lock.
+ std::lock_guard<std::mutex> Guard(DocsMutex);
+ return Docs.lookup(File);
+ }
+
+ /// Add a listener. Does not take ownership.
+ void addListener(DocumentStoreListener *DSL) { Listeners.push_back(DSL); }
+
+ /// Get name and constents of all documents in this store.
+ ///
+ /// This function is thread-safe. It returns a copies to avoid handing out
+ /// references to unguarded data.
+ std::vector<std::pair<std::string, std::string>> getAllDocuments() const {
+ std::vector<std::pair<std::string, std::string>> AllDocs;
+ std::lock_guard<std::mutex> Guard(DocsMutex);
+ for (const auto &P : Docs)
+ AllDocs.emplace_back(P.first(), P.second);
+ return AllDocs;
+ }
+
+private:
+ llvm::StringMap<std::string> Docs;
+ std::vector<DocumentStoreListener *> Listeners;
+
+ mutable std::mutex DocsMutex;
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif
diff --git a/clang-tools-extra/clangd/DraftStore.cpp b/clang-tools-extra/clangd/DraftStore.cpp
deleted file mode 100644
index 3455699a83b..00000000000
--- a/clang-tools-extra/clangd/DraftStore.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-//===--- DraftStore.cpp - File contents container ---------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "DraftStore.h"
-
-using namespace clang::clangd;
-
-VersionedDraft DraftStore::getDraft(PathRef File) const {
- std::lock_guard<std::mutex> Lock(Mutex);
-
- auto It = Drafts.find(File);
- if (It == Drafts.end())
- return {0, llvm::None};
- return It->second;
-}
-
-DocVersion DraftStore::getVersion(PathRef File) const {
- std::lock_guard<std::mutex> Lock(Mutex);
-
- auto It = Drafts.find(File);
- if (It == Drafts.end())
- return 0;
- return It->second.Version;
-}
-
-DocVersion DraftStore::updateDraft(PathRef File, StringRef Contents) {
- std::lock_guard<std::mutex> Lock(Mutex);
-
- auto &Entry = Drafts[File];
- DocVersion NewVersion = ++Entry.Version;
- Entry.Draft = Contents;
- return NewVersion;
-}
-
-DocVersion DraftStore::removeDraft(PathRef File) {
- std::lock_guard<std::mutex> Lock(Mutex);
-
- auto &Entry = Drafts[File];
- DocVersion NewVersion = ++Entry.Version;
- Entry.Draft = llvm::None;
- return NewVersion;
-}
diff --git a/clang-tools-extra/clangd/DraftStore.h b/clang-tools-extra/clangd/DraftStore.h
deleted file mode 100644
index c4e31e7c813..00000000000
--- a/clang-tools-extra/clangd/DraftStore.h
+++ /dev/null
@@ -1,61 +0,0 @@
-//===--- DraftStore.h - File contents container -----------------*- 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_DRAFTSTORE_H
-#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_DRAFTSTORE_H
-
-#include "Path.h"
-#include "clang/Basic/LLVM.h"
-#include "llvm/ADT/StringMap.h"
-#include <mutex>
-#include <string>
-#include <vector>
-
-namespace clang {
-namespace clangd {
-
-/// Using 'unsigned' here to avoid undefined behaviour on overflow.
-typedef unsigned DocVersion;
-
-/// Document draft with a version of this draft.
-struct VersionedDraft {
- DocVersion Version;
- /// If the value of the field is None, draft is now deleted
- llvm::Optional<std::string> Draft;
-};
-
-/// A thread-safe container for files opened in a workspace, addressed by
-/// filenames. The contents are owned by the DraftStore. Versions are mantained
-/// for the all added documents, including removed ones. The document version is
-/// incremented on each update and removal of the document.
-class DraftStore {
-public:
- /// \return version and contents of the stored document.
- /// For untracked files, a (0, None) pair is returned.
- VersionedDraft getDraft(PathRef File) const;
- /// \return version of the tracked document.
- /// For untracked files, 0 is returned.
- DocVersion getVersion(PathRef File) const;
-
- /// Replace contents of the draft for \p File with \p Contents.
- /// \return The new version of the draft for \p File.
- DocVersion updateDraft(PathRef File, StringRef Contents);
- /// Remove the contents of the draft
- /// \return The new version of the draft for \p File.
- DocVersion removeDraft(PathRef File);
-
-private:
- mutable std::mutex Mutex;
- llvm::StringMap<VersionedDraft> Drafts;
-};
-
-} // namespace clangd
-} // namespace clang
-
-#endif
diff --git a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp
deleted file mode 100644
index 91d77026293..00000000000
--- a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-//===--- GlobalCompilationDatabase.cpp --------------------------*- C++-*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===---------------------------------------------------------------------===//
-
-#include "GlobalCompilationDatabase.h"
-#include "clang/Tooling/CompilationDatabase.h"
-#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/Path.h"
-
-using namespace clang::clangd;
-using namespace clang;
-
-std::vector<tooling::CompileCommand>
-DirectoryBasedGlobalCompilationDatabase::getCompileCommands(PathRef File) {
- std::vector<tooling::CompileCommand> Commands;
-
- auto CDB = getCompilationDatabase(File);
- if (!CDB)
- return {};
- return CDB->getCompileCommands(File);
-}
-
-tooling::CompilationDatabase *
-DirectoryBasedGlobalCompilationDatabase::getCompilationDatabase(PathRef File) {
- std::lock_guard<std::mutex> Lock(Mutex);
-
- namespace path = llvm::sys::path;
-
- assert((path::is_absolute(File, path::Style::posix) ||
- path::is_absolute(File, path::Style::windows)) &&
- "path must be absolute");
-
- for (auto Path = path::parent_path(File); !Path.empty();
- Path = path::parent_path(Path)) {
-
- auto CachedIt = CompilationDatabases.find(Path);
- if (CachedIt != CompilationDatabases.end())
- return CachedIt->second.get();
- std::string Error;
- auto CDB = tooling::CompilationDatabase::loadFromDirectory(Path, Error);
- if (!CDB) {
- if (!Error.empty()) {
- // FIXME(ibiryukov): logging
- // Output.log("Error when trying to load compilation database from " +
- // Twine(Path) + ": " + Twine(Error) + "\n");
- }
- continue;
- }
-
- // FIXME(ibiryukov): Invalidate cached compilation databases on changes
- auto result = CDB.get();
- CompilationDatabases.insert(std::make_pair(Path, std::move(CDB)));
- return result;
- }
-
- // FIXME(ibiryukov): logging
- // Output.log("Failed to find compilation database for " + Twine(File) +
- // "\n");
- return nullptr;
-}
diff --git a/clang-tools-extra/clangd/GlobalCompilationDatabase.h b/clang-tools-extra/clangd/GlobalCompilationDatabase.h
deleted file mode 100644
index c708c5ca7cd..00000000000
--- a/clang-tools-extra/clangd/GlobalCompilationDatabase.h
+++ /dev/null
@@ -1,59 +0,0 @@
-//===--- GlobalCompilationDatabase.h ----------------------------*- 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_GLOBALCOMPILATIONDATABASE_H
-#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_GLOBALCOMPILATIONDATABASE_H
-
-#include "Path.h"
-#include "llvm/ADT/StringMap.h"
-#include <memory>
-#include <mutex>
-
-namespace clang {
-
-namespace tooling {
-class CompilationDatabase;
-struct CompileCommand;
-} // namespace tooling
-
-namespace clangd {
-
-/// Provides compilation arguments used for building ClangdUnit.
-class GlobalCompilationDatabase {
-public:
- virtual ~GlobalCompilationDatabase() = default;
-
- virtual std::vector<tooling::CompileCommand>
- getCompileCommands(PathRef File) = 0;
-
- /// FIXME(ibiryukov): add facilities to track changes to compilation flags of
- /// existing targets.
-};
-
-/// Gets compile args from tooling::CompilationDatabases built for parent
-/// directories.
-class DirectoryBasedGlobalCompilationDatabase
- : public GlobalCompilationDatabase {
-public:
- std::vector<tooling::CompileCommand>
- getCompileCommands(PathRef File) override;
-
-private:
- tooling::CompilationDatabase *getCompilationDatabase(PathRef File);
-
- std::mutex Mutex;
- /// Caches compilation databases loaded from directories(keys are
- /// directories).
- llvm::StringMap<std::unique_ptr<clang::tooling::CompilationDatabase>>
- CompilationDatabases;
-};
-} // namespace clangd
-} // namespace clang
-
-#endif
diff --git a/clang-tools-extra/clangd/Path.h b/clang-tools-extra/clangd/Path.h
deleted file mode 100644
index b4c93357a55..00000000000
--- a/clang-tools-extra/clangd/Path.h
+++ /dev/null
@@ -1,29 +0,0 @@
-//===--- Path.h - Helper typedefs --------------------------------*- 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_PATH_H
-#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PATH_H
-
-#include "llvm/ADT/StringRef.h"
-#include <string>
-
-namespace clang {
-namespace clangd {
-
-/// A typedef to represent a file path. Used solely for more descriptive
-/// signatures.
-using Path = std::string;
-/// A typedef to represent a ref to file path. Used solely for more descriptive
-/// signatures.
-using PathRef = llvm::StringRef;
-
-} // namespace clangd
-} // namespace clang
-
-#endif
diff --git a/clang-tools-extra/clangd/ProtocolHandlers.cpp b/clang-tools-extra/clangd/ProtocolHandlers.cpp
index 66edee3d716..4c81692b554 100644
--- a/clang-tools-extra/clangd/ProtocolHandlers.cpp
+++ b/clang-tools-extra/clangd/ProtocolHandlers.cpp
@@ -8,10 +8,9 @@
//===----------------------------------------------------------------------===//
#include "ProtocolHandlers.h"
-#include "ClangdServer.h"
-#include "DraftStore.h"
+#include "ASTManager.h"
+#include "DocumentStore.h"
#include "clang/Format/Format.h"
-#include "ClangdLSPServer.h"
using namespace clang;
using namespace clangd;
@@ -22,7 +21,7 @@ void TextDocumentDidOpenHandler::handleNotification(
Output.log("Failed to decode DidOpenTextDocumentParams!\n");
return;
}
- AST.openDocument(DOTDP->textDocument.uri.file, DOTDP->textDocument.text);
+ Store.addDocument(DOTDP->textDocument.uri.file, DOTDP->textDocument.text);
}
void TextDocumentDidCloseHandler::handleNotification(
@@ -33,7 +32,7 @@ void TextDocumentDidCloseHandler::handleNotification(
return;
}
- AST.closeDocument(DCTDP->textDocument.uri.file);
+ Store.removeDocument(DCTDP->textDocument.uri.file);
}
void TextDocumentDidChangeHandler::handleNotification(
@@ -44,7 +43,7 @@ void TextDocumentDidChangeHandler::handleNotification(
return;
}
// We only support full syncing right now.
- AST.openDocument(DCTDP->textDocument.uri.file, DCTDP->contentChanges[0].text);
+ Store.addDocument(DCTDP->textDocument.uri.file, DCTDP->contentChanges[0].text);
}
/// Turn a [line, column] pair into an offset in Code.
@@ -111,7 +110,7 @@ void TextDocumentRangeFormattingHandler::handleMethod(
return;
}
- std::string Code = AST.getDocument(DRFP->textDocument.uri.file);
+ std::string Code = Store.getDocument(DRFP->textDocument.uri.file);
size_t Begin = positionToOffset(Code, DRFP->range.start);
size_t Len = positionToOffset(Code, DRFP->range.end) - Begin;
@@ -130,7 +129,7 @@ void TextDocumentOnTypeFormattingHandler::handleMethod(
// Look for the previous opening brace from the character position and format
// starting from there.
- std::string Code = AST.getDocument(DOTFP->textDocument.uri.file);
+ std::string Code = Store.getDocument(DOTFP->textDocument.uri.file);
size_t CursorPos = positionToOffset(Code, DOTFP->position);
size_t PreviousLBracePos = StringRef(Code).find_last_of('{', CursorPos);
if (PreviousLBracePos == StringRef::npos)
@@ -150,7 +149,7 @@ void TextDocumentFormattingHandler::handleMethod(
}
// Format everything.
- std::string Code = AST.getDocument(DFP->textDocument.uri.file);
+ std::string Code = Store.getDocument(DFP->textDocument.uri.file);
writeMessage(formatCode(Code, DFP->textDocument.uri.file,
{clang::tooling::Range(0, Code.size())}, ID));
}
@@ -165,7 +164,7 @@ void CodeActionHandler::handleMethod(llvm::yaml::MappingNode *Params,
// We provide a code action for each diagnostic at the requested location
// which has FixIts available.
- std::string Code = AST.getDocument(CAP->textDocument.uri.file);
+ std::string Code = AST.getStore().getDocument(CAP->textDocument.uri.file);
std::string Commands;
for (Diagnostic &D : CAP->context.diagnostics) {
std::vector<clang::tooling::Replacement> Fixes = AST.getFixIts(CAP->textDocument.uri.file, D);
@@ -196,8 +195,8 @@ void CompletionHandler::handleMethod(llvm::yaml::MappingNode *Params,
return;
}
- auto Items = AST.codeComplete(TDPP->textDocument.uri.file, Position{TDPP->position.line,
- TDPP->position.character});
+ auto Items = AST.codeComplete(TDPP->textDocument.uri.file, TDPP->position.line,
+ TDPP->position.character);
std::string Completions;
for (const auto &Item : Items) {
Completions += CompletionItem::unparse(Item);
diff --git a/clang-tools-extra/clangd/ProtocolHandlers.h b/clang-tools-extra/clangd/ProtocolHandlers.h
index e957d35544a..521e6b6593c 100644
--- a/clang-tools-extra/clangd/ProtocolHandlers.h
+++ b/clang-tools-extra/clangd/ProtocolHandlers.h
@@ -22,8 +22,8 @@
namespace clang {
namespace clangd {
-class ClangdLSPServer;
-class ClangdLSPServer;
+class ASTManager;
+class DocumentStore;
struct InitializeHandler : Handler {
InitializeHandler(JSONOutput &Output) : Handler(Output) {}
@@ -56,83 +56,83 @@ private:
};
struct TextDocumentDidOpenHandler : Handler {
- TextDocumentDidOpenHandler(JSONOutput &Output, ClangdLSPServer &AST)
- : Handler(Output), AST(AST) {}
+ TextDocumentDidOpenHandler(JSONOutput &Output, DocumentStore &Store)
+ : Handler(Output), Store(Store) {}
void handleNotification(llvm::yaml::MappingNode *Params) override;
private:
- ClangdLSPServer &AST;
+ DocumentStore &Store;
};
struct TextDocumentDidChangeHandler : Handler {
- TextDocumentDidChangeHandler(JSONOutput &Output, ClangdLSPServer &AST)
- : Handler(Output), AST(AST) {}
+ TextDocumentDidChangeHandler(JSONOutput &Output, DocumentStore &Store)
+ : Handler(Output), Store(Store) {}
void handleNotification(llvm::yaml::MappingNode *Params) override;
private:
- ClangdLSPServer &AST;
+ DocumentStore &Store;
};
struct TextDocumentDidCloseHandler : Handler {
- TextDocumentDidCloseHandler(JSONOutput &Output, ClangdLSPServer &AST)
- : Handler(Output), AST(AST) {}
+ TextDocumentDidCloseHandler(JSONOutput &Output, DocumentStore &Store)
+ : Handler(Output), Store(Store) {}
void handleNotification(llvm::yaml::MappingNode *Params) override;
private:
- ClangdLSPServer &AST;
+ DocumentStore &Store;
};
struct TextDocumentOnTypeFormattingHandler : Handler {
- TextDocumentOnTypeFormattingHandler(JSONOutput &Output, ClangdLSPServer &AST)
- : Handler(Output), AST(AST) {}
+ TextDocumentOnTypeFormattingHandler(JSONOutput &Output, DocumentStore &Store)
+ : Handler(Output), Store(Store) {}
void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override;
private:
- ClangdLSPServer &AST;
+ DocumentStore &Store;
};
struct TextDocumentRangeFormattingHandler : Handler {
- TextDocumentRangeFormattingHandler(JSONOutput &Output, ClangdLSPServer &AST)
- : Handler(Output), AST(AST) {}
+ TextDocumentRangeFormattingHandler(JSONOutput &Output, DocumentStore &Store)
+ : Handler(Output), Store(Store) {}
void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override;
private:
- ClangdLSPServer &AST;
+ DocumentStore &Store;
};
struct TextDocumentFormattingHandler : Handler {
- TextDocumentFormattingHandler(JSONOutput &Output, ClangdLSPServer &AST)
- : Handler(Output), AST(AST) {}
+ TextDocumentFormattingHandler(JSONOutput &Output, DocumentStore &Store)
+ : Handler(Output), Store(Store) {}
void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override;
private:
- ClangdLSPServer &AST;
+ DocumentStore &Store;
};
struct CodeActionHandler : Handler {
- CodeActionHandler(JSONOutput &Output, ClangdLSPServer &AST)
+ CodeActionHandler(JSONOutput &Output, ASTManager &AST)
: Handler(Output), AST(AST) {}
void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override;
private:
- ClangdLSPServer &AST;
+ ASTManager &AST;
};
struct CompletionHandler : Handler {
- CompletionHandler(JSONOutput &Output, ClangdLSPServer &AST)
+ CompletionHandler(JSONOutput &Output, ASTManager &AST)
: Handler(Output), AST(AST) {}
void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override;
private:
- ClangdLSPServer &AST;
+ ASTManager &AST;
};
} // namespace clangd
OpenPOWER on IntegriCloud