-
Notifications
You must be signed in to change notification settings - Fork 13.3k
/
Copy pathFeatureModule.h
161 lines (142 loc) · 6.56 KB
/
FeatureModule.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//===--- FeatureModule.h - Plugging features into clangd ----------*-C++-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FEATUREMODULE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FEATUREMODULE_H
#include "support/Function.h"
#include "support/Threading.h"
#include "llvm/ADT/FunctionExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/JSON.h"
#include <memory>
#include <type_traits>
#include <vector>
namespace clang {
namespace clangd {
class LSPBinder;
class SymbolIndex;
class ThreadsafeFS;
class TUScheduler;
/// A FeatureModule contributes a vertical feature to clangd.
///
/// The lifetime of a module is roughly:
/// - feature modules are created before the LSP server, in ClangdMain.cpp
/// - these modules are then passed to ClangdLSPServer in a FeatureModuleSet
/// - initializeLSP() is called when the editor calls initialize.
// - initialize() is then called by ClangdServer as it is constructed.
/// - module hooks can be called by the server at this point.
/// Server facilities (scheduler etc) are available.
/// - ClangdServer will not be destroyed until all the requests are done.
/// FIXME: Block server shutdown until all the modules are idle.
/// - When shutting down, ClangdServer will wait for all requests to
/// finish, call stop(), and then blockUntilIdle().
/// - feature modules will be destroyed after ClangdLSPServer is destroyed.
///
/// FeatureModules are not threadsafe in general. A module's entrypoints are:
/// - method handlers registered in initializeLSP()
/// - public methods called directly via ClangdServer.featureModule<T>()->...
/// - specific overridable "hook" methods inherited from FeatureModule
/// Unless otherwise specified, these are only called on the main thread.
///
/// Conventionally, standard feature modules live in the `clangd` namespace,
/// and other exposed details live in a sub-namespace.
class FeatureModule {
public:
virtual ~FeatureModule() {
/// Perform shutdown sequence on destruction in case the ClangdServer was
/// never initialized. Usually redundant, but shutdown is idempotent.
stop();
blockUntilIdle(Deadline::infinity());
}
/// Called by the server to connect this feature module to LSP.
/// The module should register the methods/notifications/commands it handles,
/// and update the server capabilities to advertise them.
///
/// This is only called if the module is running in ClangdLSPServer!
/// FeatureModules with a public interface should work without LSP bindings.
virtual void initializeLSP(LSPBinder &Bind,
const llvm::json::Object &ClientCaps,
llvm::json::Object &ServerCaps) {}
/// Shared server facilities needed by the module to get its work done.
struct Facilities {
TUScheduler &Scheduler;
const SymbolIndex *Index;
const ThreadsafeFS &FS;
};
/// Called by the server to prepare this module for use.
void initialize(const Facilities &F);
/// Requests that the module cancel background work and go idle soon.
/// Does not block, the caller will call blockUntilIdle() instead.
/// After a module is stop()ed, it should not receive any more requests.
/// Called by the server when shutting down.
/// May be called multiple times, should be idempotent.
virtual void stop() {}
/// Waits until the module is idle (no background work) or a deadline expires.
/// In general all modules should eventually go idle, though it may take a
/// long time (e.g. background indexing).
/// FeatureModules should go idle quickly if stop() has been called.
/// Called by the server when shutting down, and also by tests.
virtual bool blockUntilIdle(Deadline) { return true; }
protected:
/// Accessors for modules to access shared server facilities they depend on.
Facilities &facilities();
/// The scheduler is used to run tasks on worker threads and access ASTs.
TUScheduler &scheduler() { return facilities().Scheduler; }
/// The index is used to get information about the whole codebase.
const SymbolIndex *index() { return facilities().Index; }
/// The filesystem is used to read source files on disk.
const ThreadsafeFS &fs() { return facilities().FS; }
/// Types of function objects that feature modules use for outgoing calls.
/// (Bound throuh LSPBinder, made available here for convenience).
template <typename P>
using OutgoingNotification = llvm::unique_function<void(const P &)>;
template <typename P, typename R>
using OutgoingMethod = llvm::unique_function<void(const P &, Callback<R>)>;
private:
llvm::Optional<Facilities> Fac;
};
/// A FeatureModuleSet is a collection of feature modules installed in clangd.
///
/// Modules can be looked up by type, or used via the FeatureModule interface.
/// This allows individual modules to expose a public API.
/// For this reason, there can be only one feature module of each type.
///
/// The set owns the modules. It is itself owned by main, not ClangdServer.
class FeatureModuleSet {
std::vector<std::unique_ptr<FeatureModule>> Modules;
llvm::DenseMap<void *, FeatureModule *> Map;
template <typename Mod> struct ID {
static_assert(std::is_base_of<FeatureModule, Mod>::value &&
std::is_final<Mod>::value,
"Modules must be final classes derived from clangd::Module");
static int Key;
};
bool addImpl(void *Key, std::unique_ptr<FeatureModule>, const char *Source);
public:
FeatureModuleSet() = default;
using iterator = llvm::pointee_iterator<decltype(Modules)::iterator>;
using const_iterator =
llvm::pointee_iterator<decltype(Modules)::const_iterator>;
iterator begin() { return iterator(Modules.begin()); }
iterator end() { return iterator(Modules.end()); }
const_iterator begin() const { return const_iterator(Modules.begin()); }
const_iterator end() const { return const_iterator(Modules.end()); }
template <typename Mod> bool add(std::unique_ptr<Mod> M) {
return addImpl(&ID<Mod>::Key, std::move(M), LLVM_PRETTY_FUNCTION);
}
template <typename Mod> Mod *get() {
return static_cast<Mod *>(Map.lookup(&ID<Mod>::Key));
}
template <typename Mod> const Mod *get() const {
return const_cast<FeatureModuleSet *>(this)->get<Mod>();
}
};
template <typename Mod> int FeatureModuleSet::ID<Mod>::Key;
} // namespace clangd
} // namespace clang
#endif