blob: 385768a14b020bf6df4c89777f1bbba53965825c [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"
Ilya Biryukov71028b82018-03-12 15:28:22 +000011#include "Diagnostics.h"
Ilya Biryukov38d79772017-05-16 09:38:59 +000012#include "JSONRPCDispatcher.h"
Sam McCallb536a2a2017-12-19 12:23:48 +000013#include "SourceCode.h"
Eric Liu78ed91a72018-01-29 15:37:46 +000014#include "URI.h"
Simon Marchi9569fd52018-03-16 14:30:42 +000015#include "llvm/Support/Errc.h"
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +000016#include "llvm/Support/FormatVariadic.h"
Eric Liu5740ff52018-01-31 16:26:27 +000017#include "llvm/Support/Path.h"
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +000018
Ilya Biryukov38d79772017-05-16 09:38:59 +000019using namespace clang::clangd;
20using namespace clang;
21
Ilya Biryukovafb55542017-05-16 14:40:30 +000022namespace {
23
Eric Liu5740ff52018-01-31 16:26:27 +000024/// \brief Supports a test URI scheme with relaxed constraints for lit tests.
25/// The path in a test URI will be combined with a platform-specific fake
26/// directory to form an absolute path. For example, test:///a.cpp is resolved
27/// C:\clangd-test\a.cpp on Windows and /clangd-test/a.cpp on Unix.
28class TestScheme : public URIScheme {
29public:
30 llvm::Expected<std::string>
31 getAbsolutePath(llvm::StringRef /*Authority*/, llvm::StringRef Body,
32 llvm::StringRef /*HintPath*/) const override {
33 using namespace llvm::sys;
34 // Still require "/" in body to mimic file scheme, as we want lengths of an
35 // equivalent URI in both schemes to be the same.
36 if (!Body.startswith("/"))
37 return llvm::make_error<llvm::StringError>(
38 "Expect URI body to be an absolute path starting with '/': " + Body,
39 llvm::inconvertibleErrorCode());
40 Body = Body.ltrim('/');
Nico Weber0da22902018-04-10 13:14:03 +000041#ifdef _WIN32
Eric Liu5740ff52018-01-31 16:26:27 +000042 constexpr char TestDir[] = "C:\\clangd-test";
43#else
44 constexpr char TestDir[] = "/clangd-test";
45#endif
46 llvm::SmallVector<char, 16> Path(Body.begin(), Body.end());
47 path::native(Path);
48 auto Err = fs::make_absolute(TestDir, Path);
Eric Liucda25262018-02-01 12:44:52 +000049 if (Err)
50 llvm_unreachable("Failed to make absolute path in test scheme.");
Eric Liu5740ff52018-01-31 16:26:27 +000051 return std::string(Path.begin(), Path.end());
52 }
53
54 llvm::Expected<URI>
55 uriFromAbsolutePath(llvm::StringRef AbsolutePath) const override {
56 llvm_unreachable("Clangd must never create a test URI.");
57 }
58};
59
60static URISchemeRegistry::Add<TestScheme>
61 X("test", "Test scheme for clangd lit tests.");
62
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +000063SymbolKindBitset defaultSymbolKinds() {
64 SymbolKindBitset Defaults;
65 for (size_t I = SymbolKindMin; I <= static_cast<size_t>(SymbolKind::Array);
66 ++I)
67 Defaults.set(I);
68 return Defaults;
69}
70
Ilya Biryukovafb55542017-05-16 14:40:30 +000071} // namespace
72
Sam McCalld1a7a372018-01-31 13:40:48 +000073void ClangdLSPServer::onInitialize(InitializeParams &Params) {
Ilya Biryukov7d60d202018-02-16 12:20:47 +000074 if (Params.rootUri && *Params.rootUri)
75 Server.setRootPath(Params.rootUri->file());
Ilya Biryukov23bc73b2018-02-15 14:32:57 +000076 else if (Params.rootPath && !Params.rootPath->empty())
77 Server.setRootPath(*Params.rootPath);
78
79 CCOpts.EnableSnippets =
80 Params.capabilities.textDocument.completion.completionItem.snippetSupport;
81
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +000082 if (Params.capabilities.workspace && Params.capabilities.workspace->symbol &&
83 Params.capabilities.workspace->symbol->symbolKind) {
84 for (SymbolKind Kind :
85 *Params.capabilities.workspace->symbol->symbolKind->valueSet) {
86 SupportedSymbolKinds.set(static_cast<size_t>(Kind));
87 }
88 }
89
Sam McCalld1a7a372018-01-31 13:40:48 +000090 reply(json::obj{
Sam McCall0930ab02017-11-07 15:49:35 +000091 {{"capabilities",
92 json::obj{
Simon Marchi98082622018-03-26 14:41:40 +000093 {"textDocumentSync", (int)TextDocumentSyncKind::Incremental},
Sam McCall0930ab02017-11-07 15:49:35 +000094 {"documentFormattingProvider", true},
95 {"documentRangeFormattingProvider", true},
96 {"documentOnTypeFormattingProvider",
97 json::obj{
98 {"firstTriggerCharacter", "}"},
99 {"moreTriggerCharacter", {}},
100 }},
101 {"codeActionProvider", true},
102 {"completionProvider",
103 json::obj{
104 {"resolveProvider", false},
105 {"triggerCharacters", {".", ">", ":"}},
106 }},
107 {"signatureHelpProvider",
108 json::obj{
109 {"triggerCharacters", {"(", ","}},
110 }},
111 {"definitionProvider", true},
Ilya Biryukov0e6a51f2017-12-12 12:27:47 +0000112 {"documentHighlightProvider", true},
Marc-Andre Laperle3e618ed2018-02-16 21:38:15 +0000113 {"hoverProvider", true},
Haojian Wu345099c2017-11-09 11:30:04 +0000114 {"renameProvider", true},
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000115 {"documentSymbolProvider", true},
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000116 {"workspaceSymbolProvider", true},
Sam McCall0930ab02017-11-07 15:49:35 +0000117 {"executeCommandProvider",
118 json::obj{
Eric Liu2c190532018-05-15 15:23:53 +0000119 {"commands", {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND}},
Sam McCall0930ab02017-11-07 15:49:35 +0000120 }},
121 }}}});
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) {
Simon Marchi9569fd52018-03-16 14:30:42 +0000133 PathRef File = Params.textDocument.uri.file();
Ilya Biryukovb10ef472018-06-13 09:20:41 +0000134 if (Params.metadata && !Params.metadata->extraFlags.empty()) {
135 NonCachedCDB.setExtraFlagsForFile(File,
136 std::move(Params.metadata->extraFlags));
137 CDB.invalidate(File);
138 }
139
Simon Marchi9569fd52018-03-16 14:30:42 +0000140 std::string &Contents = Params.textDocument.text;
141
Simon Marchi98082622018-03-26 14:41:40 +0000142 DraftMgr.addDraft(File, Contents);
Simon Marchi9569fd52018-03-16 14:30:42 +0000143 Server.addDocument(File, Contents, WantDiagnostics::Yes);
Ilya Biryukovafb55542017-05-16 14:40:30 +0000144}
145
Sam McCalld1a7a372018-01-31 13:40:48 +0000146void ClangdLSPServer::onDocumentDidChange(DidChangeTextDocumentParams &Params) {
Eric Liu51fed182018-02-22 18:40:39 +0000147 auto WantDiags = WantDiagnostics::Auto;
148 if (Params.wantDiagnostics.hasValue())
149 WantDiags = Params.wantDiagnostics.getValue() ? WantDiagnostics::Yes
150 : WantDiagnostics::No;
Simon Marchi9569fd52018-03-16 14:30:42 +0000151
152 PathRef File = Params.textDocument.uri.file();
Simon Marchi98082622018-03-26 14:41:40 +0000153 llvm::Expected<std::string> Contents =
154 DraftMgr.updateDraft(File, Params.contentChanges);
155 if (!Contents) {
156 // If this fails, we are most likely going to be not in sync anymore with
157 // the client. It is better to remove the draft and let further operations
158 // fail rather than giving wrong results.
159 DraftMgr.removeDraft(File);
160 Server.removeDocument(File);
Ilya Biryukovb10ef472018-06-13 09:20:41 +0000161 CDB.invalidate(File);
Simon Marchi98082622018-03-26 14:41:40 +0000162 log(llvm::toString(Contents.takeError()));
163 return;
164 }
Simon Marchi9569fd52018-03-16 14:30:42 +0000165
Simon Marchi98082622018-03-26 14:41:40 +0000166 Server.addDocument(File, *Contents, WantDiags);
Ilya Biryukovafb55542017-05-16 14:40:30 +0000167}
168
Sam McCalld1a7a372018-01-31 13:40:48 +0000169void ClangdLSPServer::onFileEvent(DidChangeWatchedFilesParams &Params) {
Marc-Andre Laperlebf114242017-10-02 18:00:37 +0000170 Server.onFileEvent(Params);
171}
172
Sam McCalld1a7a372018-01-31 13:40:48 +0000173void ClangdLSPServer::onCommand(ExecuteCommandParams &Params) {
Eric Liuc5105f92018-02-16 14:15:55 +0000174 auto ApplyEdit = [](WorkspaceEdit WE) {
175 ApplyWorkspaceEditParams Edit;
176 Edit.edit = std::move(WE);
177 // We don't need the response so id == 1 is OK.
178 // Ideally, we would wait for the response and if there is no error, we
179 // would reply success/failure to the original RPC.
180 call("workspace/applyEdit", Edit);
181 };
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000182 if (Params.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND &&
183 Params.workspaceEdit) {
184 // The flow for "apply-fix" :
185 // 1. We publish a diagnostic, including fixits
186 // 2. The user clicks on the diagnostic, the editor asks us for code actions
187 // 3. We send code actions, with the fixit embedded as context
188 // 4. The user selects the fixit, the editor asks us to apply it
189 // 5. We unwrap the changes and send them back to the editor
190 // 6. The editor applies the changes (applyEdit), and sends us a reply (but
191 // we ignore it)
192
Sam McCalld1a7a372018-01-31 13:40:48 +0000193 reply("Fix applied.");
Eric Liuc5105f92018-02-16 14:15:55 +0000194 ApplyEdit(*Params.workspaceEdit);
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000195 } else {
196 // We should not get here because ExecuteCommandParams would not have
197 // parsed in the first place and this handler should not be called. But if
198 // more commands are added, this will be here has a safe guard.
Ilya Biryukov940901e2017-12-13 12:51:22 +0000199 replyError(
Sam McCalld1a7a372018-01-31 13:40:48 +0000200 ErrorCode::InvalidParams,
Haojian Wu2375c922017-11-07 10:21:02 +0000201 llvm::formatv("Unsupported command \"{0}\".", Params.command).str());
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000202 }
203}
204
Marc-Andre Laperleb387b6e2018-04-23 20:00:52 +0000205void ClangdLSPServer::onWorkspaceSymbol(WorkspaceSymbolParams &Params) {
206 Server.workspaceSymbols(
207 Params.query, CCOpts.Limit,
208 [this](llvm::Expected<std::vector<SymbolInformation>> Items) {
209 if (!Items)
210 return replyError(ErrorCode::InternalError,
211 llvm::toString(Items.takeError()));
212 for (auto &Sym : *Items)
213 Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds);
214
215 reply(json::ary(*Items));
216 });
217}
218
Sam McCalld1a7a372018-01-31 13:40:48 +0000219void ClangdLSPServer::onRename(RenameParams &Params) {
Ilya Biryukov7d60d202018-02-16 12:20:47 +0000220 Path File = Params.textDocument.uri.file();
Simon Marchi9569fd52018-03-16 14:30:42 +0000221 llvm::Optional<std::string> Code = DraftMgr.getDraft(File);
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000222 if (!Code)
Sam McCalld1a7a372018-01-31 13:40:48 +0000223 return replyError(ErrorCode::InvalidParams,
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000224 "onRename called for non-added file");
225
Ilya Biryukov2c5e8e82018-02-15 13:15:47 +0000226 Server.rename(
227 File, Params.position, Params.newName,
228 [File, Code,
229 Params](llvm::Expected<std::vector<tooling::Replacement>> Replacements) {
230 if (!Replacements)
231 return replyError(ErrorCode::InternalError,
232 llvm::toString(Replacements.takeError()));
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000233
Eric Liu9133ecd2018-05-11 12:12:08 +0000234 // Turn the replacements into the format specified by the Language
235 // Server Protocol. Fuse them into one big JSON array.
236 std::vector<TextEdit> Edits;
237 for (const auto &R : *Replacements)
238 Edits.push_back(replacementToEdit(*Code, R));
Ilya Biryukov2c5e8e82018-02-15 13:15:47 +0000239 WorkspaceEdit WE;
240 WE.changes = {{Params.textDocument.uri.uri(), Edits}};
241 reply(WE);
242 });
Haojian Wu345099c2017-11-09 11:30:04 +0000243}
244
Sam McCalld1a7a372018-01-31 13:40:48 +0000245void ClangdLSPServer::onDocumentDidClose(DidCloseTextDocumentParams &Params) {
Simon Marchi9569fd52018-03-16 14:30:42 +0000246 PathRef File = Params.textDocument.uri.file();
247 DraftMgr.removeDraft(File);
248 Server.removeDocument(File);
Ilya Biryukovafb55542017-05-16 14:40:30 +0000249}
250
Sam McCall4db732a2017-09-30 10:08:52 +0000251void ClangdLSPServer::onDocumentOnTypeFormatting(
Sam McCalld1a7a372018-01-31 13:40:48 +0000252 DocumentOnTypeFormattingParams &Params) {
Ilya Biryukov7d60d202018-02-16 12:20:47 +0000253 auto File = Params.textDocument.uri.file();
Simon Marchi9569fd52018-03-16 14:30:42 +0000254 auto Code = DraftMgr.getDraft(File);
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000255 if (!Code)
Sam McCalld1a7a372018-01-31 13:40:48 +0000256 return replyError(ErrorCode::InvalidParams,
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000257 "onDocumentOnTypeFormatting called for non-added file");
258
259 auto ReplacementsOrError = Server.formatOnType(*Code, File, Params.position);
Raoul Wols212bcf82017-12-12 20:25:06 +0000260 if (ReplacementsOrError)
Sam McCalld1a7a372018-01-31 13:40:48 +0000261 reply(json::ary(replacementsToEdits(*Code, ReplacementsOrError.get())));
Raoul Wols212bcf82017-12-12 20:25:06 +0000262 else
Sam McCalld1a7a372018-01-31 13:40:48 +0000263 replyError(ErrorCode::UnknownErrorCode,
Ilya Biryukov940901e2017-12-13 12:51:22 +0000264 llvm::toString(ReplacementsOrError.takeError()));
Ilya Biryukovafb55542017-05-16 14:40:30 +0000265}
266
Sam McCall4db732a2017-09-30 10:08:52 +0000267void ClangdLSPServer::onDocumentRangeFormatting(
Sam McCalld1a7a372018-01-31 13:40:48 +0000268 DocumentRangeFormattingParams &Params) {
Ilya Biryukov7d60d202018-02-16 12:20:47 +0000269 auto File = Params.textDocument.uri.file();
Simon Marchi9569fd52018-03-16 14:30:42 +0000270 auto Code = DraftMgr.getDraft(File);
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000271 if (!Code)
Sam McCalld1a7a372018-01-31 13:40:48 +0000272 return replyError(ErrorCode::InvalidParams,
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000273 "onDocumentRangeFormatting called for non-added file");
274
275 auto ReplacementsOrError = Server.formatRange(*Code, File, Params.range);
Raoul Wols212bcf82017-12-12 20:25:06 +0000276 if (ReplacementsOrError)
Sam McCalld1a7a372018-01-31 13:40:48 +0000277 reply(json::ary(replacementsToEdits(*Code, ReplacementsOrError.get())));
Raoul Wols212bcf82017-12-12 20:25:06 +0000278 else
Sam McCalld1a7a372018-01-31 13:40:48 +0000279 replyError(ErrorCode::UnknownErrorCode,
Ilya Biryukov940901e2017-12-13 12:51:22 +0000280 llvm::toString(ReplacementsOrError.takeError()));
Ilya Biryukovafb55542017-05-16 14:40:30 +0000281}
282
Sam McCalld1a7a372018-01-31 13:40:48 +0000283void ClangdLSPServer::onDocumentFormatting(DocumentFormattingParams &Params) {
Ilya Biryukov7d60d202018-02-16 12:20:47 +0000284 auto File = Params.textDocument.uri.file();
Simon Marchi9569fd52018-03-16 14:30:42 +0000285 auto Code = DraftMgr.getDraft(File);
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000286 if (!Code)
Sam McCalld1a7a372018-01-31 13:40:48 +0000287 return replyError(ErrorCode::InvalidParams,
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000288 "onDocumentFormatting called for non-added file");
289
290 auto ReplacementsOrError = Server.formatFile(*Code, File);
Raoul Wols212bcf82017-12-12 20:25:06 +0000291 if (ReplacementsOrError)
Sam McCalld1a7a372018-01-31 13:40:48 +0000292 reply(json::ary(replacementsToEdits(*Code, ReplacementsOrError.get())));
Raoul Wols212bcf82017-12-12 20:25:06 +0000293 else
Sam McCalld1a7a372018-01-31 13:40:48 +0000294 replyError(ErrorCode::UnknownErrorCode,
Ilya Biryukov940901e2017-12-13 12:51:22 +0000295 llvm::toString(ReplacementsOrError.takeError()));
Sam McCall4db732a2017-09-30 10:08:52 +0000296}
297
Marc-Andre Laperle1be69702018-07-05 19:35:01 +0000298void ClangdLSPServer::onDocumentSymbol(DocumentSymbolParams &Params) {
299 Server.documentSymbols(
300 Params.textDocument.uri.file(),
301 [this](llvm::Expected<std::vector<SymbolInformation>> Items) {
302 if (!Items)
303 return replyError(ErrorCode::InvalidParams,
304 llvm::toString(Items.takeError()));
305 for (auto &Sym : *Items)
306 Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds);
307 reply(json::ary(*Items));
308 });
309}
310
Sam McCalld1a7a372018-01-31 13:40:48 +0000311void ClangdLSPServer::onCodeAction(CodeActionParams &Params) {
Ilya Biryukovafb55542017-05-16 14:40:30 +0000312 // We provide a code action for each diagnostic at the requested location
313 // which has FixIts available.
Simon Marchi9569fd52018-03-16 14:30:42 +0000314 auto Code = DraftMgr.getDraft(Params.textDocument.uri.file());
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000315 if (!Code)
Sam McCalld1a7a372018-01-31 13:40:48 +0000316 return replyError(ErrorCode::InvalidParams,
Ilya Biryukov261c72e2018-01-17 12:30:24 +0000317 "onCodeAction called for non-added file");
318
Sam McCalldd0566b2017-11-06 15:40:30 +0000319 json::ary Commands;
Ilya Biryukovafb55542017-05-16 14:40:30 +0000320 for (Diagnostic &D : Params.context.diagnostics) {
Ilya Biryukov71028b82018-03-12 15:28:22 +0000321 for (auto &F : getFixes(Params.textDocument.uri.file(), D)) {
Sam McCalldd0566b2017-11-06 15:40:30 +0000322 WorkspaceEdit WE;
Ilya Biryukov71028b82018-03-12 15:28:22 +0000323 std::vector<TextEdit> Edits(F.Edits.begin(), F.Edits.end());
Eric Liu78ed91a72018-01-29 15:37:46 +0000324 WE.changes = {{Params.textDocument.uri.uri(), std::move(Edits)}};
Sam McCalldd0566b2017-11-06 15:40:30 +0000325 Commands.push_back(json::obj{
Ilya Biryukov71028b82018-03-12 15:28:22 +0000326 {"title", llvm::formatv("Apply fix: {0}", F.Message)},
Sam McCalldd0566b2017-11-06 15:40:30 +0000327 {"command", ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND},
328 {"arguments", {WE}},
329 });
330 }
Ilya Biryukovafb55542017-05-16 14:40:30 +0000331 }
Sam McCalld1a7a372018-01-31 13:40:48 +0000332 reply(std::move(Commands));
Ilya Biryukovafb55542017-05-16 14:40:30 +0000333}
334
Sam McCalld1a7a372018-01-31 13:40:48 +0000335void ClangdLSPServer::onCompletion(TextDocumentPositionParams &Params) {
Ilya Biryukov7d60d202018-02-16 12:20:47 +0000336 Server.codeComplete(Params.textDocument.uri.file(), Params.position, CCOpts,
Sam McCalle746a2b2018-07-02 11:13:16 +0000337 [this](llvm::Expected<CodeCompleteResult> List) {
Sam McCalla7bb0cc2018-03-12 23:22:35 +0000338 if (!List)
339 return replyError(ErrorCode::InvalidParams,
340 llvm::toString(List.takeError()));
Sam McCalle746a2b2018-07-02 11:13:16 +0000341 CompletionList LSPList;
342 LSPList.isIncomplete = List->HasMore;
343 for (const auto &R : List->Completions)
344 LSPList.items.push_back(R.render(CCOpts));
345 reply(std::move(LSPList));
Sam McCalla7bb0cc2018-03-12 23:22:35 +0000346 });
Ilya Biryukovafb55542017-05-16 14:40:30 +0000347}
348
Sam McCalld1a7a372018-01-31 13:40:48 +0000349void ClangdLSPServer::onSignatureHelp(TextDocumentPositionParams &Params) {
Ilya Biryukov7d60d202018-02-16 12:20:47 +0000350 Server.signatureHelp(Params.textDocument.uri.file(), Params.position,
Sam McCalla7bb0cc2018-03-12 23:22:35 +0000351 [](llvm::Expected<SignatureHelp> SignatureHelp) {
Ilya Biryukov2c5e8e82018-02-15 13:15:47 +0000352 if (!SignatureHelp)
353 return replyError(
354 ErrorCode::InvalidParams,
355 llvm::toString(SignatureHelp.takeError()));
Sam McCalla7bb0cc2018-03-12 23:22:35 +0000356 reply(*SignatureHelp);
Ilya Biryukov2c5e8e82018-02-15 13:15:47 +0000357 });
Ilya Biryukovd9bdfe02017-10-06 11:54:17 +0000358}
359
Sam McCalld1a7a372018-01-31 13:40:48 +0000360void ClangdLSPServer::onGoToDefinition(TextDocumentPositionParams &Params) {
Ilya Biryukov2c5e8e82018-02-15 13:15:47 +0000361 Server.findDefinitions(
Ilya Biryukov7d60d202018-02-16 12:20:47 +0000362 Params.textDocument.uri.file(), Params.position,
Sam McCalla7bb0cc2018-03-12 23:22:35 +0000363 [](llvm::Expected<std::vector<Location>> Items) {
Ilya Biryukov2c5e8e82018-02-15 13:15:47 +0000364 if (!Items)
365 return replyError(ErrorCode::InvalidParams,
366 llvm::toString(Items.takeError()));
Sam McCalla7bb0cc2018-03-12 23:22:35 +0000367 reply(json::ary(*Items));
Ilya Biryukov2c5e8e82018-02-15 13:15:47 +0000368 });
Marc-Andre Laperle2cbf0372017-06-28 16:12:10 +0000369}
370
Sam McCalld1a7a372018-01-31 13:40:48 +0000371void ClangdLSPServer::onSwitchSourceHeader(TextDocumentIdentifier &Params) {
Ilya Biryukov7d60d202018-02-16 12:20:47 +0000372 llvm::Optional<Path> Result = Server.switchSourceHeader(Params.uri.file());
Sam McCalld1a7a372018-01-31 13:40:48 +0000373 reply(Result ? URI::createFile(*Result).toString() : "");
Marc-Andre Laperle6571b3e2017-09-28 03:14:40 +0000374}
375
Sam McCalld1a7a372018-01-31 13:40:48 +0000376void ClangdLSPServer::onDocumentHighlight(TextDocumentPositionParams &Params) {
Ilya Biryukov2c5e8e82018-02-15 13:15:47 +0000377 Server.findDocumentHighlights(
Ilya Biryukov7d60d202018-02-16 12:20:47 +0000378 Params.textDocument.uri.file(), Params.position,
Sam McCalla7bb0cc2018-03-12 23:22:35 +0000379 [](llvm::Expected<std::vector<DocumentHighlight>> Highlights) {
Ilya Biryukov2c5e8e82018-02-15 13:15:47 +0000380 if (!Highlights)
381 return replyError(ErrorCode::InternalError,
382 llvm::toString(Highlights.takeError()));
Sam McCalla7bb0cc2018-03-12 23:22:35 +0000383 reply(json::ary(*Highlights));
Ilya Biryukov2c5e8e82018-02-15 13:15:47 +0000384 });
Ilya Biryukov0e6a51f2017-12-12 12:27:47 +0000385}
386
Marc-Andre Laperle3e618ed2018-02-16 21:38:15 +0000387void ClangdLSPServer::onHover(TextDocumentPositionParams &Params) {
388 Server.findHover(Params.textDocument.uri.file(), Params.position,
Sam McCall682cfe72018-06-04 10:37:16 +0000389 [](llvm::Expected<llvm::Optional<Hover>> H) {
Marc-Andre Laperle3e618ed2018-02-16 21:38:15 +0000390 if (!H) {
391 replyError(ErrorCode::InternalError,
392 llvm::toString(H.takeError()));
393 return;
394 }
395
Sam McCalla7bb0cc2018-03-12 23:22:35 +0000396 reply(*H);
Marc-Andre Laperle3e618ed2018-02-16 21:38:15 +0000397 });
398}
399
Simon Marchi5178f922018-02-22 14:00:39 +0000400// FIXME: This function needs to be properly tested.
401void ClangdLSPServer::onChangeConfiguration(
402 DidChangeConfigurationParams &Params) {
403 ClangdConfigurationParamsChange &Settings = Params.settings;
404
405 // Compilation database change.
406 if (Settings.compilationDatabasePath.hasValue()) {
Ilya Biryukovb10ef472018-06-13 09:20:41 +0000407 NonCachedCDB.setCompileCommandsDir(
408 Settings.compilationDatabasePath.getValue());
409 CDB.clear();
410
Simon Marchi9569fd52018-03-16 14:30:42 +0000411 reparseOpenedFiles();
Simon Marchi5178f922018-02-22 14:00:39 +0000412 }
413}
414
Sam McCall7363a2f2018-03-05 17:28:54 +0000415ClangdLSPServer::ClangdLSPServer(JSONOutput &Out,
Sam McCalladccab62017-11-23 16:58:22 +0000416 const clangd::CodeCompleteOptions &CCOpts,
Eric Liubfac8f72017-12-19 18:00:37 +0000417 llvm::Optional<Path> CompileCommandsDir,
Sam McCall7363a2f2018-03-05 17:28:54 +0000418 const ClangdServer::Options &Opts)
Ilya Biryukovb10ef472018-06-13 09:20:41 +0000419 : Out(Out), NonCachedCDB(std::move(CompileCommandsDir)), CDB(NonCachedCDB),
420 CCOpts(CCOpts), SupportedSymbolKinds(defaultSymbolKinds()),
Sam McCall7363a2f2018-03-05 17:28:54 +0000421 Server(CDB, FSProvider, /*DiagConsumer=*/*this, Opts) {}
Ilya Biryukov38d79772017-05-16 09:38:59 +0000422
Sam McCall27a07cf2018-06-05 09:34:46 +0000423bool ClangdLSPServer::run(std::FILE *In, JSONStreamStyle InputStyle) {
Ilya Biryukovafb55542017-05-16 14:40:30 +0000424 assert(!IsDone && "Run was called before");
Ilya Biryukov38d79772017-05-16 09:38:59 +0000425
Ilya Biryukovafb55542017-05-16 14:40:30 +0000426 // Set up JSONRPCDispatcher.
Sam McCalld1a7a372018-01-31 13:40:48 +0000427 JSONRPCDispatcher Dispatcher([](const json::Expr &Params) {
428 replyError(ErrorCode::MethodNotFound, "method not found");
Ilya Biryukov940901e2017-12-13 12:51:22 +0000429 });
Simon Marchi6e8eb9d2018-03-07 21:47:25 +0000430 registerCallbackHandlers(Dispatcher, /*Callbacks=*/*this);
Ilya Biryukov38d79772017-05-16 09:38:59 +0000431
Ilya Biryukovafb55542017-05-16 14:40:30 +0000432 // Run the Language Server loop.
Sam McCall5ed599e2018-02-06 10:47:30 +0000433 runLanguageServerLoop(In, Out, InputStyle, Dispatcher, IsDone);
Ilya Biryukovafb55542017-05-16 14:40:30 +0000434
435 // Make sure IsDone is set to true after this method exits to ensure assertion
436 // at the start of the method fires if it's ever executed again.
437 IsDone = true;
Ilya Biryukov0d9b8a32017-10-25 08:45:41 +0000438
439 return ShutdownRequestReceived;
Ilya Biryukov38d79772017-05-16 09:38:59 +0000440}
441
Ilya Biryukov71028b82018-03-12 15:28:22 +0000442std::vector<Fix> ClangdLSPServer::getFixes(StringRef File,
443 const clangd::Diagnostic &D) {
Ilya Biryukov38d79772017-05-16 09:38:59 +0000444 std::lock_guard<std::mutex> Lock(FixItsMutex);
445 auto DiagToFixItsIter = FixItsMap.find(File);
446 if (DiagToFixItsIter == FixItsMap.end())
447 return {};
448
449 const auto &DiagToFixItsMap = DiagToFixItsIter->second;
450 auto FixItsIter = DiagToFixItsMap.find(D);
451 if (FixItsIter == DiagToFixItsMap.end())
452 return {};
453
454 return FixItsIter->second;
455}
456
Sam McCalla7bb0cc2018-03-12 23:22:35 +0000457void ClangdLSPServer::onDiagnosticsReady(PathRef File,
458 std::vector<Diag> Diagnostics) {
Sam McCalldd0566b2017-11-06 15:40:30 +0000459 json::ary DiagnosticsJSON;
Ilya Biryukov38d79772017-05-16 09:38:59 +0000460
461 DiagnosticToReplacementMap LocalFixIts; // Temporary storage
Sam McCalla7bb0cc2018-03-12 23:22:35 +0000462 for (auto &Diag : Diagnostics) {
Ilya Biryukov71028b82018-03-12 15:28:22 +0000463 toLSPDiags(Diag, [&](clangd::Diagnostic Diag, llvm::ArrayRef<Fix> Fixes) {
464 DiagnosticsJSON.push_back(json::obj{
465 {"range", Diag.range},
466 {"severity", Diag.severity},
467 {"message", Diag.message},
468 });
469
470 auto &FixItsForDiagnostic = LocalFixIts[Diag];
471 std::copy(Fixes.begin(), Fixes.end(),
472 std::back_inserter(FixItsForDiagnostic));
Sam McCalldd0566b2017-11-06 15:40:30 +0000473 });
Ilya Biryukov38d79772017-05-16 09:38:59 +0000474 }
475
476 // Cache FixIts
477 {
478 // FIXME(ibiryukov): should be deleted when documents are removed
479 std::lock_guard<std::mutex> Lock(FixItsMutex);
480 FixItsMap[File] = LocalFixIts;
481 }
482
483 // Publish diagnostics.
Sam McCalldd0566b2017-11-06 15:40:30 +0000484 Out.writeMessage(json::obj{
485 {"jsonrpc", "2.0"},
486 {"method", "textDocument/publishDiagnostics"},
487 {"params",
488 json::obj{
Eric Liu78ed91a72018-01-29 15:37:46 +0000489 {"uri", URIForFile{File}},
Sam McCalldd0566b2017-11-06 15:40:30 +0000490 {"diagnostics", std::move(DiagnosticsJSON)},
491 }},
492 });
Ilya Biryukov38d79772017-05-16 09:38:59 +0000493}
Simon Marchi9569fd52018-03-16 14:30:42 +0000494
495void ClangdLSPServer::reparseOpenedFiles() {
496 for (const Path &FilePath : DraftMgr.getActiveFiles())
497 Server.addDocument(FilePath, *DraftMgr.getDraft(FilePath),
Ilya Biryukovb10ef472018-06-13 09:20:41 +0000498 WantDiagnostics::Auto);
Simon Marchi9569fd52018-03-16 14:30:42 +0000499}