1 //===--- 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.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "Protocol.h"
15 #include "Logger.h"
16 #include "URI.h"
17 #include "clang/Basic/LLVM.h"
18 #include "llvm/ADT/SmallString.h"
19 #include "llvm/Support/Format.h"
20 #include "llvm/Support/FormatVariadic.h"
21 #include "llvm/Support/Path.h"
22 #include "llvm/Support/raw_ostream.h"
23 
24 namespace clang {
25 namespace clangd {
26 
27 URIForFile::URIForFile(std::string AbsPath) {
28   assert(llvm::sys::path::is_absolute(AbsPath) && "the path is relative");
29   File = std::move(AbsPath);
30 }
31 
32 bool fromJSON(const json::Expr &E, URIForFile &R) {
33   if (auto S = E.asString()) {
34     auto U = URI::parse(*S);
35     if (!U) {
36       log("Failed to parse URI " + *S + ": " + llvm::toString(U.takeError()));
37       return false;
38     }
39     if (U->scheme() != "file" && U->scheme() != "test") {
40       log("Clangd only supports 'file' URI scheme for workspace files: " + *S);
41       return false;
42     }
43     auto Path = URI::resolve(*U);
44     if (!Path) {
45       log(llvm::toString(Path.takeError()));
46       return false;
47     }
48     R = URIForFile(*Path);
49     return true;
50   }
51   return false;
52 }
53 
54 json::Expr toJSON(const URIForFile &U) { return U.uri(); }
55 
56 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const URIForFile &U) {
57   return OS << U.uri();
58 }
59 
60 json::Expr toJSON(const TextDocumentIdentifier &R) {
61   return json::obj{{"uri", R.uri}};
62 }
63 
64 bool fromJSON(const json::Expr &Params, TextDocumentIdentifier &R) {
65   json::ObjectMapper O(Params);
66   return O && O.map("uri", R.uri);
67 }
68 
69 bool fromJSON(const json::Expr &Params, Position &R) {
70   json::ObjectMapper O(Params);
71   return O && O.map("line", R.line) && O.map("character", R.character);
72 }
73 
74 json::Expr toJSON(const Position &P) {
75   return json::obj{
76       {"line", P.line},
77       {"character", P.character},
78   };
79 }
80 
81 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Position &P) {
82   return OS << P.line << ':' << P.character;
83 }
84 
85 bool fromJSON(const json::Expr &Params, Range &R) {
86   json::ObjectMapper O(Params);
87   return O && O.map("start", R.start) && O.map("end", R.end);
88 }
89 
90 json::Expr toJSON(const Range &P) {
91   return json::obj{
92       {"start", P.start},
93       {"end", P.end},
94   };
95 }
96 
97 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Range &R) {
98   return OS << R.start << '-' << R.end;
99 }
100 
101 json::Expr toJSON(const Location &P) {
102   return json::obj{
103       {"uri", P.uri},
104       {"range", P.range},
105   };
106 }
107 
108 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Location &L) {
109   return OS << L.range << '@' << L.uri;
110 }
111 
112 bool fromJSON(const json::Expr &Params, TextDocumentItem &R) {
113   json::ObjectMapper O(Params);
114   return O && O.map("uri", R.uri) && O.map("languageId", R.languageId) &&
115          O.map("version", R.version) && O.map("text", R.text);
116 }
117 
118 bool fromJSON(const json::Expr &Params, Metadata &R) {
119   json::ObjectMapper O(Params);
120   if (!O)
121     return false;
122   O.map("extraFlags", R.extraFlags);
123   return true;
124 }
125 
126 bool fromJSON(const json::Expr &Params, TextEdit &R) {
127   json::ObjectMapper O(Params);
128   return O && O.map("range", R.range) && O.map("newText", R.newText);
129 }
130 
131 json::Expr toJSON(const TextEdit &P) {
132   return json::obj{
133       {"range", P.range},
134       {"newText", P.newText},
135   };
136 }
137 
138 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const TextEdit &TE) {
139   OS << TE.range << " => \"";
140   PrintEscapedString(TE.newText, OS);
141   return OS << '"';
142 }
143 
144 bool fromJSON(const json::Expr &E, TraceLevel &Out) {
145   if (auto S = E.asString()) {
146     if (*S == "off") {
147       Out = TraceLevel::Off;
148       return true;
149     } else if (*S == "messages") {
150       Out = TraceLevel::Messages;
151       return true;
152     } else if (*S == "verbose") {
153       Out = TraceLevel::Verbose;
154       return true;
155     }
156   }
157   return false;
158 }
159 
160 bool fromJSON(const json::Expr &Params, CompletionItemClientCapabilities &R) {
161   json::ObjectMapper O(Params);
162   if (!O)
163     return false;
164   O.map("snippetSupport", R.snippetSupport);
165   O.map("commitCharacterSupport", R.commitCharacterSupport);
166   return true;
167 }
168 
169 bool fromJSON(const json::Expr &Params, CompletionClientCapabilities &R) {
170   json::ObjectMapper O(Params);
171   if (!O)
172     return false;
173   O.map("dynamicRegistration", R.dynamicRegistration);
174   O.map("completionItem", R.completionItem);
175   O.map("contextSupport", R.contextSupport);
176   return true;
177 }
178 
179 bool fromJSON(const json::Expr &Params, TextDocumentClientCapabilities &R) {
180   json::ObjectMapper O(Params);
181   if (!O)
182     return false;
183   O.map("completion", R.completion);
184   return true;
185 }
186 
187 bool fromJSON(const json::Expr &Params, ClientCapabilities &R) {
188   json::ObjectMapper O(Params);
189   if (!O)
190     return false;
191   O.map("textDocument", R.textDocument);
192   return true;
193 }
194 
195 bool fromJSON(const json::Expr &Params, InitializeParams &R) {
196   json::ObjectMapper O(Params);
197   if (!O)
198     return false;
199   // We deliberately don't fail if we can't parse individual fields.
200   // Failing to handle a slightly malformed initialize would be a disaster.
201   O.map("processId", R.processId);
202   O.map("rootUri", R.rootUri);
203   O.map("rootPath", R.rootPath);
204   O.map("capabilities", R.capabilities);
205   O.map("trace", R.trace);
206   // initializationOptions, capabilities unused
207   return true;
208 }
209 
210 bool fromJSON(const json::Expr &Params, DidOpenTextDocumentParams &R) {
211   json::ObjectMapper O(Params);
212   return O && O.map("textDocument", R.textDocument) &&
213          O.map("metadata", R.metadata);
214 }
215 
216 bool fromJSON(const json::Expr &Params, DidCloseTextDocumentParams &R) {
217   json::ObjectMapper O(Params);
218   return O && O.map("textDocument", R.textDocument);
219 }
220 
221 bool fromJSON(const json::Expr &Params, DidChangeTextDocumentParams &R) {
222   json::ObjectMapper O(Params);
223   return O && O.map("textDocument", R.textDocument) &&
224          O.map("contentChanges", R.contentChanges) &&
225          O.map("wantDiagnostics", R.wantDiagnostics);
226 }
227 
228 bool fromJSON(const json::Expr &E, FileChangeType &Out) {
229   if (auto T = E.asInteger()) {
230     if (*T < static_cast<int>(FileChangeType::Created) ||
231         *T > static_cast<int>(FileChangeType::Deleted))
232       return false;
233     Out = static_cast<FileChangeType>(*T);
234     return true;
235   }
236   return false;
237 }
238 
239 bool fromJSON(const json::Expr &Params, FileEvent &R) {
240   json::ObjectMapper O(Params);
241   return O && O.map("uri", R.uri) && O.map("type", R.type);
242 }
243 
244 bool fromJSON(const json::Expr &Params, DidChangeWatchedFilesParams &R) {
245   json::ObjectMapper O(Params);
246   return O && O.map("changes", R.changes);
247 }
248 
249 bool fromJSON(const json::Expr &Params, TextDocumentContentChangeEvent &R) {
250   json::ObjectMapper O(Params);
251   return O && O.map("text", R.text);
252 }
253 
254 bool fromJSON(const json::Expr &Params, FormattingOptions &R) {
255   json::ObjectMapper O(Params);
256   return O && O.map("tabSize", R.tabSize) &&
257          O.map("insertSpaces", R.insertSpaces);
258 }
259 
260 json::Expr toJSON(const FormattingOptions &P) {
261   return json::obj{
262       {"tabSize", P.tabSize},
263       {"insertSpaces", P.insertSpaces},
264   };
265 }
266 
267 bool fromJSON(const json::Expr &Params, DocumentRangeFormattingParams &R) {
268   json::ObjectMapper O(Params);
269   return O && O.map("textDocument", R.textDocument) &&
270          O.map("range", R.range) && O.map("options", R.options);
271 }
272 
273 bool fromJSON(const json::Expr &Params, DocumentOnTypeFormattingParams &R) {
274   json::ObjectMapper O(Params);
275   return O && O.map("textDocument", R.textDocument) &&
276          O.map("position", R.position) && O.map("ch", R.ch) &&
277          O.map("options", R.options);
278 }
279 
280 bool fromJSON(const json::Expr &Params, DocumentFormattingParams &R) {
281   json::ObjectMapper O(Params);
282   return O && O.map("textDocument", R.textDocument) &&
283          O.map("options", R.options);
284 }
285 
286 bool fromJSON(const json::Expr &Params, Diagnostic &R) {
287   json::ObjectMapper O(Params);
288   if (!O || !O.map("range", R.range) || !O.map("message", R.message))
289     return false;
290   O.map("severity", R.severity);
291   return true;
292 }
293 
294 bool fromJSON(const json::Expr &Params, CodeActionContext &R) {
295   json::ObjectMapper O(Params);
296   return O && O.map("diagnostics", R.diagnostics);
297 }
298 
299 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Diagnostic &D) {
300   OS << D.range << " [";
301   switch (D.severity) {
302     case 1:
303       OS << "error";
304       break;
305     case 2:
306       OS << "warning";
307       break;
308     case 3:
309       OS << "note";
310       break;
311     case 4:
312       OS << "remark";
313       break;
314     default:
315       OS << "diagnostic";
316       break;
317   }
318   return OS << '(' << D.severity << "): " << D.message << "]";
319 }
320 
321 bool fromJSON(const json::Expr &Params, CodeActionParams &R) {
322   json::ObjectMapper O(Params);
323   return O && O.map("textDocument", R.textDocument) &&
324          O.map("range", R.range) && O.map("context", R.context);
325 }
326 
327 bool fromJSON(const json::Expr &Params, WorkspaceEdit &R) {
328   json::ObjectMapper O(Params);
329   return O && O.map("changes", R.changes);
330 }
331 
332 const llvm::StringLiteral ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND =
333     "clangd.applyFix";
334 const llvm::StringLiteral ExecuteCommandParams::CLANGD_INSERT_HEADER_INCLUDE =
335     "clangd.insertInclude";
336 
337 bool fromJSON(const json::Expr &Params, ExecuteCommandParams &R) {
338   json::ObjectMapper O(Params);
339   if (!O || !O.map("command", R.command))
340     return false;
341 
342   auto Args = Params.asObject()->getArray("arguments");
343   if (R.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND) {
344     return Args && Args->size() == 1 &&
345            fromJSON(Args->front(), R.workspaceEdit);
346   } else if (R.command == ExecuteCommandParams::CLANGD_INSERT_HEADER_INCLUDE) {
347     return Args && Args->size() == 1 &&
348            fromJSON(Args->front(), R.includeInsertion);
349   }
350   return false; // Unrecognized command.
351 }
352 
353 json::Expr toJSON(const Command &C) {
354   auto Cmd = json::obj{{"title", C.title}, {"command", C.command}};
355   if (C.workspaceEdit)
356     Cmd["arguments"] = {*C.workspaceEdit};
357   else if (C.includeInsertion)
358     Cmd["arguments"] = {*C.includeInsertion};
359   return std::move(Cmd);
360 }
361 
362 json::Expr toJSON(const WorkspaceEdit &WE) {
363   if (!WE.changes)
364     return json::obj{};
365   json::obj FileChanges;
366   for (auto &Change : *WE.changes)
367     FileChanges[Change.first] = json::ary(Change.second);
368   return json::obj{{"changes", std::move(FileChanges)}};
369 }
370 
371 bool fromJSON(const json::Expr &II, IncludeInsertion &R) {
372   json::ObjectMapper O(II);
373   return O && O.map("textDocument", R.textDocument) &&
374          O.map("header", R.header);
375 }
376 json::Expr toJSON(const IncludeInsertion &II) {
377   return json::obj{{"textDocument", II.textDocument}, {"header", II.header}};
378 }
379 
380 json::Expr toJSON(const ApplyWorkspaceEditParams &Params) {
381   return json::obj{{"edit", Params.edit}};
382 }
383 
384 bool fromJSON(const json::Expr &Params, TextDocumentPositionParams &R) {
385   json::ObjectMapper O(Params);
386   return O && O.map("textDocument", R.textDocument) &&
387          O.map("position", R.position);
388 }
389 
390 static StringRef toTextKind(MarkupKind Kind) {
391   switch (Kind) {
392   case MarkupKind::PlainText:
393     return "plaintext";
394   case MarkupKind::Markdown:
395     return "markdown";
396   }
397   llvm_unreachable("Invalid MarkupKind");
398 }
399 
400 json::Expr toJSON(const MarkupContent &MC) {
401   if (MC.value.empty())
402     return nullptr;
403 
404   return json::obj{
405       {"kind", toTextKind(MC.kind)},
406       {"value", MC.value},
407   };
408 }
409 
410 json::Expr toJSON(const Hover &H) {
411   json::obj Result{{"contents", toJSON(H.contents)}};
412 
413   if (H.range.hasValue())
414     Result["range"] = toJSON(*H.range);
415 
416   return std::move(Result);
417 }
418 
419 json::Expr toJSON(const CompletionItem &CI) {
420   assert(!CI.label.empty() && "completion item label is required");
421   json::obj Result{{"label", CI.label}};
422   if (CI.kind != CompletionItemKind::Missing)
423     Result["kind"] = static_cast<int>(CI.kind);
424   if (!CI.detail.empty())
425     Result["detail"] = CI.detail;
426   if (!CI.documentation.empty())
427     Result["documentation"] = CI.documentation;
428   if (!CI.sortText.empty())
429     Result["sortText"] = CI.sortText;
430   if (!CI.filterText.empty())
431     Result["filterText"] = CI.filterText;
432   if (!CI.insertText.empty())
433     Result["insertText"] = CI.insertText;
434   if (CI.insertTextFormat != InsertTextFormat::Missing)
435     Result["insertTextFormat"] = static_cast<int>(CI.insertTextFormat);
436   if (CI.textEdit)
437     Result["textEdit"] = *CI.textEdit;
438   if (!CI.additionalTextEdits.empty())
439     Result["additionalTextEdits"] = json::ary(CI.additionalTextEdits);
440   if (CI.command)
441     Result["command"] = *CI.command;
442   return std::move(Result);
443 }
444 
445 bool operator<(const CompletionItem &L, const CompletionItem &R) {
446   return (L.sortText.empty() ? L.label : L.sortText) <
447          (R.sortText.empty() ? R.label : R.sortText);
448 }
449 
450 json::Expr toJSON(const CompletionList &L) {
451   return json::obj{
452       {"isIncomplete", L.isIncomplete},
453       {"items", json::ary(L.items)},
454   };
455 }
456 
457 json::Expr toJSON(const ParameterInformation &PI) {
458   assert(!PI.label.empty() && "parameter information label is required");
459   json::obj Result{{"label", PI.label}};
460   if (!PI.documentation.empty())
461     Result["documentation"] = PI.documentation;
462   return std::move(Result);
463 }
464 
465 json::Expr toJSON(const SignatureInformation &SI) {
466   assert(!SI.label.empty() && "signature information label is required");
467   json::obj Result{
468       {"label", SI.label},
469       {"parameters", json::ary(SI.parameters)},
470   };
471   if (!SI.documentation.empty())
472     Result["documentation"] = SI.documentation;
473   return std::move(Result);
474 }
475 
476 json::Expr toJSON(const SignatureHelp &SH) {
477   assert(SH.activeSignature >= 0 &&
478          "Unexpected negative value for number of active signatures.");
479   assert(SH.activeParameter >= 0 &&
480          "Unexpected negative value for active parameter index");
481   return json::obj{
482       {"activeSignature", SH.activeSignature},
483       {"activeParameter", SH.activeParameter},
484       {"signatures", json::ary(SH.signatures)},
485   };
486 }
487 
488 bool fromJSON(const json::Expr &Params, RenameParams &R) {
489   json::ObjectMapper O(Params);
490   return O && O.map("textDocument", R.textDocument) &&
491          O.map("position", R.position) && O.map("newName", R.newName);
492 }
493 
494 json::Expr toJSON(const DocumentHighlight &DH) {
495   return json::obj{
496       {"range", toJSON(DH.range)},
497       {"kind", static_cast<int>(DH.kind)},
498   };
499 }
500 
501 bool fromJSON(const json::Expr &Params, DidChangeConfigurationParams &CCP) {
502   json::ObjectMapper O(Params);
503   return O && O.map("settings", CCP.settings);
504 }
505 
506 bool fromJSON(const json::Expr &Params, ClangdConfigurationParamsChange &CCPC) {
507   json::ObjectMapper O(Params);
508   return O && O.map("compilationDatabasePath", CCPC.compilationDatabasePath);
509 }
510 
511 } // namespace clangd
512 } // namespace clang
513