| 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 "mlir/Tools/lsp-server-support/Protocol.h" |
| 14 | #include "mlir/Tools/lsp-server-support/Logging.h" |
| 15 | #include "llvm/ADT/Hashing.h" |
| 16 | #include "llvm/ADT/SmallString.h" |
| 17 | #include "llvm/ADT/StringExtras.h" |
| 18 | #include "llvm/ADT/StringSet.h" |
| 19 | #include "llvm/Support/ErrorHandling.h" |
| 20 | #include "llvm/Support/Format.h" |
| 21 | #include "llvm/Support/FormatVariadic.h" |
| 22 | #include "llvm/Support/JSON.h" |
| 23 | #include "llvm/Support/MemoryBuffer.h" |
| 24 | #include "llvm/Support/Path.h" |
| 25 | #include "llvm/Support/raw_ostream.h" |
| 26 | |
| 27 | using namespace mlir; |
| 28 | using namespace mlir::lsp; |
| 29 | |
| 30 | // Helper that doesn't treat `null` and absent fields as failures. |
| 31 | template <typename T> |
| 32 | static bool mapOptOrNull(const llvm::json::Value ¶ms, |
| 33 | llvm::StringLiteral prop, T &out, |
| 34 | llvm::json::Path path) { |
| 35 | const llvm::json::Object *o = params.getAsObject(); |
| 36 | assert(o); |
| 37 | |
| 38 | // Field is missing or null. |
| 39 | auto *v = o->get(K: prop); |
| 40 | if (!v || v->getAsNull()) |
| 41 | return true; |
| 42 | return fromJSON(*v, out, path.field(Field: prop)); |
| 43 | } |
| 44 | |
| 45 | //===----------------------------------------------------------------------===// |
| 46 | // LSPError |
| 47 | //===----------------------------------------------------------------------===// |
| 48 | |
| 49 | char LSPError::ID; |
| 50 | |
| 51 | //===----------------------------------------------------------------------===// |
| 52 | // URIForFile |
| 53 | //===----------------------------------------------------------------------===// |
| 54 | |
| 55 | static bool isWindowsPath(StringRef path) { |
| 56 | return path.size() > 1 && llvm::isAlpha(C: path[0]) && path[1] == ':'; |
| 57 | } |
| 58 | |
| 59 | static bool isNetworkPath(StringRef path) { |
| 60 | return path.size() > 2 && path[0] == path[1] && |
| 61 | llvm::sys::path::is_separator(value: path[0]); |
| 62 | } |
| 63 | |
| 64 | static bool shouldEscapeInURI(unsigned char c) { |
| 65 | // Unreserved characters. |
| 66 | if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || |
| 67 | (c >= '0' && c <= '9')) |
| 68 | return false; |
| 69 | |
| 70 | switch (c) { |
| 71 | case '-': |
| 72 | case '_': |
| 73 | case '.': |
| 74 | case '~': |
| 75 | // '/' is only reserved when parsing. |
| 76 | case '/': |
| 77 | // ':' is only reserved for relative URI paths, which we doesn't produce. |
| 78 | case ':': |
| 79 | return false; |
| 80 | } |
| 81 | return true; |
| 82 | } |
| 83 | |
| 84 | /// Encodes a string according to percent-encoding. |
| 85 | /// - Unreserved characters are not escaped. |
| 86 | /// - Reserved characters always escaped with exceptions like '/'. |
| 87 | /// - All other characters are escaped. |
| 88 | static void percentEncode(StringRef content, std::string &out) { |
| 89 | for (unsigned char c : content) { |
| 90 | if (shouldEscapeInURI(c)) { |
| 91 | out.push_back(c: '%'); |
| 92 | out.push_back(c: llvm::hexdigit(X: c / 16)); |
| 93 | out.push_back(c: llvm::hexdigit(X: c % 16)); |
| 94 | } else { |
| 95 | out.push_back(c: c); |
| 96 | } |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | /// Decodes a string according to percent-encoding. |
| 101 | static std::string percentDecode(StringRef content) { |
| 102 | std::string result; |
| 103 | for (auto i = content.begin(), e = content.end(); i != e; ++i) { |
| 104 | if (*i != '%') { |
| 105 | result += *i; |
| 106 | continue; |
| 107 | } |
| 108 | if (*i == '%' && i + 2 < content.end() && llvm::isHexDigit(C: *(i + 1)) && |
| 109 | llvm::isHexDigit(C: *(i + 2))) { |
| 110 | result.push_back(c: llvm::hexFromNibbles(MSB: *(i + 1), LSB: *(i + 2))); |
| 111 | i += 2; |
| 112 | } else { |
| 113 | result.push_back(c: *i); |
| 114 | } |
| 115 | } |
| 116 | return result; |
| 117 | } |
| 118 | |
| 119 | /// Return the set containing the supported URI schemes. |
| 120 | static StringSet<> &getSupportedSchemes() { |
| 121 | static StringSet<> schemes({"file" , "test" }); |
| 122 | return schemes; |
| 123 | } |
| 124 | |
| 125 | /// Returns true if the given scheme is structurally valid, i.e. it does not |
| 126 | /// contain any invalid scheme characters. This does not check that the scheme |
| 127 | /// is actually supported. |
| 128 | static bool isStructurallyValidScheme(StringRef scheme) { |
| 129 | if (scheme.empty()) |
| 130 | return false; |
| 131 | if (!llvm::isAlpha(C: scheme[0])) |
| 132 | return false; |
| 133 | return llvm::all_of(Range: llvm::drop_begin(RangeOrContainer&: scheme), P: [](char c) { |
| 134 | return llvm::isAlnum(C: c) || c == '+' || c == '.' || c == '-'; |
| 135 | }); |
| 136 | } |
| 137 | |
| 138 | static llvm::Expected<std::string> uriFromAbsolutePath(StringRef absolutePath, |
| 139 | StringRef scheme) { |
| 140 | std::string body; |
| 141 | StringRef authority; |
| 142 | StringRef root = llvm::sys::path::root_name(path: absolutePath); |
| 143 | if (isNetworkPath(path: root)) { |
| 144 | // Windows UNC paths e.g. \\server\share => file://server/share |
| 145 | authority = root.drop_front(N: 2); |
| 146 | absolutePath.consume_front(Prefix: root); |
| 147 | } else if (isWindowsPath(path: root)) { |
| 148 | // Windows paths e.g. X:\path => file:///X:/path |
| 149 | body = "/" ; |
| 150 | } |
| 151 | body += llvm::sys::path::convert_to_slash(path: absolutePath); |
| 152 | |
| 153 | std::string uri = scheme.str() + ":" ; |
| 154 | if (authority.empty() && body.empty()) |
| 155 | return uri; |
| 156 | |
| 157 | // If authority if empty, we only print body if it starts with "/"; otherwise, |
| 158 | // the URI is invalid. |
| 159 | if (!authority.empty() || StringRef(body).starts_with(Prefix: "/" )) { |
| 160 | uri.append(s: "//" ); |
| 161 | percentEncode(content: authority, out&: uri); |
| 162 | } |
| 163 | percentEncode(content: body, out&: uri); |
| 164 | return uri; |
| 165 | } |
| 166 | |
| 167 | static llvm::Expected<std::string> getAbsolutePath(StringRef authority, |
| 168 | StringRef body) { |
| 169 | if (!body.starts_with(Prefix: "/" )) |
| 170 | return llvm::createStringError( |
| 171 | EC: llvm::inconvertibleErrorCode(), |
| 172 | S: "File scheme: expect body to be an absolute path starting " |
| 173 | "with '/': " + |
| 174 | body); |
| 175 | SmallString<128> path; |
| 176 | if (!authority.empty()) { |
| 177 | // Windows UNC paths e.g. file://server/share => \\server\share |
| 178 | ("//" + authority).toVector(Out&: path); |
| 179 | } else if (isWindowsPath(path: body.substr(Start: 1))) { |
| 180 | // Windows paths e.g. file:///X:/path => X:\path |
| 181 | body.consume_front(Prefix: "/" ); |
| 182 | } |
| 183 | path.append(RHS: body); |
| 184 | llvm::sys::path::native(path); |
| 185 | return std::string(path); |
| 186 | } |
| 187 | |
| 188 | static llvm::Expected<std::string> parseFilePathFromURI(StringRef origUri) { |
| 189 | StringRef uri = origUri; |
| 190 | |
| 191 | // Decode the scheme of the URI. |
| 192 | size_t pos = uri.find(C: ':'); |
| 193 | if (pos == StringRef::npos) |
| 194 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
| 195 | S: "Scheme must be provided in URI: " + |
| 196 | origUri); |
| 197 | StringRef schemeStr = uri.substr(Start: 0, N: pos); |
| 198 | std::string uriScheme = percentDecode(content: schemeStr); |
| 199 | if (!isStructurallyValidScheme(scheme: uriScheme)) |
| 200 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
| 201 | S: "Invalid scheme: " + schemeStr + |
| 202 | " (decoded: " + uriScheme + ")" ); |
| 203 | uri = uri.substr(Start: pos + 1); |
| 204 | |
| 205 | // Decode the authority of the URI. |
| 206 | std::string uriAuthority; |
| 207 | if (uri.consume_front(Prefix: "//" )) { |
| 208 | pos = uri.find(C: '/'); |
| 209 | uriAuthority = percentDecode(content: uri.substr(Start: 0, N: pos)); |
| 210 | uri = uri.substr(Start: pos); |
| 211 | } |
| 212 | |
| 213 | // Decode the body of the URI. |
| 214 | std::string uriBody = percentDecode(content: uri); |
| 215 | |
| 216 | // Compute the absolute path for this uri. |
| 217 | if (!getSupportedSchemes().contains(key: uriScheme)) { |
| 218 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
| 219 | S: "unsupported URI scheme `" + uriScheme + |
| 220 | "' for workspace files" ); |
| 221 | } |
| 222 | return getAbsolutePath(authority: uriAuthority, body: uriBody); |
| 223 | } |
| 224 | |
| 225 | llvm::Expected<URIForFile> URIForFile::fromURI(StringRef uri) { |
| 226 | llvm::Expected<std::string> filePath = parseFilePathFromURI(origUri: uri); |
| 227 | if (!filePath) |
| 228 | return filePath.takeError(); |
| 229 | return URIForFile(std::move(*filePath), uri.str()); |
| 230 | } |
| 231 | |
| 232 | llvm::Expected<URIForFile> URIForFile::fromFile(StringRef absoluteFilepath, |
| 233 | StringRef scheme) { |
| 234 | llvm::Expected<std::string> uri = |
| 235 | uriFromAbsolutePath(absolutePath: absoluteFilepath, scheme); |
| 236 | if (!uri) |
| 237 | return uri.takeError(); |
| 238 | return fromURI(uri: *uri); |
| 239 | } |
| 240 | |
| 241 | StringRef URIForFile::scheme() const { return uri().split(Separator: ':').first; } |
| 242 | |
| 243 | void URIForFile::registerSupportedScheme(StringRef scheme) { |
| 244 | getSupportedSchemes().insert(key: scheme); |
| 245 | } |
| 246 | |
| 247 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, URIForFile &result, |
| 248 | llvm::json::Path path) { |
| 249 | if (std::optional<StringRef> str = value.getAsString()) { |
| 250 | llvm::Expected<URIForFile> expectedURI = URIForFile::fromURI(uri: *str); |
| 251 | if (!expectedURI) { |
| 252 | path.report(Message: "unresolvable URI" ); |
| 253 | consumeError(Err: expectedURI.takeError()); |
| 254 | return false; |
| 255 | } |
| 256 | result = std::move(*expectedURI); |
| 257 | return true; |
| 258 | } |
| 259 | return false; |
| 260 | } |
| 261 | |
| 262 | llvm::json::Value mlir::lsp::toJSON(const URIForFile &value) { |
| 263 | return value.uri(); |
| 264 | } |
| 265 | |
| 266 | raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const URIForFile &value) { |
| 267 | return os << value.uri(); |
| 268 | } |
| 269 | |
| 270 | //===----------------------------------------------------------------------===// |
| 271 | // ClientCapabilities |
| 272 | //===----------------------------------------------------------------------===// |
| 273 | |
| 274 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 275 | ClientCapabilities &result, llvm::json::Path path) { |
| 276 | const llvm::json::Object *o = value.getAsObject(); |
| 277 | if (!o) { |
| 278 | path.report(Message: "expected object" ); |
| 279 | return false; |
| 280 | } |
| 281 | if (const llvm::json::Object *textDocument = o->getObject(K: "textDocument" )) { |
| 282 | if (const llvm::json::Object *documentSymbol = |
| 283 | textDocument->getObject(K: "documentSymbol" )) { |
| 284 | if (std::optional<bool> hierarchicalSupport = |
| 285 | documentSymbol->getBoolean(K: "hierarchicalDocumentSymbolSupport" )) |
| 286 | result.hierarchicalDocumentSymbol = *hierarchicalSupport; |
| 287 | } |
| 288 | if (auto *codeAction = textDocument->getObject(K: "codeAction" )) { |
| 289 | if (codeAction->getObject(K: "codeActionLiteralSupport" )) |
| 290 | result.codeActionStructure = true; |
| 291 | } |
| 292 | if (auto *window = textDocument->getObject(K: "window" )) { |
| 293 | if (std::optional<bool> workDoneProgressSupport = |
| 294 | window->getBoolean(K: "workDoneProgress" )) |
| 295 | result.workDoneProgress = *workDoneProgressSupport; |
| 296 | } |
| 297 | } |
| 298 | return true; |
| 299 | } |
| 300 | |
| 301 | //===----------------------------------------------------------------------===// |
| 302 | // ClientInfo |
| 303 | //===----------------------------------------------------------------------===// |
| 304 | |
| 305 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, ClientInfo &result, |
| 306 | llvm::json::Path path) { |
| 307 | llvm::json::ObjectMapper o(value, path); |
| 308 | if (!o || !o.map(Prop: "name" , Out&: result.name)) |
| 309 | return false; |
| 310 | |
| 311 | // Don't fail if we can't parse version. |
| 312 | o.map(Prop: "version" , Out&: result.version); |
| 313 | return true; |
| 314 | } |
| 315 | |
| 316 | //===----------------------------------------------------------------------===// |
| 317 | // InitializeParams |
| 318 | //===----------------------------------------------------------------------===// |
| 319 | |
| 320 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, TraceLevel &result, |
| 321 | llvm::json::Path path) { |
| 322 | if (std::optional<StringRef> str = value.getAsString()) { |
| 323 | if (*str == "off" ) { |
| 324 | result = TraceLevel::Off; |
| 325 | return true; |
| 326 | } |
| 327 | if (*str == "messages" ) { |
| 328 | result = TraceLevel::Messages; |
| 329 | return true; |
| 330 | } |
| 331 | if (*str == "verbose" ) { |
| 332 | result = TraceLevel::Verbose; |
| 333 | return true; |
| 334 | } |
| 335 | } |
| 336 | return false; |
| 337 | } |
| 338 | |
| 339 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 340 | InitializeParams &result, llvm::json::Path path) { |
| 341 | llvm::json::ObjectMapper o(value, path); |
| 342 | if (!o) |
| 343 | return false; |
| 344 | // We deliberately don't fail if we can't parse individual fields. |
| 345 | o.map(Prop: "capabilities" , Out&: result.capabilities); |
| 346 | o.map(Prop: "trace" , Out&: result.trace); |
| 347 | mapOptOrNull(params: value, prop: "clientInfo" , out&: result.clientInfo, path); |
| 348 | |
| 349 | return true; |
| 350 | } |
| 351 | |
| 352 | //===----------------------------------------------------------------------===// |
| 353 | // TextDocumentItem |
| 354 | //===----------------------------------------------------------------------===// |
| 355 | |
| 356 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 357 | TextDocumentItem &result, llvm::json::Path path) { |
| 358 | llvm::json::ObjectMapper o(value, path); |
| 359 | return o && o.map(Prop: "uri" , Out&: result.uri) && |
| 360 | o.map(Prop: "languageId" , Out&: result.languageId) && o.map(Prop: "text" , Out&: result.text) && |
| 361 | o.map(Prop: "version" , Out&: result.version); |
| 362 | } |
| 363 | |
| 364 | //===----------------------------------------------------------------------===// |
| 365 | // TextDocumentIdentifier |
| 366 | //===----------------------------------------------------------------------===// |
| 367 | |
| 368 | llvm::json::Value mlir::lsp::toJSON(const TextDocumentIdentifier &value) { |
| 369 | return llvm::json::Object{{.K: "uri" , .V: value.uri}}; |
| 370 | } |
| 371 | |
| 372 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 373 | TextDocumentIdentifier &result, |
| 374 | llvm::json::Path path) { |
| 375 | llvm::json::ObjectMapper o(value, path); |
| 376 | return o && o.map(Prop: "uri" , Out&: result.uri); |
| 377 | } |
| 378 | |
| 379 | //===----------------------------------------------------------------------===// |
| 380 | // VersionedTextDocumentIdentifier |
| 381 | //===----------------------------------------------------------------------===// |
| 382 | |
| 383 | llvm::json::Value |
| 384 | mlir::lsp::toJSON(const VersionedTextDocumentIdentifier &value) { |
| 385 | return llvm::json::Object{ |
| 386 | {.K: "uri" , .V: value.uri}, |
| 387 | {.K: "version" , .V: value.version}, |
| 388 | }; |
| 389 | } |
| 390 | |
| 391 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 392 | VersionedTextDocumentIdentifier &result, |
| 393 | llvm::json::Path path) { |
| 394 | llvm::json::ObjectMapper o(value, path); |
| 395 | return o && o.map(Prop: "uri" , Out&: result.uri) && o.map(Prop: "version" , Out&: result.version); |
| 396 | } |
| 397 | |
| 398 | //===----------------------------------------------------------------------===// |
| 399 | // Position |
| 400 | //===----------------------------------------------------------------------===// |
| 401 | |
| 402 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, Position &result, |
| 403 | llvm::json::Path path) { |
| 404 | llvm::json::ObjectMapper o(value, path); |
| 405 | return o && o.map(Prop: "line" , Out&: result.line) && |
| 406 | o.map(Prop: "character" , Out&: result.character); |
| 407 | } |
| 408 | |
| 409 | llvm::json::Value mlir::lsp::toJSON(const Position &value) { |
| 410 | return llvm::json::Object{ |
| 411 | {.K: "line" , .V: value.line}, |
| 412 | {.K: "character" , .V: value.character}, |
| 413 | }; |
| 414 | } |
| 415 | |
| 416 | raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Position &value) { |
| 417 | return os << value.line << ':' << value.character; |
| 418 | } |
| 419 | |
| 420 | //===----------------------------------------------------------------------===// |
| 421 | // Range |
| 422 | //===----------------------------------------------------------------------===// |
| 423 | |
| 424 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, Range &result, |
| 425 | llvm::json::Path path) { |
| 426 | llvm::json::ObjectMapper o(value, path); |
| 427 | return o && o.map(Prop: "start" , Out&: result.start) && o.map(Prop: "end" , Out&: result.end); |
| 428 | } |
| 429 | |
| 430 | llvm::json::Value mlir::lsp::toJSON(const Range &value) { |
| 431 | return llvm::json::Object{ |
| 432 | {.K: "start" , .V: value.start}, |
| 433 | {.K: "end" , .V: value.end}, |
| 434 | }; |
| 435 | } |
| 436 | |
| 437 | raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Range &value) { |
| 438 | return os << value.start << '-' << value.end; |
| 439 | } |
| 440 | |
| 441 | //===----------------------------------------------------------------------===// |
| 442 | // Location |
| 443 | //===----------------------------------------------------------------------===// |
| 444 | |
| 445 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, Location &result, |
| 446 | llvm::json::Path path) { |
| 447 | llvm::json::ObjectMapper o(value, path); |
| 448 | return o && o.map(Prop: "uri" , Out&: result.uri) && o.map(Prop: "range" , Out&: result.range); |
| 449 | } |
| 450 | |
| 451 | llvm::json::Value mlir::lsp::toJSON(const Location &value) { |
| 452 | return llvm::json::Object{ |
| 453 | {.K: "uri" , .V: value.uri}, |
| 454 | {.K: "range" , .V: value.range}, |
| 455 | }; |
| 456 | } |
| 457 | |
| 458 | raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Location &value) { |
| 459 | return os << value.range << '@' << value.uri; |
| 460 | } |
| 461 | |
| 462 | //===----------------------------------------------------------------------===// |
| 463 | // TextDocumentPositionParams |
| 464 | //===----------------------------------------------------------------------===// |
| 465 | |
| 466 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 467 | TextDocumentPositionParams &result, |
| 468 | llvm::json::Path path) { |
| 469 | llvm::json::ObjectMapper o(value, path); |
| 470 | return o && o.map(Prop: "textDocument" , Out&: result.textDocument) && |
| 471 | o.map(Prop: "position" , Out&: result.position); |
| 472 | } |
| 473 | |
| 474 | //===----------------------------------------------------------------------===// |
| 475 | // ReferenceParams |
| 476 | //===----------------------------------------------------------------------===// |
| 477 | |
| 478 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 479 | ReferenceContext &result, llvm::json::Path path) { |
| 480 | llvm::json::ObjectMapper o(value, path); |
| 481 | return o && o.mapOptional(Prop: "includeDeclaration" , Out&: result.includeDeclaration); |
| 482 | } |
| 483 | |
| 484 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 485 | ReferenceParams &result, llvm::json::Path path) { |
| 486 | TextDocumentPositionParams &base = result; |
| 487 | llvm::json::ObjectMapper o(value, path); |
| 488 | return fromJSON(value, result&: base, path) && o && |
| 489 | o.mapOptional(Prop: "context" , Out&: result.context); |
| 490 | } |
| 491 | |
| 492 | //===----------------------------------------------------------------------===// |
| 493 | // DidOpenTextDocumentParams |
| 494 | //===----------------------------------------------------------------------===// |
| 495 | |
| 496 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 497 | DidOpenTextDocumentParams &result, |
| 498 | llvm::json::Path path) { |
| 499 | llvm::json::ObjectMapper o(value, path); |
| 500 | return o && o.map(Prop: "textDocument" , Out&: result.textDocument); |
| 501 | } |
| 502 | |
| 503 | //===----------------------------------------------------------------------===// |
| 504 | // DidCloseTextDocumentParams |
| 505 | //===----------------------------------------------------------------------===// |
| 506 | |
| 507 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 508 | DidCloseTextDocumentParams &result, |
| 509 | llvm::json::Path path) { |
| 510 | llvm::json::ObjectMapper o(value, path); |
| 511 | return o && o.map(Prop: "textDocument" , Out&: result.textDocument); |
| 512 | } |
| 513 | |
| 514 | //===----------------------------------------------------------------------===// |
| 515 | // DidChangeTextDocumentParams |
| 516 | //===----------------------------------------------------------------------===// |
| 517 | |
| 518 | LogicalResult |
| 519 | TextDocumentContentChangeEvent::applyTo(std::string &contents) const { |
| 520 | // If there is no range, the full document changed. |
| 521 | if (!range) { |
| 522 | contents = text; |
| 523 | return success(); |
| 524 | } |
| 525 | |
| 526 | // Try to map the replacement range to the content. |
| 527 | llvm::SourceMgr tmpScrMgr; |
| 528 | tmpScrMgr.AddNewSourceBuffer(F: llvm::MemoryBuffer::getMemBuffer(InputData: contents), |
| 529 | IncludeLoc: SMLoc()); |
| 530 | SMRange rangeLoc = range->getAsSMRange(mgr&: tmpScrMgr); |
| 531 | if (!rangeLoc.isValid()) |
| 532 | return failure(); |
| 533 | |
| 534 | contents.replace(pos: rangeLoc.Start.getPointer() - contents.data(), |
| 535 | n: rangeLoc.End.getPointer() - rangeLoc.Start.getPointer(), |
| 536 | str: text); |
| 537 | return success(); |
| 538 | } |
| 539 | |
| 540 | LogicalResult TextDocumentContentChangeEvent::applyTo( |
| 541 | ArrayRef<TextDocumentContentChangeEvent> changes, std::string &contents) { |
| 542 | for (const auto &change : changes) |
| 543 | if (failed(Result: change.applyTo(contents))) |
| 544 | return failure(); |
| 545 | return success(); |
| 546 | } |
| 547 | |
| 548 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 549 | TextDocumentContentChangeEvent &result, |
| 550 | llvm::json::Path path) { |
| 551 | llvm::json::ObjectMapper o(value, path); |
| 552 | return o && o.map(Prop: "range" , Out&: result.range) && |
| 553 | o.map(Prop: "rangeLength" , Out&: result.rangeLength) && o.map(Prop: "text" , Out&: result.text); |
| 554 | } |
| 555 | |
| 556 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 557 | DidChangeTextDocumentParams &result, |
| 558 | llvm::json::Path path) { |
| 559 | llvm::json::ObjectMapper o(value, path); |
| 560 | return o && o.map(Prop: "textDocument" , Out&: result.textDocument) && |
| 561 | o.map(Prop: "contentChanges" , Out&: result.contentChanges); |
| 562 | } |
| 563 | |
| 564 | //===----------------------------------------------------------------------===// |
| 565 | // MarkupContent |
| 566 | //===----------------------------------------------------------------------===// |
| 567 | |
| 568 | static llvm::StringRef toTextKind(MarkupKind kind) { |
| 569 | switch (kind) { |
| 570 | case MarkupKind::PlainText: |
| 571 | return "plaintext" ; |
| 572 | case MarkupKind::Markdown: |
| 573 | return "markdown" ; |
| 574 | } |
| 575 | llvm_unreachable("Invalid MarkupKind" ); |
| 576 | } |
| 577 | |
| 578 | raw_ostream &mlir::lsp::operator<<(raw_ostream &os, MarkupKind kind) { |
| 579 | return os << toTextKind(kind); |
| 580 | } |
| 581 | |
| 582 | llvm::json::Value mlir::lsp::toJSON(const MarkupContent &mc) { |
| 583 | if (mc.value.empty()) |
| 584 | return nullptr; |
| 585 | |
| 586 | return llvm::json::Object{ |
| 587 | {.K: "kind" , .V: toTextKind(kind: mc.kind)}, |
| 588 | {.K: "value" , .V: mc.value}, |
| 589 | }; |
| 590 | } |
| 591 | |
| 592 | //===----------------------------------------------------------------------===// |
| 593 | // Hover |
| 594 | //===----------------------------------------------------------------------===// |
| 595 | |
| 596 | llvm::json::Value mlir::lsp::toJSON(const Hover &hover) { |
| 597 | llvm::json::Object result{{.K: "contents" , .V: toJSON(mc: hover.contents)}}; |
| 598 | if (hover.range) |
| 599 | result["range" ] = toJSON(value: *hover.range); |
| 600 | return std::move(result); |
| 601 | } |
| 602 | |
| 603 | //===----------------------------------------------------------------------===// |
| 604 | // DocumentSymbol |
| 605 | //===----------------------------------------------------------------------===// |
| 606 | |
| 607 | llvm::json::Value mlir::lsp::toJSON(const DocumentSymbol &symbol) { |
| 608 | llvm::json::Object result{{.K: "name" , .V: symbol.name}, |
| 609 | {.K: "kind" , .V: static_cast<int>(symbol.kind)}, |
| 610 | {.K: "range" , .V: symbol.range}, |
| 611 | {.K: "selectionRange" , .V: symbol.selectionRange}}; |
| 612 | |
| 613 | if (!symbol.detail.empty()) |
| 614 | result["detail" ] = symbol.detail; |
| 615 | if (!symbol.children.empty()) |
| 616 | result["children" ] = symbol.children; |
| 617 | return std::move(result); |
| 618 | } |
| 619 | |
| 620 | //===----------------------------------------------------------------------===// |
| 621 | // DocumentSymbolParams |
| 622 | //===----------------------------------------------------------------------===// |
| 623 | |
| 624 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 625 | DocumentSymbolParams &result, llvm::json::Path path) { |
| 626 | llvm::json::ObjectMapper o(value, path); |
| 627 | return o && o.map(Prop: "textDocument" , Out&: result.textDocument); |
| 628 | } |
| 629 | |
| 630 | //===----------------------------------------------------------------------===// |
| 631 | // DiagnosticRelatedInformation |
| 632 | //===----------------------------------------------------------------------===// |
| 633 | |
| 634 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 635 | DiagnosticRelatedInformation &result, |
| 636 | llvm::json::Path path) { |
| 637 | llvm::json::ObjectMapper o(value, path); |
| 638 | return o && o.map(Prop: "location" , Out&: result.location) && |
| 639 | o.map(Prop: "message" , Out&: result.message); |
| 640 | } |
| 641 | |
| 642 | llvm::json::Value mlir::lsp::toJSON(const DiagnosticRelatedInformation &info) { |
| 643 | return llvm::json::Object{ |
| 644 | {.K: "location" , .V: info.location}, |
| 645 | {.K: "message" , .V: info.message}, |
| 646 | }; |
| 647 | } |
| 648 | |
| 649 | //===----------------------------------------------------------------------===// |
| 650 | // Diagnostic |
| 651 | //===----------------------------------------------------------------------===// |
| 652 | |
| 653 | llvm::json::Value mlir::lsp::toJSON(DiagnosticTag tag) { |
| 654 | return static_cast<int>(tag); |
| 655 | } |
| 656 | |
| 657 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, DiagnosticTag &result, |
| 658 | llvm::json::Path path) { |
| 659 | if (std::optional<int64_t> i = value.getAsInteger()) { |
| 660 | result = (DiagnosticTag)*i; |
| 661 | return true; |
| 662 | } |
| 663 | |
| 664 | return false; |
| 665 | } |
| 666 | |
| 667 | llvm::json::Value mlir::lsp::toJSON(const Diagnostic &diag) { |
| 668 | llvm::json::Object result{ |
| 669 | {.K: "range" , .V: diag.range}, |
| 670 | {.K: "severity" , .V: (int)diag.severity}, |
| 671 | {.K: "message" , .V: diag.message}, |
| 672 | }; |
| 673 | if (diag.category) |
| 674 | result["category" ] = *diag.category; |
| 675 | if (!diag.source.empty()) |
| 676 | result["source" ] = diag.source; |
| 677 | if (diag.relatedInformation) |
| 678 | result["relatedInformation" ] = *diag.relatedInformation; |
| 679 | if (!diag.tags.empty()) |
| 680 | result["tags" ] = diag.tags; |
| 681 | return std::move(result); |
| 682 | } |
| 683 | |
| 684 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, Diagnostic &result, |
| 685 | llvm::json::Path path) { |
| 686 | llvm::json::ObjectMapper o(value, path); |
| 687 | if (!o) |
| 688 | return false; |
| 689 | int severity = 0; |
| 690 | if (!mapOptOrNull(params: value, prop: "severity" , out&: severity, path)) |
| 691 | return false; |
| 692 | result.severity = (DiagnosticSeverity)severity; |
| 693 | |
| 694 | return o.map(Prop: "range" , Out&: result.range) && o.map(Prop: "message" , Out&: result.message) && |
| 695 | mapOptOrNull(params: value, prop: "category" , out&: result.category, path) && |
| 696 | mapOptOrNull(params: value, prop: "source" , out&: result.source, path) && |
| 697 | mapOptOrNull(params: value, prop: "relatedInformation" , out&: result.relatedInformation, |
| 698 | path) && |
| 699 | mapOptOrNull(params: value, prop: "tags" , out&: result.tags, path); |
| 700 | } |
| 701 | |
| 702 | //===----------------------------------------------------------------------===// |
| 703 | // PublishDiagnosticsParams |
| 704 | //===----------------------------------------------------------------------===// |
| 705 | |
| 706 | llvm::json::Value mlir::lsp::toJSON(const PublishDiagnosticsParams ¶ms) { |
| 707 | return llvm::json::Object{ |
| 708 | {.K: "uri" , .V: params.uri}, |
| 709 | {.K: "diagnostics" , .V: params.diagnostics}, |
| 710 | {.K: "version" , .V: params.version}, |
| 711 | }; |
| 712 | } |
| 713 | |
| 714 | //===----------------------------------------------------------------------===// |
| 715 | // TextEdit |
| 716 | //===----------------------------------------------------------------------===// |
| 717 | |
| 718 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, TextEdit &result, |
| 719 | llvm::json::Path path) { |
| 720 | llvm::json::ObjectMapper o(value, path); |
| 721 | return o && o.map(Prop: "range" , Out&: result.range) && o.map(Prop: "newText" , Out&: result.newText); |
| 722 | } |
| 723 | |
| 724 | llvm::json::Value mlir::lsp::toJSON(const TextEdit &value) { |
| 725 | return llvm::json::Object{ |
| 726 | {.K: "range" , .V: value.range}, |
| 727 | {.K: "newText" , .V: value.newText}, |
| 728 | }; |
| 729 | } |
| 730 | |
| 731 | raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const TextEdit &value) { |
| 732 | os << value.range << " => \"" ; |
| 733 | llvm::printEscapedString(Name: value.newText, Out&: os); |
| 734 | return os << '"'; |
| 735 | } |
| 736 | |
| 737 | //===----------------------------------------------------------------------===// |
| 738 | // CompletionItemKind |
| 739 | //===----------------------------------------------------------------------===// |
| 740 | |
| 741 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 742 | CompletionItemKind &result, llvm::json::Path path) { |
| 743 | if (std::optional<int64_t> intValue = value.getAsInteger()) { |
| 744 | if (*intValue < static_cast<int>(CompletionItemKind::Text) || |
| 745 | *intValue > static_cast<int>(CompletionItemKind::TypeParameter)) |
| 746 | return false; |
| 747 | result = static_cast<CompletionItemKind>(*intValue); |
| 748 | return true; |
| 749 | } |
| 750 | return false; |
| 751 | } |
| 752 | |
| 753 | CompletionItemKind mlir::lsp::adjustKindToCapability( |
| 754 | CompletionItemKind kind, |
| 755 | CompletionItemKindBitset &supportedCompletionItemKinds) { |
| 756 | size_t kindVal = static_cast<size_t>(kind); |
| 757 | if (kindVal >= kCompletionItemKindMin && |
| 758 | kindVal <= supportedCompletionItemKinds.size() && |
| 759 | supportedCompletionItemKinds[kindVal]) |
| 760 | return kind; |
| 761 | |
| 762 | // Provide some fall backs for common kinds that are close enough. |
| 763 | switch (kind) { |
| 764 | case CompletionItemKind::Folder: |
| 765 | return CompletionItemKind::File; |
| 766 | case CompletionItemKind::EnumMember: |
| 767 | return CompletionItemKind::Enum; |
| 768 | case CompletionItemKind::Struct: |
| 769 | return CompletionItemKind::Class; |
| 770 | default: |
| 771 | return CompletionItemKind::Text; |
| 772 | } |
| 773 | } |
| 774 | |
| 775 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 776 | CompletionItemKindBitset &result, |
| 777 | llvm::json::Path path) { |
| 778 | if (const llvm::json::Array *arrayValue = value.getAsArray()) { |
| 779 | for (size_t i = 0, e = arrayValue->size(); i < e; ++i) { |
| 780 | CompletionItemKind kindOut; |
| 781 | if (fromJSON(value: (*arrayValue)[i], result&: kindOut, path: path.index(Index: i))) |
| 782 | result.set(position: size_t(kindOut)); |
| 783 | } |
| 784 | return true; |
| 785 | } |
| 786 | return false; |
| 787 | } |
| 788 | |
| 789 | //===----------------------------------------------------------------------===// |
| 790 | // CompletionItem |
| 791 | //===----------------------------------------------------------------------===// |
| 792 | |
| 793 | llvm::json::Value mlir::lsp::toJSON(const CompletionItem &value) { |
| 794 | assert(!value.label.empty() && "completion item label is required" ); |
| 795 | llvm::json::Object result{{.K: "label" , .V: value.label}}; |
| 796 | if (value.kind != CompletionItemKind::Missing) |
| 797 | result["kind" ] = static_cast<int>(value.kind); |
| 798 | if (!value.detail.empty()) |
| 799 | result["detail" ] = value.detail; |
| 800 | if (value.documentation) |
| 801 | result["documentation" ] = value.documentation; |
| 802 | if (!value.sortText.empty()) |
| 803 | result["sortText" ] = value.sortText; |
| 804 | if (!value.filterText.empty()) |
| 805 | result["filterText" ] = value.filterText; |
| 806 | if (!value.insertText.empty()) |
| 807 | result["insertText" ] = value.insertText; |
| 808 | if (value.insertTextFormat != InsertTextFormat::Missing) |
| 809 | result["insertTextFormat" ] = static_cast<int>(value.insertTextFormat); |
| 810 | if (value.textEdit) |
| 811 | result["textEdit" ] = *value.textEdit; |
| 812 | if (!value.additionalTextEdits.empty()) { |
| 813 | result["additionalTextEdits" ] = |
| 814 | llvm::json::Array(value.additionalTextEdits); |
| 815 | } |
| 816 | if (value.deprecated) |
| 817 | result["deprecated" ] = value.deprecated; |
| 818 | return std::move(result); |
| 819 | } |
| 820 | |
| 821 | raw_ostream &mlir::lsp::operator<<(raw_ostream &os, |
| 822 | const CompletionItem &value) { |
| 823 | return os << value.label << " - " << toJSON(value); |
| 824 | } |
| 825 | |
| 826 | bool mlir::lsp::operator<(const CompletionItem &lhs, |
| 827 | const CompletionItem &rhs) { |
| 828 | return (lhs.sortText.empty() ? lhs.label : lhs.sortText) < |
| 829 | (rhs.sortText.empty() ? rhs.label : rhs.sortText); |
| 830 | } |
| 831 | |
| 832 | //===----------------------------------------------------------------------===// |
| 833 | // CompletionList |
| 834 | //===----------------------------------------------------------------------===// |
| 835 | |
| 836 | llvm::json::Value mlir::lsp::toJSON(const CompletionList &value) { |
| 837 | return llvm::json::Object{ |
| 838 | {.K: "isIncomplete" , .V: value.isIncomplete}, |
| 839 | {.K: "items" , .V: llvm::json::Array(value.items)}, |
| 840 | }; |
| 841 | } |
| 842 | |
| 843 | //===----------------------------------------------------------------------===// |
| 844 | // CompletionContext |
| 845 | //===----------------------------------------------------------------------===// |
| 846 | |
| 847 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 848 | CompletionContext &result, llvm::json::Path path) { |
| 849 | llvm::json::ObjectMapper o(value, path); |
| 850 | int triggerKind; |
| 851 | if (!o || !o.map(Prop: "triggerKind" , Out&: triggerKind) || |
| 852 | !mapOptOrNull(params: value, prop: "triggerCharacter" , out&: result.triggerCharacter, path)) |
| 853 | return false; |
| 854 | result.triggerKind = static_cast<CompletionTriggerKind>(triggerKind); |
| 855 | return true; |
| 856 | } |
| 857 | |
| 858 | //===----------------------------------------------------------------------===// |
| 859 | // CompletionParams |
| 860 | //===----------------------------------------------------------------------===// |
| 861 | |
| 862 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 863 | CompletionParams &result, llvm::json::Path path) { |
| 864 | if (!fromJSON(value, result&: static_cast<TextDocumentPositionParams &>(result), path)) |
| 865 | return false; |
| 866 | if (const llvm::json::Value *context = value.getAsObject()->get(K: "context" )) |
| 867 | return fromJSON(value: *context, result&: result.context, path: path.field(Field: "context" )); |
| 868 | return true; |
| 869 | } |
| 870 | |
| 871 | //===----------------------------------------------------------------------===// |
| 872 | // ParameterInformation |
| 873 | //===----------------------------------------------------------------------===// |
| 874 | |
| 875 | llvm::json::Value mlir::lsp::toJSON(const ParameterInformation &value) { |
| 876 | assert((value.labelOffsets || !value.labelString.empty()) && |
| 877 | "parameter information label is required" ); |
| 878 | llvm::json::Object result; |
| 879 | if (value.labelOffsets) |
| 880 | result["label" ] = llvm::json::Array( |
| 881 | {value.labelOffsets->first, value.labelOffsets->second}); |
| 882 | else |
| 883 | result["label" ] = value.labelString; |
| 884 | if (!value.documentation.empty()) |
| 885 | result["documentation" ] = value.documentation; |
| 886 | return std::move(result); |
| 887 | } |
| 888 | |
| 889 | //===----------------------------------------------------------------------===// |
| 890 | // SignatureInformation |
| 891 | //===----------------------------------------------------------------------===// |
| 892 | |
| 893 | llvm::json::Value mlir::lsp::toJSON(const SignatureInformation &value) { |
| 894 | assert(!value.label.empty() && "signature information label is required" ); |
| 895 | llvm::json::Object result{ |
| 896 | {.K: "label" , .V: value.label}, |
| 897 | {.K: "parameters" , .V: llvm::json::Array(value.parameters)}, |
| 898 | }; |
| 899 | if (!value.documentation.empty()) |
| 900 | result["documentation" ] = value.documentation; |
| 901 | return std::move(result); |
| 902 | } |
| 903 | |
| 904 | raw_ostream &mlir::lsp::operator<<(raw_ostream &os, |
| 905 | const SignatureInformation &value) { |
| 906 | return os << value.label << " - " << toJSON(value); |
| 907 | } |
| 908 | |
| 909 | //===----------------------------------------------------------------------===// |
| 910 | // SignatureHelp |
| 911 | //===----------------------------------------------------------------------===// |
| 912 | |
| 913 | llvm::json::Value mlir::lsp::toJSON(const SignatureHelp &value) { |
| 914 | assert(value.activeSignature >= 0 && |
| 915 | "Unexpected negative value for number of active signatures." ); |
| 916 | assert(value.activeParameter >= 0 && |
| 917 | "Unexpected negative value for active parameter index" ); |
| 918 | return llvm::json::Object{ |
| 919 | {.K: "activeSignature" , .V: value.activeSignature}, |
| 920 | {.K: "activeParameter" , .V: value.activeParameter}, |
| 921 | {.K: "signatures" , .V: llvm::json::Array(value.signatures)}, |
| 922 | }; |
| 923 | } |
| 924 | |
| 925 | //===----------------------------------------------------------------------===// |
| 926 | // DocumentLinkParams |
| 927 | //===----------------------------------------------------------------------===// |
| 928 | |
| 929 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 930 | DocumentLinkParams &result, llvm::json::Path path) { |
| 931 | llvm::json::ObjectMapper o(value, path); |
| 932 | return o && o.map(Prop: "textDocument" , Out&: result.textDocument); |
| 933 | } |
| 934 | |
| 935 | //===----------------------------------------------------------------------===// |
| 936 | // DocumentLink |
| 937 | //===----------------------------------------------------------------------===// |
| 938 | |
| 939 | llvm::json::Value mlir::lsp::toJSON(const DocumentLink &value) { |
| 940 | return llvm::json::Object{ |
| 941 | {.K: "range" , .V: value.range}, |
| 942 | {.K: "target" , .V: value.target}, |
| 943 | }; |
| 944 | } |
| 945 | |
| 946 | //===----------------------------------------------------------------------===// |
| 947 | // InlayHintsParams |
| 948 | //===----------------------------------------------------------------------===// |
| 949 | |
| 950 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 951 | InlayHintsParams &result, llvm::json::Path path) { |
| 952 | llvm::json::ObjectMapper o(value, path); |
| 953 | return o && o.map(Prop: "textDocument" , Out&: result.textDocument) && |
| 954 | o.map(Prop: "range" , Out&: result.range); |
| 955 | } |
| 956 | |
| 957 | //===----------------------------------------------------------------------===// |
| 958 | // InlayHint |
| 959 | //===----------------------------------------------------------------------===// |
| 960 | |
| 961 | llvm::json::Value mlir::lsp::toJSON(const InlayHint &value) { |
| 962 | return llvm::json::Object{{.K: "position" , .V: value.position}, |
| 963 | {.K: "kind" , .V: (int)value.kind}, |
| 964 | {.K: "label" , .V: value.label}, |
| 965 | {.K: "paddingLeft" , .V: value.paddingLeft}, |
| 966 | {.K: "paddingRight" , .V: value.paddingRight}}; |
| 967 | } |
| 968 | bool mlir::lsp::operator==(const InlayHint &lhs, const InlayHint &rhs) { |
| 969 | return std::tie(args: lhs.position, args: lhs.kind, args: lhs.label) == |
| 970 | std::tie(args: rhs.position, args: rhs.kind, args: rhs.label); |
| 971 | } |
| 972 | bool mlir::lsp::operator<(const InlayHint &lhs, const InlayHint &rhs) { |
| 973 | return std::tie(args: lhs.position, args: lhs.kind, args: lhs.label) < |
| 974 | std::tie(args: rhs.position, args: rhs.kind, args: rhs.label); |
| 975 | } |
| 976 | |
| 977 | llvm::raw_ostream &mlir::lsp::operator<<(llvm::raw_ostream &os, |
| 978 | InlayHintKind value) { |
| 979 | switch (value) { |
| 980 | case InlayHintKind::Parameter: |
| 981 | return os << "parameter" ; |
| 982 | case InlayHintKind::Type: |
| 983 | return os << "type" ; |
| 984 | } |
| 985 | llvm_unreachable("Unknown InlayHintKind" ); |
| 986 | } |
| 987 | |
| 988 | //===----------------------------------------------------------------------===// |
| 989 | // CodeActionContext |
| 990 | //===----------------------------------------------------------------------===// |
| 991 | |
| 992 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 993 | CodeActionContext &result, llvm::json::Path path) { |
| 994 | llvm::json::ObjectMapper o(value, path); |
| 995 | if (!o || !o.map(Prop: "diagnostics" , Out&: result.diagnostics)) |
| 996 | return false; |
| 997 | o.map(Prop: "only" , Out&: result.only); |
| 998 | return true; |
| 999 | } |
| 1000 | |
| 1001 | //===----------------------------------------------------------------------===// |
| 1002 | // CodeActionParams |
| 1003 | //===----------------------------------------------------------------------===// |
| 1004 | |
| 1005 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, |
| 1006 | CodeActionParams &result, llvm::json::Path path) { |
| 1007 | llvm::json::ObjectMapper o(value, path); |
| 1008 | return o && o.map(Prop: "textDocument" , Out&: result.textDocument) && |
| 1009 | o.map(Prop: "range" , Out&: result.range) && o.map(Prop: "context" , Out&: result.context); |
| 1010 | } |
| 1011 | |
| 1012 | //===----------------------------------------------------------------------===// |
| 1013 | // WorkspaceEdit |
| 1014 | //===----------------------------------------------------------------------===// |
| 1015 | |
| 1016 | bool mlir::lsp::fromJSON(const llvm::json::Value &value, WorkspaceEdit &result, |
| 1017 | llvm::json::Path path) { |
| 1018 | llvm::json::ObjectMapper o(value, path); |
| 1019 | return o && o.map(Prop: "changes" , Out&: result.changes); |
| 1020 | } |
| 1021 | |
| 1022 | llvm::json::Value mlir::lsp::toJSON(const WorkspaceEdit &value) { |
| 1023 | llvm::json::Object fileChanges; |
| 1024 | for (auto &change : value.changes) |
| 1025 | fileChanges[change.first] = llvm::json::Array(change.second); |
| 1026 | return llvm::json::Object{{.K: "changes" , .V: std::move(fileChanges)}}; |
| 1027 | } |
| 1028 | |
| 1029 | //===----------------------------------------------------------------------===// |
| 1030 | // CodeAction |
| 1031 | //===----------------------------------------------------------------------===// |
| 1032 | |
| 1033 | const llvm::StringLiteral CodeAction::kQuickFix = "quickfix" ; |
| 1034 | const llvm::StringLiteral CodeAction::kRefactor = "refactor" ; |
| 1035 | const llvm::StringLiteral CodeAction::kInfo = "info" ; |
| 1036 | |
| 1037 | llvm::json::Value mlir::lsp::toJSON(const CodeAction &value) { |
| 1038 | llvm::json::Object codeAction{{.K: "title" , .V: value.title}}; |
| 1039 | if (value.kind) |
| 1040 | codeAction["kind" ] = *value.kind; |
| 1041 | if (value.diagnostics) |
| 1042 | codeAction["diagnostics" ] = llvm::json::Array(*value.diagnostics); |
| 1043 | if (value.isPreferred) |
| 1044 | codeAction["isPreferred" ] = true; |
| 1045 | if (value.edit) |
| 1046 | codeAction["edit" ] = *value.edit; |
| 1047 | return std::move(codeAction); |
| 1048 | } |
| 1049 | |