blob: b2ac431f1d134a3a739c4449a9a458043c30601a [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 McCall2c30fbc2018-10-18 12:32:04 +0000340void ClangdLSPServer::onDocumentDidOpen(
341 const DidOpenTextDocumentParams &Params) {
Simon Marchi9569fd52018-03-16 14:30:42 +0000342 PathRef File = Params.textDocument.uri.file();
Ilya Biryukovb10ef472018-06-13 09:20:41 +0000343
Sam McCall2c30fbc2018-10-18 12:32:04 +0000344 const std::string &Contents = Params.textDocument.text;
Simon Marchi9569fd52018-03-16 14:30:42 +0000345
Simon Marchi98082622018-03-26 14:41:40 +0000346 DraftMgr.addDraft(File, Contents);
Ilya Biryukov652364b2018-09-26 05:48:29 +0000347 Server->addDocument(File, Contents, WantDiagnostics::Yes);
Ilya Biryukovafb55542017-05-16 14:40:30 +0000348}
349
Sam McCall2c30fbc2018-10-18 12:32:04 +0000350void ClangdLSPServer::onDocumentDidChange(
351 const DidChangeTextDocumentParams &Params) {
Eric Liu51fed182018-02-22 18:40:39 +0000352 auto WantDiags = WantDiagnostics::Auto;
353 if (Params.wantDiagnostics.hasValue())
354 WantDiags = Params.wantDiagnostics.getValue() ? WantDiagnostics::Yes
355 : WantDiagnostics::No;
Simon Marchi9569fd52018-03-16 14:30:42 +0000356
357 PathRef File = Params.textDocument.uri.file();
Sam McCallc008af62018-10-20 15:30:37 +0000358 Expected<std::string> Contents =
Simon Marchi98082622018-03-26 14:41:40 +0000359 DraftMgr.updateDraft(File, Params.contentChanges);
360 if (!Contents) {
361 // If this fails, we are most likely going to be not in sync anymore with
362 // the client. It is better to remove the draft and let further operations
363 // fail rather than giving wrong results.
364 DraftMgr.removeDraft(File);
Ilya Biryukov652364b2018-09-26 05:48:29 +0000365 Server->removeDocument(File);
Sam McCallbed58852018-07-11 10:35:11 +0000366 elog("Failed to update {0}: {1}", File, Contents.takeError());
Simon Marchi98082622018-03-26 14:41:40 +0000367 return;
368 }
Simon Marchi9569fd52018-03-16 14:30:42 +0000369
Ilya Biryukov652364b2018-09-26 05:48:29 +0000370 Server->addDocument(File, *Contents, WantDiags);
Ilya Biryukovafb55542017-05-16 14:40:30 +0000371}
372
Sam McCall2c30fbc2018-10-18 12:32:04 +0000373void ClangdLSPServer::onFileEvent(const DidChangeWatchedFilesParams &Params) {
Ilya Biryukov652364b2018-09-26 05:48:29 +0000374 Server->onFileEvent(Params);
Marc-Andre Laperlebf114242017-10-02 18:00:37 +0000375}
376
Sam McCall2c30fbc2018-10-18 12:32:04 +0000377void ClangdLSPServer::onCommand(const ExecuteCommandParams &Params,
378 Callback<json::Value> Reply) {
379 auto ApplyEdit = [&](WorkspaceEdit WE) {
Eric Liuc5105f92018-02-16 14:15:55 +0000380 ApplyWorkspaceEditParams Edit;
381 Edit.edit = std::move(WE);
Eric Liuc5105f92018-02-16 14:15:55 +0000382 // Ideally, we would wait for the response and if there is no error, we
383 // would reply success/failure to the original RPC.
384 call("workspace/applyEdit", Edit);
385 };
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000386 if (Params.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND &&
387 Params.workspaceEdit) {
388 // The flow for "apply-fix" :
389 // 1. We publish a diagnostic, including fixits
390 // 2. The user clicks on the diagnostic, the editor asks us for code actions
391 // 3. We send code actions, with the fixit embedded as context
392 // 4. The user selects the fixit, the editor asks us to apply it
393 // 5. We unwrap the changes and send them back to the editor
394 // 6. The editor applies the changes (applyEdit), and sends us a reply (but
395 // we ignore it)
396
Sam McCall2c30fbc2018-10-18 12:32:04 +0000397 Reply("Fix applied.");
Eric Liuc5105f92018-02-16 14:15:55 +0000398 ApplyEdit(*Params.workspaceEdit);
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000399 } else {
400 // We should not get here because ExecuteCommandParams would not have
401 // parsed in the first place and this handler should not be called. But if
402 // more commands are added, this will be here has a safe guard.
Sam McCall2c30fbc2018-10-18 12:32:04 +0000403 Reply(make_error<LSPError>(
Sam McCallc008af62018-10-20 15:30:37 +0000404 formatv("Unsupported command \"{0}\".", Params.command).str(),
Sam McCall2c30fbc2018-10-18 12:32:04 +0000405 ErrorCode::InvalidParams));
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000406 }
407}
408
Sam McCall2c30fbc2018-10-18 12:32:04 +0000409void ClangdLSPServer::onWorkspaceSymbol(
410 const WorkspaceSymbolParams &Params,
411 Callback<std::vector<SymbolInformation>> Reply) {
Ilya Biryukov652364b2018-09-26 05:48:29 +0000412 Server->workspaceSymbols(
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000413 Params.query, CCOpts.Limit,
Sam McCall2c30fbc2018-10-18 12:32:04 +0000414 Bind(
415 [this](decltype(Reply) Reply,
Sam McCallc008af62018-10-20 15:30:37 +0000416 Expected<std::vector<SymbolInformation>> Items) {
Sam McCall2c30fbc2018-10-18 12:32:04 +0000417 if (!Items)
418 return Reply(Items.takeError());
419 for (auto &Sym : *Items)
420 Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds);
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000421
Sam McCall2c30fbc2018-10-18 12:32:04 +0000422 Reply(std::move(*Items));
423 },
424 std::move(Reply)));
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000425}
426
Sam McCall2c30fbc2018-10-18 12:32:04 +0000427void ClangdLSPServer::onRename(const RenameParams &Params,
428 Callback<WorkspaceEdit> Reply) {
Ilya Biryukov7d60d202018-02-16 12:20:47 +0000429 Path File = Params.textDocument.uri.file();
Sam McCallc008af62018-10-20 15:30:37 +0000430 Optional<std::string> Code = DraftMgr.getDraft(File);
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000431 if (!Code)
Sam McCall2c30fbc2018-10-18 12:32:04 +0000432 return Reply(make_error<LSPError>("onRename called for non-added file",
433 ErrorCode::InvalidParams));
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000434
Ilya Biryukov652364b2018-09-26 05:48:29 +0000435 Server->rename(
Ilya Biryukov2c5e8e82018-02-15 13:15:47 +0000436 File, Params.position, Params.newName,
Sam McCall2c30fbc2018-10-18 12:32:04 +0000437 Bind(
Sam McCallc008af62018-10-20 15:30:37 +0000438 [File, Code,
439 Params](decltype(Reply) Reply,
440 Expected<std::vector<tooling::Replacement>> Replacements) {
Sam McCall2c30fbc2018-10-18 12:32:04 +0000441 if (!Replacements)
442 return Reply(Replacements.takeError());
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000443
Sam McCall2c30fbc2018-10-18 12:32:04 +0000444 // Turn the replacements into the format specified by the Language
445 // Server Protocol. Fuse them into one big JSON array.
446 std::vector<TextEdit> Edits;
447 for (const auto &R : *Replacements)
448 Edits.push_back(replacementToEdit(*Code, R));
449 WorkspaceEdit WE;
450 WE.changes = {{Params.textDocument.uri.uri(), Edits}};
451 Reply(WE);
452 },
453 std::move(Reply)));
Haojian Wu345099c2017-11-09 11:30:04 +0000454}
455
Sam McCall2c30fbc2018-10-18 12:32:04 +0000456void ClangdLSPServer::onDocumentDidClose(
457 const DidCloseTextDocumentParams &Params) {
Simon Marchi9569fd52018-03-16 14:30:42 +0000458 PathRef File = Params.textDocument.uri.file();
459 DraftMgr.removeDraft(File);
Ilya Biryukov652364b2018-09-26 05:48:29 +0000460 Server->removeDocument(File);
Ilya Biryukovafb55542017-05-16 14:40:30 +0000461}
462
Sam McCall4db732a2017-09-30 10:08:52 +0000463void ClangdLSPServer::onDocumentOnTypeFormatting(
Sam McCall2c30fbc2018-10-18 12:32:04 +0000464 const DocumentOnTypeFormattingParams &Params,
465 Callback<std::vector<TextEdit>> Reply) {
Ilya Biryukov7d60d202018-02-16 12:20:47 +0000466 auto File = Params.textDocument.uri.file();
Simon Marchi9569fd52018-03-16 14:30:42 +0000467 auto Code = DraftMgr.getDraft(File);
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000468 if (!Code)
Sam McCall2c30fbc2018-10-18 12:32:04 +0000469 return Reply(make_error<LSPError>(
470 "onDocumentOnTypeFormatting called for non-added file",
471 ErrorCode::InvalidParams));
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000472
Ilya Biryukov652364b2018-09-26 05:48:29 +0000473 auto ReplacementsOrError = Server->formatOnType(*Code, File, Params.position);
Raoul Wols212bcf82017-12-12 20:25:06 +0000474 if (ReplacementsOrError)
Sam McCall2c30fbc2018-10-18 12:32:04 +0000475 Reply(replacementsToEdits(*Code, ReplacementsOrError.get()));
Raoul Wols212bcf82017-12-12 20:25:06 +0000476 else
Sam McCall2c30fbc2018-10-18 12:32:04 +0000477 Reply(ReplacementsOrError.takeError());
Ilya Biryukovafb55542017-05-16 14:40:30 +0000478}
479
Sam McCall4db732a2017-09-30 10:08:52 +0000480void ClangdLSPServer::onDocumentRangeFormatting(
Sam McCall2c30fbc2018-10-18 12:32:04 +0000481 const DocumentRangeFormattingParams &Params,
482 Callback<std::vector<TextEdit>> Reply) {
Ilya Biryukov7d60d202018-02-16 12:20:47 +0000483 auto File = Params.textDocument.uri.file();
Simon Marchi9569fd52018-03-16 14:30:42 +0000484 auto Code = DraftMgr.getDraft(File);
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000485 if (!Code)
Sam McCall2c30fbc2018-10-18 12:32:04 +0000486 return Reply(make_error<LSPError>(
487 "onDocumentRangeFormatting called for non-added file",
488 ErrorCode::InvalidParams));
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000489
Ilya Biryukov652364b2018-09-26 05:48:29 +0000490 auto ReplacementsOrError = Server->formatRange(*Code, File, Params.range);
Raoul Wols212bcf82017-12-12 20:25:06 +0000491 if (ReplacementsOrError)
Sam McCall2c30fbc2018-10-18 12:32:04 +0000492 Reply(replacementsToEdits(*Code, ReplacementsOrError.get()));
Raoul Wols212bcf82017-12-12 20:25:06 +0000493 else
Sam McCall2c30fbc2018-10-18 12:32:04 +0000494 Reply(ReplacementsOrError.takeError());
Ilya Biryukovafb55542017-05-16 14:40:30 +0000495}
496
Sam McCall2c30fbc2018-10-18 12:32:04 +0000497void ClangdLSPServer::onDocumentFormatting(
498 const DocumentFormattingParams &Params,
499 Callback<std::vector<TextEdit>> Reply) {
Ilya Biryukov7d60d202018-02-16 12:20:47 +0000500 auto File = Params.textDocument.uri.file();
Simon Marchi9569fd52018-03-16 14:30:42 +0000501 auto Code = DraftMgr.getDraft(File);
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000502 if (!Code)
Sam McCall2c30fbc2018-10-18 12:32:04 +0000503 return Reply(
504 make_error<LSPError>("onDocumentFormatting called for non-added file",
505 ErrorCode::InvalidParams));
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000506
Ilya Biryukov652364b2018-09-26 05:48:29 +0000507 auto ReplacementsOrError = Server->formatFile(*Code, File);
Raoul Wols212bcf82017-12-12 20:25:06 +0000508 if (ReplacementsOrError)
Sam McCall2c30fbc2018-10-18 12:32:04 +0000509 Reply(replacementsToEdits(*Code, ReplacementsOrError.get()));
Raoul Wols212bcf82017-12-12 20:25:06 +0000510 else
Sam McCall2c30fbc2018-10-18 12:32:04 +0000511 Reply(ReplacementsOrError.takeError());
Sam McCall4db732a2017-09-30 10:08:52 +0000512}
513
Ilya Biryukov19d75602018-11-23 15:21:19 +0000514/// The functions constructs a flattened view of the DocumentSymbol hierarchy.
515/// Used by the clients that do not support the hierarchical view.
516static std::vector<SymbolInformation>
517flattenSymbolHierarchy(llvm::ArrayRef<DocumentSymbol> Symbols,
518 const URIForFile &FileURI) {
519
520 std::vector<SymbolInformation> Results;
521 std::function<void(const DocumentSymbol &, StringRef)> Process =
522 [&](const DocumentSymbol &S, Optional<StringRef> ParentName) {
523 SymbolInformation SI;
524 SI.containerName = ParentName ? "" : *ParentName;
525 SI.name = S.name;
526 SI.kind = S.kind;
527 SI.location.range = S.range;
528 SI.location.uri = FileURI;
529
530 Results.push_back(std::move(SI));
531 std::string FullName =
532 !ParentName ? S.name : (ParentName->str() + "::" + S.name);
533 for (auto &C : S.children)
534 Process(C, /*ParentName=*/FullName);
535 };
536 for (auto &S : Symbols)
537 Process(S, /*ParentName=*/"");
538 return Results;
539}
540
541void ClangdLSPServer::onDocumentSymbol(const DocumentSymbolParams &Params,
542 Callback<json::Value> Reply) {
543 URIForFile FileURI = Params.textDocument.uri;
Ilya Biryukov652364b2018-09-26 05:48:29 +0000544 Server->documentSymbols(
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000545 Params.textDocument.uri.file(),
Sam McCall2c30fbc2018-10-18 12:32:04 +0000546 Bind(
Ilya Biryukov19d75602018-11-23 15:21:19 +0000547 [this, FileURI](decltype(Reply) Reply,
548 Expected<std::vector<DocumentSymbol>> Items) {
Sam McCall2c30fbc2018-10-18 12:32:04 +0000549 if (!Items)
550 return Reply(Items.takeError());
Ilya Biryukov19d75602018-11-23 15:21:19 +0000551 adjustSymbolKinds(*Items, SupportedSymbolKinds);
552 if (SupportsHierarchicalDocumentSymbol)
553 return Reply(std::move(*Items));
554 else
555 return Reply(flattenSymbolHierarchy(*Items, FileURI));
Sam McCall2c30fbc2018-10-18 12:32:04 +0000556 },
557 std::move(Reply)));
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000558}
559
Sam McCall20841d42018-10-16 16:29:41 +0000560static Optional<Command> asCommand(const CodeAction &Action) {
561 Command Cmd;
562 if (Action.command && Action.edit)
Sam McCallc008af62018-10-20 15:30:37 +0000563 return None; // Not representable. (We never emit these anyway).
Sam McCall20841d42018-10-16 16:29:41 +0000564 if (Action.command) {
565 Cmd = *Action.command;
566 } else if (Action.edit) {
567 Cmd.command = Command::CLANGD_APPLY_FIX_COMMAND;
568 Cmd.workspaceEdit = *Action.edit;
569 } else {
Sam McCallc008af62018-10-20 15:30:37 +0000570 return None;
Sam McCall20841d42018-10-16 16:29:41 +0000571 }
572 Cmd.title = Action.title;
573 if (Action.kind && *Action.kind == CodeAction::QUICKFIX_KIND)
574 Cmd.title = "Apply fix: " + Cmd.title;
575 return Cmd;
576}
577
Sam McCall2c30fbc2018-10-18 12:32:04 +0000578void ClangdLSPServer::onCodeAction(const CodeActionParams &Params,
579 Callback<json::Value> Reply) {
580 auto Code = DraftMgr.getDraft(Params.textDocument.uri.file());
581 if (!Code)
582 return Reply(make_error<LSPError>("onCodeAction called for non-added file",
583 ErrorCode::InvalidParams));
Sam McCall20841d42018-10-16 16:29:41 +0000584 // We provide a code action for Fixes on the specified diagnostics.
Sam McCall20841d42018-10-16 16:29:41 +0000585 std::vector<CodeAction> Actions;
Sam McCall2c30fbc2018-10-18 12:32:04 +0000586 for (const Diagnostic &D : Params.context.diagnostics) {
Ilya Biryukov71028b82018-03-12 15:28:22 +0000587 for (auto &F : getFixes(Params.textDocument.uri.file(), D)) {
Sam McCall16e70702018-10-24 07:59:38 +0000588 Actions.push_back(toCodeAction(F, Params.textDocument.uri));
Sam McCall20841d42018-10-16 16:29:41 +0000589 Actions.back().diagnostics = {D};
Sam McCalldd0566b2017-11-06 15:40:30 +0000590 }
Ilya Biryukovafb55542017-05-16 14:40:30 +0000591 }
Sam McCall20841d42018-10-16 16:29:41 +0000592
593 if (SupportsCodeAction)
Sam McCall2c30fbc2018-10-18 12:32:04 +0000594 Reply(json::Array(Actions));
Sam McCall20841d42018-10-16 16:29:41 +0000595 else {
596 std::vector<Command> Commands;
597 for (const auto &Action : Actions)
598 if (auto Command = asCommand(Action))
599 Commands.push_back(std::move(*Command));
Sam McCall2c30fbc2018-10-18 12:32:04 +0000600 Reply(json::Array(Commands));
Sam McCall20841d42018-10-16 16:29:41 +0000601 }
Ilya Biryukovafb55542017-05-16 14:40:30 +0000602}
603
Sam McCall2c30fbc2018-10-18 12:32:04 +0000604void ClangdLSPServer::onCompletion(const TextDocumentPositionParams &Params,
605 Callback<CompletionList> Reply) {
Ilya Biryukov652364b2018-09-26 05:48:29 +0000606 Server->codeComplete(Params.textDocument.uri.file(), Params.position, CCOpts,
Sam McCall2c30fbc2018-10-18 12:32:04 +0000607 Bind(
608 [this](decltype(Reply) Reply,
Sam McCallc008af62018-10-20 15:30:37 +0000609 Expected<CodeCompleteResult> List) {
Sam McCall2c30fbc2018-10-18 12:32:04 +0000610 if (!List)
611 return Reply(List.takeError());
612 CompletionList LSPList;
613 LSPList.isIncomplete = List->HasMore;
614 for (const auto &R : List->Completions) {
615 CompletionItem C = R.render(CCOpts);
616 C.kind = adjustKindToCapability(
617 C.kind, SupportedCompletionItemKinds);
618 LSPList.items.push_back(std::move(C));
619 }
620 return Reply(std::move(LSPList));
621 },
622 std::move(Reply)));
Ilya Biryukovd9bdfe02017-10-06 11:54:17 +0000623}
624
Sam McCall2c30fbc2018-10-18 12:32:04 +0000625void ClangdLSPServer::onSignatureHelp(const TextDocumentPositionParams &Params,
626 Callback<SignatureHelp> Reply) {
Ilya Biryukov652364b2018-09-26 05:48:29 +0000627 Server->signatureHelp(Params.textDocument.uri.file(), Params.position,
Sam McCall2c30fbc2018-10-18 12:32:04 +0000628 std::move(Reply));
Ilya Biryukov652364b2018-09-26 05:48:29 +0000629}
630
Sam McCall2c30fbc2018-10-18 12:32:04 +0000631void ClangdLSPServer::onGoToDefinition(const TextDocumentPositionParams &Params,
632 Callback<std::vector<Location>> Reply) {
Ilya Biryukov652364b2018-09-26 05:48:29 +0000633 Server->findDefinitions(Params.textDocument.uri.file(), Params.position,
Sam McCall2c30fbc2018-10-18 12:32:04 +0000634 std::move(Reply));
Marc-Andre Laperle2cbf0372017-06-28 16:12:10 +0000635}
636
Sam McCall2c30fbc2018-10-18 12:32:04 +0000637void ClangdLSPServer::onSwitchSourceHeader(const TextDocumentIdentifier &Params,
638 Callback<std::string> Reply) {
Sam McCallc008af62018-10-20 15:30:37 +0000639 Optional<Path> Result = Server->switchSourceHeader(Params.uri.file());
Sam McCall2c30fbc2018-10-18 12:32:04 +0000640 Reply(Result ? URI::createFile(*Result).toString() : "");
Marc-Andre Laperle6571b3e2017-09-28 03:14:40 +0000641}
642
Sam McCall2c30fbc2018-10-18 12:32:04 +0000643void ClangdLSPServer::onDocumentHighlight(
644 const TextDocumentPositionParams &Params,
645 Callback<std::vector<DocumentHighlight>> Reply) {
646 Server->findDocumentHighlights(Params.textDocument.uri.file(),
647 Params.position, std::move(Reply));
Ilya Biryukov0e6a51f2017-12-12 12:27:47 +0000648}
649
Sam McCall2c30fbc2018-10-18 12:32:04 +0000650void ClangdLSPServer::onHover(const TextDocumentPositionParams &Params,
Sam McCallc008af62018-10-20 15:30:37 +0000651 Callback<Optional<Hover>> Reply) {
Ilya Biryukov652364b2018-09-26 05:48:29 +0000652 Server->findHover(Params.textDocument.uri.file(), Params.position,
Sam McCall2c30fbc2018-10-18 12:32:04 +0000653 std::move(Reply));
Marc-Andre Laperle3e618ed2018-02-16 21:38:15 +0000654}
655
Simon Marchi88016782018-08-01 11:28:49 +0000656void ClangdLSPServer::applyConfiguration(
Sam McCallbc904612018-10-25 04:22:52 +0000657 const ConfigurationSettings &Settings) {
Simon Marchiabeed662018-10-16 15:55:03 +0000658 // Per-file update to the compilation database.
Sam McCallbc904612018-10-25 04:22:52 +0000659 bool ShouldReparseOpenFiles = false;
660 for (auto &Entry : Settings.compilationDatabaseChanges) {
661 /// The opened files need to be reparsed only when some existing
662 /// entries are changed.
663 PathRef File = Entry.first;
Sam McCallc55d09a2018-11-02 13:09:36 +0000664 auto Old = CDB->getCompileCommand(File);
665 auto New =
666 tooling::CompileCommand(std::move(Entry.second.workingDirectory), File,
667 std::move(Entry.second.compilationCommand),
668 /*Output=*/"");
Sam McCall6980edb2018-11-02 14:07:51 +0000669 if (Old != New) {
Sam McCallc55d09a2018-11-02 13:09:36 +0000670 CDB->setCompileCommand(File, std::move(New));
Sam McCall6980edb2018-11-02 14:07:51 +0000671 ShouldReparseOpenFiles = true;
672 }
Alex Lorenzf8087862018-08-01 17:39:29 +0000673 }
Sam McCallbc904612018-10-25 04:22:52 +0000674 if (ShouldReparseOpenFiles)
675 reparseOpenedFiles();
Simon Marchi5178f922018-02-22 14:00:39 +0000676}
677
Simon Marchi88016782018-08-01 11:28:49 +0000678// FIXME: This function needs to be properly tested.
679void ClangdLSPServer::onChangeConfiguration(
Sam McCall2c30fbc2018-10-18 12:32:04 +0000680 const DidChangeConfigurationParams &Params) {
Simon Marchi88016782018-08-01 11:28:49 +0000681 applyConfiguration(Params.settings);
682}
683
Sam McCall2c30fbc2018-10-18 12:32:04 +0000684void ClangdLSPServer::onReference(const ReferenceParams &Params,
685 Callback<std::vector<Location>> Reply) {
Ilya Biryukov652364b2018-09-26 05:48:29 +0000686 Server->findReferences(Params.textDocument.uri.file(), Params.position,
Sam McCall2c30fbc2018-10-18 12:32:04 +0000687 std::move(Reply));
Sam McCall1ad142f2018-09-05 11:53:07 +0000688}
689
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000690ClangdLSPServer::ClangdLSPServer(class Transport &Transp,
Sam McCalladccab62017-11-23 16:58:22 +0000691 const clangd::CodeCompleteOptions &CCOpts,
Sam McCallc008af62018-10-20 15:30:37 +0000692 Optional<Path> CompileCommandsDir,
Sam McCallc55d09a2018-11-02 13:09:36 +0000693 bool UseDirBasedCDB,
Sam McCall7363a2f2018-03-05 17:28:54 +0000694 const ClangdServer::Options &Opts)
Sam McCalld1c9d112018-10-23 14:19:54 +0000695 : Transp(Transp), MsgHandler(new MessageHandler(*this)), CCOpts(CCOpts),
696 SupportedSymbolKinds(defaultSymbolKinds()),
Kadir Cetinkaya133d46f2018-09-27 17:13:07 +0000697 SupportedCompletionItemKinds(defaultCompletionItemKinds()),
Sam McCallc55d09a2018-11-02 13:09:36 +0000698 UseDirBasedCDB(UseDirBasedCDB),
Sam McCall4b86bb02018-10-25 02:22:53 +0000699 CompileCommandsDir(std::move(CompileCommandsDir)),
700 ClangdServerOpts(Opts) {
Sam McCall2c30fbc2018-10-18 12:32:04 +0000701 // clang-format off
702 MsgHandler->bind("initialize", &ClangdLSPServer::onInitialize);
703 MsgHandler->bind("shutdown", &ClangdLSPServer::onShutdown);
704 MsgHandler->bind("textDocument/rangeFormatting", &ClangdLSPServer::onDocumentRangeFormatting);
705 MsgHandler->bind("textDocument/onTypeFormatting", &ClangdLSPServer::onDocumentOnTypeFormatting);
706 MsgHandler->bind("textDocument/formatting", &ClangdLSPServer::onDocumentFormatting);
707 MsgHandler->bind("textDocument/codeAction", &ClangdLSPServer::onCodeAction);
708 MsgHandler->bind("textDocument/completion", &ClangdLSPServer::onCompletion);
709 MsgHandler->bind("textDocument/signatureHelp", &ClangdLSPServer::onSignatureHelp);
710 MsgHandler->bind("textDocument/definition", &ClangdLSPServer::onGoToDefinition);
711 MsgHandler->bind("textDocument/references", &ClangdLSPServer::onReference);
712 MsgHandler->bind("textDocument/switchSourceHeader", &ClangdLSPServer::onSwitchSourceHeader);
713 MsgHandler->bind("textDocument/rename", &ClangdLSPServer::onRename);
714 MsgHandler->bind("textDocument/hover", &ClangdLSPServer::onHover);
715 MsgHandler->bind("textDocument/documentSymbol", &ClangdLSPServer::onDocumentSymbol);
716 MsgHandler->bind("workspace/executeCommand", &ClangdLSPServer::onCommand);
717 MsgHandler->bind("textDocument/documentHighlight", &ClangdLSPServer::onDocumentHighlight);
718 MsgHandler->bind("workspace/symbol", &ClangdLSPServer::onWorkspaceSymbol);
719 MsgHandler->bind("textDocument/didOpen", &ClangdLSPServer::onDocumentDidOpen);
720 MsgHandler->bind("textDocument/didClose", &ClangdLSPServer::onDocumentDidClose);
721 MsgHandler->bind("textDocument/didChange", &ClangdLSPServer::onDocumentDidChange);
722 MsgHandler->bind("workspace/didChangeWatchedFiles", &ClangdLSPServer::onFileEvent);
723 MsgHandler->bind("workspace/didChangeConfiguration", &ClangdLSPServer::onChangeConfiguration);
724 // clang-format on
725}
726
727ClangdLSPServer::~ClangdLSPServer() = default;
Ilya Biryukov38d79772017-05-16 09:38:59 +0000728
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000729bool ClangdLSPServer::run() {
Ilya Biryukovafb55542017-05-16 14:40:30 +0000730 // Run the Language Server loop.
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000731 bool CleanExit = true;
Sam McCall2c30fbc2018-10-18 12:32:04 +0000732 if (auto Err = Transp.loop(*MsgHandler)) {
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000733 elog("Transport error: {0}", std::move(Err));
734 CleanExit = false;
735 }
Ilya Biryukovafb55542017-05-16 14:40:30 +0000736
Ilya Biryukov652364b2018-09-26 05:48:29 +0000737 // Destroy ClangdServer to ensure all worker threads finish.
738 Server.reset();
Sam McCalldc8f3cf2018-10-17 07:32:05 +0000739 return CleanExit && ShutdownRequestReceived;
Ilya Biryukov38d79772017-05-16 09:38:59 +0000740}
741
Ilya Biryukov71028b82018-03-12 15:28:22 +0000742std::vector<Fix> ClangdLSPServer::getFixes(StringRef File,
743 const clangd::Diagnostic &D) {
Ilya Biryukov38d79772017-05-16 09:38:59 +0000744 std::lock_guard<std::mutex> Lock(FixItsMutex);
745 auto DiagToFixItsIter = FixItsMap.find(File);
746 if (DiagToFixItsIter == FixItsMap.end())
747 return {};
748
749 const auto &DiagToFixItsMap = DiagToFixItsIter->second;
750 auto FixItsIter = DiagToFixItsMap.find(D);
751 if (FixItsIter == DiagToFixItsMap.end())
752 return {};
753
754 return FixItsIter->second;
755}
756
Sam McCalla7bb0cc2018-03-12 23:22:35 +0000757void ClangdLSPServer::onDiagnosticsReady(PathRef File,
758 std::vector<Diag> Diagnostics) {
Sam McCall16e70702018-10-24 07:59:38 +0000759 URIForFile URI(File);
760 std::vector<Diagnostic> LSPDiagnostics;
Ilya Biryukov38d79772017-05-16 09:38:59 +0000761 DiagnosticToReplacementMap LocalFixIts; // Temporary storage
Sam McCalla7bb0cc2018-03-12 23:22:35 +0000762 for (auto &Diag : Diagnostics) {
Sam McCall16e70702018-10-24 07:59:38 +0000763 toLSPDiags(Diag, URI, DiagOpts,
764 [&](clangd::Diagnostic Diag, ArrayRef<Fix> Fixes) {
765 auto &FixItsForDiagnostic = LocalFixIts[Diag];
766 llvm::copy(Fixes, std::back_inserter(FixItsForDiagnostic));
767 LSPDiagnostics.push_back(std::move(Diag));
768 });
Ilya Biryukov38d79772017-05-16 09:38:59 +0000769 }
770
771 // Cache FixIts
772 {
773 // FIXME(ibiryukov): should be deleted when documents are removed
774 std::lock_guard<std::mutex> Lock(FixItsMutex);
775 FixItsMap[File] = LocalFixIts;
776 }
777
778 // Publish diagnostics.
Sam McCall2c30fbc2018-10-18 12:32:04 +0000779 notify("textDocument/publishDiagnostics",
780 json::Object{
Sam McCall16e70702018-10-24 07:59:38 +0000781 {"uri", URI},
782 {"diagnostics", std::move(LSPDiagnostics)},
Sam McCall2c30fbc2018-10-18 12:32:04 +0000783 });
Ilya Biryukov38d79772017-05-16 09:38:59 +0000784}
Simon Marchi9569fd52018-03-16 14:30:42 +0000785
786void ClangdLSPServer::reparseOpenedFiles() {
787 for (const Path &FilePath : DraftMgr.getActiveFiles())
Ilya Biryukov652364b2018-09-26 05:48:29 +0000788 Server->addDocument(FilePath, *DraftMgr.getDraft(FilePath),
789 WantDiagnostics::Auto);
Simon Marchi9569fd52018-03-16 14:30:42 +0000790}
Alex Lorenzf8087862018-08-01 17:39:29 +0000791
Sam McCallc008af62018-10-20 15:30:37 +0000792} // namespace clangd
793} // namespace clang