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