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 |
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: |
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. |
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. |
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. |
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. |
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. |
113 | 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. |
118 | virtual void sawDiagnostic(const clang::Diagnostic &, clangd::Diag &) {} |
119 | }; |
120 | /// Can be called asynchronously before building an AST. |
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. |
127 | TUScheduler &scheduler() { return facilities().Scheduler; } |
128 | /// The index is used to get information about the whole codebase. |
129 | const SymbolIndex *index() { return facilities().Index; } |
130 | /// The filesystem is used to read source files on disk. |
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>; |
170 | iterator begin() { return iterator(Modules.begin()); } |
171 | iterator end() { return iterator(Modules.end()); } |
172 | const_iterator begin() const { return const_iterator(Modules.begin()); } |
173 | const_iterator end() const { return const_iterator(Modules.end()); } |
174 | |
175 | template <typename Mod> bool add(std::unique_ptr<Mod> M) { |
176 | return addImpl(Key: &ID<Mod>::Key, std::move(M), LLVM_PRETTY_FUNCTION); |
177 | } |
178 | template <typename Mod> Mod *get() { |
179 | return static_cast<Mod *>(Map.lookup(Val: &ID<Mod>::Key)); |
180 | } |
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 | |