1 //===--- FeatureModule.h - Plugging features into clangd ----------*-C++-*-===//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FEATUREMODULE_H
10 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FEATUREMODULE_H
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"
19 #include <type_traits>
23 class CompilerInstance
;
32 /// A FeatureModule contributes a vertical feature to clangd.
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.
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.
53 /// Conventionally, standard feature modules live in the `clangd` namespace,
54 /// and other exposed details live in a sub-namespace.
57 virtual ~FeatureModule() {
58 /// Perform shutdown sequence on destruction in case the ClangdServer was
59 /// never initialized. Usually redundant, but shutdown is idempotent.
61 blockUntilIdle(Deadline::infinity());
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.
68 /// This is only called if the module is running in ClangdLSPServer!
69 /// FeatureModules with a public interface should work without LSP bindings.
70 virtual void initializeLSP(LSPBinder
&Bind
,
71 const llvm::json::Object
&ClientCaps
,
72 llvm::json::Object
&ServerCaps
) {}
74 /// Shared server facilities needed by the module to get its work done.
76 TUScheduler
&Scheduler
;
77 const SymbolIndex
*Index
;
78 const ThreadsafeFS
&FS
;
80 /// Called by the server to prepare this module for use.
81 void initialize(const Facilities
&F
);
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.
88 virtual void stop() {}
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.
95 virtual bool blockUntilIdle(Deadline
) { return true; }
97 /// Tweaks implemented by this module. Can be called asynchronously when
98 /// enumerating or applying code actions.
99 virtual void contributeTweaks(std::vector
<std::unique_ptr
<Tweak
>> &Out
) {}
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.
106 /// Listeners are destroyed once the AST is built.
107 virtual ~ASTListener() = default;
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.
112 virtual void beforeExecute(CompilerInstance
&CI
) {}
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.
117 virtual void sawDiagnostic(const clang::Diagnostic
&, clangd::Diag
&) {}
119 /// Can be called asynchronously before building an AST.
120 virtual std::unique_ptr
<ASTListener
> astListeners() { return nullptr; }
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.
126 TUScheduler
&scheduler() { return facilities().Scheduler
; }
127 /// The index is used to get information about the whole codebase.
128 const SymbolIndex
*index() { return facilities().Index
; }
129 /// The filesystem is used to read source files on disk.
130 const ThreadsafeFS
&fs() { return facilities().FS
; }
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
>)>;
140 llvm::Optional
<Facilities
> Fac
;
143 /// A FeatureModuleSet is a collection of feature modules installed in clangd.
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.
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
;
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");
161 bool addImpl(void *Key
, std::unique_ptr
<FeatureModule
>, const char *Source
);
164 FeatureModuleSet() = default;
166 using iterator
= llvm::pointee_iterator
<decltype(Modules
)::iterator
>;
167 using const_iterator
=
168 llvm::pointee_iterator
<decltype(Modules
)::const_iterator
>;
169 iterator
begin() { return iterator(Modules
.begin()); }
170 iterator
end() { return iterator(Modules
.end()); }
171 const_iterator
begin() const { return const_iterator(Modules
.begin()); }
172 const_iterator
end() const { return const_iterator(Modules
.end()); }
174 template <typename Mod
> bool add(std::unique_ptr
<Mod
> M
) {
175 return addImpl(&ID
<Mod
>::Key
, std::move(M
), LLVM_PRETTY_FUNCTION
);
177 template <typename Mod
> Mod
*get() {
178 return static_cast<Mod
*>(Map
.lookup(&ID
<Mod
>::Key
));
180 template <typename Mod
> const Mod
*get() const {
181 return const_cast<FeatureModuleSet
*>(this)->get
<Mod
>();
185 template <typename Mod
> int FeatureModuleSet::ID
<Mod
>::Key
;
187 } // namespace clangd