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"
15 #include "mlir/Support/LogicalResult.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>
mapOptOrNull(const llvm::json::Value & params,llvm::StringLiteral prop,T & out,llvm::json::Path path)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 
isWindowsPath(StringRef path)55 static bool isWindowsPath(StringRef path) {
56   return path.size() > 1 && llvm::isAlpha(path[0]) && path[1] == ':';
57 }
58 
isNetworkPath(StringRef path)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 
shouldEscapeInURI(unsigned char c)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.
percentEncode(StringRef content,std::string & out)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.
percentDecode(StringRef content)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 
isValidScheme(StringRef scheme)119 static bool isValidScheme(StringRef scheme) {
120   if (scheme.empty())
121     return false;
122   if (!llvm::isAlpha(scheme[0]))
123     return false;
124   return std::all_of(scheme.begin() + 1, scheme.end(), [](char c) {
125     return llvm::isAlnum(c) || c == '+' || c == '.' || c == '-';
126   });
127 }
128 
uriFromAbsolutePath(StringRef absolutePath)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 
getAbsolutePath(StringRef authority,StringRef body)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 
parseFilePathFromURI(StringRef origUri)178 static llvm::Expected<std::string> parseFilePathFromURI(StringRef origUri) {
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 
fromURI(StringRef uri)215 llvm::Expected<URIForFile> URIForFile::fromURI(StringRef uri) {
216   llvm::Expected<std::string> filePath = parseFilePathFromURI(uri);
217   if (!filePath)
218     return filePath.takeError();
219   return URIForFile(std::move(*filePath), uri.str());
220 }
221 
fromFile(StringRef absoluteFilepath)222 llvm::Expected<URIForFile> URIForFile::fromFile(StringRef absoluteFilepath) {
223   llvm::Expected<std::string> uri = uriFromAbsolutePath(absoluteFilepath);
224   if (!uri)
225     return uri.takeError();
226   return fromURI(*uri);
227 }
228 
fromJSON(const llvm::json::Value & value,URIForFile & result,llvm::json::Path path)229 bool mlir::lsp::fromJSON(const llvm::json::Value &value, URIForFile &result,
230                          llvm::json::Path path) {
231   if (Optional<StringRef> str = value.getAsString()) {
232     llvm::Expected<URIForFile> expectedURI = URIForFile::fromURI(*str);
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 
toJSON(const URIForFile & value)244 llvm::json::Value mlir::lsp::toJSON(const URIForFile &value) {
245   return value.uri();
246 }
247 
operator <<(raw_ostream & os,const URIForFile & value)248 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const URIForFile &value) {
249   return os << value.uri();
250 }
251 
252 //===----------------------------------------------------------------------===//
253 // ClientCapabilities
254 //===----------------------------------------------------------------------===//
255 
fromJSON(const llvm::json::Value & value,ClientCapabilities & result,llvm::json::Path path)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 
fromJSON(const llvm::json::Value & value,TraceLevel & result,llvm::json::Path path)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 
fromJSON(const llvm::json::Value & value,InitializeParams & result,llvm::json::Path path)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 
fromJSON(const llvm::json::Value & value,TextDocumentItem & result,llvm::json::Path path)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 
toJSON(const TextDocumentIdentifier & value)328 llvm::json::Value mlir::lsp::toJSON(const TextDocumentIdentifier &value) {
329   return llvm::json::Object{{"uri", value.uri}};
330 }
331 
fromJSON(const llvm::json::Value & value,TextDocumentIdentifier & result,llvm::json::Path path)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
toJSON(const VersionedTextDocumentIdentifier & value)344 mlir::lsp::toJSON(const VersionedTextDocumentIdentifier &value) {
345   return llvm::json::Object{
346       {"uri", value.uri},
347       {"version", value.version},
348   };
349 }
350 
fromJSON(const llvm::json::Value & value,VersionedTextDocumentIdentifier & result,llvm::json::Path path)351 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
352                          VersionedTextDocumentIdentifier &result,
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 
fromJSON(const llvm::json::Value & value,Position & result,llvm::json::Path path)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 
toJSON(const Position & value)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 
operator <<(raw_ostream & os,const Position & value)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 
fromJSON(const llvm::json::Value & value,Range & result,llvm::json::Path path)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 
toJSON(const Range & value)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 
operator <<(raw_ostream & os,const Range & value)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 
fromJSON(const llvm::json::Value & value,Location & result,llvm::json::Path path)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 
toJSON(const Location & value)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 
operator <<(raw_ostream & os,const Location & value)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 
fromJSON(const llvm::json::Value & value,TextDocumentPositionParams & result,llvm::json::Path path)426 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
427                          TextDocumentPositionParams &result,
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 
fromJSON(const llvm::json::Value & value,ReferenceContext & result,llvm::json::Path path)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 
fromJSON(const llvm::json::Value & value,ReferenceParams & result,llvm::json::Path path)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 
fromJSON(const llvm::json::Value & value,DidOpenTextDocumentParams & result,llvm::json::Path path)456 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
457                          DidOpenTextDocumentParams &result,
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 
fromJSON(const llvm::json::Value & value,DidCloseTextDocumentParams & result,llvm::json::Path path)467 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
468                          DidCloseTextDocumentParams &result,
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 
478 LogicalResult
applyTo(std::string & contents) const479 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 
applyTo(ArrayRef<TextDocumentContentChangeEvent> changes,std::string & contents)500 LogicalResult TextDocumentContentChangeEvent::applyTo(
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 
fromJSON(const llvm::json::Value & value,TextDocumentContentChangeEvent & result,llvm::json::Path path)508 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
509                          TextDocumentContentChangeEvent &result,
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 
fromJSON(const llvm::json::Value & value,DidChangeTextDocumentParams & result,llvm::json::Path path)516 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
517                          DidChangeTextDocumentParams &result,
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 
toTextKind(MarkupKind kind)528 static llvm::StringRef toTextKind(MarkupKind kind) {
529   switch (kind) {
530   case MarkupKind::PlainText:
531     return "plaintext";
532   case MarkupKind::Markdown:
533     return "markdown";
534   }
535   llvm_unreachable("Invalid MarkupKind");
536 }
537 
operator <<(raw_ostream & os,MarkupKind kind)538 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, MarkupKind kind) {
539   return os << toTextKind(kind);
540 }
541 
toJSON(const MarkupContent & mc)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 
toJSON(const Hover & hover)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 
toJSON(const DocumentSymbol & symbol)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 
fromJSON(const llvm::json::Value & value,DocumentSymbolParams & result,llvm::json::Path path)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 
fromJSON(const llvm::json::Value & value,DiagnosticRelatedInformation & result,llvm::json::Path path)594 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
595                          DiagnosticRelatedInformation &result,
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 
toJSON(const DiagnosticRelatedInformation & info)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 
toJSON(const Diagnostic & diag)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 
fromJSON(const llvm::json::Value & value,Diagnostic & result,llvm::json::Path path)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 
toJSON(const PublishDiagnosticsParams & params)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 
fromJSON(const llvm::json::Value & value,TextEdit & result,llvm::json::Path path)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 
toJSON(const TextEdit & value)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 
operator <<(raw_ostream & os,const TextEdit & value)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 
fromJSON(const llvm::json::Value & value,CompletionItemKind & result,llvm::json::Path path)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 
adjustKindToCapability(CompletionItemKind kind,CompletionItemKindBitset & supportedCompletionItemKinds)696 CompletionItemKind mlir::lsp::adjustKindToCapability(
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) {
707   case CompletionItemKind::Folder:
708     return CompletionItemKind::File;
709   case CompletionItemKind::EnumMember:
710     return CompletionItemKind::Enum;
711   case CompletionItemKind::Struct:
712     return CompletionItemKind::Class;
713   default:
714     return CompletionItemKind::Text;
715   }
716 }
717 
fromJSON(const llvm::json::Value & value,CompletionItemKindBitset & result,llvm::json::Path path)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 
toJSON(const CompletionItem & value)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;
751   if (value.insertTextFormat != InsertTextFormat::Missing)
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 
operator <<(raw_ostream & os,const CompletionItem & value)764 raw_ostream &mlir::lsp::operator<<(raw_ostream &os,
765                                    const CompletionItem &value) {
766   return os << value.label << " - " << toJSON(value);
767 }
768 
operator <(const CompletionItem & lhs,const CompletionItem & rhs)769 bool mlir::lsp::operator<(const CompletionItem &lhs,
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 
toJSON(const CompletionList & value)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 
fromJSON(const llvm::json::Value & value,CompletionContext & result,llvm::json::Path path)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 
fromJSON(const llvm::json::Value & value,CompletionParams & result,llvm::json::Path path)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 
toJSON(const ParameterInformation & value)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 
toJSON(const SignatureInformation & value)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 
operator <<(raw_ostream & os,const SignatureInformation & value)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 
toJSON(const SignatureHelp & value)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 
fromJSON(const llvm::json::Value & value,DocumentLinkParams & result,llvm::json::Path path)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 
toJSON(const DocumentLink & value)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 
fromJSON(const llvm::json::Value & value,InlayHintsParams & result,llvm::json::Path path)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 
toJSON(const InlayHint & value)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 }
operator ==(const InlayHint & lhs,const InlayHint & rhs)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 }
operator <(const InlayHint & lhs,const InlayHint & rhs)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 
operator <<(llvm::raw_ostream & os,InlayHintKind value)920 llvm::raw_ostream &mlir::lsp::operator<<(llvm::raw_ostream &os,
921                                          InlayHintKind value) {
922   switch (value) {
923   case InlayHintKind::Parameter:
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 
fromJSON(const llvm::json::Value & value,CodeActionContext & result,llvm::json::Path path)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 
fromJSON(const llvm::json::Value & value,CodeActionParams & result,llvm::json::Path path)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 
fromJSON(const llvm::json::Value & value,WorkspaceEdit & result,llvm::json::Path path)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 
toJSON(const WorkspaceEdit & value)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 
toJSON(const CodeAction & value)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 }
992