diff options
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 |