blob: 1d38184b958f81d556b7abab264a3ea244ef5e41 [file] [log] [blame]
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +00001//===--- Protocol.cpp - Language Server Protocol Implementation -----------===//
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// This file contains the serialization code for the LSP structs.
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000011//
12//===----------------------------------------------------------------------===//
13
14#include "Protocol.h"
Marc-Andre Laperle85dcce42017-09-18 15:02:59 +000015
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000016#include "clang/Basic/LLVM.h"
17#include "llvm/ADT/SmallString.h"
18#include "llvm/Support/Format.h"
Marc-Andre Laperle85dcce42017-09-18 15:02:59 +000019#include "llvm/Support/FormatVariadic.h"
Krasimir Georgiev50117372017-04-07 11:03:26 +000020#include "llvm/Support/Path.h"
Ilya Biryukov574b7532017-08-02 09:08:39 +000021#include "llvm/Support/raw_ostream.h"
Ilya Biryukove5128f72017-09-20 07:24:15 +000022
Sam McCallff8b8742017-11-30 21:32:29 +000023namespace clang {
24namespace clangd {
Sam McCall38a04912017-11-29 11:36:46 +000025
Krasimir Georgiev50117372017-04-07 11:03:26 +000026URI URI::fromUri(llvm::StringRef uri) {
27 URI Result;
28 Result.uri = uri;
29 uri.consume_front("file://");
Benjamin Kramer8c3ba632017-04-21 15:51:18 +000030 // Also trim authority-less URIs
31 uri.consume_front("file:");
Krasimir Georgiev50117372017-04-07 11:03:26 +000032 // For Windows paths e.g. /X:
33 if (uri.size() > 2 && uri[0] == '/' && uri[2] == ':')
34 uri.consume_front("/");
35 // Make sure that file paths are in native separators
36 Result.file = llvm::sys::path::convert_to_slash(uri);
37 return Result;
38}
39
40URI URI::fromFile(llvm::StringRef file) {
41 using namespace llvm::sys;
42 URI Result;
43 Result.file = file;
44 Result.uri = "file://";
45 // For Windows paths e.g. X:
46 if (file.size() > 1 && file[1] == ':')
47 Result.uri += "/";
48 // Make sure that uri paths are with posix separators
49 Result.uri += path::convert_to_slash(file, path::Style::posix);
50 return Result;
51}
52
Sam McCallff8b8742017-11-30 21:32:29 +000053bool fromJSON(const json::Expr &E, URI &R) {
54 if (auto S = E.asString()) {
55 R = URI::fromUri(*S);
56 return true;
57 }
58 return false;
Sam McCall38a04912017-11-29 11:36:46 +000059}
60
Sam McCallff8b8742017-11-30 21:32:29 +000061json::Expr toJSON(const URI &U) { return U.uri; }
Krasimir Georgiev50117372017-04-07 11:03:26 +000062
Sam McCallff8b8742017-11-30 21:32:29 +000063bool fromJSON(const json::Expr &Params, TextDocumentIdentifier &R) {
64 json::ObjectMapper O(Params);
65 return O && O.map("uri", R.uri);
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000066}
67
Sam McCallff8b8742017-11-30 21:32:29 +000068bool fromJSON(const json::Expr &Params, Position &R) {
69 json::ObjectMapper O(Params);
70 return O && O.map("line", R.line) && O.map("character", R.character);
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000071}
72
Sam McCallff8b8742017-11-30 21:32:29 +000073json::Expr toJSON(const Position &P) {
Sam McCalldd0566b2017-11-06 15:40:30 +000074 return json::obj{
75 {"line", P.line},
76 {"character", P.character},
77 };
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000078}
79
Sam McCallff8b8742017-11-30 21:32:29 +000080bool fromJSON(const json::Expr &Params, Range &R) {
81 json::ObjectMapper O(Params);
82 return O && O.map("start", R.start) && O.map("end", R.end);
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000083}
84
Sam McCallff8b8742017-11-30 21:32:29 +000085json::Expr toJSON(const Range &P) {
Sam McCalldd0566b2017-11-06 15:40:30 +000086 return json::obj{
87 {"start", P.start},
88 {"end", P.end},
89 };
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +000090}
91
Sam McCallff8b8742017-11-30 21:32:29 +000092json::Expr toJSON(const Location &P) {
Sam McCalldd0566b2017-11-06 15:40:30 +000093 return json::obj{
94 {"uri", P.uri},
95 {"range", P.range},
96 };
Marc-Andre Laperle2cbf0372017-06-28 16:12:10 +000097}
98
Sam McCallff8b8742017-11-30 21:32:29 +000099bool fromJSON(const json::Expr &Params, TextDocumentItem &R) {
100 json::ObjectMapper O(Params);
101 return O && O.map("uri", R.uri) && O.map("languageId", R.languageId) &&
102 O.map("version", R.version) && O.map("text", R.text);
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000103}
104
Sam McCallff8b8742017-11-30 21:32:29 +0000105bool fromJSON(const json::Expr &Params, Metadata &R) {
106 json::ObjectMapper O(Params);
Sam McCallec109022017-11-28 09:37:43 +0000107 if (!O)
Sam McCallff8b8742017-11-30 21:32:29 +0000108 return false;
109 O.map("extraFlags", R.extraFlags);
110 return true;
Krasimir Georgievc2a16a32017-07-06 08:44:54 +0000111}
112
Sam McCallff8b8742017-11-30 21:32:29 +0000113bool fromJSON(const json::Expr &Params, TextEdit &R) {
114 json::ObjectMapper O(Params);
115 return O && O.map("range", R.range) && O.map("newText", R.newText);
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000116}
117
Sam McCallff8b8742017-11-30 21:32:29 +0000118json::Expr toJSON(const TextEdit &P) {
Sam McCalldd0566b2017-11-06 15:40:30 +0000119 return json::obj{
120 {"range", P.range},
121 {"newText", P.newText},
122 };
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000123}
124
Sam McCallff8b8742017-11-30 21:32:29 +0000125bool fromJSON(const json::Expr &E, TraceLevel &Out) {
126 if (auto S = E.asString()) {
127 if (*S == "off") {
128 Out = TraceLevel::Off;
129 return true;
130 } else if (*S == "messages") {
131 Out = TraceLevel::Messages;
132 return true;
133 } else if (*S == "verbose") {
134 Out = TraceLevel::Verbose;
135 return true;
136 }
137 }
138 return false;
139}
140
141bool fromJSON(const json::Expr &Params, InitializeParams &R) {
142 json::ObjectMapper O(Params);
Sam McCallec109022017-11-28 09:37:43 +0000143 if (!O)
Sam McCallff8b8742017-11-30 21:32:29 +0000144 return false;
Sam McCall38a04912017-11-29 11:36:46 +0000145 // We deliberately don't fail if we can't parse individual fields.
146 // Failing to handle a slightly malformed initialize would be a disaster.
Sam McCallff8b8742017-11-30 21:32:29 +0000147 O.map("processId", R.processId);
148 O.map("rootUri", R.rootUri);
149 O.map("rootPath", R.rootPath);
150 O.map("trace", R.trace);
Sam McCallec109022017-11-28 09:37:43 +0000151 // initializationOptions, capabilities unused
Sam McCallff8b8742017-11-30 21:32:29 +0000152 return true;
Marc-Andre Laperle37de9712017-09-27 15:31:17 +0000153}
154
Sam McCallff8b8742017-11-30 21:32:29 +0000155bool fromJSON(const json::Expr &Params, DidOpenTextDocumentParams &R) {
156 json::ObjectMapper O(Params);
157 return O && O.map("textDocument", R.textDocument) &&
158 O.map("metadata", R.metadata);
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000159}
160
Sam McCallff8b8742017-11-30 21:32:29 +0000161bool fromJSON(const json::Expr &Params, DidCloseTextDocumentParams &R) {
162 json::ObjectMapper O(Params);
163 return O && O.map("textDocument", R.textDocument);
Krasimir Georgiev561ba5e2017-04-10 13:31:39 +0000164}
165
Sam McCallff8b8742017-11-30 21:32:29 +0000166bool fromJSON(const json::Expr &Params, DidChangeTextDocumentParams &R) {
167 json::ObjectMapper O(Params);
168 return O && O.map("textDocument", R.textDocument) &&
169 O.map("contentChanges", R.contentChanges);
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000170}
171
Sam McCallff8b8742017-11-30 21:32:29 +0000172bool fromJSON(const json::Expr &E, FileChangeType &Out) {
173 if (auto T = E.asInteger()) {
174 if (*T < static_cast<int>(FileChangeType::Created) ||
175 *T > static_cast<int>(FileChangeType::Deleted))
176 return false;
177 Out = static_cast<FileChangeType>(*T);
178 return true;
179 }
180 return false;
Marc-Andre Laperlebf114242017-10-02 18:00:37 +0000181}
182
Sam McCallff8b8742017-11-30 21:32:29 +0000183bool fromJSON(const json::Expr &Params, FileEvent &R) {
184 json::ObjectMapper O(Params);
185 return O && O.map("uri", R.uri) && O.map("type", R.type);
Marc-Andre Laperlebf114242017-10-02 18:00:37 +0000186}
187
Sam McCallff8b8742017-11-30 21:32:29 +0000188bool fromJSON(const json::Expr &Params, DidChangeWatchedFilesParams &R) {
189 json::ObjectMapper O(Params);
190 return O && O.map("changes", R.changes);
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000191}
192
Sam McCallff8b8742017-11-30 21:32:29 +0000193bool fromJSON(const json::Expr &Params, TextDocumentContentChangeEvent &R) {
194 json::ObjectMapper O(Params);
195 return O && O.map("text", R.text);
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000196}
197
Sam McCallff8b8742017-11-30 21:32:29 +0000198bool fromJSON(const json::Expr &Params, FormattingOptions &R) {
199 json::ObjectMapper O(Params);
200 return O && O.map("tabSize", R.tabSize) &&
201 O.map("insertSpaces", R.insertSpaces);
202}
203
204json::Expr toJSON(const FormattingOptions &P) {
Sam McCalldd0566b2017-11-06 15:40:30 +0000205 return json::obj{
206 {"tabSize", P.tabSize},
207 {"insertSpaces", P.insertSpaces},
208 };
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000209}
210
Sam McCallff8b8742017-11-30 21:32:29 +0000211bool fromJSON(const json::Expr &Params, DocumentRangeFormattingParams &R) {
212 json::ObjectMapper O(Params);
213 return O && O.map("textDocument", R.textDocument) &&
214 O.map("range", R.range) && O.map("options", R.options);
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000215}
216
Sam McCallff8b8742017-11-30 21:32:29 +0000217bool fromJSON(const json::Expr &Params, DocumentOnTypeFormattingParams &R) {
218 json::ObjectMapper O(Params);
219 return O && O.map("textDocument", R.textDocument) &&
220 O.map("position", R.position) && O.map("ch", R.ch) &&
221 O.map("options", R.options);
Krasimir Georgiev1b8bfd42017-02-16 10:49:46 +0000222}
223
Sam McCallff8b8742017-11-30 21:32:29 +0000224bool fromJSON(const json::Expr &Params, DocumentFormattingParams &R) {
225 json::ObjectMapper O(Params);
226 return O && O.map("textDocument", R.textDocument) &&
227 O.map("options", R.options);
Benjamin Kramerbb1cdb62017-02-07 10:28:20 +0000228}
Benjamin Kramerf0af3e62017-03-01 16:16:29 +0000229
Sam McCallff8b8742017-11-30 21:32:29 +0000230bool fromJSON(const json::Expr &Params, Diagnostic &R) {
231 json::ObjectMapper O(Params);
232 if (!O || !O.map("range", R.range) || !O.map("message", R.message))
233 return false;
234 O.map("severity", R.severity);
235 return true;
Benjamin Kramerf0af3e62017-03-01 16:16:29 +0000236}
237
Sam McCallff8b8742017-11-30 21:32:29 +0000238bool fromJSON(const json::Expr &Params, CodeActionContext &R) {
239 json::ObjectMapper O(Params);
240 return O && O.map("diagnostics", R.diagnostics);
Benjamin Kramerf0af3e62017-03-01 16:16:29 +0000241}
242
Sam McCallff8b8742017-11-30 21:32:29 +0000243bool fromJSON(const json::Expr &Params, CodeActionParams &R) {
244 json::ObjectMapper O(Params);
245 return O && O.map("textDocument", R.textDocument) &&
246 O.map("range", R.range) && O.map("context", R.context);
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000247}
248
Sam McCallff8b8742017-11-30 21:32:29 +0000249bool fromJSON(const json::Expr &Params, WorkspaceEdit &R) {
250 json::ObjectMapper O(Params);
251 return O && O.map("changes", R.changes);
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000252}
253
254const std::string ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND =
255 "clangd.applyFix";
256
Sam McCallff8b8742017-11-30 21:32:29 +0000257bool fromJSON(const json::Expr &Params, ExecuteCommandParams &R) {
258 json::ObjectMapper O(Params);
259 if (!O || !O.map("command", R.command))
260 return false;
Sam McCallec109022017-11-28 09:37:43 +0000261
Sam McCallff8b8742017-11-30 21:32:29 +0000262 auto Args = Params.asObject()->getArray("arguments");
263 if (R.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND) {
264 return Args && Args->size() == 1 &&
265 fromJSON(Args->front(), R.workspaceEdit);
266 }
267 return false; // Unrecognized command.
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000268}
269
Sam McCallff8b8742017-11-30 21:32:29 +0000270json::Expr toJSON(const WorkspaceEdit &WE) {
Sam McCalldd0566b2017-11-06 15:40:30 +0000271 if (!WE.changes)
272 return json::obj{};
273 json::obj FileChanges;
274 for (auto &Change : *WE.changes)
275 FileChanges[Change.first] = json::ary(Change.second);
276 return json::obj{{"changes", std::move(FileChanges)}};
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000277}
278
Sam McCallff8b8742017-11-30 21:32:29 +0000279json::Expr toJSON(const ApplyWorkspaceEditParams &Params) {
Sam McCalldd0566b2017-11-06 15:40:30 +0000280 return json::obj{{"edit", Params.edit}};
Marc-Andre Laperlee7ec16a2017-11-03 13:39:15 +0000281}
282
Sam McCallff8b8742017-11-30 21:32:29 +0000283bool fromJSON(const json::Expr &Params, TextDocumentPositionParams &R) {
284 json::ObjectMapper O(Params);
285 return O && O.map("textDocument", R.textDocument) &&
286 O.map("position", R.position);
Krasimir Georgiev6d2131a2017-04-04 09:46:39 +0000287}
288
Sam McCallff8b8742017-11-30 21:32:29 +0000289json::Expr toJSON(const CompletionItem &CI) {
Krasimir Georgiev6d2131a2017-04-04 09:46:39 +0000290 assert(!CI.label.empty() && "completion item label is required");
Sam McCalldd0566b2017-11-06 15:40:30 +0000291 json::obj Result{{"label", CI.label}};
Krasimir Georgiev6d2131a2017-04-04 09:46:39 +0000292 if (CI.kind != CompletionItemKind::Missing)
Sam McCalldd0566b2017-11-06 15:40:30 +0000293 Result["kind"] = static_cast<int>(CI.kind);
Krasimir Georgiev6d2131a2017-04-04 09:46:39 +0000294 if (!CI.detail.empty())
Sam McCalldd0566b2017-11-06 15:40:30 +0000295 Result["detail"] = CI.detail;
Krasimir Georgiev6d2131a2017-04-04 09:46:39 +0000296 if (!CI.documentation.empty())
Sam McCalldd0566b2017-11-06 15:40:30 +0000297 Result["documentation"] = CI.documentation;
Krasimir Georgiev6d2131a2017-04-04 09:46:39 +0000298 if (!CI.sortText.empty())
Sam McCalldd0566b2017-11-06 15:40:30 +0000299 Result["sortText"] = CI.sortText;
Krasimir Georgiev6d2131a2017-04-04 09:46:39 +0000300 if (!CI.filterText.empty())
Sam McCalldd0566b2017-11-06 15:40:30 +0000301 Result["filterText"] = CI.filterText;
Krasimir Georgiev6d2131a2017-04-04 09:46:39 +0000302 if (!CI.insertText.empty())
Sam McCalldd0566b2017-11-06 15:40:30 +0000303 Result["insertText"] = CI.insertText;
304 if (CI.insertTextFormat != InsertTextFormat::Missing)
305 Result["insertTextFormat"] = static_cast<int>(CI.insertTextFormat);
Krasimir Georgiev6d2131a2017-04-04 09:46:39 +0000306 if (CI.textEdit)
Sam McCalldd0566b2017-11-06 15:40:30 +0000307 Result["textEdit"] = *CI.textEdit;
308 if (!CI.additionalTextEdits.empty())
309 Result["additionalTextEdits"] = json::ary(CI.additionalTextEdits);
310 return std::move(Result);
Krasimir Georgiev6d2131a2017-04-04 09:46:39 +0000311}
Ilya Biryukovd9bdfe02017-10-06 11:54:17 +0000312
Sam McCallff8b8742017-11-30 21:32:29 +0000313bool operator<(const CompletionItem &L, const CompletionItem &R) {
Sam McCallc78ccbd2017-11-08 07:44:12 +0000314 return (L.sortText.empty() ? L.label : L.sortText) <
315 (R.sortText.empty() ? R.label : R.sortText);
316}
317
Sam McCallff8b8742017-11-30 21:32:29 +0000318json::Expr toJSON(const CompletionList &L) {
Sam McCalla40371b2017-11-15 09:16:29 +0000319 return json::obj{
320 {"isIncomplete", L.isIncomplete},
321 {"items", json::ary(L.items)},
322 };
323}
324
Sam McCallff8b8742017-11-30 21:32:29 +0000325json::Expr toJSON(const ParameterInformation &PI) {
Ilya Biryukovd9bdfe02017-10-06 11:54:17 +0000326 assert(!PI.label.empty() && "parameter information label is required");
Sam McCalldd0566b2017-11-06 15:40:30 +0000327 json::obj Result{{"label", PI.label}};
Ilya Biryukovd9bdfe02017-10-06 11:54:17 +0000328 if (!PI.documentation.empty())
Sam McCalldd0566b2017-11-06 15:40:30 +0000329 Result["documentation"] = PI.documentation;
330 return std::move(Result);
Ilya Biryukovd9bdfe02017-10-06 11:54:17 +0000331}
332
Sam McCallff8b8742017-11-30 21:32:29 +0000333json::Expr toJSON(const SignatureInformation &SI) {
Ilya Biryukovd9bdfe02017-10-06 11:54:17 +0000334 assert(!SI.label.empty() && "signature information label is required");
Sam McCalldd0566b2017-11-06 15:40:30 +0000335 json::obj Result{
336 {"label", SI.label},
337 {"parameters", json::ary(SI.parameters)},
338 };
Ilya Biryukovd9bdfe02017-10-06 11:54:17 +0000339 if (!SI.documentation.empty())
Sam McCalldd0566b2017-11-06 15:40:30 +0000340 Result["documentation"] = SI.documentation;
341 return std::move(Result);
Ilya Biryukovd9bdfe02017-10-06 11:54:17 +0000342}
343
Sam McCallff8b8742017-11-30 21:32:29 +0000344json::Expr toJSON(const SignatureHelp &SH) {
Ilya Biryukovd9bdfe02017-10-06 11:54:17 +0000345 assert(SH.activeSignature >= 0 &&
346 "Unexpected negative value for number of active signatures.");
347 assert(SH.activeParameter >= 0 &&
348 "Unexpected negative value for active parameter index");
Sam McCalldd0566b2017-11-06 15:40:30 +0000349 return json::obj{
350 {"activeSignature", SH.activeSignature},
351 {"activeParameter", SH.activeParameter},
352 {"signatures", json::ary(SH.signatures)},
353 };
Ilya Biryukovd9bdfe02017-10-06 11:54:17 +0000354}
Haojian Wu345099c2017-11-09 11:30:04 +0000355
Sam McCallff8b8742017-11-30 21:32:29 +0000356bool fromJSON(const json::Expr &Params, RenameParams &R) {
357 json::ObjectMapper O(Params);
358 return O && O.map("textDocument", R.textDocument) &&
359 O.map("position", R.position) && O.map("newName", R.newName);
Haojian Wu345099c2017-11-09 11:30:04 +0000360}
Sam McCallff8b8742017-11-30 21:32:29 +0000361
362} // namespace clangd
363} // namespace clang