blob: f04e3f9d7dcb5c9713b7abaa0995f6340a5d350f [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//
8//===---------------------------------------------------------------------===//
9
10#include "ClangdLSPServer.h"
11#include "JSONRPCDispatcher.h"
Sam McCallb536a2a2017-12-19 12:23:48 +000012#include "SourceCode.h"
Eric Liu78ed91a72018-01-29 15:37:46 +000013#include "URI.h"
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +000014#include "llvm/Support/FormatVariadic.h"
15
Ilya Biryukov38d79772017-05-16 09:38:59 +000016using namespace clang::clangd;
17using namespace clang;
18
Ilya Biryukovafb55542017-05-16 14:40:30 +000019namespace {
20
Raoul Wols212bcf82017-12-12 20:25:06 +000021TextEdit replacementToEdit(StringRef Code, const tooling::Replacement &R) {
22 Range ReplacementRange = {
23 offsetToPosition(Code, R.getOffset()),
24 offsetToPosition(Code, R.getOffset() + R.getLength())};
25 return {ReplacementRange, R.getReplacementText()};
26}
27
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +000028std::vector<TextEdit>
Ilya Biryukovafb55542017-05-16 14:40:30 +000029replacementsToEdits(StringRef Code,
30 const std::vector<tooling::Replacement> &Replacements) {
31 // Turn the replacements into the format specified by the Language Server
Sam McCalldd0566b2017-11-06 15:40:30 +000032 // Protocol. Fuse them into one big JSON array.
33 std::vector<TextEdit> Edits;
Raoul Wols212bcf82017-12-12 20:25:06 +000034 for (const auto &R : Replacements)
35 Edits.push_back(replacementToEdit(Code, R));
36 return Edits;
37}
38
39std::vector<TextEdit> replacementsToEdits(StringRef Code,
40 const tooling::Replacements &Repls) {
41 std::vector<TextEdit> Edits;
42 for (const auto &R : Repls)
43 Edits.push_back(replacementToEdit(Code, R));
Ilya Biryukovafb55542017-05-16 14:40:30 +000044 return Edits;
45}
46
47} // namespace
48
Sam McCall8a5dded2017-10-12 13:29:58 +000049void ClangdLSPServer::onInitialize(Ctx C, InitializeParams &Params) {
Ilya Biryukov940901e2017-12-13 12:51:22 +000050 reply(C, json::obj{
Sam McCall0930ab02017-11-07 15:49:35 +000051 {{"capabilities",
52 json::obj{
53 {"textDocumentSync", 1},
54 {"documentFormattingProvider", true},
55 {"documentRangeFormattingProvider", true},
56 {"documentOnTypeFormattingProvider",
57 json::obj{
58 {"firstTriggerCharacter", "}"},
59 {"moreTriggerCharacter", {}},
60 }},
61 {"codeActionProvider", true},
62 {"completionProvider",
63 json::obj{
64 {"resolveProvider", false},
65 {"triggerCharacters", {".", ">", ":"}},
66 }},
67 {"signatureHelpProvider",
68 json::obj{
69 {"triggerCharacters", {"(", ","}},
70 }},
71 {"definitionProvider", true},
Ilya Biryukov0e6a51f2017-12-12 12:27:47 +000072 {"documentHighlightProvider", true},
Haojian Wu345099c2017-11-09 11:30:04 +000073 {"renameProvider", true},
Sam McCall0930ab02017-11-07 15:49:35 +000074 {"executeCommandProvider",
75 json::obj{
76 {"commands", {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND}},
77 }},
78 }}}});
Sam McCall8a5dded2017-10-12 13:29:58 +000079 if (Params.rootUri && !Params.rootUri->file.empty())
80 Server.setRootPath(Params.rootUri->file);
81 else if (Params.rootPath && !Params.rootPath->empty())
82 Server.setRootPath(*Params.rootPath);
Ilya Biryukovafb55542017-05-16 14:40:30 +000083}
84
Sam McCall8a5dded2017-10-12 13:29:58 +000085void ClangdLSPServer::onShutdown(Ctx C, ShutdownParams &Params) {
Ilya Biryukov0d9b8a32017-10-25 08:45:41 +000086 // Do essentially nothing, just say we're ready to exit.
87 ShutdownRequestReceived = true;
Ilya Biryukov940901e2017-12-13 12:51:22 +000088 reply(C, nullptr);
Sam McCall8a5dded2017-10-12 13:29:58 +000089}
Ilya Biryukovafb55542017-05-16 14:40:30 +000090
Ilya Biryukov0d9b8a32017-10-25 08:45:41 +000091void ClangdLSPServer::onExit(Ctx C, ExitParams &Params) { IsDone = true; }
92
Sam McCall8a5dded2017-10-12 13:29:58 +000093void ClangdLSPServer::onDocumentDidOpen(Ctx C,
94 DidOpenTextDocumentParams &Params) {
Krasimir Georgievc2a16a32017-07-06 08:44:54 +000095 if (Params.metadata && !Params.metadata->extraFlags.empty())
Sam McCall4db732a2017-09-30 10:08:52 +000096 CDB.setExtraFlagsForFile(Params.textDocument.uri.file,
97 std::move(Params.metadata->extraFlags));
Ilya Biryukov940901e2017-12-13 12:51:22 +000098 Server.addDocument(std::move(C), Params.textDocument.uri.file,
99 Params.textDocument.text);
Ilya Biryukovafb55542017-05-16 14:40:30 +0000100}
101
Sam McCall8a5dded2017-10-12 13:29:58 +0000102void ClangdLSPServer::onDocumentDidChange(Ctx C,
103 DidChangeTextDocumentParams &Params) {
Benjamin Kramerb560a9a2017-10-26 10:36:20 +0000104 if (Params.contentChanges.size() != 1)
Ilya Biryukov940901e2017-12-13 12:51:22 +0000105 return replyError(C, ErrorCode::InvalidParams,
106 "can only apply one change at a time");
Ilya Biryukovafb55542017-05-16 14:40:30 +0000107 // We only support full syncing right now.
Ilya Biryukov940901e2017-12-13 12:51:22 +0000108 Server.addDocument(std::move(C), Params.textDocument.uri.file,
Sam McCall4db732a2017-09-30 10:08:52 +0000109 Params.contentChanges[0].text);
Ilya Biryukovafb55542017-05-16 14:40:30 +0000110}
111
Sam McCall8a5dded2017-10-12 13:29:58 +0000112void ClangdLSPServer::onFileEvent(Ctx C, DidChangeWatchedFilesParams &Params) {
Marc-Andre Laperlebf114242017-10-02 18:00:37 +0000113 Server.onFileEvent(Params);
114}
115
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000116void ClangdLSPServer::onCommand(Ctx C, ExecuteCommandParams &Params) {
117 if (Params.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND &&
118 Params.workspaceEdit) {
119 // The flow for "apply-fix" :
120 // 1. We publish a diagnostic, including fixits
121 // 2. The user clicks on the diagnostic, the editor asks us for code actions
122 // 3. We send code actions, with the fixit embedded as context
123 // 4. The user selects the fixit, the editor asks us to apply it
124 // 5. We unwrap the changes and send them back to the editor
125 // 6. The editor applies the changes (applyEdit), and sends us a reply (but
126 // we ignore it)
127
128 ApplyWorkspaceEditParams ApplyEdit;
129 ApplyEdit.edit = *Params.workspaceEdit;
Ilya Biryukov940901e2017-12-13 12:51:22 +0000130 reply(C, "Fix applied.");
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000131 // We don't need the response so id == 1 is OK.
132 // Ideally, we would wait for the response and if there is no error, we
133 // would reply success/failure to the original RPC.
Ilya Biryukov940901e2017-12-13 12:51:22 +0000134 call(C, "workspace/applyEdit", ApplyEdit);
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000135 } else {
136 // We should not get here because ExecuteCommandParams would not have
137 // parsed in the first place and this handler should not be called. But if
138 // more commands are added, this will be here has a safe guard.
Ilya Biryukov940901e2017-12-13 12:51:22 +0000139 replyError(
140 C, ErrorCode::InvalidParams,
Haojian Wu2375c922017-11-07 10:21:02 +0000141 llvm::formatv("Unsupported command \"{0}\".", Params.command).str());
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000142 }
143}
144
Haojian Wu345099c2017-11-09 11:30:04 +0000145void ClangdLSPServer::onRename(Ctx C, RenameParams &Params) {
146 auto File = Params.textDocument.uri.file;
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000147 auto Code = Server.getDocument(File);
148 if (!Code)
149 return replyError(C, ErrorCode::InvalidParams,
150 "onRename called for non-added file");
151
Ilya Biryukov940901e2017-12-13 12:51:22 +0000152 auto Replacements = Server.rename(C, File, Params.position, Params.newName);
Haojian Wu345099c2017-11-09 11:30:04 +0000153 if (!Replacements) {
Ilya Biryukov940901e2017-12-13 12:51:22 +0000154 replyError(C, ErrorCode::InternalError,
155 llvm::toString(Replacements.takeError()));
Haojian Wu345099c2017-11-09 11:30:04 +0000156 return;
157 }
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000158
159 std::vector<TextEdit> Edits = replacementsToEdits(*Code, *Replacements);
Haojian Wu345099c2017-11-09 11:30:04 +0000160 WorkspaceEdit WE;
Eric Liu78ed91a72018-01-29 15:37:46 +0000161 WE.changes = {{Params.textDocument.uri.uri(), Edits}};
Ilya Biryukov940901e2017-12-13 12:51:22 +0000162 reply(C, WE);
Haojian Wu345099c2017-11-09 11:30:04 +0000163}
164
Sam McCall8a5dded2017-10-12 13:29:58 +0000165void ClangdLSPServer::onDocumentDidClose(Ctx C,
166 DidCloseTextDocumentParams &Params) {
Ilya Biryukov940901e2017-12-13 12:51:22 +0000167 Server.removeDocument(std::move(C), Params.textDocument.uri.file);
Ilya Biryukovafb55542017-05-16 14:40:30 +0000168}
169
Sam McCall4db732a2017-09-30 10:08:52 +0000170void ClangdLSPServer::onDocumentOnTypeFormatting(
Sam McCall8a5dded2017-10-12 13:29:58 +0000171 Ctx C, DocumentOnTypeFormattingParams &Params) {
Ilya Biryukovafb55542017-05-16 14:40:30 +0000172 auto File = Params.textDocument.uri.file;
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000173 auto Code = Server.getDocument(File);
174 if (!Code)
175 return replyError(C, ErrorCode::InvalidParams,
176 "onDocumentOnTypeFormatting called for non-added file");
177
178 auto ReplacementsOrError = Server.formatOnType(*Code, File, Params.position);
Raoul Wols212bcf82017-12-12 20:25:06 +0000179 if (ReplacementsOrError)
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000180 reply(C, json::ary(replacementsToEdits(*Code, ReplacementsOrError.get())));
Raoul Wols212bcf82017-12-12 20:25:06 +0000181 else
Ilya Biryukov940901e2017-12-13 12:51:22 +0000182 replyError(C, ErrorCode::UnknownErrorCode,
183 llvm::toString(ReplacementsOrError.takeError()));
Ilya Biryukovafb55542017-05-16 14:40:30 +0000184}
185
Sam McCall4db732a2017-09-30 10:08:52 +0000186void ClangdLSPServer::onDocumentRangeFormatting(
Sam McCall8a5dded2017-10-12 13:29:58 +0000187 Ctx C, DocumentRangeFormattingParams &Params) {
Ilya Biryukovafb55542017-05-16 14:40:30 +0000188 auto File = Params.textDocument.uri.file;
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000189 auto Code = Server.getDocument(File);
190 if (!Code)
191 return replyError(C, ErrorCode::InvalidParams,
192 "onDocumentRangeFormatting called for non-added file");
193
194 auto ReplacementsOrError = Server.formatRange(*Code, File, Params.range);
Raoul Wols212bcf82017-12-12 20:25:06 +0000195 if (ReplacementsOrError)
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000196 reply(C, json::ary(replacementsToEdits(*Code, ReplacementsOrError.get())));
Raoul Wols212bcf82017-12-12 20:25:06 +0000197 else
Ilya Biryukov940901e2017-12-13 12:51:22 +0000198 replyError(C, ErrorCode::UnknownErrorCode,
199 llvm::toString(ReplacementsOrError.takeError()));
Ilya Biryukovafb55542017-05-16 14:40:30 +0000200}
201
Sam McCall8a5dded2017-10-12 13:29:58 +0000202void ClangdLSPServer::onDocumentFormatting(Ctx C,
203 DocumentFormattingParams &Params) {
Sam McCall4db732a2017-09-30 10:08:52 +0000204 auto File = Params.textDocument.uri.file;
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000205 auto Code = Server.getDocument(File);
206 if (!Code)
207 return replyError(C, ErrorCode::InvalidParams,
208 "onDocumentFormatting called for non-added file");
209
210 auto ReplacementsOrError = Server.formatFile(*Code, File);
Raoul Wols212bcf82017-12-12 20:25:06 +0000211 if (ReplacementsOrError)
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000212 reply(C, json::ary(replacementsToEdits(*Code, ReplacementsOrError.get())));
Raoul Wols212bcf82017-12-12 20:25:06 +0000213 else
Ilya Biryukov940901e2017-12-13 12:51:22 +0000214 replyError(C, ErrorCode::UnknownErrorCode,
215 llvm::toString(ReplacementsOrError.takeError()));
Sam McCall4db732a2017-09-30 10:08:52 +0000216}
217
Sam McCall8a5dded2017-10-12 13:29:58 +0000218void ClangdLSPServer::onCodeAction(Ctx C, CodeActionParams &Params) {
Ilya Biryukovafb55542017-05-16 14:40:30 +0000219 // We provide a code action for each diagnostic at the requested location
220 // which has FixIts available.
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000221 auto Code = Server.getDocument(Params.textDocument.uri.file);
222 if (!Code)
223 return replyError(C, ErrorCode::InvalidParams,
224 "onCodeAction called for non-added file");
225
Sam McCalldd0566b2017-11-06 15:40:30 +0000226 json::ary Commands;
Ilya Biryukovafb55542017-05-16 14:40:30 +0000227 for (Diagnostic &D : Params.context.diagnostics) {
Sam McCall8111d3b2017-12-13 08:48:42 +0000228 auto Edits = getFixIts(Params.textDocument.uri.file, D);
Sam McCalldd0566b2017-11-06 15:40:30 +0000229 if (!Edits.empty()) {
230 WorkspaceEdit WE;
Eric Liu78ed91a72018-01-29 15:37:46 +0000231 WE.changes = {{Params.textDocument.uri.uri(), std::move(Edits)}};
Sam McCalldd0566b2017-11-06 15:40:30 +0000232 Commands.push_back(json::obj{
233 {"title", llvm::formatv("Apply FixIt {0}", D.message)},
234 {"command", ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND},
235 {"arguments", {WE}},
236 });
237 }
Ilya Biryukovafb55542017-05-16 14:40:30 +0000238 }
Ilya Biryukov940901e2017-12-13 12:51:22 +0000239 reply(C, std::move(Commands));
Ilya Biryukovafb55542017-05-16 14:40:30 +0000240}
241
Sam McCall8a5dded2017-10-12 13:29:58 +0000242void ClangdLSPServer::onCompletion(Ctx C, TextDocumentPositionParams &Params) {
Ilya Biryukov940901e2017-12-13 12:51:22 +0000243 auto Reply = Server
244 .codeComplete(std::move(C), Params.textDocument.uri.file,
245 Position{Params.position.line,
246 Params.position.character},
247 CCOpts)
248 .get(); // FIXME(ibiryukov): This could be made async if we
249 // had an API that would allow to attach callbacks to
250 // futures returned by ClangdServer.
251
252 // We have std::move'd from C, now restore it from response of codeComplete.
253 C = std::move(Reply.first);
254 auto List = std::move(Reply.second.Value);
255 reply(C, List);
Ilya Biryukovafb55542017-05-16 14:40:30 +0000256}
257
Sam McCall8a5dded2017-10-12 13:29:58 +0000258void ClangdLSPServer::onSignatureHelp(Ctx C,
259 TextDocumentPositionParams &Params) {
Benjamin Krameree19f162017-10-26 12:28:13 +0000260 auto SignatureHelp = Server.signatureHelp(
Ilya Biryukov940901e2017-12-13 12:51:22 +0000261 C, Params.textDocument.uri.file,
Benjamin Krameree19f162017-10-26 12:28:13 +0000262 Position{Params.position.line, Params.position.character});
263 if (!SignatureHelp)
Ilya Biryukov940901e2017-12-13 12:51:22 +0000264 return replyError(C, ErrorCode::InvalidParams,
265 llvm::toString(SignatureHelp.takeError()));
266 reply(C, SignatureHelp->Value);
Ilya Biryukovd9bdfe02017-10-06 11:54:17 +0000267}
268
Sam McCall8a5dded2017-10-12 13:29:58 +0000269void ClangdLSPServer::onGoToDefinition(Ctx C,
270 TextDocumentPositionParams &Params) {
Benjamin Krameree19f162017-10-26 12:28:13 +0000271 auto Items = Server.findDefinitions(
Ilya Biryukov940901e2017-12-13 12:51:22 +0000272 C, Params.textDocument.uri.file,
Benjamin Krameree19f162017-10-26 12:28:13 +0000273 Position{Params.position.line, Params.position.character});
274 if (!Items)
Ilya Biryukov940901e2017-12-13 12:51:22 +0000275 return replyError(C, ErrorCode::InvalidParams,
276 llvm::toString(Items.takeError()));
277 reply(C, json::ary(Items->Value));
Marc-Andre Laperle2cbf0372017-06-28 16:12:10 +0000278}
279
Sam McCall8a5dded2017-10-12 13:29:58 +0000280void ClangdLSPServer::onSwitchSourceHeader(Ctx C,
281 TextDocumentIdentifier &Params) {
Sam McCall4db732a2017-09-30 10:08:52 +0000282 llvm::Optional<Path> Result = Server.switchSourceHeader(Params.uri.file);
Eric Liu78ed91a72018-01-29 15:37:46 +0000283 reply(C, Result ? URI::createFile(*Result).toString() : "");
Marc-Andre Laperle6571b3e2017-09-28 03:14:40 +0000284}
285
Ilya Biryukov0e6a51f2017-12-12 12:27:47 +0000286void ClangdLSPServer::onDocumentHighlight(Ctx C,
287 TextDocumentPositionParams &Params) {
288
289 auto Highlights = Server.findDocumentHighlights(
Ilya Biryukov940901e2017-12-13 12:51:22 +0000290 C, Params.textDocument.uri.file,
Ilya Biryukov0e6a51f2017-12-12 12:27:47 +0000291 Position{Params.position.line, Params.position.character});
292
293 if (!Highlights) {
Ilya Biryukov940901e2017-12-13 12:51:22 +0000294 replyError(C, ErrorCode::InternalError,
295 llvm::toString(Highlights.takeError()));
Ilya Biryukov0e6a51f2017-12-12 12:27:47 +0000296 return;
297 }
298
Ilya Biryukov940901e2017-12-13 12:51:22 +0000299 reply(C, json::ary(Highlights->Value));
Ilya Biryukov0e6a51f2017-12-12 12:27:47 +0000300}
301
Ilya Biryukovdb8b2d72017-08-14 08:45:47 +0000302ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount,
Ilya Biryukove9eb7f02017-11-16 16:25:18 +0000303 bool StorePreamblesInMemory,
Sam McCalladccab62017-11-23 16:58:22 +0000304 const clangd::CodeCompleteOptions &CCOpts,
Ilya Biryukov0c1ca6b2017-10-02 15:13:20 +0000305 llvm::Optional<StringRef> ResourceDir,
Eric Liubfac8f72017-12-19 18:00:37 +0000306 llvm::Optional<Path> CompileCommandsDir,
Haojian Wuba28e9a2018-01-10 14:44:34 +0000307 bool BuildDynamicSymbolIndex,
308 SymbolIndex *StaticIdx)
Ilya Biryukov940901e2017-12-13 12:51:22 +0000309 : Out(Out), CDB(std::move(CompileCommandsDir)), CCOpts(CCOpts),
310 Server(CDB, /*DiagConsumer=*/*this, FSProvider, AsyncThreadsCount,
Haojian Wuba28e9a2018-01-10 14:44:34 +0000311 StorePreamblesInMemory, BuildDynamicSymbolIndex, StaticIdx,
312 ResourceDir) {}
Ilya Biryukov38d79772017-05-16 09:38:59 +0000313
Ilya Biryukov0d9b8a32017-10-25 08:45:41 +0000314bool ClangdLSPServer::run(std::istream &In) {
Ilya Biryukovafb55542017-05-16 14:40:30 +0000315 assert(!IsDone && "Run was called before");
Ilya Biryukov38d79772017-05-16 09:38:59 +0000316
Ilya Biryukovafb55542017-05-16 14:40:30 +0000317 // Set up JSONRPCDispatcher.
Ilya Biryukov940901e2017-12-13 12:51:22 +0000318 JSONRPCDispatcher Dispatcher([](Context Ctx, const json::Expr &Params) {
319 replyError(Ctx, ErrorCode::MethodNotFound, "method not found");
320 });
Sam McCall4db732a2017-09-30 10:08:52 +0000321 registerCallbackHandlers(Dispatcher, Out, /*Callbacks=*/*this);
Ilya Biryukov38d79772017-05-16 09:38:59 +0000322
Ilya Biryukovafb55542017-05-16 14:40:30 +0000323 // Run the Language Server loop.
324 runLanguageServerLoop(In, Out, Dispatcher, IsDone);
325
326 // Make sure IsDone is set to true after this method exits to ensure assertion
327 // at the start of the method fires if it's ever executed again.
328 IsDone = true;
Ilya Biryukov0d9b8a32017-10-25 08:45:41 +0000329
330 return ShutdownRequestReceived;
Ilya Biryukov38d79772017-05-16 09:38:59 +0000331}
332
Sam McCall8111d3b2017-12-13 08:48:42 +0000333std::vector<TextEdit> ClangdLSPServer::getFixIts(StringRef File,
334 const clangd::Diagnostic &D) {
Ilya Biryukov38d79772017-05-16 09:38:59 +0000335 std::lock_guard<std::mutex> Lock(FixItsMutex);
336 auto DiagToFixItsIter = FixItsMap.find(File);
337 if (DiagToFixItsIter == FixItsMap.end())
338 return {};
339
340 const auto &DiagToFixItsMap = DiagToFixItsIter->second;
341 auto FixItsIter = DiagToFixItsMap.find(D);
342 if (FixItsIter == DiagToFixItsMap.end())
343 return {};
344
345 return FixItsIter->second;
346}
347
Sam McCall4db732a2017-09-30 10:08:52 +0000348void ClangdLSPServer::onDiagnosticsReady(
Ilya Biryukov95558392018-01-10 17:59:27 +0000349 const Context &Ctx, PathRef File,
350 Tagged<std::vector<DiagWithFixIts>> Diagnostics) {
Sam McCalldd0566b2017-11-06 15:40:30 +0000351 json::ary DiagnosticsJSON;
Ilya Biryukov38d79772017-05-16 09:38:59 +0000352
353 DiagnosticToReplacementMap LocalFixIts; // Temporary storage
Sam McCall4db732a2017-09-30 10:08:52 +0000354 for (auto &DiagWithFixes : Diagnostics.Value) {
Ilya Biryukov38d79772017-05-16 09:38:59 +0000355 auto Diag = DiagWithFixes.Diag;
Sam McCalldd0566b2017-11-06 15:40:30 +0000356 DiagnosticsJSON.push_back(json::obj{
357 {"range", Diag.range},
358 {"severity", Diag.severity},
359 {"message", Diag.message},
360 });
Ilya Biryukov38d79772017-05-16 09:38:59 +0000361 // We convert to Replacements to become independent of the SourceManager.
362 auto &FixItsForDiagnostic = LocalFixIts[Diag];
363 std::copy(DiagWithFixes.FixIts.begin(), DiagWithFixes.FixIts.end(),
364 std::back_inserter(FixItsForDiagnostic));
365 }
366
367 // Cache FixIts
368 {
369 // FIXME(ibiryukov): should be deleted when documents are removed
370 std::lock_guard<std::mutex> Lock(FixItsMutex);
371 FixItsMap[File] = LocalFixIts;
372 }
373
374 // Publish diagnostics.
Sam McCalldd0566b2017-11-06 15:40:30 +0000375 Out.writeMessage(json::obj{
376 {"jsonrpc", "2.0"},
377 {"method", "textDocument/publishDiagnostics"},
378 {"params",
379 json::obj{
Eric Liu78ed91a72018-01-29 15:37:46 +0000380 {"uri", URIForFile{File}},
Sam McCalldd0566b2017-11-06 15:40:30 +0000381 {"diagnostics", std::move(DiagnosticsJSON)},
382 }},
383 });
Ilya Biryukov38d79772017-05-16 09:38:59 +0000384}