-
Notifications
You must be signed in to change notification settings - Fork 71
Expand file tree
/
Copy pathServer.h
More file actions
248 lines (173 loc) · 7.67 KB
/
Server.h
File metadata and controls
248 lines (173 loc) · 7.67 KB
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
#pragma once
#include "Config.h"
#include "Convert.h"
#include "Indexer.h"
#include "Async/Async.h"
#include "Compiler/Command.h"
#include "Compiler/Diagnostic.h"
#include "Compiler/Preamble.h"
#include "Feature/DocumentLink.h"
#include "Protocol/Protocol.h"
namespace clice {
struct OpenFile {
/// The file version, every edition will increase it.
std::uint32_t version = 0;
/// The file content.
std::string content;
/// We build PCH for every opened file.
std::optional<PCHInfo> pch;
async::Task<bool> pch_build_task;
async::Event pch_built_event;
std::vector<feature::DocumentLink> pch_includes;
/// For each opened file, we would like to build an AST for it.
std::shared_ptr<CompilationUnit> ast;
async::Task<> ast_build_task;
async::Lock ast_built_lock;
/// For header with context, it may have multiple ASTs, use
/// an chain to store them.
std::unique_ptr<OpenFile> next;
};
/// A manager for all OpenFile with LRU cache.
class ActiveFileManager {
public:
/// Use shared_ptr to manage the lifetime of OpenFile object in async function.
using ActiveFile = std::shared_ptr<OpenFile>;
/// A double-linked list to store all opened files. While the `first` field of pair (each node
/// of list) refers to a key in `index`, the `second` field refers to the OpenFile object.
/// In another word, the `index` holds the ownership of path and the `items` holds the
/// ownership of OpenFile object.
using ListContainer = std::list<std::pair<llvm::StringRef, ActiveFile>>;
struct ActiveFileIterator : public ListContainer::const_iterator {};
constexpr static size_t DefaultMaxActiveFileNum = 8;
constexpr static size_t UnlimitedActiveFileNum = 512;
public:
/// Create an ActiveFileManager with a default size.
ActiveFileManager() : capability(DefaultMaxActiveFileNum) {}
ActiveFileManager(const ActiveFileManager&) = delete;
ActiveFileManager& operator=(const ActiveFileManager&) = delete;
/// Set the maximum active file count and it will be clamped to [1, UnlimitedActiveFileNum].
void set_capability(size_t size) {
// Use static_cast to make MSVC happy.
capability = std::clamp(size, static_cast<size_t>(1), UnlimitedActiveFileNum);
}
/// Get the maximum size of the cache.
size_t max_size() const {
return capability;
}
/// Get the current size of the cache.
size_t size() const {
return index.size();
}
/// Try get OpenFile from manager, default construct one if not exists.
[[nodiscard]] ActiveFile& get_or_add(llvm::StringRef path);
/// Add a OpenFile to the manager.
ActiveFile& add(llvm::StringRef path, OpenFile file);
[[nodiscard]] bool contains(llvm::StringRef path) const {
return index.contains(path);
}
ActiveFileIterator begin() const {
return ActiveFileIterator(items.begin());
}
ActiveFileIterator end() const {
return ActiveFileIterator(items.end());
}
private:
ActiveFile& lru_put_impl(llvm::StringRef path, OpenFile file);
private:
/// The maximum size of the cache.
size_t capability;
/// The first element is the most recently used, and the last
/// element is the least recently used.
/// When a file is accessed, it will be moved to the front of the list.
/// When a new file is added, if the size exceeds the maximum size,
/// the last element will be removed.
ListContainer items;
/// A map from path to the iterator of the list.
llvm::StringMap<ListContainer::iterator> index;
};
class Server {
public:
Server();
using Self = Server;
using Callback = async::Task<json::Value> (*)(Server&, json::Value);
template <auto method>
void register_callback(llvm::StringRef name) {
using MF = decltype(method);
static_assert(std::is_member_function_pointer_v<MF>, "");
using F = member_type_t<MF>;
using Ret = function_return_t<F>;
using Params = std::tuple_element_t<0, function_args_t<F>>;
Callback callback = [](Server& server, json::Value value) -> async::Task<json::Value> {
if constexpr(std::is_same_v<Ret, async::Task<>>) {
co_await (server.*method)(json::deserialize<Params>(value));
co_return json::Value(nullptr);
} else {
co_return co_await (server.*method)(json::deserialize<Params>(value));
}
};
callbacks.try_emplace(name, callback);
}
async::Task<> on_receive(json::Value value);
private:
/// Send a request to the client.
async::Task<> request(llvm::StringRef method, json::Value params);
/// Send a notification to the client.
async::Task<> notify(llvm::StringRef method, json::Value params);
/// Send a response to the client.
async::Task<> response(json::Value id, json::Value result);
async::Task<> response(json::Value id, proto::ErrorCodes code, llvm::StringRef message = "");
/// Send an register capability to the client.
async::Task<> registerCapacity(llvm::StringRef id,
llvm::StringRef method,
json::Value registerOptions);
private:
async::Task<json::Value> on_initialize(proto::InitializeParams params);
async::Task<> on_initialized(proto::InitializedParams);
async::Task<json::Value> on_shutdown(proto::ShutdownParams params);
async::Task<> on_exit(proto::ExitParams params);
private:
/// Load the cache info from disk.
void load_cache_info();
/// Save the cache info to disk.
void save_cache_info();
async::Task<bool> build_pch(std::string file, std::string preamble);
async::Task<> build_ast(std::string file, std::string content);
async::Task<std::shared_ptr<OpenFile>> add_document(std::string path, std::string content);
private:
async::Task<> on_did_open(proto::DidOpenTextDocumentParams params);
async::Task<> on_did_change(proto::DidChangeTextDocumentParams params);
async::Task<> on_did_save(proto::DidSaveTextDocumentParams params);
async::Task<> on_did_close(proto::DidCloseTextDocumentParams params);
private:
using Result = async::Task<json::Value>;
auto on_execute_command(proto::ExecuteCommandParams params) -> Result;
auto on_completion(proto::CompletionParams params) -> Result;
auto on_hover(proto::HoverParams params) -> Result;
auto on_signature_help(proto::SignatureHelpParams params) -> Result;
auto on_go_to_declaration(proto::DeclarationParams params) -> Result;
auto on_go_to_definition(proto::DefinitionParams params) -> Result;
auto on_find_references(proto::ReferenceParams params) -> Result;
auto on_document_symbol(proto::DocumentSymbolParams params) -> Result;
auto on_document_link(proto::DocumentLinkParams params) -> Result;
auto on_document_format(proto::DocumentFormattingParams params) -> Result;
auto on_document_range_format(proto::DocumentRangeFormattingParams params) -> Result;
auto on_folding_range(proto::FoldingRangeParams params) -> Result;
auto on_semantic_token(proto::SemanticTokensParams params) -> Result;
auto on_inlay_hint(proto::InlayHintParams params) -> Result;
private:
/// The current request id.
std::uint32_t server_request_id = 0;
std::uint32_t client_request_id = 0;
/// All registered LSP callbacks.
llvm::StringMap<Callback> callbacks;
PositionEncodingKind kind = PositionEncodingKind::UTF16;
std::string workspace;
/// The compilation database.
CompilationDatabase database;
/// All opening files.
ActiveFileManager opening_files;
PathMapping mapping;
config::Config config;
Indexer indexer;
};
} // namespace clice