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