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