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