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