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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FEATUREMODULE_H 10 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FEATUREMODULE_H 11 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 <memory> 19 #include <type_traits> 20 #include <vector> 21 22 namespace clang { 23 namespace clangd { 24 struct Diag; 25 class LSPBinder; 26 class SymbolIndex; 27 class ThreadsafeFS; 28 class TUScheduler; 29 class Tweak; 30 31 /// A FeatureModule contributes a vertical feature to clangd. 32 /// 33 /// The lifetime of a module is roughly: 34 /// - feature modules are created before the LSP server, in ClangdMain.cpp 35 /// - these modules are then passed to ClangdLSPServer in a FeatureModuleSet 36 /// - initializeLSP() is called when the editor calls initialize. 37 // - initialize() is then called by ClangdServer as it is constructed. 38 /// - module hooks can be called by the server at this point. 39 /// Server facilities (scheduler etc) are available. 40 /// - ClangdServer will not be destroyed until all the requests are done. 41 /// FIXME: Block server shutdown until all the modules are idle. 42 /// - When shutting down, ClangdServer will wait for all requests to 43 /// finish, call stop(), and then blockUntilIdle(). 44 /// - feature modules will be destroyed after ClangdLSPServer is destroyed. 45 /// 46 /// FeatureModules are not threadsafe in general. A module's entrypoints are: 47 /// - method handlers registered in initializeLSP() 48 /// - public methods called directly via ClangdServer.featureModule<T>()->... 49 /// - specific overridable "hook" methods inherited from FeatureModule 50 /// Unless otherwise specified, these are only called on the main thread. 51 /// 52 /// Conventionally, standard feature modules live in the `clangd` namespace, 53 /// and other exposed details live in a sub-namespace. 54 class FeatureModule { 55 public: 56 virtual ~FeatureModule() { 57 /// Perform shutdown sequence on destruction in case the ClangdServer was 58 /// never initialized. Usually redundant, but shutdown is idempotent. 59 stop(); 60 blockUntilIdle(Deadline::infinity()); 61 } 62 63 /// Called by the server to connect this feature module to LSP. 64 /// The module should register the methods/notifications/commands it handles, 65 /// and update the server capabilities to advertise them. 66 /// 67 /// This is only called if the module is running in ClangdLSPServer! 68 /// FeatureModules with a public interface should work without LSP bindings. 69 virtual void initializeLSP(LSPBinder &Bind, 70 const llvm::json::Object &ClientCaps, 71 llvm::json::Object &ServerCaps) {} 72 73 /// Shared server facilities needed by the module to get its work done. 74 struct Facilities { 75 TUScheduler &Scheduler; 76 const SymbolIndex *Index; 77 const ThreadsafeFS &FS; 78 }; 79 /// Called by the server to prepare this module for use. 80 void initialize(const Facilities &F); 81 82 /// Requests that the module cancel background work and go idle soon. 83 /// Does not block, the caller will call blockUntilIdle() instead. 84 /// After a module is stop()ed, it should not receive any more requests. 85 /// Called by the server when shutting down. 86 /// May be called multiple times, should be idempotent. 87 virtual void stop() {} 88 89 /// Waits until the module is idle (no background work) or a deadline expires. 90 /// In general all modules should eventually go idle, though it may take a 91 /// long time (e.g. background indexing). 92 /// FeatureModules should go idle quickly if stop() has been called. 93 /// Called by the server when shutting down, and also by tests. 94 virtual bool blockUntilIdle(Deadline) { return true; } 95 96 /// Tweaks implemented by this module. Can be called asynchronously when 97 /// enumerating or applying code actions. 98 virtual void contributeTweaks(std::vector<std::unique_ptr<Tweak>> &Out) {} 99 100 /// Extension point that allows modules to observe and modify an AST build. 101 /// One instance is created each time clangd produces a ParsedAST or 102 /// PrecompiledPreamble. For a given instance, lifecycle methods are always 103 /// called on a single thread. 104 struct ASTListener { 105 /// Listeners are destroyed once the AST is built. 106 virtual ~ASTListener() = default; 107 108 /// Called everytime a diagnostic is encountered. Modules can use this 109 /// modify the final diagnostic, or store some information to surface code 110 /// actions later on. 111 virtual void sawDiagnostic(const clang::Diagnostic &, clangd::Diag &) {} 112 }; 113 /// Can be called asynchronously before building an AST. 114 virtual std::unique_ptr<ASTListener> astListeners() { return nullptr; } 115 116 protected: 117 /// Accessors for modules to access shared server facilities they depend on. 118 Facilities &facilities(); 119 /// The scheduler is used to run tasks on worker threads and access ASTs. 120 TUScheduler &scheduler() { return facilities().Scheduler; } 121 /// The index is used to get information about the whole codebase. 122 const SymbolIndex *index() { return facilities().Index; } 123 /// The filesystem is used to read source files on disk. 124 const ThreadsafeFS &fs() { return facilities().FS; } 125 126 /// Types of function objects that feature modules use for outgoing calls. 127 /// (Bound throuh LSPBinder, made available here for convenience). 128 template <typename P> 129 using OutgoingNotification = llvm::unique_function<void(const P &)>; 130 template <typename P, typename R> 131 using OutgoingMethod = llvm::unique_function<void(const P &, Callback<R>)>; 132 133 private: 134 llvm::Optional<Facilities> Fac; 135 }; 136 137 /// A FeatureModuleSet is a collection of feature modules installed in clangd. 138 /// 139 /// Modules can be looked up by type, or used via the FeatureModule interface. 140 /// This allows individual modules to expose a public API. 141 /// For this reason, there can be only one feature module of each type. 142 /// 143 /// The set owns the modules. It is itself owned by main, not ClangdServer. 144 class FeatureModuleSet { 145 std::vector<std::unique_ptr<FeatureModule>> Modules; 146 llvm::DenseMap<void *, FeatureModule *> Map; 147 148 template <typename Mod> struct ID { 149 static_assert(std::is_base_of<FeatureModule, Mod>::value && 150 std::is_final<Mod>::value, 151 "Modules must be final classes derived from clangd::Module"); 152 static int Key; 153 }; 154 155 bool addImpl(void *Key, std::unique_ptr<FeatureModule>, const char *Source); 156 157 public: 158 FeatureModuleSet() = default; 159 160 using iterator = llvm::pointee_iterator<decltype(Modules)::iterator>; 161 using const_iterator = 162 llvm::pointee_iterator<decltype(Modules)::const_iterator>; 163 iterator begin() { return iterator(Modules.begin()); } 164 iterator end() { return iterator(Modules.end()); } 165 const_iterator begin() const { return const_iterator(Modules.begin()); } 166 const_iterator end() const { return const_iterator(Modules.end()); } 167 168 template <typename Mod> bool add(std::unique_ptr<Mod> M) { 169 return addImpl(&ID<Mod>::Key, std::move(M), LLVM_PRETTY_FUNCTION); 170 } 171 template <typename Mod> Mod *get() { 172 return static_cast<Mod *>(Map.lookup(&ID<Mod>::Key)); 173 } 174 template <typename Mod> const Mod *get() const { 175 return const_cast<FeatureModuleSet *>(this)->get<Mod>(); 176 } 177 }; 178 179 template <typename Mod> int FeatureModuleSet::ID<Mod>::Key; 180 181 } // namespace clangd 182 } // namespace clang 183 #endif 184