blob: ba7a414ae8d0575f710720dd00fba66e65595232 [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"
Eric Liu5740ff52018-01-31 16:26:27 +000015#include "llvm/Support/Path.h"
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +000016
Ilya Biryukov38d79772017-05-16 09:38:59 +000017using namespace clang::clangd;
18using namespace clang;
19
Ilya Biryukovafb55542017-05-16 14:40:30 +000020namespace {
21
Eric Liu5740ff52018-01-31 16:26:27 +000022/// \brief Supports a test URI scheme with relaxed constraints for lit tests.
23/// The path in a test URI will be combined with a platform-specific fake
24/// directory to form an absolute path. For example, test:///a.cpp is resolved
25/// C:\clangd-test\a.cpp on Windows and /clangd-test/a.cpp on Unix.
26class TestScheme : public URIScheme {
27public:
28 llvm::Expected<std::string>
29 getAbsolutePath(llvm::StringRef /*Authority*/, llvm::StringRef Body,
30 llvm::StringRef /*HintPath*/) const override {
31 using namespace llvm::sys;
32 // Still require "/" in body to mimic file scheme, as we want lengths of an
33 // equivalent URI in both schemes to be the same.
34 if (!Body.startswith("/"))
35 return llvm::make_error<llvm::StringError>(
36 "Expect URI body to be an absolute path starting with '/': " + Body,
37 llvm::inconvertibleErrorCode());
38 Body = Body.ltrim('/');
39#ifdef LLVM_ON_WIN32
40 constexpr char TestDir[] = "C:\\clangd-test";
41#else
42 constexpr char TestDir[] = "/clangd-test";
43#endif
44 llvm::SmallVector<char, 16> Path(Body.begin(), Body.end());
45 path::native(Path);
46 auto Err = fs::make_absolute(TestDir, Path);
47 assert(!Err);
48 return std::string(Path.begin(), Path.end());
49 }
50
51 llvm::Expected<URI>
52 uriFromAbsolutePath(llvm::StringRef AbsolutePath) const override {
53 llvm_unreachable("Clangd must never create a test URI.");
54 }
55};
56
57static URISchemeRegistry::Add<TestScheme>
58 X("test", "Test scheme for clangd lit tests.");
59
Raoul Wols212bcf82017-12-12 20:25:06 +000060TextEdit replacementToEdit(StringRef Code, const tooling::Replacement &R) {
61 Range ReplacementRange = {
62 offsetToPosition(Code, R.getOffset()),
63 offsetToPosition(Code, R.getOffset() + R.getLength())};
64 return {ReplacementRange, R.getReplacementText()};
65}
66
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +000067std::vector<TextEdit>
Ilya Biryukovafb55542017-05-16 14:40:30 +000068replacementsToEdits(StringRef Code,
69 const std::vector<tooling::Replacement> &Replacements) {
70 // Turn the replacements into the format specified by the Language Server
Sam McCalldd0566b2017-11-06 15:40:30 +000071 // Protocol. Fuse them into one big JSON array.
72 std::vector<TextEdit> Edits;
Raoul Wols212bcf82017-12-12 20:25:06 +000073 for (const auto &R : Replacements)
74 Edits.push_back(replacementToEdit(Code, R));
75 return Edits;
76}
77
78std::vector<TextEdit> replacementsToEdits(StringRef Code,
79 const tooling::Replacements &Repls) {
80 std::vector<TextEdit> Edits;
81 for (const auto &R : Repls)
82 Edits.push_back(replacementToEdit(Code, R));
Ilya Biryukovafb55542017-05-16 14:40:30 +000083 return Edits;
84}
85
86} // namespace
87
Sam McCalld1a7a372018-01-31 13:40:48 +000088void ClangdLSPServer::onInitialize(InitializeParams &Params) {
89 reply(json::obj{
Sam McCall0930ab02017-11-07 15:49:35 +000090 {{"capabilities",
91 json::obj{
92 {"textDocumentSync", 1},
93 {"documentFormattingProvider", true},
94 {"documentRangeFormattingProvider", true},
95 {"documentOnTypeFormattingProvider",
96 json::obj{
97 {"firstTriggerCharacter", "}"},
98 {"moreTriggerCharacter", {}},
99 }},
100 {"codeActionProvider", true},
101 {"completionProvider",
102 json::obj{
103 {"resolveProvider", false},
104 {"triggerCharacters", {".", ">", ":"}},
105 }},
106 {"signatureHelpProvider",
107 json::obj{
108 {"triggerCharacters", {"(", ","}},
109 }},
110 {"definitionProvider", true},
Ilya Biryukov0e6a51f2017-12-12 12:27:47 +0000111 {"documentHighlightProvider", true},
Haojian Wu345099c2017-11-09 11:30:04 +0000112 {"renameProvider", true},
Sam McCall0930ab02017-11-07 15:49:35 +0000113 {"executeCommandProvider",
114 json::obj{
115 {"commands", {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND}},
116 }},
117 }}}});
Sam McCall8a5dded2017-10-12 13:29:58 +0000118 if (Params.rootUri && !Params.rootUri->file.empty())
119 Server.setRootPath(Params.rootUri->file);
120 else if (Params.rootPath && !Params.rootPath->empty())
121 Server.setRootPath(*Params.rootPath);
Ilya Biryukovafb55542017-05-16 14:40:30 +0000122}
123
Sam McCalld1a7a372018-01-31 13:40:48 +0000124void ClangdLSPServer::onShutdown(ShutdownParams &Params) {
Ilya Biryukov0d9b8a32017-10-25 08:45:41 +0000125 // Do essentially nothing, just say we're ready to exit.
126 ShutdownRequestReceived = true;
Sam McCalld1a7a372018-01-31 13:40:48 +0000127 reply(nullptr);
Sam McCall8a5dded2017-10-12 13:29:58 +0000128}
Ilya Biryukovafb55542017-05-16 14:40:30 +0000129
Sam McCalld1a7a372018-01-31 13:40:48 +0000130void ClangdLSPServer::onExit(ExitParams &Params) { IsDone = true; }
Ilya Biryukov0d9b8a32017-10-25 08:45:41 +0000131
Sam McCalld1a7a372018-01-31 13:40:48 +0000132void ClangdLSPServer::onDocumentDidOpen(DidOpenTextDocumentParams &Params) {
Krasimir Georgievc2a16a32017-07-06 08:44:54 +0000133 if (Params.metadata && !Params.metadata->extraFlags.empty())
Sam McCall4db732a2017-09-30 10:08:52 +0000134 CDB.setExtraFlagsForFile(Params.textDocument.uri.file,
135 std::move(Params.metadata->extraFlags));
Sam McCalld1a7a372018-01-31 13:40:48 +0000136 Server.addDocument(Params.textDocument.uri.file, Params.textDocument.text);
Ilya Biryukovafb55542017-05-16 14:40:30 +0000137}
138
Sam McCalld1a7a372018-01-31 13:40:48 +0000139void ClangdLSPServer::onDocumentDidChange(DidChangeTextDocumentParams &Params) {
Benjamin Kramerb560a9a2017-10-26 10:36:20 +0000140 if (Params.contentChanges.size() != 1)
Sam McCalld1a7a372018-01-31 13:40:48 +0000141 return replyError(ErrorCode::InvalidParams,
Ilya Biryukov940901e2017-12-13 12:51:22 +0000142 "can only apply one change at a time");
Ilya Biryukovafb55542017-05-16 14:40:30 +0000143 // We only support full syncing right now.
Sam McCalld1a7a372018-01-31 13:40:48 +0000144 Server.addDocument(Params.textDocument.uri.file,
Sam McCall4db732a2017-09-30 10:08:52 +0000145 Params.contentChanges[0].text);
Ilya Biryukovafb55542017-05-16 14:40:30 +0000146}
147
Sam McCalld1a7a372018-01-31 13:40:48 +0000148void ClangdLSPServer::onFileEvent(DidChangeWatchedFilesParams &Params) {
Marc-Andre Laperlebf114242017-10-02 18:00:37 +0000149 Server.onFileEvent(Params);
150}
151
Sam McCalld1a7a372018-01-31 13:40:48 +0000152void ClangdLSPServer::onCommand(ExecuteCommandParams &Params) {
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000153 if (Params.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND &&
154 Params.workspaceEdit) {
155 // The flow for "apply-fix" :
156 // 1. We publish a diagnostic, including fixits
157 // 2. The user clicks on the diagnostic, the editor asks us for code actions
158 // 3. We send code actions, with the fixit embedded as context
159 // 4. The user selects the fixit, the editor asks us to apply it
160 // 5. We unwrap the changes and send them back to the editor
161 // 6. The editor applies the changes (applyEdit), and sends us a reply (but
162 // we ignore it)
163
164 ApplyWorkspaceEditParams ApplyEdit;
165 ApplyEdit.edit = *Params.workspaceEdit;
Sam McCalld1a7a372018-01-31 13:40:48 +0000166 reply("Fix applied.");
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000167 // We don't need the response so id == 1 is OK.
168 // Ideally, we would wait for the response and if there is no error, we
169 // would reply success/failure to the original RPC.
Sam McCalld1a7a372018-01-31 13:40:48 +0000170 call("workspace/applyEdit", ApplyEdit);
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000171 } else {
172 // We should not get here because ExecuteCommandParams would not have
173 // parsed in the first place and this handler should not be called. But if
174 // more commands are added, this will be here has a safe guard.
Ilya Biryukov940901e2017-12-13 12:51:22 +0000175 replyError(
Sam McCalld1a7a372018-01-31 13:40:48 +0000176 ErrorCode::InvalidParams,
Haojian Wu2375c922017-11-07 10:21:02 +0000177 llvm::formatv("Unsupported command \"{0}\".", Params.command).str());
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000178 }
179}
180
Sam McCalld1a7a372018-01-31 13:40:48 +0000181void ClangdLSPServer::onRename(RenameParams &Params) {
Haojian Wu345099c2017-11-09 11:30:04 +0000182 auto File = Params.textDocument.uri.file;
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000183 auto Code = Server.getDocument(File);
184 if (!Code)
Sam McCalld1a7a372018-01-31 13:40:48 +0000185 return replyError(ErrorCode::InvalidParams,
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000186 "onRename called for non-added file");
187
Sam McCalld1a7a372018-01-31 13:40:48 +0000188 auto Replacements = Server.rename(File, Params.position, Params.newName);
Haojian Wu345099c2017-11-09 11:30:04 +0000189 if (!Replacements) {
Sam McCalld1a7a372018-01-31 13:40:48 +0000190 replyError(ErrorCode::InternalError,
Ilya Biryukov940901e2017-12-13 12:51:22 +0000191 llvm::toString(Replacements.takeError()));
Haojian Wu345099c2017-11-09 11:30:04 +0000192 return;
193 }
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000194
195 std::vector<TextEdit> Edits = replacementsToEdits(*Code, *Replacements);
Haojian Wu345099c2017-11-09 11:30:04 +0000196 WorkspaceEdit WE;
Eric Liu78ed91a72018-01-29 15:37:46 +0000197 WE.changes = {{Params.textDocument.uri.uri(), Edits}};
Sam McCalld1a7a372018-01-31 13:40:48 +0000198 reply(WE);
Haojian Wu345099c2017-11-09 11:30:04 +0000199}
200
Sam McCalld1a7a372018-01-31 13:40:48 +0000201void ClangdLSPServer::onDocumentDidClose(DidCloseTextDocumentParams &Params) {
202 Server.removeDocument(Params.textDocument.uri.file);
Ilya Biryukovafb55542017-05-16 14:40:30 +0000203}
204
Sam McCall4db732a2017-09-30 10:08:52 +0000205void ClangdLSPServer::onDocumentOnTypeFormatting(
Sam McCalld1a7a372018-01-31 13:40:48 +0000206 DocumentOnTypeFormattingParams &Params) {
Ilya Biryukovafb55542017-05-16 14:40:30 +0000207 auto File = Params.textDocument.uri.file;
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000208 auto Code = Server.getDocument(File);
209 if (!Code)
Sam McCalld1a7a372018-01-31 13:40:48 +0000210 return replyError(ErrorCode::InvalidParams,
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000211 "onDocumentOnTypeFormatting called for non-added file");
212
213 auto ReplacementsOrError = Server.formatOnType(*Code, File, Params.position);
Raoul Wols212bcf82017-12-12 20:25:06 +0000214 if (ReplacementsOrError)
Sam McCalld1a7a372018-01-31 13:40:48 +0000215 reply(json::ary(replacementsToEdits(*Code, ReplacementsOrError.get())));
Raoul Wols212bcf82017-12-12 20:25:06 +0000216 else
Sam McCalld1a7a372018-01-31 13:40:48 +0000217 replyError(ErrorCode::UnknownErrorCode,
Ilya Biryukov940901e2017-12-13 12:51:22 +0000218 llvm::toString(ReplacementsOrError.takeError()));
Ilya Biryukovafb55542017-05-16 14:40:30 +0000219}
220
Sam McCall4db732a2017-09-30 10:08:52 +0000221void ClangdLSPServer::onDocumentRangeFormatting(
Sam McCalld1a7a372018-01-31 13:40:48 +0000222 DocumentRangeFormattingParams &Params) {
Ilya Biryukovafb55542017-05-16 14:40:30 +0000223 auto File = Params.textDocument.uri.file;
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000224 auto Code = Server.getDocument(File);
225 if (!Code)
Sam McCalld1a7a372018-01-31 13:40:48 +0000226 return replyError(ErrorCode::InvalidParams,
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000227 "onDocumentRangeFormatting called for non-added file");
228
229 auto ReplacementsOrError = Server.formatRange(*Code, File, Params.range);
Raoul Wols212bcf82017-12-12 20:25:06 +0000230 if (ReplacementsOrError)
Sam McCalld1a7a372018-01-31 13:40:48 +0000231 reply(json::ary(replacementsToEdits(*Code, ReplacementsOrError.get())));
Raoul Wols212bcf82017-12-12 20:25:06 +0000232 else
Sam McCalld1a7a372018-01-31 13:40:48 +0000233 replyError(ErrorCode::UnknownErrorCode,
Ilya Biryukov940901e2017-12-13 12:51:22 +0000234 llvm::toString(ReplacementsOrError.takeError()));
Ilya Biryukovafb55542017-05-16 14:40:30 +0000235}
236
Sam McCalld1a7a372018-01-31 13:40:48 +0000237void ClangdLSPServer::onDocumentFormatting(DocumentFormattingParams &Params) {
Sam McCall4db732a2017-09-30 10:08:52 +0000238 auto File = Params.textDocument.uri.file;
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000239 auto Code = Server.getDocument(File);
240 if (!Code)
Sam McCalld1a7a372018-01-31 13:40:48 +0000241 return replyError(ErrorCode::InvalidParams,
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000242 "onDocumentFormatting called for non-added file");
243
244 auto ReplacementsOrError = Server.formatFile(*Code, File);
Raoul Wols212bcf82017-12-12 20:25:06 +0000245 if (ReplacementsOrError)
Sam McCalld1a7a372018-01-31 13:40:48 +0000246 reply(json::ary(replacementsToEdits(*Code, ReplacementsOrError.get())));
Raoul Wols212bcf82017-12-12 20:25:06 +0000247 else
Sam McCalld1a7a372018-01-31 13:40:48 +0000248 replyError(ErrorCode::UnknownErrorCode,
Ilya Biryukov940901e2017-12-13 12:51:22 +0000249 llvm::toString(ReplacementsOrError.takeError()));
Sam McCall4db732a2017-09-30 10:08:52 +0000250}
251
Sam McCalld1a7a372018-01-31 13:40:48 +0000252void ClangdLSPServer::onCodeAction(CodeActionParams &Params) {
Ilya Biryukovafb55542017-05-16 14:40:30 +0000253 // We provide a code action for each diagnostic at the requested location
254 // which has FixIts available.
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000255 auto Code = Server.getDocument(Params.textDocument.uri.file);
256 if (!Code)
Sam McCalld1a7a372018-01-31 13:40:48 +0000257 return replyError(ErrorCode::InvalidParams,
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000258 "onCodeAction called for non-added file");
259
Sam McCalldd0566b2017-11-06 15:40:30 +0000260 json::ary Commands;
Ilya Biryukovafb55542017-05-16 14:40:30 +0000261 for (Diagnostic &D : Params.context.diagnostics) {
Sam McCall8111d3b2017-12-13 08:48:42 +0000262 auto Edits = getFixIts(Params.textDocument.uri.file, D);
Sam McCalldd0566b2017-11-06 15:40:30 +0000263 if (!Edits.empty()) {
264 WorkspaceEdit WE;
Eric Liu78ed91a72018-01-29 15:37:46 +0000265 WE.changes = {{Params.textDocument.uri.uri(), std::move(Edits)}};
Sam McCalldd0566b2017-11-06 15:40:30 +0000266 Commands.push_back(json::obj{
267 {"title", llvm::formatv("Apply FixIt {0}", D.message)},
268 {"command", ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND},
269 {"arguments", {WE}},
270 });
271 }
Ilya Biryukovafb55542017-05-16 14:40:30 +0000272 }
Sam McCalld1a7a372018-01-31 13:40:48 +0000273 reply(std::move(Commands));
Ilya Biryukovafb55542017-05-16 14:40:30 +0000274}
275
Sam McCalld1a7a372018-01-31 13:40:48 +0000276void ClangdLSPServer::onCompletion(TextDocumentPositionParams &Params) {
277 Server.codeComplete(Params.textDocument.uri.file,
278 Position{Params.position.line, Params.position.character},
279 CCOpts,
280 [](Tagged<CompletionList> List) { reply(List.Value); });
Ilya Biryukovafb55542017-05-16 14:40:30 +0000281}
282
Sam McCalld1a7a372018-01-31 13:40:48 +0000283void ClangdLSPServer::onSignatureHelp(TextDocumentPositionParams &Params) {
Benjamin Krameree19f162017-10-26 12:28:13 +0000284 auto SignatureHelp = Server.signatureHelp(
Sam McCalld1a7a372018-01-31 13:40:48 +0000285 Params.textDocument.uri.file,
Benjamin Krameree19f162017-10-26 12:28:13 +0000286 Position{Params.position.line, Params.position.character});
287 if (!SignatureHelp)
Sam McCalld1a7a372018-01-31 13:40:48 +0000288 return replyError(ErrorCode::InvalidParams,
Ilya Biryukov940901e2017-12-13 12:51:22 +0000289 llvm::toString(SignatureHelp.takeError()));
Sam McCalld1a7a372018-01-31 13:40:48 +0000290 reply(SignatureHelp->Value);
Ilya Biryukovd9bdfe02017-10-06 11:54:17 +0000291}
292
Sam McCalld1a7a372018-01-31 13:40:48 +0000293void ClangdLSPServer::onGoToDefinition(TextDocumentPositionParams &Params) {
Benjamin Krameree19f162017-10-26 12:28:13 +0000294 auto Items = Server.findDefinitions(
Sam McCalld1a7a372018-01-31 13:40:48 +0000295 Params.textDocument.uri.file,
Benjamin Krameree19f162017-10-26 12:28:13 +0000296 Position{Params.position.line, Params.position.character});
297 if (!Items)
Sam McCalld1a7a372018-01-31 13:40:48 +0000298 return replyError(ErrorCode::InvalidParams,
Ilya Biryukov940901e2017-12-13 12:51:22 +0000299 llvm::toString(Items.takeError()));
Sam McCalld1a7a372018-01-31 13:40:48 +0000300 reply(json::ary(Items->Value));
Marc-Andre Laperle2cbf0372017-06-28 16:12:10 +0000301}
302
Sam McCalld1a7a372018-01-31 13:40:48 +0000303void ClangdLSPServer::onSwitchSourceHeader(TextDocumentIdentifier &Params) {
Sam McCall4db732a2017-09-30 10:08:52 +0000304 llvm::Optional<Path> Result = Server.switchSourceHeader(Params.uri.file);
Sam McCalld1a7a372018-01-31 13:40:48 +0000305 reply(Result ? URI::createFile(*Result).toString() : "");
Marc-Andre Laperle6571b3e2017-09-28 03:14:40 +0000306}
307
Sam McCalld1a7a372018-01-31 13:40:48 +0000308void ClangdLSPServer::onDocumentHighlight(TextDocumentPositionParams &Params) {
Ilya Biryukov0e6a51f2017-12-12 12:27:47 +0000309 auto Highlights = Server.findDocumentHighlights(
Sam McCalld1a7a372018-01-31 13:40:48 +0000310 Params.textDocument.uri.file,
Ilya Biryukov0e6a51f2017-12-12 12:27:47 +0000311 Position{Params.position.line, Params.position.character});
312
313 if (!Highlights) {
Sam McCalld1a7a372018-01-31 13:40:48 +0000314 replyError(ErrorCode::InternalError,
Ilya Biryukov940901e2017-12-13 12:51:22 +0000315 llvm::toString(Highlights.takeError()));
Ilya Biryukov0e6a51f2017-12-12 12:27:47 +0000316 return;
317 }
318
Sam McCalld1a7a372018-01-31 13:40:48 +0000319 reply(json::ary(Highlights->Value));
Ilya Biryukov0e6a51f2017-12-12 12:27:47 +0000320}
321
Ilya Biryukovdb8b2d72017-08-14 08:45:47 +0000322ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount,
Ilya Biryukove9eb7f02017-11-16 16:25:18 +0000323 bool StorePreamblesInMemory,
Sam McCalladccab62017-11-23 16:58:22 +0000324 const clangd::CodeCompleteOptions &CCOpts,
Ilya Biryukov0c1ca6b2017-10-02 15:13:20 +0000325 llvm::Optional<StringRef> ResourceDir,
Eric Liubfac8f72017-12-19 18:00:37 +0000326 llvm::Optional<Path> CompileCommandsDir,
Haojian Wuba28e9a2018-01-10 14:44:34 +0000327 bool BuildDynamicSymbolIndex,
328 SymbolIndex *StaticIdx)
Ilya Biryukov940901e2017-12-13 12:51:22 +0000329 : Out(Out), CDB(std::move(CompileCommandsDir)), CCOpts(CCOpts),
330 Server(CDB, /*DiagConsumer=*/*this, FSProvider, AsyncThreadsCount,
Haojian Wuba28e9a2018-01-10 14:44:34 +0000331 StorePreamblesInMemory, BuildDynamicSymbolIndex, StaticIdx,
332 ResourceDir) {}
Ilya Biryukov38d79772017-05-16 09:38:59 +0000333
Ilya Biryukov0d9b8a32017-10-25 08:45:41 +0000334bool ClangdLSPServer::run(std::istream &In) {
Ilya Biryukovafb55542017-05-16 14:40:30 +0000335 assert(!IsDone && "Run was called before");
Ilya Biryukov38d79772017-05-16 09:38:59 +0000336
Ilya Biryukovafb55542017-05-16 14:40:30 +0000337 // Set up JSONRPCDispatcher.
Sam McCalld1a7a372018-01-31 13:40:48 +0000338 JSONRPCDispatcher Dispatcher([](const json::Expr &Params) {
339 replyError(ErrorCode::MethodNotFound, "method not found");
Ilya Biryukov940901e2017-12-13 12:51:22 +0000340 });
Sam McCall4db732a2017-09-30 10:08:52 +0000341 registerCallbackHandlers(Dispatcher, Out, /*Callbacks=*/*this);
Ilya Biryukov38d79772017-05-16 09:38:59 +0000342
Ilya Biryukovafb55542017-05-16 14:40:30 +0000343 // Run the Language Server loop.
344 runLanguageServerLoop(In, Out, Dispatcher, IsDone);
345
346 // Make sure IsDone is set to true after this method exits to ensure assertion
347 // at the start of the method fires if it's ever executed again.
348 IsDone = true;
Ilya Biryukov0d9b8a32017-10-25 08:45:41 +0000349
350 return ShutdownRequestReceived;
Ilya Biryukov38d79772017-05-16 09:38:59 +0000351}
352
Sam McCall8111d3b2017-12-13 08:48:42 +0000353std::vector<TextEdit> ClangdLSPServer::getFixIts(StringRef File,
354 const clangd::Diagnostic &D) {
Ilya Biryukov38d79772017-05-16 09:38:59 +0000355 std::lock_guard<std::mutex> Lock(FixItsMutex);
356 auto DiagToFixItsIter = FixItsMap.find(File);
357 if (DiagToFixItsIter == FixItsMap.end())
358 return {};
359
360 const auto &DiagToFixItsMap = DiagToFixItsIter->second;
361 auto FixItsIter = DiagToFixItsMap.find(D);
362 if (FixItsIter == DiagToFixItsMap.end())
363 return {};
364
365 return FixItsIter->second;
366}
367
Sam McCall4db732a2017-09-30 10:08:52 +0000368void ClangdLSPServer::onDiagnosticsReady(
Sam McCalld1a7a372018-01-31 13:40:48 +0000369 PathRef File, Tagged<std::vector<DiagWithFixIts>> Diagnostics) {
Sam McCalldd0566b2017-11-06 15:40:30 +0000370 json::ary DiagnosticsJSON;
Ilya Biryukov38d79772017-05-16 09:38:59 +0000371
372 DiagnosticToReplacementMap LocalFixIts; // Temporary storage
Sam McCall4db732a2017-09-30 10:08:52 +0000373 for (auto &DiagWithFixes : Diagnostics.Value) {
Ilya Biryukov38d79772017-05-16 09:38:59 +0000374 auto Diag = DiagWithFixes.Diag;
Sam McCalldd0566b2017-11-06 15:40:30 +0000375 DiagnosticsJSON.push_back(json::obj{
376 {"range", Diag.range},
377 {"severity", Diag.severity},
378 {"message", Diag.message},
379 });
Ilya Biryukov38d79772017-05-16 09:38:59 +0000380 // We convert to Replacements to become independent of the SourceManager.
381 auto &FixItsForDiagnostic = LocalFixIts[Diag];
382 std::copy(DiagWithFixes.FixIts.begin(), DiagWithFixes.FixIts.end(),
383 std::back_inserter(FixItsForDiagnostic));
384 }
385
386 // Cache FixIts
387 {
388 // FIXME(ibiryukov): should be deleted when documents are removed
389 std::lock_guard<std::mutex> Lock(FixItsMutex);
390 FixItsMap[File] = LocalFixIts;
391 }
392
393 // Publish diagnostics.
Sam McCalldd0566b2017-11-06 15:40:30 +0000394 Out.writeMessage(json::obj{
395 {"jsonrpc", "2.0"},
396 {"method", "textDocument/publishDiagnostics"},
397 {"params",
398 json::obj{
Eric Liu78ed91a72018-01-29 15:37:46 +0000399 {"uri", URIForFile{File}},
Sam McCalldd0566b2017-11-06 15:40:30 +0000400 {"diagnostics", std::move(DiagnosticsJSON)},
401 }},
402 });
Ilya Biryukov38d79772017-05-16 09:38:59 +0000403}