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