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