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 class CompilerInstance; 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: ~FeatureModule()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. initializeLSP(LSPBinder & Bind,const llvm::json::Object & ClientCaps,llvm::json::Object & ServerCaps)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. stop()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. blockUntilIdle(Deadline)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. contributeTweaks(std::vector<std::unique_ptr<Tweak>> & Out)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 before every AST build, both for main file and preamble. The call 110 /// happens immediately before FrontendAction::Execute(), with Preprocessor 111 /// set up already and after BeginSourceFile() on main file was called. beforeExecuteASTListener112 virtual void beforeExecute(CompilerInstance &CI) {} 113 114 /// Called everytime a diagnostic is encountered. Modules can use this 115 /// modify the final diagnostic, or store some information to surface code 116 /// actions later on. sawDiagnosticASTListener117 virtual void sawDiagnostic(const clang::Diagnostic &, clangd::Diag &) {} 118 }; 119 /// Can be called asynchronously before building an AST. astListeners()120 virtual std::unique_ptr<ASTListener> astListeners() { return nullptr; } 121 122 protected: 123 /// Accessors for modules to access shared server facilities they depend on. 124 Facilities &facilities(); 125 /// The scheduler is used to run tasks on worker threads and access ASTs. scheduler()126 TUScheduler &scheduler() { return facilities().Scheduler; } 127 /// The index is used to get information about the whole codebase. index()128 const SymbolIndex *index() { return facilities().Index; } 129 /// The filesystem is used to read source files on disk. fs()130 const ThreadsafeFS &fs() { return facilities().FS; } 131 132 /// Types of function objects that feature modules use for outgoing calls. 133 /// (Bound throuh LSPBinder, made available here for convenience). 134 template <typename P> 135 using OutgoingNotification = llvm::unique_function<void(const P &)>; 136 template <typename P, typename R> 137 using OutgoingMethod = llvm::unique_function<void(const P &, Callback<R>)>; 138 139 private: 140 llvm::Optional<Facilities> Fac; 141 }; 142 143 /// A FeatureModuleSet is a collection of feature modules installed in clangd. 144 /// 145 /// Modules can be looked up by type, or used via the FeatureModule interface. 146 /// This allows individual modules to expose a public API. 147 /// For this reason, there can be only one feature module of each type. 148 /// 149 /// The set owns the modules. It is itself owned by main, not ClangdServer. 150 class FeatureModuleSet { 151 std::vector<std::unique_ptr<FeatureModule>> Modules; 152 llvm::DenseMap<void *, FeatureModule *> Map; 153 154 template <typename Mod> struct ID { 155 static_assert(std::is_base_of<FeatureModule, Mod>::value && 156 std::is_final<Mod>::value, 157 "Modules must be final classes derived from clangd::Module"); 158 static int Key; 159 }; 160 161 bool addImpl(void *Key, std::unique_ptr<FeatureModule>, const char *Source); 162 163 public: 164 FeatureModuleSet() = default; 165 166 using iterator = llvm::pointee_iterator<decltype(Modules)::iterator>; 167 using const_iterator = 168 llvm::pointee_iterator<decltype(Modules)::const_iterator>; begin()169 iterator begin() { return iterator(Modules.begin()); } end()170 iterator end() { return iterator(Modules.end()); } begin()171 const_iterator begin() const { return const_iterator(Modules.begin()); } end()172 const_iterator end() const { return const_iterator(Modules.end()); } 173 add(std::unique_ptr<Mod> M)174 template <typename Mod> bool add(std::unique_ptr<Mod> M) { 175 return addImpl(&ID<Mod>::Key, std::move(M), LLVM_PRETTY_FUNCTION); 176 } get()177 template <typename Mod> Mod *get() { 178 return static_cast<Mod *>(Map.lookup(&ID<Mod>::Key)); 179 } get()180 template <typename Mod> const Mod *get() const { 181 return const_cast<FeatureModuleSet *>(this)->get<Mod>(); 182 } 183 }; 184 185 template <typename Mod> int FeatureModuleSet::ID<Mod>::Key; 186 187 } // namespace clangd 188 } // namespace clang 189 #endif 190