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