blob: d1fdb657d4268b744d17b23abaf3d225795991cd [file] [log] [blame]
Ilya Biryukov38d79772017-05-16 09:38:59 +00001//===--- ClangdLSPServer.cpp - LSP server ------------------------*- C++-*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
Kirill Bobyrev8e35f1e2018-08-14 16:03:32 +00008//===----------------------------------------------------------------------===//
Ilya Biryukov38d79772017-05-16 09:38:59 +00009
10#include "ClangdLSPServer.h"
Ilya Biryukov71028b82018-03-12 15:28:22 +000011#include "Diagnostics.h"
Sam McCallb536a2a2017-12-19 12:23:48 +000012#include "SourceCode.h"
Sam McCall2c30fbc2018-10-18 12:32:04 +000013#include "Trace.h"
Eric Liu78ed91a72018-01-29 15:37:46 +000014#include "URI.h"
Kadir Cetinkaya689bf932018-08-24 13:09:41 +000015#include "llvm/ADT/ScopeExit.h"
Simon Marchi9569fd52018-03-16 14:30:42 +000016#include "llvm/Support/Errc.h"
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +000017#include "llvm/Support/FormatVariadic.h"
Eric Liu5740ff52018-01-31 16:26:27 +000018#include "llvm/Support/Path.h"
Sam McCall2c30fbc2018-10-18 12:32:04 +000019#include "llvm/Support/ScopedPrinter.h"
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +000020
Sam McCalld20d7982018-07-09 14:25:59 +000021using namespace llvm;
Sam McCallc008af62018-10-20 15:30:37 +000022namespace clang {
23namespace clangd {
Ilya Biryukovafb55542017-05-16 14:40:30 +000024namespace {
25
Ilya Biryukov19d75602018-11-23 15:21:19 +000026void adjustSymbolKinds(llvm::MutableArrayRef<DocumentSymbol> Syms,
27 SymbolKindBitset Kinds) {
28 for (auto &S : Syms) {
29 S.kind = adjustKindToCapability(S.kind, Kinds);
30 adjustSymbolKinds(S.children, Kinds);
31 }
32}
33
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +000034SymbolKindBitset defaultSymbolKinds() {
35 SymbolKindBitset Defaults;
36 for (size_t I = SymbolKindMin; I <= static_cast<size_t>(SymbolKind::Array);
37 ++I)
38 Defaults.set(I);
39 return Defaults;
40}
41
Kadir Cetinkaya133d46f2018-09-27 17:13:07 +000042CompletionItemKindBitset defaultCompletionItemKinds() {
43 CompletionItemKindBitset Defaults;
44 for (size_t I = CompletionItemKindMin;
45 I <= static_cast<size_t>(CompletionItemKind::Reference); ++I)
46 Defaults.set(I);
47 return Defaults;
48}
49
Ilya Biryukovafb55542017-05-16 14:40:30 +000050} // namespace
51
Sam McCall2c30fbc2018-10-18 12:32:04 +000052// MessageHandler dispatches incoming LSP messages.
53// It handles cross-cutting concerns:
54// - serializes/deserializes protocol objects to JSON
55// - logging of inbound messages
56// - cancellation handling
57// - basic call tracing
Sam McCall3d0adbe2018-10-18 14:41:50 +000058// MessageHandler ensures that initialize() is called before any other handler.
Sam McCall2c30fbc2018-10-18 12:32:04 +000059class ClangdLSPServer::MessageHandler : public Transport::MessageHandler {
60public:
61 MessageHandler(ClangdLSPServer &Server) : Server(Server) {}
62
63 bool onNotify(StringRef Method, json::Value Params) override {
64 log("<-- {0}", Method);
65 if (Method == "exit")
66 return false;
Sam McCall3d0adbe2018-10-18 14:41:50 +000067 if (!Server.Server)
68 elog("Notification {0} before initialization", Method);
69 else if (Method == "$/cancelRequest")
Sam McCall2c30fbc2018-10-18 12:32:04 +000070 onCancel(std::move(Params));
71 else if (auto Handler = Notifications.lookup(Method))
72 Handler(std::move(Params));
73 else
74 log("unhandled notification {0}", Method);
75 return true;
76 }
77
78 bool onCall(StringRef Method, json::Value Params, json::Value ID) override {
Sam McCalle2f3a732018-10-24 14:26:26 +000079 // Calls can be canceled by the client. Add cancellation context.
80 WithContext WithCancel(cancelableRequestContext(ID));
81 trace::Span Tracer(Method);
82 SPAN_ATTACH(Tracer, "Params", Params);
83 ReplyOnce Reply(ID, Method, &Server, Tracer.Args);
Sam McCall2c30fbc2018-10-18 12:32:04 +000084 log("<-- {0}({1})", Method, ID);
Sam McCall3d0adbe2018-10-18 14:41:50 +000085 if (!Server.Server && Method != "initialize") {
86 elog("Call {0} before initialization.", Method);
Sam McCalle2f3a732018-10-24 14:26:26 +000087 Reply(make_error<LSPError>("server not initialized",
88 ErrorCode::ServerNotInitialized));
Sam McCall3d0adbe2018-10-18 14:41:50 +000089 } else if (auto Handler = Calls.lookup(Method))
Sam McCalle2f3a732018-10-24 14:26:26 +000090 Handler(std::move(Params), std::move(Reply));
Sam McCall2c30fbc2018-10-18 12:32:04 +000091 else
Sam McCalle2f3a732018-10-24 14:26:26 +000092 Reply(
93 make_error<LSPError>("method not found", ErrorCode::MethodNotFound));
Sam McCall2c30fbc2018-10-18 12:32:04 +000094 return true;
95 }
96
97 bool onReply(json::Value ID, Expected<json::Value> Result) override {
98 // We ignore replies, just log them.
99 if (Result)
100 log("<-- reply({0})", ID);
101 else
Sam McCallc008af62018-10-20 15:30:37 +0000102 log("<-- reply({0}) error: {1}", ID, toString(Result.takeError()));
Sam McCall2c30fbc2018-10-18 12:32:04 +0000103 return true;
104 }
105
106 // Bind an LSP method name to a call.
Sam McCalle2f3a732018-10-24 14:26:26 +0000107 template <typename Param, typename Result>
Sam McCall2c30fbc2018-10-18 12:32:04 +0000108 void bind(const char *Method,
Sam McCalle2f3a732018-10-24 14:26:26 +0000109 void (ClangdLSPServer::*Handler)(const Param &, Callback<Result>)) {
Sam McCall2c30fbc2018-10-18 12:32:04 +0000110 Calls[Method] = [Method, Handler, this](json::Value RawParams,
Sam McCalle2f3a732018-10-24 14:26:26 +0000111 ReplyOnce Reply) {
Sam McCall2c30fbc2018-10-18 12:32:04 +0000112 Param P;
Sam McCalle2f3a732018-10-24 14:26:26 +0000113 if (fromJSON(RawParams, P)) {
114 (Server.*Handler)(P, std::move(Reply));
115 } else {
Sam McCall2c30fbc2018-10-18 12:32:04 +0000116 elog("Failed to decode {0} request.", Method);
Sam McCalle2f3a732018-10-24 14:26:26 +0000117 Reply(make_error<LSPError>("failed to decode request",
118 ErrorCode::InvalidRequest));
Sam McCall2c30fbc2018-10-18 12:32:04 +0000119 }
Sam McCall2c30fbc2018-10-18 12:32:04 +0000120 };
121 }
122
123 // Bind an LSP method name to a notification.
124 template <typename Param>
125 void bind(const char *Method,
126 void (ClangdLSPServer::*Handler)(const Param &)) {
127 Notifications[Method] = [Method, Handler, this](json::Value RawParams) {
128 Param P;
129 if (!fromJSON(RawParams, P)) {
130 elog("Failed to decode {0} request.", Method);
131 return;
132 }
133 trace::Span Tracer(Method);
134 SPAN_ATTACH(Tracer, "Params", RawParams);
135 (Server.*Handler)(P);
136 };
137 }
138
139private:
Sam McCalle2f3a732018-10-24 14:26:26 +0000140 // Function object to reply to an LSP call.
141 // Each instance must be called exactly once, otherwise:
142 // - the bug is logged, and (in debug mode) an assert will fire
143 // - if there was no reply, an error reply is sent
144 // - if there were multiple replies, only the first is sent
145 class ReplyOnce {
146 std::atomic<bool> Replied = {false};
Sam McCalld7babe42018-10-24 15:18:40 +0000147 std::chrono::steady_clock::time_point Start;
Sam McCalle2f3a732018-10-24 14:26:26 +0000148 json::Value ID;
149 std::string Method;
150 ClangdLSPServer *Server; // Null when moved-from.
151 json::Object *TraceArgs;
152
153 public:
154 ReplyOnce(const json::Value &ID, StringRef Method, ClangdLSPServer *Server,
155 json::Object *TraceArgs)
Sam McCalld7babe42018-10-24 15:18:40 +0000156 : Start(std::chrono::steady_clock::now()), ID(ID), Method(Method),
157 Server(Server), TraceArgs(TraceArgs) {
Sam McCalle2f3a732018-10-24 14:26:26 +0000158 assert(Server);
159 }
160 ReplyOnce(ReplyOnce &&Other)
Sam McCalld7babe42018-10-24 15:18:40 +0000161 : Replied(Other.Replied.load()), Start(Other.Start),
162 ID(std::move(Other.ID)), Method(std::move(Other.Method)),
163 Server(Other.Server), TraceArgs(Other.TraceArgs) {
Sam McCalle2f3a732018-10-24 14:26:26 +0000164 Other.Server = nullptr;
165 }
166 ReplyOnce& operator=(ReplyOnce&&) = delete;
167 ReplyOnce(const ReplyOnce &) = delete;
168 ReplyOnce& operator=(const ReplyOnce&) = delete;
169
170 ~ReplyOnce() {
171 if (Server && !Replied) {
172 elog("No reply to message {0}({1})", Method, ID);
173 assert(false && "must reply to all calls!");
174 (*this)(make_error<LSPError>("server failed to reply",
175 ErrorCode::InternalError));
176 }
177 }
178
179 void operator()(Expected<json::Value> Reply) {
180 assert(Server && "moved-from!");
181 if (Replied.exchange(true)) {
182 elog("Replied twice to message {0}({1})", Method, ID);
183 assert(false && "must reply to each call only once!");
184 return;
185 }
Sam McCalld7babe42018-10-24 15:18:40 +0000186 auto Duration = std::chrono::steady_clock::now() - Start;
187 if (Reply) {
188 log("--> reply:{0}({1}) {2:ms}", Method, ID, Duration);
189 if (TraceArgs)
Sam McCalle2f3a732018-10-24 14:26:26 +0000190 (*TraceArgs)["Reply"] = *Reply;
Sam McCalld7babe42018-10-24 15:18:40 +0000191 std::lock_guard<std::mutex> Lock(Server->TranspWriter);
192 Server->Transp.reply(std::move(ID), std::move(Reply));
193 } else {
194 Error Err = Reply.takeError();
195 log("--> reply:{0}({1}) {2:ms}, error: {3}", Method, ID, Duration, Err);
196 if (TraceArgs)
Sam McCalle2f3a732018-10-24 14:26:26 +0000197 (*TraceArgs)["Error"] = to_string(Err);
Sam McCalld7babe42018-10-24 15:18:40 +0000198 std::lock_guard<std::mutex> Lock(Server->TranspWriter);
199 Server->Transp.reply(std::move(ID), std::move(Err));
Sam McCalle2f3a732018-10-24 14:26:26 +0000200 }
Sam McCalle2f3a732018-10-24 14:26:26 +0000201 }
202 };
203
Sam McCallc008af62018-10-20 15:30:37 +0000204 StringMap<std::function<void(json::Value)>> Notifications;
Sam McCalle2f3a732018-10-24 14:26:26 +0000205 StringMap<std::function<void(json::Value, ReplyOnce)>> Calls;
Sam McCall2c30fbc2018-10-18 12:32:04 +0000206
207 // Method calls may be cancelled by ID, so keep track of their state.
208 // This needs a mutex: handlers may finish on a different thread, and that's
209 // when we clean up entries in the map.
210 mutable std::mutex RequestCancelersMutex;
Sam McCallc008af62018-10-20 15:30:37 +0000211 StringMap<std::pair<Canceler, /*Cookie*/ unsigned>> RequestCancelers;
Sam McCall2c30fbc2018-10-18 12:32:04 +0000212 unsigned NextRequestCookie = 0; // To disambiguate reused IDs, see below.
Sam McCallc008af62018-10-20 15:30:37 +0000213 void onCancel(const json::Value &Params) {
Sam McCall2c30fbc2018-10-18 12:32:04 +0000214 const json::Value *ID = nullptr;
215 if (auto *O = Params.getAsObject())
216 ID = O->get("id");
217 if (!ID) {
218 elog("Bad cancellation request: {0}", Params);
219 return;
220 }
Sam McCallc008af62018-10-20 15:30:37 +0000221 auto StrID = to_string(*ID);
Sam McCall2c30fbc2018-10-18 12:32:04 +0000222 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
223 auto It = RequestCancelers.find(StrID);
224 if (It != RequestCancelers.end())
225 It->second.first(); // Invoke the canceler.
226 }
227 // We run cancelable requests in a context that does two things:
228 // - allows cancellation using RequestCancelers[ID]
229 // - cleans up the entry in RequestCancelers when it's no longer needed
230 // If a client reuses an ID, the last wins and the first cannot be canceled.
231 Context cancelableRequestContext(const json::Value &ID) {
232 auto Task = cancelableTask();
Sam McCallc008af62018-10-20 15:30:37 +0000233 auto StrID = to_string(ID); // JSON-serialize ID for map key.
Sam McCall2c30fbc2018-10-18 12:32:04 +0000234 auto Cookie = NextRequestCookie++; // No lock, only called on main thread.
235 {
236 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
237 RequestCancelers[StrID] = {std::move(Task.second), Cookie};
238 }
239 // When the request ends, we can clean up the entry we just added.
240 // The cookie lets us check that it hasn't been overwritten due to ID
241 // reuse.
242 return Task.first.derive(make_scope_exit([this, StrID, Cookie] {
243 std::lock_guard<std::mutex> Lock(RequestCancelersMutex);
244 auto It = RequestCancelers.find(StrID);
245 if (It != RequestCancelers.end() && It->second.second == Cookie)
246 RequestCancelers.erase(It);
247 }));
248 }
249
250 ClangdLSPServer &Server;
251};
252
253// call(), notify(), and reply() wrap the Transport, adding logging and locking.
254void ClangdLSPServer::call(StringRef Method, json::Value Params) {
255 auto ID = NextCallID++;
256 log("--> {0}({1})", Method, ID);
257 // We currently don't handle responses, so no need to store ID anywhere.
258 std::lock_guard<std::mutex> Lock(TranspWriter);
259 Transp.call(Method, std::move(Params), ID);
260}
261
262void ClangdLSPServer::notify(StringRef Method, json::Value Params) {
263 log("--> {0}", Method);
264 std::lock_guard<std::mutex> Lock(TranspWriter);
265 Transp.notify(Method, std::move(Params));
266}
267
Sam McCall2c30fbc2018-10-18 12:32:04 +0000268void ClangdLSPServer::onInitialize(const InitializeParams &Params,
269 Callback<json::Value> Reply) {
Sam McCall0d9b40f2018-10-19 15:42:23 +0000270 if (Params.rootUri && *Params.rootUri)
271 ClangdServerOpts.WorkspaceRoot = Params.rootUri->file();
272 else if (Params.rootPath && !Params.rootPath->empty())
273 ClangdServerOpts.WorkspaceRoot = *Params.rootPath;
Sam McCall3d0adbe2018-10-18 14:41:50 +0000274 if (Server)
275 return Reply(make_error<LSPError>("server already initialized",
276 ErrorCode::InvalidRequest));
Sam McCallbc904612018-10-25 04:22:52 +0000277 if (const auto &Dir = Params.initializationOptions.compilationDatabasePath)
278 CompileCommandsDir = Dir;
Sam McCallc55d09a2018-11-02 13:09:36 +0000279 if (UseDirBasedCDB)
280 BaseCDB = llvm::make_unique<DirectoryBasedGlobalCompilationDatabase>(
281 CompileCommandsDir);
Sam McCall6980edb2018-11-02 14:07:51 +0000282 CDB.emplace(BaseCDB.get(), Params.initializationOptions.fallbackFlags);
Sam McCallc55d09a2018-11-02 13:09:36 +0000283 Server.emplace(*CDB, FSProvider, static_cast<DiagnosticsConsumer &>(*this),
284 ClangdServerOpts);
Sam McCallbc904612018-10-25 04:22:52 +0000285 applyConfiguration(Params.initializationOptions.ConfigSettings);
Simon Marchi88016782018-08-01 11:28:49 +0000286
Sam McCallbf6a2fc2018-10-17 07:33:42 +0000287 CCOpts.EnableSnippets = Params.capabilities.CompletionSnippets;
288 DiagOpts.EmbedFixesInDiagnostics = Params.capabilities.DiagnosticFixes;
289 DiagOpts.SendDiagnosticCategory = Params.capabilities.DiagnosticCategory;
290 if (Params.capabilities.WorkspaceSymbolKinds)
291 SupportedSymbolKinds |= *Params.capabilities.WorkspaceSymbolKinds;
292 if (Params.capabilities.CompletionItemKinds)
293 SupportedCompletionItemKinds |= *Params.capabilities.CompletionItemKinds;
294 SupportsCodeAction = Params.capabilities.CodeActionStructure;
Ilya Biryukov19d75602018-11-23 15:21:19 +0000295 SupportsHierarchicalDocumentSymbol =
296 Params.capabilities.HierarchicalDocumentSymbol;
Kadir Cetinkaya133d46f2018-09-27 17:13:07 +0000297
Sam McCall2c30fbc2018-10-18 12:32:04 +0000298 Reply(json::Object{
Sam McCall0930ab02017-11-07 15:49:35 +0000299 {{"capabilities",
Sam McCalld20d7982018-07-09 14:25:59 +0000300 json::Object{
Simon Marchi98082622018-03-26 14:41:40 +0000301 {"textDocumentSync", (int)TextDocumentSyncKind::Incremental},
Sam McCall0930ab02017-11-07 15:49:35 +0000302 {"documentFormattingProvider", true},
303 {"documentRangeFormattingProvider", true},
304 {"documentOnTypeFormattingProvider",
Sam McCalld20d7982018-07-09 14:25:59 +0000305 json::Object{
Sam McCall0930ab02017-11-07 15:49:35 +0000306 {"firstTriggerCharacter", "}"},
307 {"moreTriggerCharacter", {}},
308 }},
309 {"codeActionProvider", true},
310 {"completionProvider",
Sam McCalld20d7982018-07-09 14:25:59 +0000311 json::Object{
Sam McCall0930ab02017-11-07 15:49:35 +0000312 {"resolveProvider", false},
313 {"triggerCharacters", {".", ">", ":"}},
314 }},
315 {"signatureHelpProvider",
Sam McCalld20d7982018-07-09 14:25:59 +0000316 json::Object{
Sam McCall0930ab02017-11-07 15:49:35 +0000317 {"triggerCharacters", {"(", ","}},
318 }},
319 {"definitionProvider", true},
Ilya Biryukov0e6a51f2017-12-12 12:27:47 +0000320 {"documentHighlightProvider", true},
Marc-Andre Laperle3e618ed2018-02-16 21:38:15 +0000321 {"hoverProvider", true},
Haojian Wu345099c2017-11-09 11:30:04 +0000322 {"renameProvider", true},
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000323 {"documentSymbolProvider", true},
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000324 {"workspaceSymbolProvider", true},
Sam McCall1ad142f2018-09-05 11:53:07 +0000325 {"referencesProvider", true},
Sam McCall0930ab02017-11-07 15:49:35 +0000326 {"executeCommandProvider",
Sam McCalld20d7982018-07-09 14:25:59 +0000327 json::Object{
Eric Liu2c190532018-05-15 15:23:53 +0000328 {"commands", {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND}},
Sam McCall0930ab02017-11-07 15:49:35 +0000329 }},
330 }}}});
Ilya Biryukovafb55542017-05-16 14:40:30 +0000331}
332
Sam McCall2c30fbc2018-10-18 12:32:04 +0000333void ClangdLSPServer::onShutdown(const ShutdownParams &Params,
334 Callback<std::nullptr_t> Reply) {
Ilya Biryukov0d9b8a32017-10-25 08:45:41 +0000335 // Do essentially nothing, just say we're ready to exit.
336 ShutdownRequestReceived = true;
Sam McCall2c30fbc2018-10-18 12:32:04 +0000337 Reply(nullptr);
Sam McCall8a5dded2017-10-12 13:29:58 +0000338}
Ilya Biryukovafb55542017-05-16 14:40:30 +0000339
Sam McCall422c8282018-11-26 16:00:11 +0000340// sync is a clangd extension: it blocks until all background work completes.
341// It blocks the calling thread, so no messages are processed until it returns!
342void ClangdLSPServer::onSync(const NoParams &Params,
343 Callback<std::nullptr_t> Reply) {
344 if (Server->blockUntilIdleForTest(/*TimeoutSeconds=*/60))
345 Reply(nullptr);
346 else
347 Reply(createStringError(llvm::inconvertibleErrorCode(),
348 "Not idle after a minute"));
349}
350
Sam McCall2c30fbc2018-10-18 12:32:04 +0000351void ClangdLSPServer::onDocumentDidOpen(
352 const DidOpenTextDocumentParams &Params) {
Simon Marchi9569fd52018-03-16 14:30:42 +0000353 PathRef File = Params.textDocument.uri.file();
Ilya Biryukovb10ef472018-06-13 09:20:41 +0000354
Sam McCall2c30fbc2018-10-18 12:32:04 +0000355 const std::string &Contents = Params.textDocument.text;
Simon Marchi9569fd52018-03-16 14:30:42 +0000356
Simon Marchi98082622018-03-26 14:41:40 +0000357 DraftMgr.addDraft(File, Contents);
Ilya Biryukov652364b2018-09-26 05:48:29 +0000358 Server->addDocument(File, Contents, WantDiagnostics::Yes);
Ilya Biryukovafb55542017-05-16 14:40:30 +0000359}
360
Sam McCall2c30fbc2018-10-18 12:32:04 +0000361void ClangdLSPServer::onDocumentDidChange(
362 const DidChangeTextDocumentParams &Params) {
Eric Liu51fed182018-02-22 18:40:39 +0000363 auto WantDiags = WantDiagnostics::Auto;
364 if (Params.wantDiagnostics.hasValue())
365 WantDiags = Params.wantDiagnostics.getValue() ? WantDiagnostics::Yes
366 : WantDiagnostics::No;
Simon Marchi9569fd52018-03-16 14:30:42 +0000367
368 PathRef File = Params.textDocument.uri.file();
Sam McCallc008af62018-10-20 15:30:37 +0000369 Expected<std::string> Contents =
Simon Marchi98082622018-03-26 14:41:40 +0000370 DraftMgr.updateDraft(File, Params.contentChanges);
371 if (!Contents) {
372 // If this fails, we are most likely going to be not in sync anymore with
373 // the client. It is better to remove the draft and let further operations
374 // fail rather than giving wrong results.
375 DraftMgr.removeDraft(File);
Ilya Biryukov652364b2018-09-26 05:48:29 +0000376 Server->removeDocument(File);
Sam McCallbed58852018-07-11 10:35:11 +0000377 elog("Failed to update {0}: {1}", File, Contents.takeError());
Simon Marchi98082622018-03-26 14:41:40 +0000378 return;
379 }
Simon Marchi9569fd52018-03-16 14:30:42 +0000380
Ilya Biryukov652364b2018-09-26 05:48:29 +0000381 Server->addDocument(File, *Contents, WantDiags);
Ilya Biryukovafb55542017-05-16 14:40:30 +0000382}
383
Sam McCall2c30fbc2018-10-18 12:32:04 +0000384void ClangdLSPServer::onFileEvent(const DidChangeWatchedFilesParams &Params) {
Ilya Biryukov652364b2018-09-26 05:48:29 +0000385 Server->onFileEvent(Params);
Marc-Andre Laperlebf114242017-10-02 18:00:37 +0000386}
387
Sam McCall2c30fbc2018-10-18 12:32:04 +0000388void ClangdLSPServer::onCommand(const ExecuteCommandParams &Params,
389 Callback<json::Value> Reply) {
390 auto ApplyEdit = [&](WorkspaceEdit WE) {
Eric Liuc5105f92018-02-16 14:15:55 +0000391 ApplyWorkspaceEditParams Edit;
392 Edit.edit = std::move(WE);
Eric Liuc5105f92018-02-16 14:15:55 +0000393 // Ideally, we would wait for the response and if there is no error, we
394 // would reply success/failure to the original RPC.
395 call("workspace/applyEdit", Edit);
396 };
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000397 if (Params.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND &&
398 Params.workspaceEdit) {
399 // The flow for "apply-fix" :
400 // 1. We publish a diagnostic, including fixits
401 // 2. The user clicks on the diagnostic, the editor asks us for code actions
402 // 3. We send code actions, with the fixit embedded as context
403 // 4. The user selects the fixit, the editor asks us to apply it
404 // 5. We unwrap the changes and send them back to the editor
405 // 6. The editor applies the changes (applyEdit), and sends us a reply (but
406 // we ignore it)
407
Sam McCall2c30fbc2018-10-18 12:32:04 +0000408 Reply("Fix applied.");
Eric Liuc5105f92018-02-16 14:15:55 +0000409 ApplyEdit(*Params.workspaceEdit);
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000410 } else {
411 // We should not get here because ExecuteCommandParams would not have
412 // parsed in the first place and this handler should not be called. But if
413 // more commands are added, this will be here has a safe guard.
Sam McCall2c30fbc2018-10-18 12:32:04 +0000414 Reply(make_error<LSPError>(
Sam McCallc008af62018-10-20 15:30:37 +0000415 formatv("Unsupported command \"{0}\".", Params.command).str(),
Sam McCall2c30fbc2018-10-18 12:32:04 +0000416 ErrorCode::InvalidParams));
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000417 }
418}
419
Sam McCall2c30fbc2018-10-18 12:32:04 +0000420void ClangdLSPServer::onWorkspaceSymbol(
421 const WorkspaceSymbolParams &Params,
422 Callback<std::vector<SymbolInformation>> Reply) {
Ilya Biryukov652364b2018-09-26 05:48:29 +0000423 Server->workspaceSymbols(
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000424 Params.query, CCOpts.Limit,
Sam McCall2c30fbc2018-10-18 12:32:04 +0000425 Bind(
426 [this](decltype(Reply) Reply,
Sam McCallc008af62018-10-20 15:30:37 +0000427 Expected<std::vector<SymbolInformation>> Items) {
Sam McCall2c30fbc2018-10-18 12:32:04 +0000428 if (!Items)
429 return Reply(Items.takeError());
430 for (auto &Sym : *Items)
431 Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds);
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000432
Sam McCall2c30fbc2018-10-18 12:32:04 +0000433 Reply(std::move(*Items));
434 },
435 std::move(Reply)));
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000436}
437
Sam McCall2c30fbc2018-10-18 12:32:04 +0000438void ClangdLSPServer::onRename(const RenameParams &Params,
439 Callback<WorkspaceEdit> Reply) {
Ilya Biryukov7d60d202018-02-16 12:20:47 +0000440 Path File = Params.textDocument.uri.file();
Sam McCallc008af62018-10-20 15:30:37 +0000441 Optional<std::string> Code = DraftMgr.getDraft(File);
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000442 if (!Code)
Sam McCall2c30fbc2018-10-18 12:32:04 +0000443 return Reply(make_error<LSPError>("onRename called for non-added file",
444 ErrorCode::InvalidParams));
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000445
Ilya Biryukov652364b2018-09-26 05:48:29 +0000446 Server->rename(
Ilya Biryukov2c5e8e82018-02-15 13:15:47 +0000447 File, Params.position, Params.newName,
Sam McCall2c30fbc2018-10-18 12:32:04 +0000448 Bind(
Sam McCallc008af62018-10-20 15:30:37 +0000449 [File, Code,
450 Params](decltype(Reply) Reply,
451 Expected<std::vector<tooling::Replacement>> Replacements) {
Sam McCall2c30fbc2018-10-18 12:32:04 +0000452 if (!Replacements)
453 return Reply(Replacements.takeError());
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000454
Sam McCall2c30fbc2018-10-18 12:32:04 +0000455 // Turn the replacements into the format specified by the Language
456 // Server Protocol. Fuse them into one big JSON array.
457 std::vector<TextEdit> Edits;
458 for (const auto &R : *Replacements)
459 Edits.push_back(replacementToEdit(*Code, R));
460 WorkspaceEdit WE;
461 WE.changes = {{Params.textDocument.uri.uri(), Edits}};
462 Reply(WE);
463 },
464 std::move(Reply)));
Haojian Wu345099c2017-11-09 11:30:04 +0000465}
466
Sam McCall2c30fbc2018-10-18 12:32:04 +0000467void ClangdLSPServer::onDocumentDidClose(
468 const DidCloseTextDocumentParams &Params) {
Simon Marchi9569fd52018-03-16 14:30:42 +0000469 PathRef File = Params.textDocument.uri.file();
470 DraftMgr.removeDraft(File);
Ilya Biryukov652364b2018-09-26 05:48:29 +0000471 Server->removeDocument(File);
Ilya Biryukovafb55542017-05-16 14:40:30 +0000472}
473
Sam McCall4db732a2017-09-30 10:08:52 +0000474void ClangdLSPServer::onDocumentOnTypeFormatting(
Sam McCall2c30fbc2018-10-18 12:32:04 +0000475 const DocumentOnTypeFormattingParams &Params,
476 Callback<std::vector<TextEdit>> Reply) {
Ilya Biryukov7d60d202018-02-16 12:20:47 +0000477 auto File = Params.textDocument.uri.file();
Simon Marchi9569fd52018-03-16 14:30:42 +0000478 auto Code = DraftMgr.getDraft(File);
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000479 if (!Code)
Sam McCall2c30fbc2018-10-18 12:32:04 +0000480 return Reply(make_error<LSPError>(
481 "onDocumentOnTypeFormatting called for non-added file",
482 ErrorCode::InvalidParams));
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000483
Ilya Biryukov652364b2018-09-26 05:48:29 +0000484 auto ReplacementsOrError = Server->formatOnType(*Code, File, Params.position);
Raoul Wols212bcf82017-12-12 20:25:06 +0000485 if (ReplacementsOrError)
Sam McCall2c30fbc2018-10-18 12:32:04 +0000486 Reply(replacementsToEdits(*Code, ReplacementsOrError.get()));
Raoul Wols212bcf82017-12-12 20:25:06 +0000487 else
Sam McCall2c30fbc2018-10-18 12:32:04 +0000488 Reply(ReplacementsOrError.takeError());
Ilya Biryukovafb55542017-05-16 14:40:30 +0000489}
490
Sam McCall4db732a2017-09-30 10:08:52 +0000491void ClangdLSPServer::onDocumentRangeFormatting(
Sam McCall2c30fbc2018-10-18 12:32:04 +0000492 const DocumentRangeFormattingParams &Params,
493 Callback<std::vector<TextEdit>> Reply) {
Ilya Biryukov7d60d202018-02-16 12:20:47 +0000494 auto File = Params.textDocument.uri.file();
Simon Marchi9569fd52018-03-16 14:30:42 +0000495 auto Code = DraftMgr.getDraft(File);
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000496 if (!Code)
Sam McCall2c30fbc2018-10-18 12:32:04 +0000497 return Reply(make_error<LSPError>(
498 "onDocumentRangeFormatting called for non-added file",
499 ErrorCode::InvalidParams));
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000500
Ilya Biryukov652364b2018-09-26 05:48:29 +0000501 auto ReplacementsOrError = Server->formatRange(*Code, File, Params.range);
Raoul Wols212bcf82017-12-12 20:25:06 +0000502 if (ReplacementsOrError)
Sam McCall2c30fbc2018-10-18 12:32:04 +0000503 Reply(replacementsToEdits(*Code, ReplacementsOrError.get()));
Raoul Wols212bcf82017-12-12 20:25:06 +0000504 else
Sam McCall2c30fbc2018-10-18 12:32:04 +0000505 Reply(ReplacementsOrError.takeError());
Ilya Biryukovafb55542017-05-16 14:40:30 +0000506}
507
Sam McCall2c30fbc2018-10-18 12:32:04 +0000508void ClangdLSPServer::onDocumentFormatting(
509 const DocumentFormattingParams &Params,
510 Callback<std::vector<TextEdit>> Reply) {
Ilya Biryukov7d60d202018-02-16 12:20:47 +0000511 auto File = Params.textDocument.uri.file();
Simon Marchi9569fd52018-03-16 14:30:42 +0000512 auto Code = DraftMgr.getDraft(File);
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000513 if (!Code)
Sam McCall2c30fbc2018-10-18 12:32:04 +0000514 return Reply(
515 make_error<LSPError>("onDocumentFormatting called for non-added file",
516 ErrorCode::InvalidParams));
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000517
Ilya Biryukov652364b2018-09-26 05:48:29 +0000518 auto ReplacementsOrError = Server->formatFile(*Code, File);
Raoul Wols212bcf82017-12-12 20:25:06 +0000519 if (ReplacementsOrError)
Sam McCall2c30fbc2018-10-18 12:32:04 +0000520 Reply(replacementsToEdits(*Code, ReplacementsOrError.get()));
Raoul Wols212bcf82017-12-12 20:25:06 +0000521 else
Sam McCall2c30fbc2018-10-18 12:32:04 +0000522 Reply(ReplacementsOrError.takeError());
Sam McCall4db732a2017-09-30 10:08:52 +0000523}
524
Ilya Biryukov19d75602018-11-23 15:21:19 +0000525/// The functions constructs a flattened view of the DocumentSymbol hierarchy.
526/// Used by the clients that do not support the hierarchical view.
527static std::vector<SymbolInformation>
528flattenSymbolHierarchy(llvm::ArrayRef<DocumentSymbol> Symbols,
529 const URIForFile &FileURI) {
530
531 std::vector<SymbolInformation> Results;
532 std::function<void(const DocumentSymbol &, StringRef)> Process =
533 [&](const DocumentSymbol &S, Optional<StringRef> ParentName) {
534 SymbolInformation SI;
535 SI.containerName = ParentName ? "" : *ParentName;
536 SI.name = S.name;
537 SI.kind = S.kind;
538 SI.location.range = S.range;
539 SI.location.uri = FileURI;
540
541 Results.push_back(std::move(SI));
542 std::string FullName =
543 !ParentName ? S.name : (ParentName->str() + "::" + S.name);
544 for (auto &C : S.children)
545 Process(C, /*ParentName=*/FullName);
546 };
547 for (auto &S : Symbols)
548 Process(S, /*ParentName=*/"");
549 return Results;
550}
551
552void ClangdLSPServer::onDocumentSymbol(const DocumentSymbolParams &Params,
553 Callback<json::Value> Reply) {
554 URIForFile FileURI = Params.textDocument.uri;
Ilya Biryukov652364b2018-09-26 05:48:29 +0000555 Server->documentSymbols(
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000556 Params.textDocument.uri.file(),
Sam McCall2c30fbc2018-10-18 12:32:04 +0000557 Bind(
Ilya Biryukov19d75602018-11-23 15:21:19 +0000558 [this, FileURI](decltype(Reply) Reply,
559 Expected<std::vector<DocumentSymbol>> Items) {
Sam McCall2c30fbc2018-10-18 12:32:04 +0000560 if (!Items)
561 return Reply(Items.takeError());
Ilya Biryukov19d75602018-11-23 15:21:19 +0000562 adjustSymbolKinds(*Items, SupportedSymbolKinds);
563 if (SupportsHierarchicalDocumentSymbol)
564 return Reply(std::move(*Items));
565 else
566 return Reply(flattenSymbolHierarchy(*Items, FileURI));
Sam McCall2c30fbc2018-10-18 12:32:04 +0000567 },
568 std::move(Reply)));
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000569}
570
Sam McCall20841d42018-10-16 16:29:41 +0000571static Optional<Command> asCommand(const CodeAction &Action) {
572 Command Cmd;
573 if (Action.command && Action.edit)
Sam McCallc008af62018-10-20 15:30:37 +0000574 return None; // Not representable. (We never emit these anyway).
Sam McCall20841d42018-10-16 16:29:41 +0000575 if (Action.command) {
576 Cmd = *Action.command;
577 } else if (Action.edit) {
578 Cmd.command = Command::CLANGD_APPLY_FIX_COMMAND;
579 Cmd.workspaceEdit = *Action.edit;
580 } else {
Sam McCallc008af62018-10-20 15:30:37 +0000581 return None;
Sam McCall20841d42018-10-16 16:29:41 +0000582 }
583 Cmd.title = Action.title;
584 if (Action.kind && *Action.kind == CodeAction::QUICKFIX_KIND)
585 Cmd.title = "Apply fix: " + Cmd.title;
586 return Cmd;
587}
588
Sam McCall2c30fbc2018-10-18 12:32:04 +0000589void ClangdLSPServer::onCodeAction(const CodeActionParams &Params,
590 Callback<json::Value> Reply) {
591 auto Code = DraftMgr.getDraft(Params.textDocument.uri.file());
592 if (!Code)
593 return Reply(make_error<LSPError>("onCodeAction called for non-added file",
594 ErrorCode::InvalidParams));
Sam McCall20841d42018-10-16 16:29:41 +0000595 // We provide a code action for Fixes on the specified diagnostics.
Sam McCall20841d42018-10-16 16:29:41 +0000596 std::vector<CodeAction> Actions;
Sam McCall2c30fbc2018-10-18 12:32:04 +0000597 for (const Diagnostic &D : Params.context.diagnostics) {
Ilya Biryukov71028b82018-03-12 15:28:22 +0000598 for (auto &F : getFixes(Params.textDocument.uri.file(), D)) {
Sam McCall16e70702018-10-24 07:59:38 +0000599 Actions.push_back(toCodeAction(F, Params.textDocument.uri));
Sam McCall20841d42018-10-16 16:29:41 +0000600 Actions.back().diagnostics = {D};
Sam McCalldd0566b2017-11-06 15:40:30 +0000601 }
Ilya Biryukovafb55542017-05-16 14:40:30 +0000602 }
Sam McCall20841d42018-10-16 16:29:41 +0000603
604 if (SupportsCodeAction)
Sam McCall2c30fbc2018-10-18 12:32:04 +0000605 Reply(json::Array(Actions));
Sam McCall20841d42018-10-16 16:29:41 +0000606 else {
607 std::vector<Command> Commands;
608 for (const auto &Action : Actions)
609 if (auto Command = asCommand(Action))
610 Commands.push_back(std::move(*Command));
Sam McCall2c30fbc2018-10-18 12:32:04 +0000611 Reply(json::Array(Commands));
Sam McCall20841d42018-10-16 16:29:41 +0000612 }
Ilya Biryukovafb55542017-05-16 14:40:30 +0000613}
614
Sam McCall2c30fbc2018-10-18 12:32:04 +0000615void ClangdLSPServer::onCompletion(const TextDocumentPositionParams &Params,
616 Callback<CompletionList> Reply) {
Ilya Biryukov652364b2018-09-26 05:48:29 +0000617 Server->codeComplete(Params.textDocument.uri.file(), Params.position, CCOpts,
Sam McCall2c30fbc2018-10-18 12:32:04 +0000618 Bind(
619 [this](decltype(Reply) Reply,
Sam McCallc008af62018-10-20 15:30:37 +0000620 Expected<CodeCompleteResult> List) {
Sam McCall2c30fbc2018-10-18 12:32:04 +0000621 if (!List)
622 return Reply(List.takeError());
623 CompletionList LSPList;
624 LSPList.isIncomplete = List->HasMore;
625 for (const auto &R : List->Completions) {
626 CompletionItem C = R.render(CCOpts);
627 C.kind = adjustKindToCapability(
628 C.kind, SupportedCompletionItemKinds);
629 LSPList.items.push_back(std::move(C));
630 }
631 return Reply(std::move(LSPList));
632 },
633 std::move(Reply)));
Ilya Biryukovd9bdfe02017-10-06 11:54:17 +0000634}
635
Sam McCall2c30fbc2018-10-18 12:32:04 +0000636void ClangdLSPServer::onSignatureHelp(const TextDocumentPositionParams &Params,
637 Callback<SignatureHelp> Reply) {
Ilya Biryukov652364b2018-09-26 05:48:29 +0000638 Server->signatureHelp(Params.textDocument.uri.file(), Params.position,
Sam McCall2c30fbc2018-10-18 12:32:04 +0000639 std::move(Reply));
Ilya Biryukov652364b2018-09-26 05:48:29 +0000640}
641
Sam McCall2c30fbc2018-10-18 12:32:04 +0000642void ClangdLSPServer::onGoToDefinition(const TextDocumentPositionParams &Params,
643 Callback<std::vector<Location>> Reply) {
Ilya Biryukov652364b2018-09-26 05:48:29 +0000644 Server->findDefinitions(Params.textDocument.uri.file(), Params.position,
Sam McCall2c30fbc2018-10-18 12:32:04 +0000645 std::move(Reply));
Marc-Andre Laperle2cbf0372017-06-28 16:12:10 +0000646}
647
Sam McCall2c30fbc2018-10-18 12:32:04 +0000648void ClangdLSPServer::onSwitchSourceHeader(const TextDocumentIdentifier &Params,
649 Callback<std::string> Reply) {
Sam McCallc008af62018-10-20 15:30:37 +0000650 Optional<Path> Result = Server->switchSourceHeader(Params.uri.file());
Sam McCall2c30fbc2018-10-18 12:32:04 +0000651 Reply(Result ? URI::createFile(*Result).toString() : "");
Marc-Andre Laperle6571b3e2017-09-28 03:14:40 +0000652}
653
Sam McCall2c30fbc2018-10-18 12:32:04 +0000654void ClangdLSPServer::onDocumentHighlight(
655 const TextDocumentPositionParams &Params,
656 Callback<std::vector<DocumentHighlight>> Reply) {
657 Server->findDocumentHighlights(Params.textDocument.uri.file(),
658 Params.position, std::move(Reply));
Ilya Biryukov0e6a51f2017-12-12 12:27:47 +0000659}
660
Sam McCall2c30fbc2018-10-18 12:32:04 +0000661void ClangdLSPServer::onHover(const TextDocumentPositionParams &Params,
Sam McCallc008af62018-10-20 15:30:37 +0000662 Callback<Optional<Hover>> Reply) {
Ilya Biryukov652364b2018-09-26 05:48:29 +0000663 Server->findHover(Params.textDocument.uri.file(), Params.position,
Sam McCall2c30fbc2018-10-18 12:32:04 +0000664 std::move(Reply));
Marc-Andre Laperle3e618ed2018-02-16 21:38:15 +0000665}
666
Simon Marchi88016782018-08-01 11:28:49 +0000667void ClangdLSPServer::applyConfiguration(
Sam McCallbc904612018-10-25 04:22:52 +0000668 const ConfigurationSettings &Settings) {
Simon Marchiabeed662018-10-16 15:55:03 +0000669 // Per-file update to the compilation database.
Sam McCallbc904612018-10-25 04:22:52 +0000670 bool ShouldReparseOpenFiles = false;
671 for (auto &Entry : Settings.compilationDatabaseChanges) {
672 /// The opened files need to be reparsed only when some existing
673 /// entries are changed.
674 PathRef File = Entry.first;
Sam McCallc55d09a2018-11-02 13:09:36 +0000675 auto Old = CDB->getCompileCommand(File);
676 auto New =
677 tooling::CompileCommand(std::move(Entry.second.workingDirectory), File,
678 std::move(Entry.second.compilationCommand),
679 /*Output=*/"");
Sam McCall6980edb2018-11-02 14:07:51 +0000680 if (Old != New) {
Sam McCallc55d09a2018-11-02 13:09:36 +0000681 CDB->setCompileCommand(File, std::move(New));
Sam McCall6980edb2018-11-02 14:07:51 +0000682 ShouldReparseOpenFiles = true;
683 }
Alex Lorenzf8087862018-08-01 17:39:29 +0000684 }
Sam McCallbc904612018-10-25 04:22:52 +0000685 if (ShouldReparseOpenFiles)
686 reparseOpenedFiles();
Simon Marchi5178f922018-02-22 14:00:39 +0000687}
688
Simon Marchi88016782018-08-01 11:28:49 +0000689// FIXME: This function needs to be properly tested.
690void ClangdLSPServer::onChangeConfiguration(
Sam McCall2c30fbc2018-10-18 12:32:04 +0000691 const DidChangeConfigurationParams &Params) {
Simon Marchi88016782018-08-01 11:28:49 +0000692 applyConfiguration(Params.settings);
693}
694
Sam McCall2c30fbc2018-10-18 12:32:04 +0000695void ClangdLSPServer::onReference(const ReferenceParams &Params,
696 Callback<std::vector<Location>> Reply) {
Ilya Biryukov652364b2018-09-26 05:48:29 +0000697 Server->findReferences(Params.textDocument.uri.file(), Params.position,
Sam McCall2c30fbc2018-10-18 12:32:04 +0000698 std::move(Reply));
Sam McCall1ad142f2018-09-05 11:53:07 +0000699}
700
Jan Korousb4067012018-11-27 16:40:46 +0000701void ClangdLSPServer::onSymbolInfo(const TextDocumentPositionParams &Params,
702 Callback<std::vector<SymbolDetails>> Reply) {
703 Server->symbolInfo(Params.textDocument.uri.file(), Params.position,
704 std::move(Reply));
705}
706
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000707ClangdLSPServer::ClangdLSPServer(class Transport &Transp,
Sam McCalladccab62017-11-23 16:58:22 +0000708 const clangd::CodeCompleteOptions &CCOpts,
Sam McCallc008af62018-10-20 15:30:37 +0000709 Optional<Path> CompileCommandsDir,
Sam McCallc55d09a2018-11-02 13:09:36 +0000710 bool UseDirBasedCDB,
Sam McCall7363a2f2018-03-05 17:28:54 +0000711 const ClangdServer::Options &Opts)
Sam McCalld1c9d112018-10-23 14:19:54 +0000712 : Transp(Transp), MsgHandler(new MessageHandler(*this)), CCOpts(CCOpts),
713 SupportedSymbolKinds(defaultSymbolKinds()),
Kadir Cetinkaya133d46f2018-09-27 17:13:07 +0000714 SupportedCompletionItemKinds(defaultCompletionItemKinds()),
Sam McCallc55d09a2018-11-02 13:09:36 +0000715 UseDirBasedCDB(UseDirBasedCDB),
Sam McCall4b86bb02018-10-25 02:22:53 +0000716 CompileCommandsDir(std::move(CompileCommandsDir)),
717 ClangdServerOpts(Opts) {
Sam McCall2c30fbc2018-10-18 12:32:04 +0000718 // clang-format off
719 MsgHandler->bind("initialize", &ClangdLSPServer::onInitialize);
720 MsgHandler->bind("shutdown", &ClangdLSPServer::onShutdown);
Sam McCall422c8282018-11-26 16:00:11 +0000721 MsgHandler->bind("sync", &ClangdLSPServer::onSync);
Sam McCall2c30fbc2018-10-18 12:32:04 +0000722 MsgHandler->bind("textDocument/rangeFormatting", &ClangdLSPServer::onDocumentRangeFormatting);
723 MsgHandler->bind("textDocument/onTypeFormatting", &ClangdLSPServer::onDocumentOnTypeFormatting);
724 MsgHandler->bind("textDocument/formatting", &ClangdLSPServer::onDocumentFormatting);
725 MsgHandler->bind("textDocument/codeAction", &ClangdLSPServer::onCodeAction);
726 MsgHandler->bind("textDocument/completion", &ClangdLSPServer::onCompletion);
727 MsgHandler->bind("textDocument/signatureHelp", &ClangdLSPServer::onSignatureHelp);
728 MsgHandler->bind("textDocument/definition", &ClangdLSPServer::onGoToDefinition);
729 MsgHandler->bind("textDocument/references", &ClangdLSPServer::onReference);
730 MsgHandler->bind("textDocument/switchSourceHeader", &ClangdLSPServer::onSwitchSourceHeader);
731 MsgHandler->bind("textDocument/rename", &ClangdLSPServer::onRename);
732 MsgHandler->bind("textDocument/hover", &ClangdLSPServer::onHover);
733 MsgHandler->bind("textDocument/documentSymbol", &ClangdLSPServer::onDocumentSymbol);
734 MsgHandler->bind("workspace/executeCommand", &ClangdLSPServer::onCommand);
735 MsgHandler->bind("textDocument/documentHighlight", &ClangdLSPServer::onDocumentHighlight);
736 MsgHandler->bind("workspace/symbol", &ClangdLSPServer::onWorkspaceSymbol);
737 MsgHandler->bind("textDocument/didOpen", &ClangdLSPServer::onDocumentDidOpen);
738 MsgHandler->bind("textDocument/didClose", &ClangdLSPServer::onDocumentDidClose);
739 MsgHandler->bind("textDocument/didChange", &ClangdLSPServer::onDocumentDidChange);
740 MsgHandler->bind("workspace/didChangeWatchedFiles", &ClangdLSPServer::onFileEvent);
741 MsgHandler->bind("workspace/didChangeConfiguration", &ClangdLSPServer::onChangeConfiguration);
Jan Korousb4067012018-11-27 16:40:46 +0000742 MsgHandler->bind("textDocument/symbolInfo", &ClangdLSPServer::onSymbolInfo);
Sam McCall2c30fbc2018-10-18 12:32:04 +0000743 // clang-format on
744}
745
746ClangdLSPServer::~ClangdLSPServer() = default;
Ilya Biryukov38d79772017-05-16 09:38:59 +0000747
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000748bool ClangdLSPServer::run() {
Ilya Biryukovafb55542017-05-16 14:40:30 +0000749 // Run the Language Server loop.
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000750 bool CleanExit = true;
Sam McCall2c30fbc2018-10-18 12:32:04 +0000751 if (auto Err = Transp.loop(*MsgHandler)) {
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000752 elog("Transport error: {0}", std::move(Err));
753 CleanExit = false;
754 }
Ilya Biryukovafb55542017-05-16 14:40:30 +0000755
Ilya Biryukov652364b2018-09-26 05:48:29 +0000756 // Destroy ClangdServer to ensure all worker threads finish.
757 Server.reset();
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000758 return CleanExit && ShutdownRequestReceived;
Ilya Biryukov38d79772017-05-16 09:38:59 +0000759}
760
Ilya Biryukov71028b82018-03-12 15:28:22 +0000761std::vector<Fix> ClangdLSPServer::getFixes(StringRef File,
762 const clangd::Diagnostic &D) {
Ilya Biryukov38d79772017-05-16 09:38:59 +0000763 std::lock_guard<std::mutex> Lock(FixItsMutex);
764 auto DiagToFixItsIter = FixItsMap.find(File);
765 if (DiagToFixItsIter == FixItsMap.end())
766 return {};
767
768 const auto &DiagToFixItsMap = DiagToFixItsIter->second;
769 auto FixItsIter = DiagToFixItsMap.find(D);
770 if (FixItsIter == DiagToFixItsMap.end())
771 return {};
772
773 return FixItsIter->second;
774}
775
Sam McCalla7bb0cc2018-03-12 23:22:35 +0000776void ClangdLSPServer::onDiagnosticsReady(PathRef File,
777 std::vector<Diag> Diagnostics) {
Sam McCall16e70702018-10-24 07:59:38 +0000778 URIForFile URI(File);
779 std::vector<Diagnostic> LSPDiagnostics;
Ilya Biryukov38d79772017-05-16 09:38:59 +0000780 DiagnosticToReplacementMap LocalFixIts; // Temporary storage
Sam McCalla7bb0cc2018-03-12 23:22:35 +0000781 for (auto &Diag : Diagnostics) {
Sam McCall16e70702018-10-24 07:59:38 +0000782 toLSPDiags(Diag, URI, DiagOpts,
783 [&](clangd::Diagnostic Diag, ArrayRef<Fix> Fixes) {
784 auto &FixItsForDiagnostic = LocalFixIts[Diag];
785 llvm::copy(Fixes, std::back_inserter(FixItsForDiagnostic));
786 LSPDiagnostics.push_back(std::move(Diag));
787 });
Ilya Biryukov38d79772017-05-16 09:38:59 +0000788 }
789
790 // Cache FixIts
791 {
792 // FIXME(ibiryukov): should be deleted when documents are removed
793 std::lock_guard<std::mutex> Lock(FixItsMutex);
794 FixItsMap[File] = LocalFixIts;
795 }
796
797 // Publish diagnostics.
Sam McCall2c30fbc2018-10-18 12:32:04 +0000798 notify("textDocument/publishDiagnostics",
799 json::Object{
Sam McCall16e70702018-10-24 07:59:38 +0000800 {"uri", URI},
801 {"diagnostics", std::move(LSPDiagnostics)},
Sam McCall2c30fbc2018-10-18 12:32:04 +0000802 });
Ilya Biryukov38d79772017-05-16 09:38:59 +0000803}
Simon Marchi9569fd52018-03-16 14:30:42 +0000804
805void ClangdLSPServer::reparseOpenedFiles() {
806 for (const Path &FilePath : DraftMgr.getActiveFiles())
Ilya Biryukov652364b2018-09-26 05:48:29 +0000807 Server->addDocument(FilePath, *DraftMgr.getDraft(FilePath),
808 WantDiagnostics::Auto);
Simon Marchi9569fd52018-03-16 14:30:42 +0000809}
Alex Lorenzf8087862018-08-01 17:39:29 +0000810
Sam McCallc008af62018-10-20 15:30:37 +0000811} // namespace clangd
812} // namespace clang