| 1 | //===--- Protocol.cpp - Language Server Protocol Implementation -----------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This file contains the serialization code for the LSP structs. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include "Protocol.h" |
| 14 | #include "URI.h" |
| 15 | #include "support/Logger.h" |
| 16 | #include "clang/Basic/LLVM.h" |
| 17 | #include "clang/Index/IndexSymbol.h" |
| 18 | #include "llvm/ADT/StringExtras.h" |
| 19 | #include "llvm/ADT/StringRef.h" |
| 20 | #include "llvm/ADT/StringSwitch.h" |
| 21 | #include "llvm/Support/ErrorHandling.h" |
| 22 | #include "llvm/Support/JSON.h" |
| 23 | #include "llvm/Support/Path.h" |
| 24 | #include "llvm/Support/raw_ostream.h" |
| 25 | |
| 26 | namespace clang { |
| 27 | namespace clangd { |
| 28 | namespace { |
| 29 | |
| 30 | // Helper that doesn't treat `null` and absent fields as failures. |
| 31 | template <typename T> |
| 32 | bool mapOptOrNull(const llvm::json::Value &Params, llvm::StringLiteral Prop, |
| 33 | T &Out, llvm::json::Path P) { |
| 34 | auto *O = Params.getAsObject(); |
| 35 | assert(O); |
| 36 | auto *V = O->get(K: Prop); |
| 37 | // Field is missing or null. |
| 38 | if (!V || V->getAsNull()) |
| 39 | return true; |
| 40 | return fromJSON(*V, Out, P.field(Field: Prop)); |
| 41 | } |
| 42 | } // namespace |
| 43 | |
| 44 | char LSPError::ID; |
| 45 | |
| 46 | URIForFile URIForFile::canonicalize(llvm::StringRef AbsPath, |
| 47 | llvm::StringRef TUPath) { |
| 48 | assert(llvm::sys::path::is_absolute(AbsPath) && "the path is relative" ); |
| 49 | auto Resolved = URI::resolvePath(AbsPath, HintPath: TUPath); |
| 50 | if (!Resolved) { |
| 51 | elog(Fmt: "URIForFile: failed to resolve path {0} with TU path {1}: " |
| 52 | "{2}.\nUsing unresolved path." , |
| 53 | Vals&: AbsPath, Vals&: TUPath, Vals: Resolved.takeError()); |
| 54 | return URIForFile(std::string(AbsPath)); |
| 55 | } |
| 56 | return URIForFile(std::move(*Resolved)); |
| 57 | } |
| 58 | |
| 59 | llvm::Expected<URIForFile> URIForFile::fromURI(const URI &U, |
| 60 | llvm::StringRef HintPath) { |
| 61 | auto Resolved = URI::resolve(U, HintPath); |
| 62 | if (!Resolved) |
| 63 | return Resolved.takeError(); |
| 64 | return URIForFile(std::move(*Resolved)); |
| 65 | } |
| 66 | |
| 67 | bool fromJSON(const llvm::json::Value &E, URIForFile &R, llvm::json::Path P) { |
| 68 | if (auto S = E.getAsString()) { |
| 69 | auto Parsed = URI::parse(Uri: *S); |
| 70 | if (!Parsed) { |
| 71 | consumeError(Err: Parsed.takeError()); |
| 72 | P.report(Message: "failed to parse URI" ); |
| 73 | return false; |
| 74 | } |
| 75 | if (Parsed->scheme() != "file" && Parsed->scheme() != "test" ) { |
| 76 | P.report(Message: "clangd only supports 'file' URI scheme for workspace files" ); |
| 77 | return false; |
| 78 | } |
| 79 | // "file" and "test" schemes do not require hint path. |
| 80 | auto U = URIForFile::fromURI(U: *Parsed, /*HintPath=*/"" ); |
| 81 | if (!U) { |
| 82 | P.report(Message: "unresolvable URI" ); |
| 83 | consumeError(Err: U.takeError()); |
| 84 | return false; |
| 85 | } |
| 86 | R = std::move(*U); |
| 87 | return true; |
| 88 | } |
| 89 | return false; |
| 90 | } |
| 91 | |
| 92 | llvm::json::Value toJSON(const URIForFile &U) { return U.uri(); } |
| 93 | |
| 94 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const URIForFile &U) { |
| 95 | return OS << U.uri(); |
| 96 | } |
| 97 | |
| 98 | llvm::json::Value toJSON(const TextDocumentIdentifier &R) { |
| 99 | return llvm::json::Object{{.K: "uri" , .V: R.uri}}; |
| 100 | } |
| 101 | |
| 102 | bool fromJSON(const llvm::json::Value &Params, TextDocumentIdentifier &R, |
| 103 | llvm::json::Path P) { |
| 104 | llvm::json::ObjectMapper O(Params, P); |
| 105 | return O && O.map(Prop: "uri" , Out&: R.uri); |
| 106 | } |
| 107 | |
| 108 | llvm::json::Value toJSON(const VersionedTextDocumentIdentifier &R) { |
| 109 | auto Result = toJSON(R: static_cast<const TextDocumentIdentifier &>(R)); |
| 110 | Result.getAsObject()->try_emplace(K: "version" , Args: R.version); |
| 111 | return Result; |
| 112 | } |
| 113 | |
| 114 | bool fromJSON(const llvm::json::Value &Params, |
| 115 | VersionedTextDocumentIdentifier &R, llvm::json::Path P) { |
| 116 | llvm::json::ObjectMapper O(Params, P); |
| 117 | return fromJSON(Params, R&: static_cast<TextDocumentIdentifier &>(R), P) && O && |
| 118 | O.map(Prop: "version" , Out&: R.version); |
| 119 | } |
| 120 | |
| 121 | bool fromJSON(const llvm::json::Value &Params, Position &R, |
| 122 | llvm::json::Path P) { |
| 123 | llvm::json::ObjectMapper O(Params, P); |
| 124 | return O && O.map(Prop: "line" , Out&: R.line) && O.map(Prop: "character" , Out&: R.character); |
| 125 | } |
| 126 | |
| 127 | llvm::json::Value toJSON(const Position &P) { |
| 128 | return llvm::json::Object{ |
| 129 | {.K: "line" , .V: P.line}, |
| 130 | {.K: "character" , .V: P.character}, |
| 131 | }; |
| 132 | } |
| 133 | |
| 134 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Position &P) { |
| 135 | return OS << P.line << ':' << P.character; |
| 136 | } |
| 137 | |
| 138 | bool fromJSON(const llvm::json::Value &Params, Range &R, llvm::json::Path P) { |
| 139 | llvm::json::ObjectMapper O(Params, P); |
| 140 | return O && O.map(Prop: "start" , Out&: R.start) && O.map(Prop: "end" , Out&: R.end); |
| 141 | } |
| 142 | |
| 143 | llvm::json::Value toJSON(const Range &P) { |
| 144 | return llvm::json::Object{ |
| 145 | {.K: "start" , .V: P.start}, |
| 146 | {.K: "end" , .V: P.end}, |
| 147 | }; |
| 148 | } |
| 149 | |
| 150 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Range &R) { |
| 151 | return OS << R.start << '-' << R.end; |
| 152 | } |
| 153 | |
| 154 | llvm::json::Value toJSON(const Location &P) { |
| 155 | return llvm::json::Object{ |
| 156 | {.K: "uri" , .V: P.uri}, |
| 157 | {.K: "range" , .V: P.range}, |
| 158 | }; |
| 159 | } |
| 160 | |
| 161 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Location &L) { |
| 162 | return OS << L.range << '@' << L.uri; |
| 163 | } |
| 164 | |
| 165 | llvm::json::Value toJSON(const ReferenceLocation &P) { |
| 166 | llvm::json::Object Result{ |
| 167 | {.K: "uri" , .V: P.uri}, |
| 168 | {.K: "range" , .V: P.range}, |
| 169 | }; |
| 170 | if (P.containerName) |
| 171 | Result.insert(E: {.K: "containerName" , .V: P.containerName}); |
| 172 | return Result; |
| 173 | } |
| 174 | |
| 175 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, |
| 176 | const ReferenceLocation &L) { |
| 177 | return OS << L.range << '@' << L.uri << " (container: " << L.containerName |
| 178 | << ")" ; |
| 179 | } |
| 180 | |
| 181 | bool fromJSON(const llvm::json::Value &Params, TextDocumentItem &R, |
| 182 | llvm::json::Path P) { |
| 183 | llvm::json::ObjectMapper O(Params, P); |
| 184 | return O && O.map(Prop: "uri" , Out&: R.uri) && O.map(Prop: "languageId" , Out&: R.languageId) && |
| 185 | O.map(Prop: "version" , Out&: R.version) && O.map(Prop: "text" , Out&: R.text); |
| 186 | } |
| 187 | |
| 188 | bool fromJSON(const llvm::json::Value &Params, TextEdit &R, |
| 189 | llvm::json::Path P) { |
| 190 | llvm::json::ObjectMapper O(Params, P); |
| 191 | return O && O.map(Prop: "range" , Out&: R.range) && O.map(Prop: "newText" , Out&: R.newText) && |
| 192 | O.mapOptional(Prop: "annotationId" , Out&: R.annotationId); |
| 193 | } |
| 194 | |
| 195 | llvm::json::Value toJSON(const TextEdit &P) { |
| 196 | llvm::json::Object Result{ |
| 197 | {.K: "range" , .V: P.range}, |
| 198 | {.K: "newText" , .V: P.newText}, |
| 199 | }; |
| 200 | if (!P.annotationId.empty()) |
| 201 | Result["annotationId" ] = P.annotationId; |
| 202 | return Result; |
| 203 | } |
| 204 | |
| 205 | bool fromJSON(const llvm::json::Value &Params, ChangeAnnotation &R, |
| 206 | llvm::json::Path P) { |
| 207 | llvm::json::ObjectMapper O(Params, P); |
| 208 | return O && O.map(Prop: "label" , Out&: R.label) && |
| 209 | O.map(Prop: "needsConfirmation" , Out&: R.needsConfirmation) && |
| 210 | O.mapOptional(Prop: "description" , Out&: R.description); |
| 211 | } |
| 212 | llvm::json::Value toJSON(const ChangeAnnotation & CA) { |
| 213 | llvm::json::Object Result{{.K: "label" , .V: CA.label}}; |
| 214 | if (CA.needsConfirmation) |
| 215 | Result["needsConfirmation" ] = *CA.needsConfirmation; |
| 216 | if (!CA.description.empty()) |
| 217 | Result["description" ] = CA.description; |
| 218 | return Result; |
| 219 | } |
| 220 | |
| 221 | bool fromJSON(const llvm::json::Value &Params, TextDocumentEdit &R, |
| 222 | llvm::json::Path P) { |
| 223 | llvm::json::ObjectMapper O(Params, P); |
| 224 | return O && O.map(Prop: "textDocument" , Out&: R.textDocument) && O.map(Prop: "edits" , Out&: R.edits); |
| 225 | } |
| 226 | llvm::json::Value toJSON(const TextDocumentEdit &P) { |
| 227 | llvm::json::Object Result{{.K: "textDocument" , .V: P.textDocument}, |
| 228 | {.K: "edits" , .V: P.edits}}; |
| 229 | return Result; |
| 230 | } |
| 231 | |
| 232 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const TextEdit &TE) { |
| 233 | OS << TE.range << " => \"" ; |
| 234 | llvm::printEscapedString(Name: TE.newText, Out&: OS); |
| 235 | return OS << '"'; |
| 236 | } |
| 237 | |
| 238 | bool fromJSON(const llvm::json::Value &E, TraceLevel &Out, llvm::json::Path P) { |
| 239 | if (auto S = E.getAsString()) { |
| 240 | if (*S == "off" ) { |
| 241 | Out = TraceLevel::Off; |
| 242 | return true; |
| 243 | } |
| 244 | if (*S == "messages" ) { |
| 245 | Out = TraceLevel::Messages; |
| 246 | return true; |
| 247 | } |
| 248 | if (*S == "verbose" ) { |
| 249 | Out = TraceLevel::Verbose; |
| 250 | return true; |
| 251 | } |
| 252 | } |
| 253 | return false; |
| 254 | } |
| 255 | |
| 256 | bool fromJSON(const llvm::json::Value &E, SymbolKind &Out, llvm::json::Path P) { |
| 257 | if (auto T = E.getAsInteger()) { |
| 258 | if (*T < static_cast<int>(SymbolKind::File) || |
| 259 | *T > static_cast<int>(SymbolKind::TypeParameter)) |
| 260 | return false; |
| 261 | Out = static_cast<SymbolKind>(*T); |
| 262 | return true; |
| 263 | } |
| 264 | return false; |
| 265 | } |
| 266 | |
| 267 | bool fromJSON(const llvm::json::Value &E, SymbolKindBitset &Out, |
| 268 | llvm::json::Path P) { |
| 269 | if (auto *A = E.getAsArray()) { |
| 270 | for (size_t I = 0; I < A->size(); ++I) { |
| 271 | SymbolKind KindOut; |
| 272 | if (fromJSON(E: (*A)[I], Out&: KindOut, P: P.index(Index: I))) |
| 273 | Out.set(position: size_t(KindOut)); |
| 274 | } |
| 275 | return true; |
| 276 | } |
| 277 | return false; |
| 278 | } |
| 279 | |
| 280 | SymbolKind adjustKindToCapability(SymbolKind Kind, |
| 281 | SymbolKindBitset &SupportedSymbolKinds) { |
| 282 | auto KindVal = static_cast<size_t>(Kind); |
| 283 | if (KindVal >= SymbolKindMin && KindVal <= SupportedSymbolKinds.size() && |
| 284 | SupportedSymbolKinds[KindVal]) |
| 285 | return Kind; |
| 286 | |
| 287 | switch (Kind) { |
| 288 | // Provide some fall backs for common kinds that are close enough. |
| 289 | case SymbolKind::Struct: |
| 290 | return SymbolKind::Class; |
| 291 | case SymbolKind::EnumMember: |
| 292 | return SymbolKind::Enum; |
| 293 | default: |
| 294 | return SymbolKind::String; |
| 295 | } |
| 296 | } |
| 297 | |
| 298 | SymbolKind indexSymbolKindToSymbolKind(index::SymbolKind Kind) { |
| 299 | switch (Kind) { |
| 300 | case index::SymbolKind::Unknown: |
| 301 | return SymbolKind::Variable; |
| 302 | case index::SymbolKind::Module: |
| 303 | return SymbolKind::Module; |
| 304 | case index::SymbolKind::Namespace: |
| 305 | return SymbolKind::Namespace; |
| 306 | case index::SymbolKind::NamespaceAlias: |
| 307 | return SymbolKind::Namespace; |
| 308 | case index::SymbolKind::Macro: |
| 309 | return SymbolKind::String; |
| 310 | case index::SymbolKind::Enum: |
| 311 | return SymbolKind::Enum; |
| 312 | case index::SymbolKind::Struct: |
| 313 | return SymbolKind::Struct; |
| 314 | case index::SymbolKind::Class: |
| 315 | return SymbolKind::Class; |
| 316 | case index::SymbolKind::Protocol: |
| 317 | return SymbolKind::Interface; |
| 318 | case index::SymbolKind::Extension: |
| 319 | return SymbolKind::Interface; |
| 320 | case index::SymbolKind::Union: |
| 321 | return SymbolKind::Class; |
| 322 | case index::SymbolKind::TypeAlias: |
| 323 | return SymbolKind::Class; |
| 324 | case index::SymbolKind::Function: |
| 325 | return SymbolKind::Function; |
| 326 | case index::SymbolKind::Variable: |
| 327 | return SymbolKind::Variable; |
| 328 | case index::SymbolKind::Field: |
| 329 | return SymbolKind::Field; |
| 330 | case index::SymbolKind::EnumConstant: |
| 331 | return SymbolKind::EnumMember; |
| 332 | case index::SymbolKind::InstanceMethod: |
| 333 | case index::SymbolKind::ClassMethod: |
| 334 | case index::SymbolKind::StaticMethod: |
| 335 | return SymbolKind::Method; |
| 336 | case index::SymbolKind::InstanceProperty: |
| 337 | case index::SymbolKind::ClassProperty: |
| 338 | case index::SymbolKind::StaticProperty: |
| 339 | return SymbolKind::Property; |
| 340 | case index::SymbolKind::Constructor: |
| 341 | case index::SymbolKind::Destructor: |
| 342 | return SymbolKind::Constructor; |
| 343 | case index::SymbolKind::ConversionFunction: |
| 344 | return SymbolKind::Function; |
| 345 | case index::SymbolKind::Parameter: |
| 346 | case index::SymbolKind::NonTypeTemplateParm: |
| 347 | return SymbolKind::Variable; |
| 348 | case index::SymbolKind::Using: |
| 349 | return SymbolKind::Namespace; |
| 350 | case index::SymbolKind::TemplateTemplateParm: |
| 351 | case index::SymbolKind::TemplateTypeParm: |
| 352 | return SymbolKind::TypeParameter; |
| 353 | case index::SymbolKind::Concept: |
| 354 | return SymbolKind::Interface; |
| 355 | } |
| 356 | llvm_unreachable("invalid symbol kind" ); |
| 357 | } |
| 358 | |
| 359 | bool fromJSON(const llvm::json::Value &Params, ClientCapabilities &R, |
| 360 | llvm::json::Path P) { |
| 361 | const llvm::json::Object *O = Params.getAsObject(); |
| 362 | if (!O) { |
| 363 | P.report(Message: "expected object" ); |
| 364 | return false; |
| 365 | } |
| 366 | if (auto *TextDocument = O->getObject(K: "textDocument" )) { |
| 367 | if (auto *SemanticHighlighting = |
| 368 | TextDocument->getObject(K: "semanticHighlightingCapabilities" )) { |
| 369 | if (auto SemanticHighlightingSupport = |
| 370 | SemanticHighlighting->getBoolean(K: "semanticHighlighting" )) |
| 371 | R.TheiaSemanticHighlighting = *SemanticHighlightingSupport; |
| 372 | } |
| 373 | if (auto *InactiveRegions = |
| 374 | TextDocument->getObject(K: "inactiveRegionsCapabilities" )) { |
| 375 | if (auto InactiveRegionsSupport = |
| 376 | InactiveRegions->getBoolean(K: "inactiveRegions" )) { |
| 377 | R.InactiveRegions = *InactiveRegionsSupport; |
| 378 | } |
| 379 | } |
| 380 | if (TextDocument->getObject(K: "semanticTokens" )) |
| 381 | R.SemanticTokens = true; |
| 382 | if (auto *Diagnostics = TextDocument->getObject(K: "publishDiagnostics" )) { |
| 383 | if (auto CategorySupport = Diagnostics->getBoolean(K: "categorySupport" )) |
| 384 | R.DiagnosticCategory = *CategorySupport; |
| 385 | if (auto CodeActions = Diagnostics->getBoolean(K: "codeActionsInline" )) |
| 386 | R.DiagnosticFixes = *CodeActions; |
| 387 | if (auto RelatedInfo = Diagnostics->getBoolean(K: "relatedInformation" )) |
| 388 | R.DiagnosticRelatedInformation = *RelatedInfo; |
| 389 | } |
| 390 | if (auto *References = TextDocument->getObject(K: "references" )) |
| 391 | if (auto ContainerSupport = References->getBoolean(K: "container" )) |
| 392 | R.ReferenceContainer = *ContainerSupport; |
| 393 | if (auto *Completion = TextDocument->getObject(K: "completion" )) { |
| 394 | if (auto *Item = Completion->getObject(K: "completionItem" )) { |
| 395 | if (auto SnippetSupport = Item->getBoolean(K: "snippetSupport" )) |
| 396 | R.CompletionSnippets = *SnippetSupport; |
| 397 | if (auto LabelDetailsSupport = Item->getBoolean(K: "labelDetailsSupport" )) |
| 398 | R.CompletionLabelDetail = *LabelDetailsSupport; |
| 399 | if (const auto *DocumentationFormat = |
| 400 | Item->getArray(K: "documentationFormat" )) { |
| 401 | for (const auto &Format : *DocumentationFormat) { |
| 402 | if (fromJSON(Format, R.CompletionDocumentationFormat, P)) |
| 403 | break; |
| 404 | } |
| 405 | } |
| 406 | } |
| 407 | if (auto *ItemKind = Completion->getObject(K: "completionItemKind" )) { |
| 408 | if (auto *ValueSet = ItemKind->get(K: "valueSet" )) { |
| 409 | R.CompletionItemKinds.emplace(); |
| 410 | if (!fromJSON(*ValueSet, *R.CompletionItemKinds, |
| 411 | P.field(Field: "textDocument" ) |
| 412 | .field(Field: "completion" ) |
| 413 | .field(Field: "completionItemKind" ) |
| 414 | .field(Field: "valueSet" ))) |
| 415 | return false; |
| 416 | } |
| 417 | } |
| 418 | if (auto EditsNearCursor = Completion->getBoolean(K: "editsNearCursor" )) |
| 419 | R.CompletionFixes = *EditsNearCursor; |
| 420 | } |
| 421 | if (auto *CodeAction = TextDocument->getObject(K: "codeAction" )) { |
| 422 | if (CodeAction->getObject(K: "codeActionLiteralSupport" )) |
| 423 | R.CodeActionStructure = true; |
| 424 | } |
| 425 | if (auto *DocumentSymbol = TextDocument->getObject(K: "documentSymbol" )) { |
| 426 | if (auto HierarchicalSupport = |
| 427 | DocumentSymbol->getBoolean(K: "hierarchicalDocumentSymbolSupport" )) |
| 428 | R.HierarchicalDocumentSymbol = *HierarchicalSupport; |
| 429 | } |
| 430 | if (auto *Hover = TextDocument->getObject(K: "hover" )) { |
| 431 | if (auto *ContentFormat = Hover->getArray(K: "contentFormat" )) { |
| 432 | for (const auto &Format : *ContentFormat) { |
| 433 | if (fromJSON(Format, R.HoverContentFormat, P)) |
| 434 | break; |
| 435 | } |
| 436 | } |
| 437 | } |
| 438 | if (auto *Help = TextDocument->getObject(K: "signatureHelp" )) { |
| 439 | R.HasSignatureHelp = true; |
| 440 | if (auto *Info = Help->getObject(K: "signatureInformation" )) { |
| 441 | if (auto *Parameter = Info->getObject(K: "parameterInformation" )) { |
| 442 | if (auto OffsetSupport = Parameter->getBoolean(K: "labelOffsetSupport" )) |
| 443 | R.OffsetsInSignatureHelp = *OffsetSupport; |
| 444 | } |
| 445 | if (const auto *DocumentationFormat = |
| 446 | Info->getArray(K: "documentationFormat" )) { |
| 447 | for (const auto &Format : *DocumentationFormat) { |
| 448 | if (fromJSON(Format, R.SignatureHelpDocumentationFormat, P)) |
| 449 | break; |
| 450 | } |
| 451 | } |
| 452 | } |
| 453 | } |
| 454 | if (auto *Folding = TextDocument->getObject(K: "foldingRange" )) { |
| 455 | if (auto LineFolding = Folding->getBoolean(K: "lineFoldingOnly" )) |
| 456 | R.LineFoldingOnly = *LineFolding; |
| 457 | } |
| 458 | if (auto *Rename = TextDocument->getObject(K: "rename" )) { |
| 459 | if (auto RenameSupport = Rename->getBoolean(K: "prepareSupport" )) |
| 460 | R.RenamePrepareSupport = *RenameSupport; |
| 461 | } |
| 462 | } |
| 463 | if (auto *Workspace = O->getObject(K: "workspace" )) { |
| 464 | if (auto *Symbol = Workspace->getObject(K: "symbol" )) { |
| 465 | if (auto *SymbolKind = Symbol->getObject(K: "symbolKind" )) { |
| 466 | if (auto *ValueSet = SymbolKind->get(K: "valueSet" )) { |
| 467 | R.WorkspaceSymbolKinds.emplace(); |
| 468 | if (!fromJSON(E: *ValueSet, Out&: *R.WorkspaceSymbolKinds, |
| 469 | P: P.field(Field: "workspace" ) |
| 470 | .field(Field: "symbol" ) |
| 471 | .field(Field: "symbolKind" ) |
| 472 | .field(Field: "valueSet" ))) |
| 473 | return false; |
| 474 | } |
| 475 | } |
| 476 | } |
| 477 | if (auto *SemanticTokens = Workspace->getObject(K: "semanticTokens" )) { |
| 478 | if (auto RefreshSupport = SemanticTokens->getBoolean(K: "refreshSupport" )) |
| 479 | R.SemanticTokenRefreshSupport = *RefreshSupport; |
| 480 | } |
| 481 | if (auto *WorkspaceEdit = Workspace->getObject(K: "workspaceEdit" )) { |
| 482 | if (auto DocumentChanges = WorkspaceEdit->getBoolean(K: "documentChanges" )) |
| 483 | R.DocumentChanges = *DocumentChanges; |
| 484 | if (WorkspaceEdit->getObject(K: "changeAnnotationSupport" )) { |
| 485 | R.ChangeAnnotation = true; |
| 486 | } |
| 487 | } |
| 488 | } |
| 489 | if (auto *Window = O->getObject(K: "window" )) { |
| 490 | if (auto WorkDoneProgress = Window->getBoolean(K: "workDoneProgress" )) |
| 491 | R.WorkDoneProgress = *WorkDoneProgress; |
| 492 | if (auto Implicit = Window->getBoolean(K: "implicitWorkDoneProgressCreate" )) |
| 493 | R.ImplicitProgressCreation = *Implicit; |
| 494 | } |
| 495 | if (auto *General = O->getObject(K: "general" )) { |
| 496 | if (auto *StaleRequestSupport = General->getObject(K: "staleRequestSupport" )) { |
| 497 | if (auto Cancel = StaleRequestSupport->getBoolean(K: "cancel" )) |
| 498 | R.CancelsStaleRequests = *Cancel; |
| 499 | } |
| 500 | if (auto *PositionEncodings = General->get(K: "positionEncodings" )) { |
| 501 | R.PositionEncodings.emplace(); |
| 502 | if (!fromJSON(E: *PositionEncodings, Out&: *R.PositionEncodings, |
| 503 | P: P.field(Field: "general" ).field(Field: "positionEncodings" ))) |
| 504 | return false; |
| 505 | } |
| 506 | } |
| 507 | if (auto *OffsetEncoding = O->get(K: "offsetEncoding" )) { |
| 508 | R.PositionEncodings.emplace(); |
| 509 | elog(Fmt: "offsetEncoding capability is a deprecated clangd extension that'll " |
| 510 | "go away with clangd 23. Migrate to standard positionEncodings " |
| 511 | "capability introduced by LSP 3.17" ); |
| 512 | if (!fromJSON(E: *OffsetEncoding, Out&: *R.PositionEncodings, |
| 513 | P: P.field(Field: "offsetEncoding" ))) |
| 514 | return false; |
| 515 | } |
| 516 | |
| 517 | if (auto *Experimental = O->getObject(K: "experimental" )) { |
| 518 | if (auto *TextDocument = Experimental->getObject(K: "textDocument" )) { |
| 519 | if (auto *Completion = TextDocument->getObject(K: "completion" )) { |
| 520 | if (auto EditsNearCursor = Completion->getBoolean(K: "editsNearCursor" )) |
| 521 | R.CompletionFixes |= *EditsNearCursor; |
| 522 | } |
| 523 | if (auto *References = TextDocument->getObject(K: "references" )) { |
| 524 | if (auto ContainerSupport = References->getBoolean(K: "container" )) { |
| 525 | R.ReferenceContainer |= *ContainerSupport; |
| 526 | } |
| 527 | } |
| 528 | if (auto *Diagnostics = TextDocument->getObject(K: "publishDiagnostics" )) { |
| 529 | if (auto CodeActions = Diagnostics->getBoolean(K: "codeActionsInline" )) { |
| 530 | R.DiagnosticFixes |= *CodeActions; |
| 531 | } |
| 532 | } |
| 533 | if (auto *InactiveRegions = |
| 534 | TextDocument->getObject(K: "inactiveRegionsCapabilities" )) { |
| 535 | if (auto InactiveRegionsSupport = |
| 536 | InactiveRegions->getBoolean(K: "inactiveRegions" )) { |
| 537 | R.InactiveRegions |= *InactiveRegionsSupport; |
| 538 | } |
| 539 | } |
| 540 | } |
| 541 | if (auto *Window = Experimental->getObject(K: "window" )) { |
| 542 | if (auto Implicit = |
| 543 | Window->getBoolean(K: "implicitWorkDoneProgressCreate" )) { |
| 544 | R.ImplicitProgressCreation |= *Implicit; |
| 545 | } |
| 546 | } |
| 547 | if (auto *OffsetEncoding = Experimental->get(K: "offsetEncoding" )) { |
| 548 | R.PositionEncodings.emplace(); |
| 549 | elog(Fmt: "offsetEncoding capability is a deprecated clangd extension that'll " |
| 550 | "go away with clangd 23. Migrate to standard positionEncodings " |
| 551 | "capability introduced by LSP 3.17" ); |
| 552 | if (!fromJSON(E: *OffsetEncoding, Out&: *R.PositionEncodings, |
| 553 | P: P.field(Field: "offsetEncoding" ))) |
| 554 | return false; |
| 555 | } |
| 556 | } |
| 557 | |
| 558 | return true; |
| 559 | } |
| 560 | |
| 561 | bool fromJSON(const llvm::json::Value &Params, InitializeParams &R, |
| 562 | llvm::json::Path P) { |
| 563 | llvm::json::ObjectMapper O(Params, P); |
| 564 | if (!O) |
| 565 | return false; |
| 566 | // We deliberately don't fail if we can't parse individual fields. |
| 567 | // Failing to handle a slightly malformed initialize would be a disaster. |
| 568 | O.map(Prop: "processId" , Out&: R.processId); |
| 569 | O.map(Prop: "rootUri" , Out&: R.rootUri); |
| 570 | O.map(Prop: "rootPath" , Out&: R.rootPath); |
| 571 | O.map(Prop: "capabilities" , Out&: R.capabilities); |
| 572 | if (auto *RawCaps = Params.getAsObject()->getObject(K: "capabilities" )) |
| 573 | R.rawCapabilities = *RawCaps; |
| 574 | O.map(Prop: "trace" , Out&: R.trace); |
| 575 | O.map(Prop: "initializationOptions" , Out&: R.initializationOptions); |
| 576 | return true; |
| 577 | } |
| 578 | |
| 579 | llvm::json::Value toJSON(const WorkDoneProgressCreateParams &P) { |
| 580 | return llvm::json::Object{{.K: "token" , .V: P.token}}; |
| 581 | } |
| 582 | |
| 583 | llvm::json::Value toJSON(const WorkDoneProgressBegin &P) { |
| 584 | llvm::json::Object Result{ |
| 585 | {.K: "kind" , .V: "begin" }, |
| 586 | {.K: "title" , .V: P.title}, |
| 587 | }; |
| 588 | if (P.cancellable) |
| 589 | Result["cancellable" ] = true; |
| 590 | if (P.percentage) |
| 591 | Result["percentage" ] = 0; |
| 592 | |
| 593 | // FIXME: workaround for older gcc/clang |
| 594 | return std::move(Result); |
| 595 | } |
| 596 | |
| 597 | llvm::json::Value toJSON(const WorkDoneProgressReport &P) { |
| 598 | llvm::json::Object Result{{.K: "kind" , .V: "report" }}; |
| 599 | if (P.cancellable) |
| 600 | Result["cancellable" ] = *P.cancellable; |
| 601 | if (P.message) |
| 602 | Result["message" ] = *P.message; |
| 603 | if (P.percentage) |
| 604 | Result["percentage" ] = *P.percentage; |
| 605 | // FIXME: workaround for older gcc/clang |
| 606 | return std::move(Result); |
| 607 | } |
| 608 | |
| 609 | llvm::json::Value toJSON(const WorkDoneProgressEnd &P) { |
| 610 | llvm::json::Object Result{{.K: "kind" , .V: "end" }}; |
| 611 | if (P.message) |
| 612 | Result["message" ] = *P.message; |
| 613 | // FIXME: workaround for older gcc/clang |
| 614 | return std::move(Result); |
| 615 | } |
| 616 | |
| 617 | llvm::json::Value toJSON(const MessageType &R) { |
| 618 | return static_cast<int64_t>(R); |
| 619 | } |
| 620 | |
| 621 | llvm::json::Value toJSON(const ShowMessageParams &R) { |
| 622 | return llvm::json::Object{{.K: "type" , .V: R.type}, {.K: "message" , .V: R.message}}; |
| 623 | } |
| 624 | |
| 625 | bool fromJSON(const llvm::json::Value &Params, DidOpenTextDocumentParams &R, |
| 626 | llvm::json::Path P) { |
| 627 | llvm::json::ObjectMapper O(Params, P); |
| 628 | return O && O.map(Prop: "textDocument" , Out&: R.textDocument); |
| 629 | } |
| 630 | |
| 631 | bool fromJSON(const llvm::json::Value &Params, DidCloseTextDocumentParams &R, |
| 632 | llvm::json::Path P) { |
| 633 | llvm::json::ObjectMapper O(Params, P); |
| 634 | return O && O.map(Prop: "textDocument" , Out&: R.textDocument); |
| 635 | } |
| 636 | |
| 637 | bool fromJSON(const llvm::json::Value &Params, DidSaveTextDocumentParams &R, |
| 638 | llvm::json::Path P) { |
| 639 | llvm::json::ObjectMapper O(Params, P); |
| 640 | return O && O.map(Prop: "textDocument" , Out&: R.textDocument); |
| 641 | } |
| 642 | |
| 643 | bool fromJSON(const llvm::json::Value &Params, DidChangeTextDocumentParams &R, |
| 644 | llvm::json::Path P) { |
| 645 | llvm::json::ObjectMapper O(Params, P); |
| 646 | return O && O.map(Prop: "textDocument" , Out&: R.textDocument) && |
| 647 | O.map(Prop: "contentChanges" , Out&: R.contentChanges) && |
| 648 | O.map(Prop: "wantDiagnostics" , Out&: R.wantDiagnostics) && |
| 649 | mapOptOrNull(Params, Prop: "forceRebuild" , Out&: R.forceRebuild, P); |
| 650 | } |
| 651 | |
| 652 | bool fromJSON(const llvm::json::Value &E, FileChangeType &Out, |
| 653 | llvm::json::Path P) { |
| 654 | if (auto T = E.getAsInteger()) { |
| 655 | if (*T < static_cast<int>(FileChangeType::Created) || |
| 656 | *T > static_cast<int>(FileChangeType::Deleted)) |
| 657 | return false; |
| 658 | Out = static_cast<FileChangeType>(*T); |
| 659 | return true; |
| 660 | } |
| 661 | return false; |
| 662 | } |
| 663 | |
| 664 | bool fromJSON(const llvm::json::Value &Params, FileEvent &R, |
| 665 | llvm::json::Path P) { |
| 666 | llvm::json::ObjectMapper O(Params, P); |
| 667 | return O && O.map(Prop: "uri" , Out&: R.uri) && O.map(Prop: "type" , Out&: R.type); |
| 668 | } |
| 669 | |
| 670 | bool fromJSON(const llvm::json::Value &Params, DidChangeWatchedFilesParams &R, |
| 671 | llvm::json::Path P) { |
| 672 | llvm::json::ObjectMapper O(Params, P); |
| 673 | return O && O.map(Prop: "changes" , Out&: R.changes); |
| 674 | } |
| 675 | |
| 676 | bool fromJSON(const llvm::json::Value &Params, |
| 677 | TextDocumentContentChangeEvent &R, llvm::json::Path P) { |
| 678 | llvm::json::ObjectMapper O(Params, P); |
| 679 | return O && O.map(Prop: "range" , Out&: R.range) && O.map(Prop: "rangeLength" , Out&: R.rangeLength) && |
| 680 | O.map(Prop: "text" , Out&: R.text); |
| 681 | } |
| 682 | |
| 683 | bool fromJSON(const llvm::json::Value &Params, DocumentRangeFormattingParams &R, |
| 684 | llvm::json::Path P) { |
| 685 | llvm::json::ObjectMapper O(Params, P); |
| 686 | return O && O.map(Prop: "textDocument" , Out&: R.textDocument) && O.map(Prop: "range" , Out&: R.range); |
| 687 | ; |
| 688 | } |
| 689 | |
| 690 | bool fromJSON(const llvm::json::Value &Params, |
| 691 | DocumentRangesFormattingParams &R, llvm::json::Path P) { |
| 692 | llvm::json::ObjectMapper O(Params, P); |
| 693 | return O && O.map(Prop: "textDocument" , Out&: R.textDocument) && |
| 694 | O.map(Prop: "ranges" , Out&: R.ranges); |
| 695 | ; |
| 696 | } |
| 697 | |
| 698 | bool fromJSON(const llvm::json::Value &Params, |
| 699 | DocumentOnTypeFormattingParams &R, llvm::json::Path P) { |
| 700 | llvm::json::ObjectMapper O(Params, P); |
| 701 | return O && O.map(Prop: "textDocument" , Out&: R.textDocument) && |
| 702 | O.map(Prop: "position" , Out&: R.position) && O.map(Prop: "ch" , Out&: R.ch); |
| 703 | } |
| 704 | |
| 705 | bool fromJSON(const llvm::json::Value &Params, DocumentFormattingParams &R, |
| 706 | llvm::json::Path P) { |
| 707 | llvm::json::ObjectMapper O(Params, P); |
| 708 | return O && O.map(Prop: "textDocument" , Out&: R.textDocument); |
| 709 | } |
| 710 | |
| 711 | bool fromJSON(const llvm::json::Value &Params, DocumentSymbolParams &R, |
| 712 | llvm::json::Path P) { |
| 713 | llvm::json::ObjectMapper O(Params, P); |
| 714 | return O && O.map(Prop: "textDocument" , Out&: R.textDocument); |
| 715 | } |
| 716 | |
| 717 | llvm::json::Value toJSON(const DiagnosticRelatedInformation &DRI) { |
| 718 | return llvm::json::Object{ |
| 719 | {.K: "location" , .V: DRI.location}, |
| 720 | {.K: "message" , .V: DRI.message}, |
| 721 | }; |
| 722 | } |
| 723 | |
| 724 | llvm::json::Value toJSON(DiagnosticTag Tag) { return static_cast<int>(Tag); } |
| 725 | |
| 726 | llvm::json::Value toJSON(const CodeDescription &D) { |
| 727 | return llvm::json::Object{{.K: "href" , .V: D.href}}; |
| 728 | } |
| 729 | |
| 730 | llvm::json::Value toJSON(const Diagnostic &D) { |
| 731 | llvm::json::Object Diag{ |
| 732 | {.K: "range" , .V: D.range}, |
| 733 | {.K: "severity" , .V: D.severity}, |
| 734 | {.K: "message" , .V: D.message}, |
| 735 | }; |
| 736 | if (D.category) |
| 737 | Diag["category" ] = *D.category; |
| 738 | if (D.codeActions) |
| 739 | Diag["codeActions" ] = D.codeActions; |
| 740 | if (!D.code.empty()) |
| 741 | Diag["code" ] = D.code; |
| 742 | if (D.codeDescription) |
| 743 | Diag["codeDescription" ] = *D.codeDescription; |
| 744 | if (!D.source.empty()) |
| 745 | Diag["source" ] = D.source; |
| 746 | if (D.relatedInformation) |
| 747 | Diag["relatedInformation" ] = *D.relatedInformation; |
| 748 | if (!D.data.empty()) |
| 749 | Diag["data" ] = llvm::json::Object(D.data); |
| 750 | if (!D.tags.empty()) |
| 751 | Diag["tags" ] = llvm::json::Array{D.tags}; |
| 752 | // FIXME: workaround for older gcc/clang |
| 753 | return std::move(Diag); |
| 754 | } |
| 755 | |
| 756 | bool fromJSON(const llvm::json::Value &Params, Diagnostic &R, |
| 757 | llvm::json::Path P) { |
| 758 | llvm::json::ObjectMapper O(Params, P); |
| 759 | if (!O) |
| 760 | return false; |
| 761 | if (auto *Data = Params.getAsObject()->getObject(K: "data" )) |
| 762 | R.data = *Data; |
| 763 | return O.map(Prop: "range" , Out&: R.range) && O.map(Prop: "message" , Out&: R.message) && |
| 764 | mapOptOrNull(Params, Prop: "severity" , Out&: R.severity, P) && |
| 765 | mapOptOrNull(Params, Prop: "category" , Out&: R.category, P) && |
| 766 | mapOptOrNull(Params, Prop: "code" , Out&: R.code, P) && |
| 767 | mapOptOrNull(Params, Prop: "source" , Out&: R.source, P); |
| 768 | } |
| 769 | |
| 770 | llvm::json::Value toJSON(const PublishDiagnosticsParams &PDP) { |
| 771 | llvm::json::Object Result{ |
| 772 | {.K: "uri" , .V: PDP.uri}, |
| 773 | {.K: "diagnostics" , .V: PDP.diagnostics}, |
| 774 | }; |
| 775 | if (PDP.version) |
| 776 | Result["version" ] = PDP.version; |
| 777 | return std::move(Result); |
| 778 | } |
| 779 | |
| 780 | bool fromJSON(const llvm::json::Value &Params, CodeActionContext &R, |
| 781 | llvm::json::Path P) { |
| 782 | llvm::json::ObjectMapper O(Params, P); |
| 783 | if (!O || !O.map(Prop: "diagnostics" , Out&: R.diagnostics)) |
| 784 | return false; |
| 785 | O.map(Prop: "only" , Out&: R.only); |
| 786 | return true; |
| 787 | } |
| 788 | |
| 789 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Diagnostic &D) { |
| 790 | OS << D.range << " [" ; |
| 791 | switch (D.severity) { |
| 792 | case 1: |
| 793 | OS << "error" ; |
| 794 | break; |
| 795 | case 2: |
| 796 | OS << "warning" ; |
| 797 | break; |
| 798 | case 3: |
| 799 | OS << "note" ; |
| 800 | break; |
| 801 | case 4: |
| 802 | OS << "remark" ; |
| 803 | break; |
| 804 | default: |
| 805 | OS << "diagnostic" ; |
| 806 | break; |
| 807 | } |
| 808 | return OS << '(' << D.severity << "): " << D.message << "]" ; |
| 809 | } |
| 810 | |
| 811 | bool fromJSON(const llvm::json::Value &Params, CodeActionParams &R, |
| 812 | llvm::json::Path P) { |
| 813 | llvm::json::ObjectMapper O(Params, P); |
| 814 | return O && O.map(Prop: "textDocument" , Out&: R.textDocument) && |
| 815 | O.map(Prop: "range" , Out&: R.range) && O.map(Prop: "context" , Out&: R.context); |
| 816 | } |
| 817 | |
| 818 | bool fromJSON(const llvm::json::Value &Params, WorkspaceEdit &R, |
| 819 | llvm::json::Path P) { |
| 820 | llvm::json::ObjectMapper O(Params, P); |
| 821 | return O && O.map(Prop: "changes" , Out&: R.changes) && |
| 822 | O.map(Prop: "documentChanges" , Out&: R.documentChanges) && |
| 823 | O.mapOptional(Prop: "changeAnnotations" , Out&: R.changeAnnotations); |
| 824 | } |
| 825 | |
| 826 | bool fromJSON(const llvm::json::Value &Params, ExecuteCommandParams &R, |
| 827 | llvm::json::Path P) { |
| 828 | llvm::json::ObjectMapper O(Params, P); |
| 829 | if (!O || !O.map(Prop: "command" , Out&: R.command)) |
| 830 | return false; |
| 831 | |
| 832 | const auto *Args = Params.getAsObject()->get(K: "arguments" ); |
| 833 | if (!Args) |
| 834 | return true; // Missing args is ok, argument is null. |
| 835 | const auto *ArgsArray = Args->getAsArray(); |
| 836 | if (!ArgsArray) { |
| 837 | P.field(Field: "arguments" ).report(Message: "expected array" ); |
| 838 | return false; |
| 839 | } |
| 840 | if (ArgsArray->size() > 1) { |
| 841 | P.field(Field: "arguments" ).report(Message: "Command should have 0 or 1 argument" ); |
| 842 | return false; |
| 843 | } |
| 844 | if (ArgsArray->size() == 1) { |
| 845 | R.argument = ArgsArray->front(); |
| 846 | } |
| 847 | return true; |
| 848 | } |
| 849 | |
| 850 | llvm::json::Value toJSON(const SymbolInformation &P) { |
| 851 | llvm::json::Object O{ |
| 852 | {.K: "name" , .V: P.name}, |
| 853 | {.K: "kind" , .V: static_cast<int>(P.kind)}, |
| 854 | {.K: "location" , .V: P.location}, |
| 855 | {.K: "containerName" , .V: P.containerName}, |
| 856 | }; |
| 857 | if (P.score) |
| 858 | O["score" ] = *P.score; |
| 859 | return std::move(O); |
| 860 | } |
| 861 | |
| 862 | llvm::raw_ostream &operator<<(llvm::raw_ostream &O, |
| 863 | const SymbolInformation &SI) { |
| 864 | O << SI.containerName << "::" << SI.name << " - " << toJSON(P: SI); |
| 865 | return O; |
| 866 | } |
| 867 | |
| 868 | bool operator==(const SymbolDetails &LHS, const SymbolDetails &RHS) { |
| 869 | return LHS.name == RHS.name && LHS.containerName == RHS.containerName && |
| 870 | LHS.USR == RHS.USR && LHS.ID == RHS.ID && |
| 871 | LHS.declarationRange == RHS.declarationRange && |
| 872 | LHS.definitionRange == RHS.definitionRange; |
| 873 | } |
| 874 | |
| 875 | llvm::json::Value toJSON(const SymbolDetails &P) { |
| 876 | llvm::json::Object Result{{.K: "name" , .V: llvm::json::Value(nullptr)}, |
| 877 | {.K: "containerName" , .V: llvm::json::Value(nullptr)}, |
| 878 | {.K: "usr" , .V: llvm::json::Value(nullptr)}, |
| 879 | {.K: "id" , .V: llvm::json::Value(nullptr)}}; |
| 880 | |
| 881 | if (!P.name.empty()) |
| 882 | Result["name" ] = P.name; |
| 883 | |
| 884 | if (!P.containerName.empty()) |
| 885 | Result["containerName" ] = P.containerName; |
| 886 | |
| 887 | if (!P.USR.empty()) |
| 888 | Result["usr" ] = P.USR; |
| 889 | |
| 890 | if (P.ID) |
| 891 | Result["id" ] = P.ID.str(); |
| 892 | |
| 893 | if (P.declarationRange) |
| 894 | Result["declarationRange" ] = *P.declarationRange; |
| 895 | |
| 896 | if (P.definitionRange) |
| 897 | Result["definitionRange" ] = *P.definitionRange; |
| 898 | |
| 899 | // FIXME: workaround for older gcc/clang |
| 900 | return std::move(Result); |
| 901 | } |
| 902 | |
| 903 | llvm::raw_ostream &operator<<(llvm::raw_ostream &O, const SymbolDetails &S) { |
| 904 | if (!S.containerName.empty()) { |
| 905 | O << S.containerName; |
| 906 | llvm::StringRef ContNameRef; |
| 907 | if (!ContNameRef.ends_with(Suffix: "::" )) { |
| 908 | O << " " ; |
| 909 | } |
| 910 | } |
| 911 | O << S.name << " - " << toJSON(P: S); |
| 912 | return O; |
| 913 | } |
| 914 | |
| 915 | bool fromJSON(const llvm::json::Value &Params, WorkspaceSymbolParams &R, |
| 916 | llvm::json::Path P) { |
| 917 | llvm::json::ObjectMapper O(Params, P); |
| 918 | return O && O.map(Prop: "query" , Out&: R.query) && |
| 919 | mapOptOrNull(Params, Prop: "limit" , Out&: R.limit, P); |
| 920 | } |
| 921 | |
| 922 | llvm::json::Value toJSON(const Command &C) { |
| 923 | auto Cmd = llvm::json::Object{{.K: "title" , .V: C.title}, {.K: "command" , .V: C.command}}; |
| 924 | if (!C.argument.getAsNull()) |
| 925 | Cmd["arguments" ] = llvm::json::Array{C.argument}; |
| 926 | return std::move(Cmd); |
| 927 | } |
| 928 | |
| 929 | const llvm::StringLiteral CodeAction::QUICKFIX_KIND = "quickfix" ; |
| 930 | const llvm::StringLiteral CodeAction::REFACTOR_KIND = "refactor" ; |
| 931 | const llvm::StringLiteral CodeAction::INFO_KIND = "info" ; |
| 932 | |
| 933 | llvm::json::Value toJSON(const CodeAction &CA) { |
| 934 | auto CodeAction = llvm::json::Object{{.K: "title" , .V: CA.title}}; |
| 935 | if (CA.kind) |
| 936 | CodeAction["kind" ] = *CA.kind; |
| 937 | if (CA.diagnostics) |
| 938 | CodeAction["diagnostics" ] = llvm::json::Array(*CA.diagnostics); |
| 939 | if (CA.isPreferred) |
| 940 | CodeAction["isPreferred" ] = true; |
| 941 | if (CA.edit) |
| 942 | CodeAction["edit" ] = *CA.edit; |
| 943 | if (CA.command) |
| 944 | CodeAction["command" ] = *CA.command; |
| 945 | return std::move(CodeAction); |
| 946 | } |
| 947 | |
| 948 | llvm::raw_ostream &operator<<(llvm::raw_ostream &O, const DocumentSymbol &S) { |
| 949 | return O << S.name << " - " << toJSON(S); |
| 950 | } |
| 951 | |
| 952 | llvm::json::Value toJSON(const DocumentSymbol &S) { |
| 953 | llvm::json::Object Result{{.K: "name" , .V: S.name}, |
| 954 | {.K: "kind" , .V: static_cast<int>(S.kind)}, |
| 955 | {.K: "range" , .V: S.range}, |
| 956 | {.K: "selectionRange" , .V: S.selectionRange}}; |
| 957 | |
| 958 | if (!S.detail.empty()) |
| 959 | Result["detail" ] = S.detail; |
| 960 | if (!S.children.empty()) |
| 961 | Result["children" ] = S.children; |
| 962 | if (S.deprecated) |
| 963 | Result["deprecated" ] = true; |
| 964 | // FIXME: workaround for older gcc/clang |
| 965 | return std::move(Result); |
| 966 | } |
| 967 | |
| 968 | llvm::json::Value toJSON(const WorkspaceEdit &WE) { |
| 969 | llvm::json::Object Result; |
| 970 | if (WE.changes) { |
| 971 | llvm::json::Object FileChanges; |
| 972 | for (auto &Change : *WE.changes) |
| 973 | FileChanges[Change.first] = llvm::json::Array(Change.second); |
| 974 | Result["changes" ] = std::move(FileChanges); |
| 975 | } |
| 976 | if (WE.documentChanges) |
| 977 | Result["documentChanges" ] = *WE.documentChanges; |
| 978 | if (!WE.changeAnnotations.empty()) { |
| 979 | llvm::json::Object ChangeAnnotations; |
| 980 | for (auto &Annotation : WE.changeAnnotations) |
| 981 | ChangeAnnotations[Annotation.first] = Annotation.second; |
| 982 | Result["changeAnnotations" ] = std::move(ChangeAnnotations); |
| 983 | } |
| 984 | return Result; |
| 985 | } |
| 986 | |
| 987 | bool fromJSON(const llvm::json::Value &Params, TweakArgs &A, |
| 988 | llvm::json::Path P) { |
| 989 | llvm::json::ObjectMapper O(Params, P); |
| 990 | return O && O.map(Prop: "file" , Out&: A.file) && O.map(Prop: "selection" , Out&: A.selection) && |
| 991 | O.map(Prop: "tweakID" , Out&: A.tweakID); |
| 992 | } |
| 993 | |
| 994 | llvm::json::Value toJSON(const TweakArgs &A) { |
| 995 | return llvm::json::Object{ |
| 996 | {.K: "tweakID" , .V: A.tweakID}, {.K: "selection" , .V: A.selection}, {.K: "file" , .V: A.file}}; |
| 997 | } |
| 998 | |
| 999 | llvm::json::Value toJSON(const ApplyWorkspaceEditParams &Params) { |
| 1000 | return llvm::json::Object{{.K: "edit" , .V: Params.edit}}; |
| 1001 | } |
| 1002 | |
| 1003 | bool fromJSON(const llvm::json::Value &Response, ApplyWorkspaceEditResponse &R, |
| 1004 | llvm::json::Path P) { |
| 1005 | llvm::json::ObjectMapper O(Response, P); |
| 1006 | return O && O.map(Prop: "applied" , Out&: R.applied) && |
| 1007 | O.map(Prop: "failureReason" , Out&: R.failureReason); |
| 1008 | } |
| 1009 | |
| 1010 | bool fromJSON(const llvm::json::Value &Params, TextDocumentPositionParams &R, |
| 1011 | llvm::json::Path P) { |
| 1012 | llvm::json::ObjectMapper O(Params, P); |
| 1013 | return O && O.map(Prop: "textDocument" , Out&: R.textDocument) && |
| 1014 | O.map(Prop: "position" , Out&: R.position); |
| 1015 | } |
| 1016 | |
| 1017 | bool fromJSON(const llvm::json::Value &Params, CompletionContext &R, |
| 1018 | llvm::json::Path P) { |
| 1019 | llvm::json::ObjectMapper O(Params, P); |
| 1020 | int TriggerKind; |
| 1021 | if (!O || !O.map(Prop: "triggerKind" , Out&: TriggerKind) || |
| 1022 | !mapOptOrNull(Params, Prop: "triggerCharacter" , Out&: R.triggerCharacter, P)) |
| 1023 | return false; |
| 1024 | R.triggerKind = static_cast<CompletionTriggerKind>(TriggerKind); |
| 1025 | return true; |
| 1026 | } |
| 1027 | |
| 1028 | bool fromJSON(const llvm::json::Value &Params, CompletionParams &R, |
| 1029 | llvm::json::Path P) { |
| 1030 | if (!fromJSON(Params, R&: static_cast<TextDocumentPositionParams &>(R), P) || |
| 1031 | !mapOptOrNull(Params, Prop: "limit" , Out&: R.limit, P)) |
| 1032 | return false; |
| 1033 | if (auto *Context = Params.getAsObject()->get(K: "context" )) |
| 1034 | return fromJSON(Params: *Context, R&: R.context, P: P.field(Field: "context" )); |
| 1035 | return true; |
| 1036 | } |
| 1037 | |
| 1038 | static llvm::StringRef toTextKind(MarkupKind Kind) { |
| 1039 | switch (Kind) { |
| 1040 | case MarkupKind::PlainText: |
| 1041 | return "plaintext" ; |
| 1042 | case MarkupKind::Markdown: |
| 1043 | return "markdown" ; |
| 1044 | } |
| 1045 | llvm_unreachable("Invalid MarkupKind" ); |
| 1046 | } |
| 1047 | |
| 1048 | bool fromJSON(const llvm::json::Value &V, MarkupKind &K, llvm::json::Path P) { |
| 1049 | auto Str = V.getAsString(); |
| 1050 | if (!Str) { |
| 1051 | P.report(Message: "expected string" ); |
| 1052 | return false; |
| 1053 | } |
| 1054 | if (*Str == "plaintext" ) |
| 1055 | K = MarkupKind::PlainText; |
| 1056 | else if (*Str == "markdown" ) |
| 1057 | K = MarkupKind::Markdown; |
| 1058 | else { |
| 1059 | P.report(Message: "unknown markup kind" ); |
| 1060 | return false; |
| 1061 | } |
| 1062 | return true; |
| 1063 | } |
| 1064 | |
| 1065 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, MarkupKind K) { |
| 1066 | return OS << toTextKind(Kind: K); |
| 1067 | } |
| 1068 | |
| 1069 | llvm::json::Value toJSON(const MarkupContent &MC) { |
| 1070 | if (MC.value.empty()) |
| 1071 | return nullptr; |
| 1072 | |
| 1073 | return llvm::json::Object{ |
| 1074 | {.K: "kind" , .V: toTextKind(Kind: MC.kind)}, |
| 1075 | {.K: "value" , .V: MC.value}, |
| 1076 | }; |
| 1077 | } |
| 1078 | |
| 1079 | llvm::json::Value toJSON(const Hover &H) { |
| 1080 | llvm::json::Object Result{{.K: "contents" , .V: toJSON(MC: H.contents)}}; |
| 1081 | |
| 1082 | if (H.range) |
| 1083 | Result["range" ] = toJSON(P: *H.range); |
| 1084 | |
| 1085 | return std::move(Result); |
| 1086 | } |
| 1087 | |
| 1088 | bool fromJSON(const llvm::json::Value &E, CompletionItemKind &Out, |
| 1089 | llvm::json::Path P) { |
| 1090 | if (auto T = E.getAsInteger()) { |
| 1091 | if (*T < static_cast<int>(CompletionItemKind::Text) || |
| 1092 | *T > static_cast<int>(CompletionItemKind::TypeParameter)) |
| 1093 | return false; |
| 1094 | Out = static_cast<CompletionItemKind>(*T); |
| 1095 | return true; |
| 1096 | } |
| 1097 | return false; |
| 1098 | } |
| 1099 | |
| 1100 | CompletionItemKind |
| 1101 | adjustKindToCapability(CompletionItemKind Kind, |
| 1102 | CompletionItemKindBitset &SupportedCompletionItemKinds) { |
| 1103 | auto KindVal = static_cast<size_t>(Kind); |
| 1104 | if (KindVal >= CompletionItemKindMin && |
| 1105 | KindVal <= SupportedCompletionItemKinds.size() && |
| 1106 | SupportedCompletionItemKinds[KindVal]) |
| 1107 | return Kind; |
| 1108 | |
| 1109 | switch (Kind) { |
| 1110 | // Provide some fall backs for common kinds that are close enough. |
| 1111 | case CompletionItemKind::Folder: |
| 1112 | return CompletionItemKind::File; |
| 1113 | case CompletionItemKind::EnumMember: |
| 1114 | return CompletionItemKind::Enum; |
| 1115 | case CompletionItemKind::Struct: |
| 1116 | return CompletionItemKind::Class; |
| 1117 | default: |
| 1118 | return CompletionItemKind::Text; |
| 1119 | } |
| 1120 | } |
| 1121 | |
| 1122 | bool fromJSON(const llvm::json::Value &E, CompletionItemKindBitset &Out, |
| 1123 | llvm::json::Path P) { |
| 1124 | if (auto *A = E.getAsArray()) { |
| 1125 | for (size_t I = 0; I < A->size(); ++I) { |
| 1126 | CompletionItemKind KindOut; |
| 1127 | if (fromJSON(E: (*A)[I], Out&: KindOut, P: P.index(Index: I))) |
| 1128 | Out.set(position: size_t(KindOut)); |
| 1129 | } |
| 1130 | return true; |
| 1131 | } |
| 1132 | return false; |
| 1133 | } |
| 1134 | |
| 1135 | llvm::json::Value toJSON(const CompletionItemLabelDetails &CD) { |
| 1136 | llvm::json::Object Result; |
| 1137 | if (!CD.detail.empty()) |
| 1138 | Result["detail" ] = CD.detail; |
| 1139 | if (!CD.description.empty()) |
| 1140 | Result["description" ] = CD.description; |
| 1141 | return Result; |
| 1142 | } |
| 1143 | |
| 1144 | void removeCompletionLabelDetails(CompletionItem &C) { |
| 1145 | if (!C.labelDetails) |
| 1146 | return; |
| 1147 | if (!C.labelDetails->detail.empty()) |
| 1148 | C.label += C.labelDetails->detail; |
| 1149 | if (!C.labelDetails->description.empty()) |
| 1150 | C.label = C.labelDetails->description + C.label; |
| 1151 | C.labelDetails.reset(); |
| 1152 | } |
| 1153 | |
| 1154 | llvm::json::Value toJSON(const CompletionItem &CI) { |
| 1155 | assert(!CI.label.empty() && "completion item label is required" ); |
| 1156 | llvm::json::Object Result{{.K: "label" , .V: CI.label}}; |
| 1157 | if (CI.kind != CompletionItemKind::Missing) |
| 1158 | Result["kind" ] = static_cast<int>(CI.kind); |
| 1159 | if (!CI.detail.empty()) |
| 1160 | Result["detail" ] = CI.detail; |
| 1161 | if (CI.labelDetails) |
| 1162 | Result["labelDetails" ] = *CI.labelDetails; |
| 1163 | if (CI.documentation) |
| 1164 | Result["documentation" ] = CI.documentation; |
| 1165 | if (!CI.sortText.empty()) |
| 1166 | Result["sortText" ] = CI.sortText; |
| 1167 | if (!CI.filterText.empty()) |
| 1168 | Result["filterText" ] = CI.filterText; |
| 1169 | if (!CI.insertText.empty()) |
| 1170 | Result["insertText" ] = CI.insertText; |
| 1171 | if (CI.insertTextFormat != InsertTextFormat::Missing) |
| 1172 | Result["insertTextFormat" ] = static_cast<int>(CI.insertTextFormat); |
| 1173 | if (CI.textEdit) |
| 1174 | Result["textEdit" ] = *CI.textEdit; |
| 1175 | if (!CI.additionalTextEdits.empty()) |
| 1176 | Result["additionalTextEdits" ] = llvm::json::Array(CI.additionalTextEdits); |
| 1177 | if (CI.deprecated) |
| 1178 | Result["deprecated" ] = CI.deprecated; |
| 1179 | Result["score" ] = CI.score; |
| 1180 | return std::move(Result); |
| 1181 | } |
| 1182 | |
| 1183 | llvm::raw_ostream &operator<<(llvm::raw_ostream &O, const CompletionItem &I) { |
| 1184 | O << I.label << " - " << toJSON(CI: I); |
| 1185 | return O; |
| 1186 | } |
| 1187 | |
| 1188 | bool operator<(const CompletionItem &L, const CompletionItem &R) { |
| 1189 | return (L.sortText.empty() ? L.label : L.sortText) < |
| 1190 | (R.sortText.empty() ? R.label : R.sortText); |
| 1191 | } |
| 1192 | |
| 1193 | llvm::json::Value toJSON(const CompletionList &L) { |
| 1194 | return llvm::json::Object{ |
| 1195 | {.K: "isIncomplete" , .V: L.isIncomplete}, |
| 1196 | {.K: "items" , .V: llvm::json::Array(L.items)}, |
| 1197 | }; |
| 1198 | } |
| 1199 | |
| 1200 | llvm::json::Value toJSON(const ParameterInformation &PI) { |
| 1201 | assert((PI.labelOffsets || !PI.labelString.empty()) && |
| 1202 | "parameter information label is required" ); |
| 1203 | llvm::json::Object Result; |
| 1204 | if (PI.labelOffsets) |
| 1205 | Result["label" ] = |
| 1206 | llvm::json::Array({PI.labelOffsets->first, PI.labelOffsets->second}); |
| 1207 | else |
| 1208 | Result["label" ] = PI.labelString; |
| 1209 | if (!PI.documentation.empty()) |
| 1210 | Result["documentation" ] = PI.documentation; |
| 1211 | return std::move(Result); |
| 1212 | } |
| 1213 | |
| 1214 | llvm::json::Value toJSON(const SignatureInformation &SI) { |
| 1215 | assert(!SI.label.empty() && "signature information label is required" ); |
| 1216 | llvm::json::Object Result{ |
| 1217 | {.K: "label" , .V: SI.label}, |
| 1218 | {.K: "parameters" , .V: llvm::json::Array(SI.parameters)}, |
| 1219 | }; |
| 1220 | if (!SI.documentation.value.empty()) |
| 1221 | Result["documentation" ] = SI.documentation; |
| 1222 | return std::move(Result); |
| 1223 | } |
| 1224 | |
| 1225 | llvm::raw_ostream &operator<<(llvm::raw_ostream &O, |
| 1226 | const SignatureInformation &I) { |
| 1227 | O << I.label << " - " << toJSON(SI: I); |
| 1228 | return O; |
| 1229 | } |
| 1230 | |
| 1231 | llvm::json::Value toJSON(const SignatureHelp &SH) { |
| 1232 | assert(SH.activeSignature >= 0 && |
| 1233 | "Unexpected negative value for number of active signatures." ); |
| 1234 | assert(SH.activeParameter >= 0 && |
| 1235 | "Unexpected negative value for active parameter index" ); |
| 1236 | return llvm::json::Object{ |
| 1237 | {.K: "activeSignature" , .V: SH.activeSignature}, |
| 1238 | {.K: "activeParameter" , .V: SH.activeParameter}, |
| 1239 | {.K: "signatures" , .V: llvm::json::Array(SH.signatures)}, |
| 1240 | }; |
| 1241 | } |
| 1242 | |
| 1243 | bool fromJSON(const llvm::json::Value &Params, RenameParams &R, |
| 1244 | llvm::json::Path P) { |
| 1245 | llvm::json::ObjectMapper O(Params, P); |
| 1246 | return O && O.map(Prop: "textDocument" , Out&: R.textDocument) && |
| 1247 | O.map(Prop: "position" , Out&: R.position) && O.map(Prop: "newName" , Out&: R.newName); |
| 1248 | } |
| 1249 | |
| 1250 | llvm::json::Value toJSON(const RenameParams &R) { |
| 1251 | return llvm::json::Object{ |
| 1252 | {.K: "textDocument" , .V: R.textDocument}, |
| 1253 | {.K: "position" , .V: R.position}, |
| 1254 | {.K: "newName" , .V: R.newName}, |
| 1255 | }; |
| 1256 | } |
| 1257 | |
| 1258 | llvm::json::Value toJSON(const PrepareRenameResult &PRR) { |
| 1259 | if (PRR.placeholder.empty()) |
| 1260 | return toJSON(P: PRR.range); |
| 1261 | return llvm::json::Object{ |
| 1262 | {.K: "range" , .V: toJSON(P: PRR.range)}, |
| 1263 | {.K: "placeholder" , .V: PRR.placeholder}, |
| 1264 | }; |
| 1265 | } |
| 1266 | |
| 1267 | llvm::json::Value toJSON(const DocumentHighlight &DH) { |
| 1268 | return llvm::json::Object{ |
| 1269 | {.K: "range" , .V: toJSON(P: DH.range)}, |
| 1270 | {.K: "kind" , .V: static_cast<int>(DH.kind)}, |
| 1271 | }; |
| 1272 | } |
| 1273 | |
| 1274 | llvm::json::Value toJSON(const FileStatus &FStatus) { |
| 1275 | return llvm::json::Object{ |
| 1276 | {.K: "uri" , .V: FStatus.uri}, |
| 1277 | {.K: "state" , .V: FStatus.state}, |
| 1278 | }; |
| 1279 | } |
| 1280 | |
| 1281 | constexpr unsigned SemanticTokenEncodingSize = 5; |
| 1282 | static llvm::json::Value encodeTokens(llvm::ArrayRef<SemanticToken> Toks) { |
| 1283 | llvm::json::Array Result; |
| 1284 | Result.reserve(S: SemanticTokenEncodingSize * Toks.size()); |
| 1285 | for (const auto &Tok : Toks) { |
| 1286 | Result.push_back(E: Tok.deltaLine); |
| 1287 | Result.push_back(E: Tok.deltaStart); |
| 1288 | Result.push_back(E: Tok.length); |
| 1289 | Result.push_back(E: Tok.tokenType); |
| 1290 | Result.push_back(E: Tok.tokenModifiers); |
| 1291 | } |
| 1292 | assert(Result.size() == SemanticTokenEncodingSize * Toks.size()); |
| 1293 | return std::move(Result); |
| 1294 | } |
| 1295 | |
| 1296 | bool operator==(const SemanticToken &L, const SemanticToken &R) { |
| 1297 | return std::tie(args: L.deltaLine, args: L.deltaStart, args: L.length, args: L.tokenType, |
| 1298 | args: L.tokenModifiers) == std::tie(args: R.deltaLine, args: R.deltaStart, |
| 1299 | args: R.length, args: R.tokenType, |
| 1300 | args: R.tokenModifiers); |
| 1301 | } |
| 1302 | |
| 1303 | llvm::json::Value toJSON(const SemanticTokens &Tokens) { |
| 1304 | return llvm::json::Object{{.K: "resultId" , .V: Tokens.resultId}, |
| 1305 | {.K: "data" , .V: encodeTokens(Toks: Tokens.tokens)}}; |
| 1306 | } |
| 1307 | |
| 1308 | llvm::json::Value toJSON(const SemanticTokensEdit &Edit) { |
| 1309 | return llvm::json::Object{ |
| 1310 | {.K: "start" , .V: SemanticTokenEncodingSize * Edit.startToken}, |
| 1311 | {.K: "deleteCount" , .V: SemanticTokenEncodingSize * Edit.deleteTokens}, |
| 1312 | {.K: "data" , .V: encodeTokens(Toks: Edit.tokens)}}; |
| 1313 | } |
| 1314 | |
| 1315 | llvm::json::Value toJSON(const SemanticTokensOrDelta &TE) { |
| 1316 | llvm::json::Object Result{{.K: "resultId" , .V: TE.resultId}}; |
| 1317 | if (TE.edits) |
| 1318 | Result["edits" ] = *TE.edits; |
| 1319 | if (TE.tokens) |
| 1320 | Result["data" ] = encodeTokens(Toks: *TE.tokens); |
| 1321 | return std::move(Result); |
| 1322 | } |
| 1323 | |
| 1324 | bool fromJSON(const llvm::json::Value &Params, SemanticTokensParams &R, |
| 1325 | llvm::json::Path P) { |
| 1326 | llvm::json::ObjectMapper O(Params, P); |
| 1327 | return O && O.map(Prop: "textDocument" , Out&: R.textDocument); |
| 1328 | } |
| 1329 | |
| 1330 | bool fromJSON(const llvm::json::Value &Params, SemanticTokensDeltaParams &R, |
| 1331 | llvm::json::Path P) { |
| 1332 | llvm::json::ObjectMapper O(Params, P); |
| 1333 | return O && O.map(Prop: "textDocument" , Out&: R.textDocument) && |
| 1334 | O.map(Prop: "previousResultId" , Out&: R.previousResultId); |
| 1335 | } |
| 1336 | |
| 1337 | llvm::json::Value toJSON(const InactiveRegionsParams &InactiveRegions) { |
| 1338 | return llvm::json::Object{ |
| 1339 | {.K: "textDocument" , .V: InactiveRegions.TextDocument}, |
| 1340 | {.K: "regions" , .V: std::move(InactiveRegions.InactiveRegions)}}; |
| 1341 | } |
| 1342 | |
| 1343 | llvm::raw_ostream &operator<<(llvm::raw_ostream &O, |
| 1344 | const DocumentHighlight &V) { |
| 1345 | O << V.range; |
| 1346 | if (V.kind == DocumentHighlightKind::Read) |
| 1347 | O << "(r)" ; |
| 1348 | if (V.kind == DocumentHighlightKind::Write) |
| 1349 | O << "(w)" ; |
| 1350 | return O; |
| 1351 | } |
| 1352 | |
| 1353 | bool fromJSON(const llvm::json::Value &Params, |
| 1354 | DidChangeConfigurationParams &CCP, llvm::json::Path P) { |
| 1355 | llvm::json::ObjectMapper O(Params, P); |
| 1356 | return O && O.map(Prop: "settings" , Out&: CCP.settings); |
| 1357 | } |
| 1358 | |
| 1359 | bool fromJSON(const llvm::json::Value &Params, ClangdCompileCommand &CDbUpdate, |
| 1360 | llvm::json::Path P) { |
| 1361 | llvm::json::ObjectMapper O(Params, P); |
| 1362 | return O && O.map(Prop: "workingDirectory" , Out&: CDbUpdate.workingDirectory) && |
| 1363 | O.map(Prop: "compilationCommand" , Out&: CDbUpdate.compilationCommand); |
| 1364 | } |
| 1365 | |
| 1366 | bool fromJSON(const llvm::json::Value &Params, ConfigurationSettings &S, |
| 1367 | llvm::json::Path P) { |
| 1368 | llvm::json::ObjectMapper O(Params, P); |
| 1369 | if (!O) |
| 1370 | return true; // 'any' type in LSP. |
| 1371 | return mapOptOrNull(Params, Prop: "compilationDatabaseChanges" , |
| 1372 | Out&: S.compilationDatabaseChanges, P); |
| 1373 | } |
| 1374 | |
| 1375 | bool fromJSON(const llvm::json::Value &Params, InitializationOptions &Opts, |
| 1376 | llvm::json::Path P) { |
| 1377 | llvm::json::ObjectMapper O(Params, P); |
| 1378 | if (!O) |
| 1379 | return true; // 'any' type in LSP. |
| 1380 | |
| 1381 | return fromJSON(Params, S&: Opts.ConfigSettings, P) && |
| 1382 | O.map(Prop: "compilationDatabasePath" , Out&: Opts.compilationDatabasePath) && |
| 1383 | mapOptOrNull(Params, Prop: "fallbackFlags" , Out&: Opts.fallbackFlags, P) && |
| 1384 | mapOptOrNull(Params, Prop: "clangdFileStatus" , Out&: Opts.FileStatus, P); |
| 1385 | } |
| 1386 | |
| 1387 | bool fromJSON(const llvm::json::Value &E, TypeHierarchyDirection &Out, |
| 1388 | llvm::json::Path P) { |
| 1389 | auto T = E.getAsInteger(); |
| 1390 | if (!T) |
| 1391 | return false; |
| 1392 | if (*T < static_cast<int>(TypeHierarchyDirection::Children) || |
| 1393 | *T > static_cast<int>(TypeHierarchyDirection::Both)) |
| 1394 | return false; |
| 1395 | Out = static_cast<TypeHierarchyDirection>(*T); |
| 1396 | return true; |
| 1397 | } |
| 1398 | |
| 1399 | bool fromJSON(const llvm::json::Value &Params, TypeHierarchyPrepareParams &R, |
| 1400 | llvm::json::Path P) { |
| 1401 | llvm::json::ObjectMapper O(Params, P); |
| 1402 | return O && O.map(Prop: "textDocument" , Out&: R.textDocument) && |
| 1403 | O.map(Prop: "position" , Out&: R.position) && |
| 1404 | mapOptOrNull(Params, Prop: "resolve" , Out&: R.resolve, P) && |
| 1405 | mapOptOrNull(Params, Prop: "direction" , Out&: R.direction, P); |
| 1406 | } |
| 1407 | |
| 1408 | llvm::raw_ostream &operator<<(llvm::raw_ostream &O, |
| 1409 | const TypeHierarchyItem &I) { |
| 1410 | return O << I.name << " - " << toJSON(I); |
| 1411 | } |
| 1412 | |
| 1413 | llvm::json::Value toJSON(const TypeHierarchyItem::ResolveParams &RP) { |
| 1414 | llvm::json::Object Result{{.K: "symbolID" , .V: RP.symbolID}}; |
| 1415 | if (RP.parents) |
| 1416 | Result["parents" ] = RP.parents; |
| 1417 | return std::move(Result); |
| 1418 | } |
| 1419 | bool fromJSON(const llvm::json::Value &Params, |
| 1420 | TypeHierarchyItem::ResolveParams &RP, llvm::json::Path P) { |
| 1421 | llvm::json::ObjectMapper O(Params, P); |
| 1422 | return O && O.map(Prop: "symbolID" , Out&: RP.symbolID) && |
| 1423 | mapOptOrNull(Params, Prop: "parents" , Out&: RP.parents, P); |
| 1424 | } |
| 1425 | |
| 1426 | llvm::json::Value toJSON(const TypeHierarchyItem &I) { |
| 1427 | llvm::json::Object Result{ |
| 1428 | {.K: "name" , .V: I.name}, {.K: "kind" , .V: static_cast<int>(I.kind)}, |
| 1429 | {.K: "range" , .V: I.range}, {.K: "selectionRange" , .V: I.selectionRange}, |
| 1430 | {.K: "uri" , .V: I.uri}, {.K: "data" , .V: I.data}, |
| 1431 | }; |
| 1432 | |
| 1433 | if (I.detail) |
| 1434 | Result["detail" ] = I.detail; |
| 1435 | return std::move(Result); |
| 1436 | } |
| 1437 | |
| 1438 | bool fromJSON(const llvm::json::Value &Params, TypeHierarchyItem &I, |
| 1439 | llvm::json::Path P) { |
| 1440 | llvm::json::ObjectMapper O(Params, P); |
| 1441 | |
| 1442 | // Required fields. |
| 1443 | return O && O.map(Prop: "name" , Out&: I.name) && O.map(Prop: "kind" , Out&: I.kind) && |
| 1444 | O.map(Prop: "uri" , Out&: I.uri) && O.map(Prop: "range" , Out&: I.range) && |
| 1445 | O.map(Prop: "selectionRange" , Out&: I.selectionRange) && |
| 1446 | mapOptOrNull(Params, Prop: "detail" , Out&: I.detail, P) && |
| 1447 | mapOptOrNull(Params, Prop: "deprecated" , Out&: I.deprecated, P) && |
| 1448 | mapOptOrNull(Params, Prop: "parents" , Out&: I.parents, P) && |
| 1449 | mapOptOrNull(Params, Prop: "children" , Out&: I.children, P) && |
| 1450 | mapOptOrNull(Params, Prop: "data" , Out&: I.data, P); |
| 1451 | } |
| 1452 | |
| 1453 | bool fromJSON(const llvm::json::Value &Params, |
| 1454 | ResolveTypeHierarchyItemParams &R, llvm::json::Path P) { |
| 1455 | llvm::json::ObjectMapper O(Params, P); |
| 1456 | return O && O.map(Prop: "item" , Out&: R.item) && |
| 1457 | mapOptOrNull(Params, Prop: "resolve" , Out&: R.resolve, P) && |
| 1458 | mapOptOrNull(Params, Prop: "direction" , Out&: R.direction, P); |
| 1459 | } |
| 1460 | |
| 1461 | bool fromJSON(const llvm::json::Value &Params, ReferenceContext &R, |
| 1462 | llvm::json::Path P) { |
| 1463 | llvm::json::ObjectMapper O(Params, P); |
| 1464 | return O && O.mapOptional(Prop: "includeDeclaration" , Out&: R.includeDeclaration); |
| 1465 | } |
| 1466 | |
| 1467 | bool fromJSON(const llvm::json::Value &Params, ReferenceParams &R, |
| 1468 | llvm::json::Path P) { |
| 1469 | TextDocumentPositionParams &Base = R; |
| 1470 | llvm::json::ObjectMapper O(Params, P); |
| 1471 | return fromJSON(Params, R&: Base, P) && O && O.mapOptional(Prop: "context" , Out&: R.context); |
| 1472 | } |
| 1473 | |
| 1474 | llvm::json::Value toJSON(SymbolTag Tag) { |
| 1475 | return llvm::json::Value(static_cast<int>(Tag)); |
| 1476 | } |
| 1477 | |
| 1478 | llvm::json::Value toJSON(const CallHierarchyItem &I) { |
| 1479 | llvm::json::Object Result{{.K: "name" , .V: I.name}, |
| 1480 | {.K: "kind" , .V: static_cast<int>(I.kind)}, |
| 1481 | {.K: "range" , .V: I.range}, |
| 1482 | {.K: "selectionRange" , .V: I.selectionRange}, |
| 1483 | {.K: "uri" , .V: I.uri}}; |
| 1484 | if (!I.tags.empty()) |
| 1485 | Result["tags" ] = I.tags; |
| 1486 | if (!I.detail.empty()) |
| 1487 | Result["detail" ] = I.detail; |
| 1488 | if (!I.data.empty()) |
| 1489 | Result["data" ] = I.data; |
| 1490 | return std::move(Result); |
| 1491 | } |
| 1492 | |
| 1493 | bool fromJSON(const llvm::json::Value &Params, CallHierarchyItem &I, |
| 1494 | llvm::json::Path P) { |
| 1495 | llvm::json::ObjectMapper O(Params, P); |
| 1496 | |
| 1497 | // Populate the required fields only. We don't care about the |
| 1498 | // optional fields `Tags` and `Detail` for the purpose of |
| 1499 | // client --> server communication. |
| 1500 | return O && O.map(Prop: "name" , Out&: I.name) && O.map(Prop: "kind" , Out&: I.kind) && |
| 1501 | O.map(Prop: "uri" , Out&: I.uri) && O.map(Prop: "range" , Out&: I.range) && |
| 1502 | O.map(Prop: "selectionRange" , Out&: I.selectionRange) && |
| 1503 | mapOptOrNull(Params, Prop: "data" , Out&: I.data, P); |
| 1504 | } |
| 1505 | |
| 1506 | bool fromJSON(const llvm::json::Value &Params, |
| 1507 | CallHierarchyIncomingCallsParams &C, llvm::json::Path P) { |
| 1508 | llvm::json::ObjectMapper O(Params, P); |
| 1509 | return O.map(Prop: "item" , Out&: C.item); |
| 1510 | } |
| 1511 | |
| 1512 | llvm::json::Value toJSON(const CallHierarchyIncomingCall &C) { |
| 1513 | return llvm::json::Object{{.K: "from" , .V: C.from}, {.K: "fromRanges" , .V: C.fromRanges}}; |
| 1514 | } |
| 1515 | |
| 1516 | bool fromJSON(const llvm::json::Value &Params, |
| 1517 | CallHierarchyOutgoingCallsParams &C, llvm::json::Path P) { |
| 1518 | llvm::json::ObjectMapper O(Params, P); |
| 1519 | return O.map(Prop: "item" , Out&: C.item); |
| 1520 | } |
| 1521 | |
| 1522 | llvm::json::Value toJSON(const CallHierarchyOutgoingCall &C) { |
| 1523 | return llvm::json::Object{{.K: "to" , .V: C.to}, {.K: "fromRanges" , .V: C.fromRanges}}; |
| 1524 | } |
| 1525 | |
| 1526 | bool fromJSON(const llvm::json::Value &Params, InlayHintsParams &R, |
| 1527 | llvm::json::Path P) { |
| 1528 | llvm::json::ObjectMapper O(Params, P); |
| 1529 | return O && O.map(Prop: "textDocument" , Out&: R.textDocument) && O.map(Prop: "range" , Out&: R.range); |
| 1530 | } |
| 1531 | |
| 1532 | llvm::json::Value toJSON(const InlayHintKind &Kind) { |
| 1533 | switch (Kind) { |
| 1534 | case InlayHintKind::Type: |
| 1535 | return 1; |
| 1536 | case InlayHintKind::Parameter: |
| 1537 | return 2; |
| 1538 | case InlayHintKind::Designator: |
| 1539 | case InlayHintKind::BlockEnd: |
| 1540 | case InlayHintKind::DefaultArgument: |
| 1541 | // This is an extension, don't serialize. |
| 1542 | return nullptr; |
| 1543 | } |
| 1544 | llvm_unreachable("Unknown clang.clangd.InlayHintKind" ); |
| 1545 | } |
| 1546 | |
| 1547 | llvm::json::Value toJSON(const InlayHint &H) { |
| 1548 | llvm::json::Object Result{{.K: "position" , .V: H.position}, |
| 1549 | {.K: "label" , .V: H.label}, |
| 1550 | {.K: "paddingLeft" , .V: H.paddingLeft}, |
| 1551 | {.K: "paddingRight" , .V: H.paddingRight}}; |
| 1552 | auto K = toJSON(Kind: H.kind); |
| 1553 | if (!K.getAsNull()) |
| 1554 | Result["kind" ] = std::move(K); |
| 1555 | return std::move(Result); |
| 1556 | } |
| 1557 | bool operator==(const InlayHint &A, const InlayHint &B) { |
| 1558 | return std::tie(args: A.position, args: A.range, args: A.kind, args: A.label) == |
| 1559 | std::tie(args: B.position, args: B.range, args: B.kind, args: B.label); |
| 1560 | } |
| 1561 | bool operator<(const InlayHint &A, const InlayHint &B) { |
| 1562 | return std::tie(args: A.position, args: A.range, args: A.kind, args: A.label) < |
| 1563 | std::tie(args: B.position, args: B.range, args: B.kind, args: B.label); |
| 1564 | } |
| 1565 | std::string InlayHint::joinLabels() const { |
| 1566 | return llvm::join(R: llvm::map_range(C: label, F: [](auto &L) { return L.value; }), |
| 1567 | Separator: "" ); |
| 1568 | } |
| 1569 | |
| 1570 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, InlayHintKind Kind) { |
| 1571 | auto ToString = [](InlayHintKind K) { |
| 1572 | switch (K) { |
| 1573 | case InlayHintKind::Parameter: |
| 1574 | return "parameter" ; |
| 1575 | case InlayHintKind::Type: |
| 1576 | return "type" ; |
| 1577 | case InlayHintKind::Designator: |
| 1578 | return "designator" ; |
| 1579 | case InlayHintKind::BlockEnd: |
| 1580 | return "block-end" ; |
| 1581 | case InlayHintKind::DefaultArgument: |
| 1582 | return "default-argument" ; |
| 1583 | } |
| 1584 | llvm_unreachable("Unknown clang.clangd.InlayHintKind" ); |
| 1585 | }; |
| 1586 | return OS << ToString(Kind); |
| 1587 | } |
| 1588 | |
| 1589 | llvm::json::Value toJSON(const InlayHintLabelPart &L) { |
| 1590 | llvm::json::Object Result{{.K: "value" , .V: L.value}}; |
| 1591 | if (L.tooltip) |
| 1592 | Result["tooltip" ] = *L.tooltip; |
| 1593 | if (L.location) |
| 1594 | Result["location" ] = *L.location; |
| 1595 | if (L.command) |
| 1596 | Result["command" ] = *L.command; |
| 1597 | return Result; |
| 1598 | } |
| 1599 | |
| 1600 | bool operator==(const InlayHintLabelPart &LHS, const InlayHintLabelPart &RHS) { |
| 1601 | return std::tie(args: LHS.value, args: LHS.location) == std::tie(args: RHS.value, args: RHS.location); |
| 1602 | } |
| 1603 | |
| 1604 | bool operator<(const InlayHintLabelPart &LHS, const InlayHintLabelPart &RHS) { |
| 1605 | return std::tie(args: LHS.value, args: LHS.location) < std::tie(args: RHS.value, args: RHS.location); |
| 1606 | } |
| 1607 | |
| 1608 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, |
| 1609 | const InlayHintLabelPart &L) { |
| 1610 | OS << L.value; |
| 1611 | if (L.location) |
| 1612 | OS << " (" << L.location << ")" ; |
| 1613 | return OS; |
| 1614 | } |
| 1615 | |
| 1616 | static const char *toString(OffsetEncoding OE) { |
| 1617 | switch (OE) { |
| 1618 | case OffsetEncoding::UTF8: |
| 1619 | return "utf-8" ; |
| 1620 | case OffsetEncoding::UTF16: |
| 1621 | return "utf-16" ; |
| 1622 | case OffsetEncoding::UTF32: |
| 1623 | return "utf-32" ; |
| 1624 | case OffsetEncoding::UnsupportedEncoding: |
| 1625 | return "unknown" ; |
| 1626 | } |
| 1627 | llvm_unreachable("Unknown clang.clangd.OffsetEncoding" ); |
| 1628 | } |
| 1629 | llvm::json::Value toJSON(const OffsetEncoding &OE) { return toString(OE); } |
| 1630 | bool fromJSON(const llvm::json::Value &V, OffsetEncoding &OE, |
| 1631 | llvm::json::Path P) { |
| 1632 | auto Str = V.getAsString(); |
| 1633 | if (!Str) |
| 1634 | return false; |
| 1635 | OE = llvm::StringSwitch<OffsetEncoding>(*Str) |
| 1636 | .Case(S: "utf-8" , Value: OffsetEncoding::UTF8) |
| 1637 | .Case(S: "utf-16" , Value: OffsetEncoding::UTF16) |
| 1638 | .Case(S: "utf-32" , Value: OffsetEncoding::UTF32) |
| 1639 | .Default(Value: OffsetEncoding::UnsupportedEncoding); |
| 1640 | return true; |
| 1641 | } |
| 1642 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, OffsetEncoding Enc) { |
| 1643 | return OS << toString(OE: Enc); |
| 1644 | } |
| 1645 | |
| 1646 | bool fromJSON(const llvm::json::Value &Params, SelectionRangeParams &S, |
| 1647 | llvm::json::Path P) { |
| 1648 | llvm::json::ObjectMapper O(Params, P); |
| 1649 | return O && O.map(Prop: "textDocument" , Out&: S.textDocument) && |
| 1650 | O.map(Prop: "positions" , Out&: S.positions); |
| 1651 | } |
| 1652 | |
| 1653 | llvm::json::Value toJSON(const SelectionRange &Out) { |
| 1654 | if (Out.parent) { |
| 1655 | return llvm::json::Object{{.K: "range" , .V: Out.range}, |
| 1656 | {.K: "parent" , .V: toJSON(Out: *Out.parent)}}; |
| 1657 | } |
| 1658 | return llvm::json::Object{{.K: "range" , .V: Out.range}}; |
| 1659 | } |
| 1660 | |
| 1661 | bool fromJSON(const llvm::json::Value &Params, DocumentLinkParams &R, |
| 1662 | llvm::json::Path P) { |
| 1663 | llvm::json::ObjectMapper O(Params, P); |
| 1664 | return O && O.map(Prop: "textDocument" , Out&: R.textDocument); |
| 1665 | } |
| 1666 | |
| 1667 | llvm::json::Value toJSON(const DocumentLink &DocumentLink) { |
| 1668 | return llvm::json::Object{ |
| 1669 | {.K: "range" , .V: DocumentLink.range}, |
| 1670 | {.K: "target" , .V: DocumentLink.target}, |
| 1671 | }; |
| 1672 | } |
| 1673 | |
| 1674 | bool fromJSON(const llvm::json::Value &Params, FoldingRangeParams &R, |
| 1675 | llvm::json::Path P) { |
| 1676 | llvm::json::ObjectMapper O(Params, P); |
| 1677 | return O && O.map(Prop: "textDocument" , Out&: R.textDocument); |
| 1678 | } |
| 1679 | |
| 1680 | const llvm::StringLiteral FoldingRange::REGION_KIND = "region" ; |
| 1681 | const llvm::StringLiteral FoldingRange:: = "comment" ; |
| 1682 | const llvm::StringLiteral FoldingRange::IMPORT_KIND = "import" ; |
| 1683 | |
| 1684 | llvm::json::Value toJSON(const FoldingRange &Range) { |
| 1685 | llvm::json::Object Result{ |
| 1686 | {.K: "startLine" , .V: Range.startLine}, |
| 1687 | {.K: "endLine" , .V: Range.endLine}, |
| 1688 | }; |
| 1689 | if (Range.startCharacter) |
| 1690 | Result["startCharacter" ] = Range.startCharacter; |
| 1691 | if (Range.endCharacter) |
| 1692 | Result["endCharacter" ] = Range.endCharacter; |
| 1693 | if (!Range.kind.empty()) |
| 1694 | Result["kind" ] = Range.kind; |
| 1695 | return Result; |
| 1696 | } |
| 1697 | |
| 1698 | llvm::json::Value toJSON(const MemoryTree &MT) { |
| 1699 | llvm::json::Object Out; |
| 1700 | int64_t Total = MT.self(); |
| 1701 | Out["_self" ] = Total; |
| 1702 | for (const auto &Entry : MT.children()) { |
| 1703 | auto Child = toJSON(MT: Entry.getSecond()); |
| 1704 | Total += *Child.getAsObject()->getInteger(K: "_total" ); |
| 1705 | Out[Entry.first] = std::move(Child); |
| 1706 | } |
| 1707 | Out["_total" ] = Total; |
| 1708 | return Out; |
| 1709 | } |
| 1710 | |
| 1711 | bool fromJSON(const llvm::json::Value &Params, ASTParams &R, |
| 1712 | llvm::json::Path P) { |
| 1713 | llvm::json::ObjectMapper O(Params, P); |
| 1714 | return O && O.map(Prop: "textDocument" , Out&: R.textDocument) && O.map(Prop: "range" , Out&: R.range); |
| 1715 | } |
| 1716 | |
| 1717 | llvm::json::Value toJSON(const ASTNode &N) { |
| 1718 | llvm::json::Object Result{ |
| 1719 | {.K: "role" , .V: N.role}, |
| 1720 | {.K: "kind" , .V: N.kind}, |
| 1721 | }; |
| 1722 | if (!N.children.empty()) |
| 1723 | Result["children" ] = N.children; |
| 1724 | if (!N.detail.empty()) |
| 1725 | Result["detail" ] = N.detail; |
| 1726 | if (!N.arcana.empty()) |
| 1727 | Result["arcana" ] = N.arcana; |
| 1728 | if (N.range) |
| 1729 | Result["range" ] = *N.range; |
| 1730 | return Result; |
| 1731 | } |
| 1732 | |
| 1733 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const ASTNode &Root) { |
| 1734 | std::function<void(const ASTNode &, unsigned)> Print = [&](const ASTNode &N, |
| 1735 | unsigned Level) { |
| 1736 | OS.indent(NumSpaces: 2 * Level) << N.role << ": " << N.kind; |
| 1737 | if (!N.detail.empty()) |
| 1738 | OS << " - " << N.detail; |
| 1739 | OS << "\n" ; |
| 1740 | for (const ASTNode &C : N.children) |
| 1741 | Print(C, Level + 1); |
| 1742 | }; |
| 1743 | Print(Root, 0); |
| 1744 | return OS; |
| 1745 | } |
| 1746 | |
| 1747 | bool fromJSON(const llvm::json::Value &E, SymbolID &S, llvm::json::Path P) { |
| 1748 | auto Str = E.getAsString(); |
| 1749 | if (!Str) { |
| 1750 | P.report(Message: "expected a string" ); |
| 1751 | return false; |
| 1752 | } |
| 1753 | auto ID = SymbolID::fromStr(*Str); |
| 1754 | if (!ID) { |
| 1755 | elog(Fmt: "Malformed symbolid: {0}" , Vals: ID.takeError()); |
| 1756 | P.report(Message: "malformed symbolid" ); |
| 1757 | return false; |
| 1758 | } |
| 1759 | S = *ID; |
| 1760 | return true; |
| 1761 | } |
| 1762 | llvm::json::Value toJSON(const SymbolID &S) { return S.str(); } |
| 1763 | |
| 1764 | } // namespace clangd |
| 1765 | } // namespace clang |
| 1766 | |