207 lines · c
1//===--- FeatureModule.h - Plugging features into clangd ----------*-C++-*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FEATUREMODULE_H10#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FEATUREMODULE_H11 12#include "support/Function.h"13#include "support/Threading.h"14#include "clang/Basic/Diagnostic.h"15#include "llvm/ADT/FunctionExtras.h"16#include "llvm/Support/Compiler.h"17#include "llvm/Support/JSON.h"18#include "llvm/Support/Registry.h"19#include <memory>20#include <optional>21#include <type_traits>22#include <vector>23 24namespace clang {25class CompilerInstance;26namespace clangd {27struct Diag;28class LSPBinder;29class SymbolIndex;30class ThreadsafeFS;31class TUScheduler;32class Tweak;33 34/// A FeatureModule contributes a vertical feature to clangd.35///36/// The lifetime of a module is roughly:37/// - feature modules are created before the LSP server, in ClangdMain.cpp38/// - these modules are then passed to ClangdLSPServer in a FeatureModuleSet39/// - initializeLSP() is called when the editor calls initialize.40// - initialize() is then called by ClangdServer as it is constructed.41/// - module hooks can be called by the server at this point.42/// Server facilities (scheduler etc) are available.43/// - ClangdServer will not be destroyed until all the requests are done.44/// FIXME: Block server shutdown until all the modules are idle.45/// - When shutting down, ClangdServer will wait for all requests to46/// finish, call stop(), and then blockUntilIdle().47/// - feature modules will be destroyed after ClangdLSPServer is destroyed.48///49/// FeatureModules are not threadsafe in general. A module's entrypoints are:50/// - method handlers registered in initializeLSP()51/// - public methods called directly via ClangdServer.featureModule<T>()->...52/// - specific overridable "hook" methods inherited from FeatureModule53/// Unless otherwise specified, these are only called on the main thread.54///55/// Conventionally, standard feature modules live in the `clangd` namespace,56/// and other exposed details live in a sub-namespace.57class FeatureModule {58public:59 virtual ~FeatureModule() {60 /// Perform shutdown sequence on destruction in case the ClangdServer was61 /// never initialized. Usually redundant, but shutdown is idempotent.62 stop();63 blockUntilIdle(Deadline::infinity());64 }65 66 /// Called by the server to connect this feature module to LSP.67 /// The module should register the methods/notifications/commands it handles,68 /// and update the server capabilities to advertise them.69 ///70 /// This is only called if the module is running in ClangdLSPServer!71 /// FeatureModules with a public interface should work without LSP bindings.72 virtual void initializeLSP(LSPBinder &Bind,73 const llvm::json::Object &ClientCaps,74 llvm::json::Object &ServerCaps) {}75 76 /// Shared server facilities needed by the module to get its work done.77 struct Facilities {78 TUScheduler &Scheduler;79 const SymbolIndex *Index;80 const ThreadsafeFS &FS;81 };82 /// Called by the server to prepare this module for use.83 void initialize(const Facilities &F);84 85 /// Requests that the module cancel background work and go idle soon.86 /// Does not block, the caller will call blockUntilIdle() instead.87 /// After a module is stop()ed, it should not receive any more requests.88 /// Called by the server when shutting down.89 /// May be called multiple times, should be idempotent.90 virtual void stop() {}91 92 /// Waits until the module is idle (no background work) or a deadline expires.93 /// In general all modules should eventually go idle, though it may take a94 /// long time (e.g. background indexing).95 /// FeatureModules should go idle quickly if stop() has been called.96 /// Called by the server when shutting down, and also by tests.97 virtual bool blockUntilIdle(Deadline) { return true; }98 99 /// Tweaks implemented by this module. Can be called asynchronously when100 /// enumerating or applying code actions.101 virtual void contributeTweaks(std::vector<std::unique_ptr<Tweak>> &Out) {}102 103 /// Extension point that allows modules to observe and modify an AST build.104 /// One instance is created each time clangd produces a ParsedAST or105 /// PrecompiledPreamble. For a given instance, lifecycle methods are always106 /// called on a single thread.107 struct ASTListener {108 /// Listeners are destroyed once the AST is built.109 virtual ~ASTListener() = default;110 111 /// Called before every AST build, both for main file and preamble. The call112 /// happens immediately before FrontendAction::Execute(), with Preprocessor113 /// set up already and after BeginSourceFile() on main file was called.114 virtual void beforeExecute(CompilerInstance &CI) {}115 116 /// Called everytime a diagnostic is encountered. Modules can use this117 /// modify the final diagnostic, or store some information to surface code118 /// actions later on.119 virtual void sawDiagnostic(const clang::Diagnostic &, clangd::Diag &) {}120 };121 /// Can be called asynchronously before building an AST.122 virtual std::unique_ptr<ASTListener> astListeners() { return nullptr; }123 124protected:125 /// Accessors for modules to access shared server facilities they depend on.126 Facilities &facilities();127 /// The scheduler is used to run tasks on worker threads and access ASTs.128 TUScheduler &scheduler() { return facilities().Scheduler; }129 /// The index is used to get information about the whole codebase.130 const SymbolIndex *index() { return facilities().Index; }131 /// The filesystem is used to read source files on disk.132 const ThreadsafeFS &fs() { return facilities().FS; }133 134 /// Types of function objects that feature modules use for outgoing calls.135 /// (Bound throuh LSPBinder, made available here for convenience).136 template <typename P>137 using OutgoingNotification = llvm::unique_function<void(const P &)>;138 template <typename P, typename R>139 using OutgoingMethod = llvm::unique_function<void(const P &, Callback<R>)>;140 141private:142 std::optional<Facilities> Fac;143};144 145/// A FeatureModuleSet is a collection of feature modules installed in clangd.146///147/// Modules added with explicit type specification can be looked up by type, or148/// used via the FeatureModule interface. This allows individual modules to149/// expose a public API. For this reason, there can be only one feature module150/// of each type.151///152/// Modules added using a base class pointer can be used only via the153/// FeatureModule interface and can't be looked up by type, thus custom public154/// API (if provided by the module) can't be used.155///156/// The set owns the modules. It is itself owned by main, not ClangdServer.157class FeatureModuleSet {158 std::vector<std::unique_ptr<FeatureModule>> Modules;159 llvm::DenseMap<void *, FeatureModule *> Map;160 161 template <typename Mod> struct ID {162 static_assert(std::is_base_of<FeatureModule, Mod>::value &&163 std::is_final<Mod>::value,164 "Modules must be final classes derived from clangd::Module");165 static int Key;166 };167 168 bool addImpl(void *Key, std::unique_ptr<FeatureModule>, const char *Source);169 170public:171 FeatureModuleSet() = default;172 173 static FeatureModuleSet fromRegistry();174 175 using iterator = llvm::pointee_iterator<decltype(Modules)::iterator>;176 using const_iterator =177 llvm::pointee_iterator<decltype(Modules)::const_iterator>;178 iterator begin() { return iterator(Modules.begin()); }179 iterator end() { return iterator(Modules.end()); }180 const_iterator begin() const { return const_iterator(Modules.begin()); }181 const_iterator end() const { return const_iterator(Modules.end()); }182 183 void add(std::unique_ptr<FeatureModule> M);184 template <typename Mod> bool add(std::unique_ptr<Mod> M) {185 return addImpl(&ID<Mod>::Key, std::move(M), LLVM_PRETTY_FUNCTION);186 }187 template <typename Mod> Mod *get() {188 return static_cast<Mod *>(Map.lookup(&ID<Mod>::Key));189 }190 template <typename Mod> const Mod *get() const {191 return const_cast<FeatureModuleSet *>(this)->get<Mod>();192 }193};194 195template <typename Mod> int FeatureModuleSet::ID<Mod>::Key;196 197using FeatureModuleRegistry = llvm::Registry<FeatureModule>;198 199} // namespace clangd200} // namespace clang201 202namespace llvm {203extern template class Registry<clang::clangd::FeatureModule>;204} // namespace llvm205 206#endif207