MLIR  16.0.0git
Protocol.cpp
Go to the documentation of this file.
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 "Logging.h"
16 #include "llvm/ADT/Hashing.h"
17 #include "llvm/ADT/SmallString.h"
18 #include "llvm/ADT/StringSwitch.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 &params,
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(prop);
40  if (!v || v->getAsNull())
41  return true;
42  return fromJSON(*v, out, path.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(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(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('%');
92  out.push_back(llvm::hexdigit(c / 16));
93  out.push_back(llvm::hexdigit(c % 16));
94  } else {
95  out.push_back(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(*(i + 1)) &&
109  llvm::isHexDigit(*(i + 2))) {
110  result.push_back(llvm::hexFromNibbles(*(i + 1), *(i + 2)));
111  i += 2;
112  } else {
113  result.push_back(*i);
114  }
115  }
116  return result;
117 }
118 
119 static bool isValidScheme(StringRef scheme) {
120  if (scheme.empty())
121  return false;
122  if (!llvm::isAlpha(scheme[0]))
123  return false;
124  return llvm::all_of(llvm::drop_begin(scheme), [](char c) {
125  return llvm::isAlnum(c) || c == '+' || c == '.' || c == '-';
126  });
127 }
128 
129 static llvm::Expected<std::string> uriFromAbsolutePath(StringRef absolutePath) {
130  std::string body;
131  StringRef authority;
132  StringRef root = llvm::sys::path::root_name(absolutePath);
133  if (isNetworkPath(root)) {
134  // Windows UNC paths e.g. \\server\share => file://server/share
135  authority = root.drop_front(2);
136  absolutePath.consume_front(root);
137  } else if (isWindowsPath(root)) {
138  // Windows paths e.g. X:\path => file:///X:/path
139  body = "/";
140  }
141  body += llvm::sys::path::convert_to_slash(absolutePath);
142 
143  std::string uri = "file:";
144  if (authority.empty() && body.empty())
145  return uri;
146 
147  // If authority if empty, we only print body if it starts with "/"; otherwise,
148  // the URI is invalid.
149  if (!authority.empty() || StringRef(body).startswith("/")) {
150  uri.append("//");
151  percentEncode(authority, uri);
152  }
153  percentEncode(body, uri);
154  return uri;
155 }
156 
157 static llvm::Expected<std::string> getAbsolutePath(StringRef authority,
158  StringRef body) {
159  if (!body.startswith("/"))
160  return llvm::createStringError(
161  llvm::inconvertibleErrorCode(),
162  "File scheme: expect body to be an absolute path starting "
163  "with '/': " +
164  body);
165  SmallString<128> path;
166  if (!authority.empty()) {
167  // Windows UNC paths e.g. file://server/share => \\server\share
168  ("//" + authority).toVector(path);
169  } else if (isWindowsPath(body.substr(1))) {
170  // Windows paths e.g. file:///X:/path => X:\path
171  body.consume_front("/");
172  }
173  path.append(body);
174  llvm::sys::path::native(path);
175  return std::string(path);
176 }
177 
179  StringRef uri = origUri;
180 
181  // Decode the scheme of the URI.
182  size_t pos = uri.find(':');
183  if (pos == StringRef::npos)
184  return llvm::createStringError(llvm::inconvertibleErrorCode(),
185  "Scheme must be provided in URI: " +
186  origUri);
187  StringRef schemeStr = uri.substr(0, pos);
188  std::string uriScheme = percentDecode(schemeStr);
189  if (!isValidScheme(uriScheme))
190  return llvm::createStringError(llvm::inconvertibleErrorCode(),
191  "Invalid scheme: " + schemeStr +
192  " (decoded: " + uriScheme + ")");
193  uri = uri.substr(pos + 1);
194 
195  // Decode the authority of the URI.
196  std::string uriAuthority;
197  if (uri.consume_front("//")) {
198  pos = uri.find('/');
199  uriAuthority = percentDecode(uri.substr(0, pos));
200  uri = uri.substr(pos);
201  }
202 
203  // Decode the body of the URI.
204  std::string uriBody = percentDecode(uri);
205 
206  // Compute the absolute path for this uri.
207  if (uriScheme != "file" && uriScheme != "test") {
208  return llvm::createStringError(
209  llvm::inconvertibleErrorCode(),
210  "mlir-lsp-server only supports 'file' URI scheme for workspace files");
211  }
212  return getAbsolutePath(uriAuthority, uriBody);
213 }
214 
217  if (!filePath)
218  return filePath.takeError();
219  return URIForFile(std::move(*filePath), uri.str());
220 }
221 
224  if (!uri)
225  return uri.takeError();
226  return fromURI(*uri);
227 }
228 
229 bool mlir::lsp::fromJSON(const llvm::json::Value &value, URIForFile &result,
230  llvm::json::Path path) {
231  if (Optional<StringRef> str = value.getAsString()) {
233  if (!expectedURI) {
234  path.report("unresolvable URI");
235  consumeError(expectedURI.takeError());
236  return false;
237  }
238  result = std::move(*expectedURI);
239  return true;
240  }
241  return false;
242 }
243 
244 llvm::json::Value mlir::lsp::toJSON(const URIForFile &value) {
245  return value.uri();
246 }
247 
248 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const URIForFile &value) {
249  return os << value.uri();
250 }
251 
252 //===----------------------------------------------------------------------===//
253 // ClientCapabilities
254 //===----------------------------------------------------------------------===//
255 
256 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
257  ClientCapabilities &result, llvm::json::Path path) {
258  const llvm::json::Object *o = value.getAsObject();
259  if (!o) {
260  path.report("expected object");
261  return false;
262  }
263  if (const llvm::json::Object *textDocument = o->getObject("textDocument")) {
264  if (const llvm::json::Object *documentSymbol =
265  textDocument->getObject("documentSymbol")) {
266  if (Optional<bool> hierarchicalSupport =
267  documentSymbol->getBoolean("hierarchicalDocumentSymbolSupport"))
268  result.hierarchicalDocumentSymbol = *hierarchicalSupport;
269  }
270  if (auto *codeAction = textDocument->getObject("codeAction")) {
271  if (codeAction->getObject("codeActionLiteralSupport"))
272  result.codeActionStructure = true;
273  }
274  }
275  return true;
276 }
277 
278 //===----------------------------------------------------------------------===//
279 // InitializeParams
280 //===----------------------------------------------------------------------===//
281 
282 bool mlir::lsp::fromJSON(const llvm::json::Value &value, TraceLevel &result,
283  llvm::json::Path path) {
284  if (Optional<StringRef> str = value.getAsString()) {
285  if (*str == "off") {
286  result = TraceLevel::Off;
287  return true;
288  }
289  if (*str == "messages") {
290  result = TraceLevel::Messages;
291  return true;
292  }
293  if (*str == "verbose") {
294  result = TraceLevel::Verbose;
295  return true;
296  }
297  }
298  return false;
299 }
300 
301 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
302  InitializeParams &result, llvm::json::Path path) {
303  llvm::json::ObjectMapper o(value, path);
304  if (!o)
305  return false;
306  // We deliberately don't fail if we can't parse individual fields.
307  o.map("capabilities", result.capabilities);
308  o.map("trace", result.trace);
309  return true;
310 }
311 
312 //===----------------------------------------------------------------------===//
313 // TextDocumentItem
314 //===----------------------------------------------------------------------===//
315 
316 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
317  TextDocumentItem &result, llvm::json::Path path) {
318  llvm::json::ObjectMapper o(value, path);
319  return o && o.map("uri", result.uri) &&
320  o.map("languageId", result.languageId) && o.map("text", result.text) &&
321  o.map("version", result.version);
322 }
323 
324 //===----------------------------------------------------------------------===//
325 // TextDocumentIdentifier
326 //===----------------------------------------------------------------------===//
327 
328 llvm::json::Value mlir::lsp::toJSON(const TextDocumentIdentifier &value) {
329  return llvm::json::Object{{"uri", value.uri}};
330 }
331 
332 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
333  TextDocumentIdentifier &result,
334  llvm::json::Path path) {
335  llvm::json::ObjectMapper o(value, path);
336  return o && o.map("uri", result.uri);
337 }
338 
339 //===----------------------------------------------------------------------===//
340 // VersionedTextDocumentIdentifier
341 //===----------------------------------------------------------------------===//
342 
343 llvm::json::Value
345  return llvm::json::Object{
346  {"uri", value.uri},
347  {"version", value.version},
348  };
349 }
350 
351 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
353  llvm::json::Path path) {
354  llvm::json::ObjectMapper o(value, path);
355  return o && o.map("uri", result.uri) && o.map("version", result.version);
356 }
357 
358 //===----------------------------------------------------------------------===//
359 // Position
360 //===----------------------------------------------------------------------===//
361 
362 bool mlir::lsp::fromJSON(const llvm::json::Value &value, Position &result,
363  llvm::json::Path path) {
364  llvm::json::ObjectMapper o(value, path);
365  return o && o.map("line", result.line) &&
366  o.map("character", result.character);
367 }
368 
369 llvm::json::Value mlir::lsp::toJSON(const Position &value) {
370  return llvm::json::Object{
371  {"line", value.line},
372  {"character", value.character},
373  };
374 }
375 
376 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Position &value) {
377  return os << value.line << ':' << value.character;
378 }
379 
380 //===----------------------------------------------------------------------===//
381 // Range
382 //===----------------------------------------------------------------------===//
383 
384 bool mlir::lsp::fromJSON(const llvm::json::Value &value, Range &result,
385  llvm::json::Path path) {
386  llvm::json::ObjectMapper o(value, path);
387  return o && o.map("start", result.start) && o.map("end", result.end);
388 }
389 
390 llvm::json::Value mlir::lsp::toJSON(const Range &value) {
391  return llvm::json::Object{
392  {"start", value.start},
393  {"end", value.end},
394  };
395 }
396 
397 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Range &value) {
398  return os << value.start << '-' << value.end;
399 }
400 
401 //===----------------------------------------------------------------------===//
402 // Location
403 //===----------------------------------------------------------------------===//
404 
405 bool mlir::lsp::fromJSON(const llvm::json::Value &value, Location &result,
406  llvm::json::Path path) {
407  llvm::json::ObjectMapper o(value, path);
408  return o && o.map("uri", result.uri) && o.map("range", result.range);
409 }
410 
411 llvm::json::Value mlir::lsp::toJSON(const Location &value) {
412  return llvm::json::Object{
413  {"uri", value.uri},
414  {"range", value.range},
415  };
416 }
417 
418 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Location &value) {
419  return os << value.range << '@' << value.uri;
420 }
421 
422 //===----------------------------------------------------------------------===//
423 // TextDocumentPositionParams
424 //===----------------------------------------------------------------------===//
425 
426 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
428  llvm::json::Path path) {
429  llvm::json::ObjectMapper o(value, path);
430  return o && o.map("textDocument", result.textDocument) &&
431  o.map("position", result.position);
432 }
433 
434 //===----------------------------------------------------------------------===//
435 // ReferenceParams
436 //===----------------------------------------------------------------------===//
437 
438 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
439  ReferenceContext &result, llvm::json::Path path) {
440  llvm::json::ObjectMapper o(value, path);
441  return o && o.mapOptional("includeDeclaration", result.includeDeclaration);
442 }
443 
444 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
445  ReferenceParams &result, llvm::json::Path path) {
446  TextDocumentPositionParams &base = result;
447  llvm::json::ObjectMapper o(value, path);
448  return fromJSON(value, base, path) && o &&
449  o.mapOptional("context", result.context);
450 }
451 
452 //===----------------------------------------------------------------------===//
453 // DidOpenTextDocumentParams
454 //===----------------------------------------------------------------------===//
455 
456 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
458  llvm::json::Path path) {
459  llvm::json::ObjectMapper o(value, path);
460  return o && o.map("textDocument", result.textDocument);
461 }
462 
463 //===----------------------------------------------------------------------===//
464 // DidCloseTextDocumentParams
465 //===----------------------------------------------------------------------===//
466 
467 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
469  llvm::json::Path path) {
470  llvm::json::ObjectMapper o(value, path);
471  return o && o.map("textDocument", result.textDocument);
472 }
473 
474 //===----------------------------------------------------------------------===//
475 // DidChangeTextDocumentParams
476 //===----------------------------------------------------------------------===//
477 
479 TextDocumentContentChangeEvent::applyTo(std::string &contents) const {
480  // If there is no range, the full document changed.
481  if (!range) {
482  contents = text;
483  return success();
484  }
485 
486  // Try to map the replacement range to the content.
487  llvm::SourceMgr tmpScrMgr;
488  tmpScrMgr.AddNewSourceBuffer(llvm::MemoryBuffer::getMemBuffer(contents),
489  SMLoc());
490  SMRange rangeLoc = range->getAsSMRange(tmpScrMgr);
491  if (!rangeLoc.isValid())
492  return failure();
493 
494  contents.replace(rangeLoc.Start.getPointer() - contents.data(),
495  rangeLoc.End.getPointer() - rangeLoc.Start.getPointer(),
496  text);
497  return success();
498 }
499 
501  ArrayRef<TextDocumentContentChangeEvent> changes, std::string &contents) {
502  for (const auto &change : changes)
503  if (failed(change.applyTo(contents)))
504  return failure();
505  return success();
506 }
507 
508 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
510  llvm::json::Path path) {
511  llvm::json::ObjectMapper o(value, path);
512  return o && o.map("range", result.range) &&
513  o.map("rangeLength", result.rangeLength) && o.map("text", result.text);
514 }
515 
516 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
518  llvm::json::Path path) {
519  llvm::json::ObjectMapper o(value, path);
520  return o && o.map("textDocument", result.textDocument) &&
521  o.map("contentChanges", result.contentChanges);
522 }
523 
524 //===----------------------------------------------------------------------===//
525 // MarkupContent
526 //===----------------------------------------------------------------------===//
527 
528 static llvm::StringRef toTextKind(MarkupKind kind) {
529  switch (kind) {
531  return "plaintext";
533  return "markdown";
534  }
535  llvm_unreachable("Invalid MarkupKind");
536 }
537 
538 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, MarkupKind kind) {
539  return os << toTextKind(kind);
540 }
541 
542 llvm::json::Value mlir::lsp::toJSON(const MarkupContent &mc) {
543  if (mc.value.empty())
544  return nullptr;
545 
546  return llvm::json::Object{
547  {"kind", toTextKind(mc.kind)},
548  {"value", mc.value},
549  };
550 }
551 
552 //===----------------------------------------------------------------------===//
553 // Hover
554 //===----------------------------------------------------------------------===//
555 
556 llvm::json::Value mlir::lsp::toJSON(const Hover &hover) {
557  llvm::json::Object result{{"contents", toJSON(hover.contents)}};
558  if (hover.range)
559  result["range"] = toJSON(*hover.range);
560  return std::move(result);
561 }
562 
563 //===----------------------------------------------------------------------===//
564 // DocumentSymbol
565 //===----------------------------------------------------------------------===//
566 
567 llvm::json::Value mlir::lsp::toJSON(const DocumentSymbol &symbol) {
568  llvm::json::Object result{{"name", symbol.name},
569  {"kind", static_cast<int>(symbol.kind)},
570  {"range", symbol.range},
571  {"selectionRange", symbol.selectionRange}};
572 
573  if (!symbol.detail.empty())
574  result["detail"] = symbol.detail;
575  if (!symbol.children.empty())
576  result["children"] = symbol.children;
577  return std::move(result);
578 }
579 
580 //===----------------------------------------------------------------------===//
581 // DocumentSymbolParams
582 //===----------------------------------------------------------------------===//
583 
584 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
585  DocumentSymbolParams &result, llvm::json::Path path) {
586  llvm::json::ObjectMapper o(value, path);
587  return o && o.map("textDocument", result.textDocument);
588 }
589 
590 //===----------------------------------------------------------------------===//
591 // DiagnosticRelatedInformation
592 //===----------------------------------------------------------------------===//
593 
594 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
596  llvm::json::Path path) {
597  llvm::json::ObjectMapper o(value, path);
598  return o && o.map("location", result.location) &&
599  o.map("message", result.message);
600 }
601 
602 llvm::json::Value mlir::lsp::toJSON(const DiagnosticRelatedInformation &info) {
603  return llvm::json::Object{
604  {"location", info.location},
605  {"message", info.message},
606  };
607 }
608 
609 //===----------------------------------------------------------------------===//
610 // Diagnostic
611 //===----------------------------------------------------------------------===//
612 
613 llvm::json::Value mlir::lsp::toJSON(const Diagnostic &diag) {
614  llvm::json::Object result{
615  {"range", diag.range},
616  {"severity", (int)diag.severity},
617  {"message", diag.message},
618  };
619  if (diag.category)
620  result["category"] = *diag.category;
621  if (!diag.source.empty())
622  result["source"] = diag.source;
623  if (diag.relatedInformation)
624  result["relatedInformation"] = *diag.relatedInformation;
625  return std::move(result);
626 }
627 
628 bool mlir::lsp::fromJSON(const llvm::json::Value &value, Diagnostic &result,
629  llvm::json::Path path) {
630  llvm::json::ObjectMapper o(value, path);
631  if (!o)
632  return false;
633  int severity = 0;
634  if (!mapOptOrNull(value, "severity", severity, path))
635  return false;
636  result.severity = (DiagnosticSeverity)severity;
637 
638  return o.map("range", result.range) && o.map("message", result.message) &&
639  mapOptOrNull(value, "category", result.category, path) &&
640  mapOptOrNull(value, "source", result.source, path) &&
641  mapOptOrNull(value, "relatedInformation", result.relatedInformation,
642  path);
643 }
644 
645 //===----------------------------------------------------------------------===//
646 // PublishDiagnosticsParams
647 //===----------------------------------------------------------------------===//
648 
649 llvm::json::Value mlir::lsp::toJSON(const PublishDiagnosticsParams &params) {
650  return llvm::json::Object{
651  {"uri", params.uri},
652  {"diagnostics", params.diagnostics},
653  {"version", params.version},
654  };
655 }
656 
657 //===----------------------------------------------------------------------===//
658 // TextEdit
659 //===----------------------------------------------------------------------===//
660 
661 bool mlir::lsp::fromJSON(const llvm::json::Value &value, TextEdit &result,
662  llvm::json::Path path) {
663  llvm::json::ObjectMapper o(value, path);
664  return o && o.map("range", result.range) && o.map("newText", result.newText);
665 }
666 
667 llvm::json::Value mlir::lsp::toJSON(const TextEdit &value) {
668  return llvm::json::Object{
669  {"range", value.range},
670  {"newText", value.newText},
671  };
672 }
673 
674 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const TextEdit &value) {
675  os << value.range << " => \"";
676  llvm::printEscapedString(value.newText, os);
677  return os << '"';
678 }
679 
680 //===----------------------------------------------------------------------===//
681 // CompletionItemKind
682 //===----------------------------------------------------------------------===//
683 
684 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
685  CompletionItemKind &result, llvm::json::Path path) {
686  if (Optional<int64_t> intValue = value.getAsInteger()) {
687  if (*intValue < static_cast<int>(CompletionItemKind::Text) ||
688  *intValue > static_cast<int>(CompletionItemKind::TypeParameter))
689  return false;
690  result = static_cast<CompletionItemKind>(*intValue);
691  return true;
692  }
693  return false;
694 }
695 
697  CompletionItemKind kind,
698  CompletionItemKindBitset &supportedCompletionItemKinds) {
699  size_t kindVal = static_cast<size_t>(kind);
700  if (kindVal >= kCompletionItemKindMin &&
701  kindVal <= supportedCompletionItemKinds.size() &&
702  supportedCompletionItemKinds[kindVal])
703  return kind;
704 
705  // Provide some fall backs for common kinds that are close enough.
706  switch (kind) {
713  default:
715  }
716 }
717 
718 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
719  CompletionItemKindBitset &result,
720  llvm::json::Path path) {
721  if (const llvm::json::Array *arrayValue = value.getAsArray()) {
722  for (size_t i = 0, e = arrayValue->size(); i < e; ++i) {
723  CompletionItemKind kindOut;
724  if (fromJSON((*arrayValue)[i], kindOut, path.index(i)))
725  result.set(size_t(kindOut));
726  }
727  return true;
728  }
729  return false;
730 }
731 
732 //===----------------------------------------------------------------------===//
733 // CompletionItem
734 //===----------------------------------------------------------------------===//
735 
736 llvm::json::Value mlir::lsp::toJSON(const CompletionItem &value) {
737  assert(!value.label.empty() && "completion item label is required");
738  llvm::json::Object result{{"label", value.label}};
739  if (value.kind != CompletionItemKind::Missing)
740  result["kind"] = static_cast<int>(value.kind);
741  if (!value.detail.empty())
742  result["detail"] = value.detail;
743  if (value.documentation)
744  result["documentation"] = value.documentation;
745  if (!value.sortText.empty())
746  result["sortText"] = value.sortText;
747  if (!value.filterText.empty())
748  result["filterText"] = value.filterText;
749  if (!value.insertText.empty())
750  result["insertText"] = value.insertText;
752  result["insertTextFormat"] = static_cast<int>(value.insertTextFormat);
753  if (value.textEdit)
754  result["textEdit"] = *value.textEdit;
755  if (!value.additionalTextEdits.empty()) {
756  result["additionalTextEdits"] =
757  llvm::json::Array(value.additionalTextEdits);
758  }
759  if (value.deprecated)
760  result["deprecated"] = value.deprecated;
761  return std::move(result);
762 }
763 
764 raw_ostream &mlir::lsp::operator<<(raw_ostream &os,
765  const CompletionItem &value) {
766  return os << value.label << " - " << toJSON(value);
767 }
768 
770  const CompletionItem &rhs) {
771  return (lhs.sortText.empty() ? lhs.label : lhs.sortText) <
772  (rhs.sortText.empty() ? rhs.label : rhs.sortText);
773 }
774 
775 //===----------------------------------------------------------------------===//
776 // CompletionList
777 //===----------------------------------------------------------------------===//
778 
779 llvm::json::Value mlir::lsp::toJSON(const CompletionList &value) {
780  return llvm::json::Object{
781  {"isIncomplete", value.isIncomplete},
782  {"items", llvm::json::Array(value.items)},
783  };
784 }
785 
786 //===----------------------------------------------------------------------===//
787 // CompletionContext
788 //===----------------------------------------------------------------------===//
789 
790 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
791  CompletionContext &result, llvm::json::Path path) {
792  llvm::json::ObjectMapper o(value, path);
793  int triggerKind;
794  if (!o || !o.map("triggerKind", triggerKind) ||
795  !mapOptOrNull(value, "triggerCharacter", result.triggerCharacter, path))
796  return false;
797  result.triggerKind = static_cast<CompletionTriggerKind>(triggerKind);
798  return true;
799 }
800 
801 //===----------------------------------------------------------------------===//
802 // CompletionParams
803 //===----------------------------------------------------------------------===//
804 
805 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
806  CompletionParams &result, llvm::json::Path path) {
807  if (!fromJSON(value, static_cast<TextDocumentPositionParams &>(result), path))
808  return false;
809  if (const llvm::json::Value *context = value.getAsObject()->get("context"))
810  return fromJSON(*context, result.context, path.field("context"));
811  return true;
812 }
813 
814 //===----------------------------------------------------------------------===//
815 // ParameterInformation
816 //===----------------------------------------------------------------------===//
817 
818 llvm::json::Value mlir::lsp::toJSON(const ParameterInformation &value) {
819  assert((value.labelOffsets || !value.labelString.empty()) &&
820  "parameter information label is required");
821  llvm::json::Object result;
822  if (value.labelOffsets)
823  result["label"] = llvm::json::Array(
824  {value.labelOffsets->first, value.labelOffsets->second});
825  else
826  result["label"] = value.labelString;
827  if (!value.documentation.empty())
828  result["documentation"] = value.documentation;
829  return std::move(result);
830 }
831 
832 //===----------------------------------------------------------------------===//
833 // SignatureInformation
834 //===----------------------------------------------------------------------===//
835 
836 llvm::json::Value mlir::lsp::toJSON(const SignatureInformation &value) {
837  assert(!value.label.empty() && "signature information label is required");
838  llvm::json::Object result{
839  {"label", value.label},
840  {"parameters", llvm::json::Array(value.parameters)},
841  };
842  if (!value.documentation.empty())
843  result["documentation"] = value.documentation;
844  return std::move(result);
845 }
846 
847 raw_ostream &mlir::lsp::operator<<(raw_ostream &os,
848  const SignatureInformation &value) {
849  return os << value.label << " - " << toJSON(value);
850 }
851 
852 //===----------------------------------------------------------------------===//
853 // SignatureHelp
854 //===----------------------------------------------------------------------===//
855 
856 llvm::json::Value mlir::lsp::toJSON(const SignatureHelp &value) {
857  assert(value.activeSignature >= 0 &&
858  "Unexpected negative value for number of active signatures.");
859  assert(value.activeParameter >= 0 &&
860  "Unexpected negative value for active parameter index");
861  return llvm::json::Object{
862  {"activeSignature", value.activeSignature},
863  {"activeParameter", value.activeParameter},
864  {"signatures", llvm::json::Array(value.signatures)},
865  };
866 }
867 
868 //===----------------------------------------------------------------------===//
869 // DocumentLinkParams
870 //===----------------------------------------------------------------------===//
871 
872 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
873  DocumentLinkParams &result, llvm::json::Path path) {
874  llvm::json::ObjectMapper o(value, path);
875  return o && o.map("textDocument", result.textDocument);
876 }
877 
878 //===----------------------------------------------------------------------===//
879 // DocumentLink
880 //===----------------------------------------------------------------------===//
881 
882 llvm::json::Value mlir::lsp::toJSON(const DocumentLink &value) {
883  return llvm::json::Object{
884  {"range", value.range},
885  {"target", value.target},
886  };
887 }
888 
889 //===----------------------------------------------------------------------===//
890 // InlayHintsParams
891 //===----------------------------------------------------------------------===//
892 
893 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
894  InlayHintsParams &result, llvm::json::Path path) {
895  llvm::json::ObjectMapper o(value, path);
896  return o && o.map("textDocument", result.textDocument) &&
897  o.map("range", result.range);
898 }
899 
900 //===----------------------------------------------------------------------===//
901 // InlayHint
902 //===----------------------------------------------------------------------===//
903 
904 llvm::json::Value mlir::lsp::toJSON(const InlayHint &value) {
905  return llvm::json::Object{{"position", value.position},
906  {"kind", (int)value.kind},
907  {"label", value.label},
908  {"paddingLeft", value.paddingLeft},
909  {"paddingRight", value.paddingRight}};
910 }
911 bool mlir::lsp::operator==(const InlayHint &lhs, const InlayHint &rhs) {
912  return std::tie(lhs.position, lhs.kind, lhs.label) ==
913  std::tie(rhs.position, rhs.kind, rhs.label);
914 }
915 bool mlir::lsp::operator<(const InlayHint &lhs, const InlayHint &rhs) {
916  return std::tie(lhs.position, lhs.kind, lhs.label) <
917  std::tie(rhs.position, rhs.kind, rhs.label);
918 }
919 
920 llvm::raw_ostream &mlir::lsp::operator<<(llvm::raw_ostream &os,
922  switch (value) {
924  return os << "parameter";
925  case InlayHintKind::Type:
926  return os << "type";
927  }
928  llvm_unreachable("Unknown InlayHintKind");
929 }
930 
931 //===----------------------------------------------------------------------===//
932 // CodeActionContext
933 //===----------------------------------------------------------------------===//
934 
935 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
936  CodeActionContext &result, llvm::json::Path path) {
937  llvm::json::ObjectMapper o(value, path);
938  if (!o || !o.map("diagnostics", result.diagnostics))
939  return false;
940  o.map("only", result.only);
941  return true;
942 }
943 
944 //===----------------------------------------------------------------------===//
945 // CodeActionParams
946 //===----------------------------------------------------------------------===//
947 
948 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
949  CodeActionParams &result, llvm::json::Path path) {
950  llvm::json::ObjectMapper o(value, path);
951  return o && o.map("textDocument", result.textDocument) &&
952  o.map("range", result.range) && o.map("context", result.context);
953 }
954 
955 //===----------------------------------------------------------------------===//
956 // WorkspaceEdit
957 //===----------------------------------------------------------------------===//
958 
959 bool mlir::lsp::fromJSON(const llvm::json::Value &value, WorkspaceEdit &result,
960  llvm::json::Path path) {
961  llvm::json::ObjectMapper o(value, path);
962  return o && o.map("changes", result.changes);
963 }
964 
965 llvm::json::Value mlir::lsp::toJSON(const WorkspaceEdit &value) {
966  llvm::json::Object fileChanges;
967  for (auto &change : value.changes)
968  fileChanges[change.first] = llvm::json::Array(change.second);
969  return llvm::json::Object{{"changes", std::move(fileChanges)}};
970 }
971 
972 //===----------------------------------------------------------------------===//
973 // CodeAction
974 //===----------------------------------------------------------------------===//
975 
976 const llvm::StringLiteral CodeAction::kQuickFix = "quickfix";
977 const llvm::StringLiteral CodeAction::kRefactor = "refactor";
978 const llvm::StringLiteral CodeAction::kInfo = "info";
979 
980 llvm::json::Value mlir::lsp::toJSON(const CodeAction &value) {
981  llvm::json::Object codeAction{{"title", value.title}};
982  if (value.kind)
983  codeAction["kind"] = *value.kind;
984  if (value.diagnostics)
985  codeAction["diagnostics"] = llvm::json::Array(*value.diagnostics);
986  if (value.isPreferred)
987  codeAction["isPreferred"] = true;
988  if (value.edit)
989  codeAction["edit"] = *value.edit;
990  return std::move(codeAction);
991 }
TextDocumentIdentifier textDocument
The document to provide document links for.
Definition: Protocol.h:977
Include the generated interface declarations.
std::vector< TextDocumentContentChangeEvent > contentChanges
The actual content changes.
Definition: Protocol.h:479
static std::string percentDecode(StringRef content)
Decodes a string according to percent-encoding.
Definition: Protocol.cpp:101
bool operator==(const TextEdit &lhs, const TextEdit &rhs)
Definition: Protocol.h:714
static std::string diag(llvm::Value &v)
int activeSignature
The active signature.
Definition: Protocol.h:961
static bool isNetworkPath(StringRef path)
Definition: Protocol.cpp:59
Parameters for the document link request.
Definition: Protocol.h:975
CompletionContext context
Definition: Protocol.h:904
std::vector< std::string > only
Requested kind of actions to return.
Definition: Protocol.h:1117
URIForFile uri
The text document&#39;s URI.
Definition: Protocol.h:237
Represents a related message and source code location for a diagnostic.
Definition: Protocol.h:620
bool paddingLeft
Render padding before the hint.
Definition: Protocol.h:1084
static llvm::StringRef toTextKind(MarkupKind kind)
Definition: Protocol.cpp:528
static llvm::Expected< std::string > getAbsolutePath(StringRef authority, StringRef body)
Definition: Protocol.cpp:157
std::string label
The label of this hint.
Definition: Protocol.h:1073
bool paddingRight
Render padding after the hint.
Definition: Protocol.h:1091
CompletionTriggerKind
Definition: Protocol.h:873
static bool mapOptOrNull(const llvm::json::Value &params, llvm::StringLiteral prop, T &out, llvm::json::Path path)
Definition: Protocol.cpp:32
Position position
The position inside the text document.
Definition: Protocol.h:395
VersionedTextDocumentIdentifier textDocument
The document that changed.
Definition: Protocol.h:476
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value...
Definition: LogicalResult.h:72
DiagnosticSeverity severity
The diagnostic&#39;s severity.
Definition: Protocol.h:656
Optional< std::string > category
The diagnostic&#39;s category.
Definition: Protocol.h:673
std::vector< CompletionItem > items
The completion items.
Definition: Protocol.h:863
URIForFile uri
The text document&#39;s URI.
Definition: Protocol.h:223
int64_t version
The version number of this document.
Definition: Protocol.h:239
Optional< Range > range
An optional range is a range inside a text document that is used to visualize a hover, e.g.
Definition: Protocol.h:519
URI in "file" scheme for a file.
Definition: Protocol.h:99
Optional< TextEdit > textEdit
An edit which is applied to a document when selecting this completion.
Definition: Protocol.h:836
std::string detail
More detail for this symbol, e.g the signature of a function.
Definition: Protocol.h:578
TextDocumentItem textDocument
The document that was opened.
Definition: Protocol.h:429
Range range
The range enclosing this symbol not including leading/trailing whitespace but everything else like co...
Definition: Protocol.h:587
int64_t version
The version number of this document.
Definition: Protocol.h:210
std::vector< ParameterInformation > parameters
The parameters of this signature.
Definition: Protocol.h:944
static const llvm::StringLiteral kRefactor
Definition: Protocol.h:1177
An inlay hint that for a type annotation.
static bool shouldEscapeInURI(unsigned char c)
Definition: Protocol.cpp:64
TextDocumentIdentifier textDocument
The text document.
Definition: Protocol.h:392
std::string documentation
The documentation of this signature. Optional.
Definition: Protocol.h:941
Optional< std::pair< unsigned, unsigned > > labelOffsets
Inclusive start and exclusive end offsets withing the containing signature label. ...
Definition: Protocol.h:922
Range range
The visible document range for which inlay hints should be computed.
Definition: Protocol.h:1030
CompletionItemKind adjustKindToCapability(CompletionItemKind kind, CompletionItemKindBitset &supportedCompletionItemKinds)
Definition: Protocol.cpp:696
bool includeDeclaration
Include the declaration of the current symbol.
Definition: Protocol.h:408
InlayHintKind kind
The kind of this hint.
Definition: Protocol.h:1077
static constexpr const bool value
Position position
The position of this hint.
Definition: Protocol.h:1067
A parameter literal used in inlay hint requests.
Definition: Protocol.h:1025
TextDocumentIdentifier textDocument
The document that was closed.
Definition: Protocol.h:442
Optional< TraceLevel > trace
The initial trace setting. If omitted trace is disabled (&#39;off&#39;).
Definition: Protocol.h:178
LogicalResult applyTo(std::string &contents) const
Try to apply this change to the given contents string.
Definition: Protocol.cpp:479
bool codeActionStructure
Client supports CodeAction return value for textDocument/codeAction.
Definition: Protocol.h:152
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:56
std::string source
A human-readable string describing the source of this diagnostic, e.g.
Definition: Protocol.h:660
TextDocumentIdentifier textDocument
The text document.
Definition: Protocol.h:1027
This class represents an efficient way to signal success or failure.
Definition: LogicalResult.h:26
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:62
CompletionTriggerKind triggerKind
How the completion was triggered.
Definition: Protocol.h:888
std::string text
The content of the opened text document.
Definition: Protocol.h:207
bool operator<(const CompletionItem &lhs, const CompletionItem &rhs)
Definition: Protocol.cpp:769
std::string languageId
The text document&#39;s language identifier.
Definition: Protocol.h:204
int character
Character offset on a line in a document (zero-based).
Definition: Protocol.h:266
static llvm::Expected< URIForFile > fromURI(StringRef uri)
Try to build a URIForFile from the given URI string.
Definition: Protocol.cpp:215
static bool isValidScheme(StringRef scheme)
Definition: Protocol.cpp:119
static llvm::Expected< std::string > uriFromAbsolutePath(StringRef absolutePath)
Definition: Protocol.cpp:129
TextDocumentIdentifier textDocument
The document in which the command was invoked.
Definition: Protocol.h:1130
std::string insertText
A string that should be inserted to a document when selecting this completion.
Definition: Protocol.h:825
Optional< WorkspaceEdit > edit
The workspace edit this code action performs.
Definition: Protocol.h:1191
int activeParameter
The active parameter of the active signature.
Definition: Protocol.h:964
int line
Line position in a document (zero-based).
Definition: Protocol.h:263
URIForFile uri
The text document&#39;s URI.
Definition: Protocol.h:201
Optional< MarkupContent > documentation
A human-readable string that represents a doc-comment.
Definition: Protocol.h:813
SymbolKind kind
The kind of this symbol.
Definition: Protocol.h:581
raw_ostream & operator<<(raw_ostream &os, const URIForFile &value)
Definition: Protocol.cpp:248
static void percentEncode(StringRef content, std::string &out)
Encodes a string according to percent-encoding.
Definition: Protocol.cpp:88
std::string title
A short, human-readable, title for this code action.
Definition: Protocol.h:1171
static char ID
Definition: Protocol.h:81
static const llvm::StringLiteral kQuickFix
Definition: Protocol.h:1176
Position end
The range&#39;s end position.
Definition: Protocol.h:315
bool deprecated
Indicates if this item is deprecated.
Definition: Protocol.h:844
Optional< std::string > kind
The kind of the code action.
Definition: Protocol.h:1175
static bool isWindowsPath(StringRef path)
Definition: Protocol.cpp:55
Represents a collection of completion items to be presented in the editor.
Definition: Protocol.h:857
int64_t version
The version number of the document the diagnostics are published for.
Definition: Protocol.h:694
std::string text
The new text of the range/document.
Definition: Protocol.h:467
CodeActionContext context
Context carrying additional information.
Definition: Protocol.h:1136
std::vector< Diagnostic > diagnostics
An array of diagnostics known on the client side overlapping the range provided to the textDocument/c...
Definition: Protocol.h:1111
MarkupContent contents
The hover&#39;s content.
Definition: Protocol.h:515
std::vector< DocumentSymbol > children
Children of this symbol, e.g. properties of a class.
Definition: Protocol.h:594
std::bitset< kCompletionItemKindMax+1 > CompletionItemKindBitset
Definition: Protocol.h:763
std::string message
The diagnostic&#39;s message.
Definition: Protocol.h:663
Inlay hint information.
Definition: Protocol.h:1063
Range range
The range for which the command was invoked.
Definition: Protocol.h:1133
URIForFile uri
The URI for which diagnostic information is reported.
Definition: Protocol.h:690
std::string label
The label of this signature. Mandatory.
Definition: Protocol.h:938
std::vector< SignatureInformation > signatures
The resulting signatures.
Definition: Protocol.h:958
An inlay hint that is for a parameter.
Position start
The range&#39;s start position.
Definition: Protocol.h:312
std::vector< Diagnostic > diagnostics
The list of reported diagnostics.
Definition: Protocol.h:692
A code action represents a change that can be performed in code, e.g.
Definition: Protocol.h:1169
std::map< std::string, std::vector< TextEdit > > changes
Holds changes to existing resources.
Definition: Protocol.h:1149
Optional< std::vector< DiagnosticRelatedInformation > > relatedInformation
An array of related diagnostic information, e.g.
Definition: Protocol.h:667
std::string filterText
A string that should be used when filtering a set of completion items.
Definition: Protocol.h:821
URIForFile uri
The text document&#39;s URI.
Definition: Protocol.h:364
std::string label
The label of this completion item.
Definition: Protocol.h:802
std::string newText
The string to be inserted.
Definition: Protocol.h:711
A single parameter of a particular signature.
Definition: Protocol.h:916
bool isPreferred
Marks this as a preferred action.
Definition: Protocol.h:1188
DiagnosticSeverity
Definition: Protocol.h:640
std::string detail
A human-readable string with additional information about this item, like type or symbol information...
Definition: Protocol.h:810
std::string name
The name of this symbol.
Definition: Protocol.h:575
Range range
The range of the text document to be manipulated.
Definition: Protocol.h:707
Range range
The source range where the message applies.
Definition: Protocol.h:652
Optional< int > rangeLength
The length of the range that got replaced.
Definition: Protocol.h:464
Range selectionRange
The range that should be selected and revealed when this symbol is being picked, e.g the name of a function.
Definition: Protocol.h:591
ReferenceContext context
Definition: Protocol.h:416
InlayHintKind
Inlay hint kinds.
Definition: Protocol.h:1042
Optional< Range > range
The range of the document that changed.
Definition: Protocol.h:461
Represents the signature of a callable.
Definition: Protocol.h:956
Location location
The location of this related diagnostic information.
Definition: Protocol.h:626
ClientCapabilities capabilities
The capabilities provided by the client (editor or tool).
Definition: Protocol.h:175
constexpr auto kCompletionItemKindMin
Definition: Protocol.h:759
TextDocumentIdentifier textDocument
Definition: Protocol.h:606
static llvm::Expected< std::string > parseFilePathFromURI(StringRef origUri)
Definition: Protocol.cpp:178
StringRef uri() const
Returns the original uri of the file.
Definition: Protocol.h:113
llvm::json::Value toJSON(const URIForFile &value)
Add support for JSON serialization.
Definition: Protocol.cpp:244
bool hierarchicalDocumentSymbol
Client supports hierarchical document symbols.
Definition: Protocol.h:148
InsertTextFormat insertTextFormat
The format of the insert text.
Definition: Protocol.h:829
MarkupKind
Describes the content type that a client supports in various result literals like Hover...
Definition: Protocol.h:492
static llvm::Expected< URIForFile > fromFile(StringRef absoluteFilepath)
Try to build a URIForFile from the given absolute file path.
Definition: Protocol.cpp:222
std::string documentation
The documentation of this parameter. Optional.
Definition: Protocol.h:925
Represents programming constructs like variables, classes, interfaces etc.
Definition: Protocol.h:566
std::vector< TextEdit > additionalTextEdits
An optional array of additional text edits that are applied when selecting this completion.
Definition: Protocol.h:841
static const llvm::StringLiteral kInfo
Definition: Protocol.h:1178
bool fromJSON(const llvm::json::Value &value, URIForFile &result, llvm::json::Path path)
Definition: Protocol.cpp:229
bool isIncomplete
The list is not complete.
Definition: Protocol.h:860
Represents the signature of something callable.
Definition: Protocol.h:936
Optional< std::vector< Diagnostic > > diagnostics
The diagnostics that this code action resolves.
Definition: Protocol.h:1181
std::string sortText
A string that should be used when comparing this item with other items.
Definition: Protocol.h:817
std::string labelString
The label of this parameter. Ignored when labelOffsets is set.
Definition: Protocol.h:918
std::string triggerCharacter
The trigger character (a single character) that has trigger code complete.
Definition: Protocol.h:892
std::string message
The message of this related diagnostic information.
Definition: Protocol.h:628
CompletionItemKind kind
The kind of this completion item.
Definition: Protocol.h:806
CompletionItemKind
The kind of a completion entry.
Definition: Protocol.h:728