|  | //===--- FeatureModule.h - Plugging features into clangd ----------*-C++-*-===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FEATUREMODULE_H | 
|  | #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FEATUREMODULE_H | 
|  |  | 
|  | #include "support/Function.h" | 
|  | #include "support/Threading.h" | 
|  | #include "clang/Basic/Diagnostic.h" | 
|  | #include "llvm/ADT/FunctionExtras.h" | 
|  | #include "llvm/Support/Compiler.h" | 
|  | #include "llvm/Support/JSON.h" | 
|  | #include <memory> | 
|  | #include <optional> | 
|  | #include <type_traits> | 
|  | #include <vector> | 
|  |  | 
|  | namespace clang { | 
|  | class CompilerInstance; | 
|  | namespace clangd { | 
|  | struct Diag; | 
|  | class LSPBinder; | 
|  | class SymbolIndex; | 
|  | class ThreadsafeFS; | 
|  | class TUScheduler; | 
|  | class Tweak; | 
|  |  | 
|  | /// A FeatureModule contributes a vertical feature to clangd. | 
|  | /// | 
|  | /// The lifetime of a module is roughly: | 
|  | ///  - feature modules are created before the LSP server, in ClangdMain.cpp | 
|  | ///  - these modules are then passed to ClangdLSPServer in a FeatureModuleSet | 
|  | ///  - initializeLSP() is called when the editor calls initialize. | 
|  | //   - initialize() is then called by ClangdServer as it is constructed. | 
|  | ///  - module hooks can be called by the server at this point. | 
|  | ///    Server facilities (scheduler etc) are available. | 
|  | ///  - ClangdServer will not be destroyed until all the requests are done. | 
|  | ///    FIXME: Block server shutdown until all the modules are idle. | 
|  | ///  - When shutting down, ClangdServer will wait for all requests to | 
|  | ///    finish, call stop(), and then blockUntilIdle(). | 
|  | ///  - feature modules will be destroyed after ClangdLSPServer is destroyed. | 
|  | /// | 
|  | /// FeatureModules are not threadsafe in general. A module's entrypoints are: | 
|  | ///   - method handlers registered in initializeLSP() | 
|  | ///   - public methods called directly via ClangdServer.featureModule<T>()->... | 
|  | ///   - specific overridable "hook" methods inherited from FeatureModule | 
|  | /// Unless otherwise specified, these are only called on the main thread. | 
|  | /// | 
|  | /// Conventionally, standard feature modules live in the `clangd` namespace, | 
|  | /// and other exposed details live in a sub-namespace. | 
|  | class FeatureModule { | 
|  | public: | 
|  | virtual ~FeatureModule() { | 
|  | /// Perform shutdown sequence on destruction in case the ClangdServer was | 
|  | /// never initialized. Usually redundant, but shutdown is idempotent. | 
|  | stop(); | 
|  | blockUntilIdle(Deadline::infinity()); | 
|  | } | 
|  |  | 
|  | /// Called by the server to connect this feature module to LSP. | 
|  | /// The module should register the methods/notifications/commands it handles, | 
|  | /// and update the server capabilities to advertise them. | 
|  | /// | 
|  | /// This is only called if the module is running in ClangdLSPServer! | 
|  | /// FeatureModules with a public interface should work without LSP bindings. | 
|  | virtual void initializeLSP(LSPBinder &Bind, | 
|  | const llvm::json::Object &ClientCaps, | 
|  | llvm::json::Object &ServerCaps) {} | 
|  |  | 
|  | /// Shared server facilities needed by the module to get its work done. | 
|  | struct Facilities { | 
|  | TUScheduler &Scheduler; | 
|  | const SymbolIndex *Index; | 
|  | const ThreadsafeFS &FS; | 
|  | }; | 
|  | /// Called by the server to prepare this module for use. | 
|  | void initialize(const Facilities &F); | 
|  |  | 
|  | /// Requests that the module cancel background work and go idle soon. | 
|  | /// Does not block, the caller will call blockUntilIdle() instead. | 
|  | /// After a module is stop()ed, it should not receive any more requests. | 
|  | /// Called by the server when shutting down. | 
|  | /// May be called multiple times, should be idempotent. | 
|  | virtual void stop() {} | 
|  |  | 
|  | /// Waits until the module is idle (no background work) or a deadline expires. | 
|  | /// In general all modules should eventually go idle, though it may take a | 
|  | /// long time (e.g. background indexing). | 
|  | /// FeatureModules should go idle quickly if stop() has been called. | 
|  | /// Called by the server when shutting down, and also by tests. | 
|  | virtual bool blockUntilIdle(Deadline) { return true; } | 
|  |  | 
|  | /// Tweaks implemented by this module. Can be called asynchronously when | 
|  | /// enumerating or applying code actions. | 
|  | virtual void contributeTweaks(std::vector<std::unique_ptr<Tweak>> &Out) {} | 
|  |  | 
|  | /// Extension point that allows modules to observe and modify an AST build. | 
|  | /// One instance is created each time clangd produces a ParsedAST or | 
|  | /// PrecompiledPreamble. For a given instance, lifecycle methods are always | 
|  | /// called on a single thread. | 
|  | struct ASTListener { | 
|  | /// Listeners are destroyed once the AST is built. | 
|  | virtual ~ASTListener() = default; | 
|  |  | 
|  | /// Called before every AST build, both for main file and preamble. The call | 
|  | /// happens immediately before FrontendAction::Execute(), with Preprocessor | 
|  | /// set up already and after BeginSourceFile() on main file was called. | 
|  | virtual void beforeExecute(CompilerInstance &CI) {} | 
|  |  | 
|  | /// Called everytime a diagnostic is encountered. Modules can use this | 
|  | /// modify the final diagnostic, or store some information to surface code | 
|  | /// actions later on. | 
|  | virtual void sawDiagnostic(const clang::Diagnostic &, clangd::Diag &) {} | 
|  | }; | 
|  | /// Can be called asynchronously before building an AST. | 
|  | virtual std::unique_ptr<ASTListener> astListeners() { return nullptr; } | 
|  |  | 
|  | protected: | 
|  | /// Accessors for modules to access shared server facilities they depend on. | 
|  | Facilities &facilities(); | 
|  | /// The scheduler is used to run tasks on worker threads and access ASTs. | 
|  | TUScheduler &scheduler() { return facilities().Scheduler; } | 
|  | /// The index is used to get information about the whole codebase. | 
|  | const SymbolIndex *index() { return facilities().Index; } | 
|  | /// The filesystem is used to read source files on disk. | 
|  | const ThreadsafeFS &fs() { return facilities().FS; } | 
|  |  | 
|  | /// Types of function objects that feature modules use for outgoing calls. | 
|  | /// (Bound throuh LSPBinder, made available here for convenience). | 
|  | template <typename P> | 
|  | using OutgoingNotification = llvm::unique_function<void(const P &)>; | 
|  | template <typename P, typename R> | 
|  | using OutgoingMethod = llvm::unique_function<void(const P &, Callback<R>)>; | 
|  |  | 
|  | private: | 
|  | std::optional<Facilities> Fac; | 
|  | }; | 
|  |  | 
|  | /// A FeatureModuleSet is a collection of feature modules installed in clangd. | 
|  | /// | 
|  | /// Modules can be looked up by type, or used via the FeatureModule interface. | 
|  | /// This allows individual modules to expose a public API. | 
|  | /// For this reason, there can be only one feature module of each type. | 
|  | /// | 
|  | /// The set owns the modules. It is itself owned by main, not ClangdServer. | 
|  | class FeatureModuleSet { | 
|  | std::vector<std::unique_ptr<FeatureModule>> Modules; | 
|  | llvm::DenseMap<void *, FeatureModule *> Map; | 
|  |  | 
|  | template <typename Mod> struct ID { | 
|  | static_assert(std::is_base_of<FeatureModule, Mod>::value && | 
|  | std::is_final<Mod>::value, | 
|  | "Modules must be final classes derived from clangd::Module"); | 
|  | static int Key; | 
|  | }; | 
|  |  | 
|  | bool addImpl(void *Key, std::unique_ptr<FeatureModule>, const char *Source); | 
|  |  | 
|  | public: | 
|  | FeatureModuleSet() = default; | 
|  |  | 
|  | using iterator = llvm::pointee_iterator<decltype(Modules)::iterator>; | 
|  | using const_iterator = | 
|  | llvm::pointee_iterator<decltype(Modules)::const_iterator>; | 
|  | iterator begin() { return iterator(Modules.begin()); } | 
|  | iterator end() { return iterator(Modules.end()); } | 
|  | const_iterator begin() const { return const_iterator(Modules.begin()); } | 
|  | const_iterator end() const { return const_iterator(Modules.end()); } | 
|  |  | 
|  | template <typename Mod> bool add(std::unique_ptr<Mod> M) { | 
|  | return addImpl(&ID<Mod>::Key, std::move(M), LLVM_PRETTY_FUNCTION); | 
|  | } | 
|  | template <typename Mod> Mod *get() { | 
|  | return static_cast<Mod *>(Map.lookup(&ID<Mod>::Key)); | 
|  | } | 
|  | template <typename Mod> const Mod *get() const { | 
|  | return const_cast<FeatureModuleSet *>(this)->get<Mod>(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <typename Mod> int FeatureModuleSet::ID<Mod>::Key; | 
|  |  | 
|  | } // namespace clangd | 
|  | } // namespace clang | 
|  | #endif |