|  | //===--- Protocol.cpp - Language Server Protocol Implementation -----------===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // This file contains the serialization code for the LSP structs. | 
|  | // FIXME: This is extremely repetetive and ugly. Is there a better way? | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "Protocol.h" | 
|  |  | 
|  | #include "clang/Basic/LLVM.h" | 
|  | #include "llvm/ADT/SmallString.h" | 
|  | #include "llvm/Support/Format.h" | 
|  | #include "llvm/Support/FormatVariadic.h" | 
|  | #include "llvm/Support/Path.h" | 
|  | #include "llvm/Support/raw_ostream.h" | 
|  |  | 
|  | using namespace clang; | 
|  | using namespace clang::clangd; | 
|  |  | 
|  | URI URI::fromUri(llvm::StringRef uri) { | 
|  | URI Result; | 
|  | Result.uri = uri; | 
|  | uri.consume_front("file://"); | 
|  | // Also trim authority-less URIs | 
|  | uri.consume_front("file:"); | 
|  | // For Windows paths e.g. /X: | 
|  | if (uri.size() > 2 && uri[0] == '/' && uri[2] == ':') | 
|  | uri.consume_front("/"); | 
|  | // Make sure that file paths are in native separators | 
|  | Result.file = llvm::sys::path::convert_to_slash(uri); | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | URI URI::fromFile(llvm::StringRef file) { | 
|  | using namespace llvm::sys; | 
|  | URI Result; | 
|  | Result.file = file; | 
|  | Result.uri = "file://"; | 
|  | // For Windows paths e.g. X: | 
|  | if (file.size() > 1 && file[1] == ':') | 
|  | Result.uri += "/"; | 
|  | // Make sure that uri paths are with posix separators | 
|  | Result.uri += path::convert_to_slash(file, path::Style::posix); | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | json::Expr URI::unparse(const URI &U) { return U.uri; } | 
|  |  | 
|  | llvm::Optional<TextDocumentIdentifier> | 
|  | TextDocumentIdentifier::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | TextDocumentIdentifier Result; | 
|  | if (auto U = O->getString("uri")) | 
|  | Result.uri = URI::parse(*U); | 
|  | // FIXME: parse 'version', but only for VersionedTextDocumentIdentifiers. | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | llvm::Optional<Position> Position::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | Position Result; | 
|  | if (auto L = O->getInteger("line")) | 
|  | Result.line = *L; | 
|  | if (auto C = O->getInteger("character")) | 
|  | Result.character = *C; | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | json::Expr Position::unparse(const Position &P) { | 
|  | return json::obj{ | 
|  | {"line", P.line}, | 
|  | {"character", P.character}, | 
|  | }; | 
|  | } | 
|  |  | 
|  | llvm::Optional<Range> Range::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | Range Result; | 
|  | if (auto *S = O->get("start")) { | 
|  | if (auto P = Position::parse(*S)) | 
|  | Result.start = std::move(*P); | 
|  | else | 
|  | return None; | 
|  | } | 
|  | if (auto *E = O->get("end")) { | 
|  | if (auto P = Position::parse(*E)) | 
|  | Result.end = std::move(*P); | 
|  | else | 
|  | return None; | 
|  | } | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | json::Expr Range::unparse(const Range &P) { | 
|  | return json::obj{ | 
|  | {"start", P.start}, | 
|  | {"end", P.end}, | 
|  | }; | 
|  | } | 
|  |  | 
|  | json::Expr Location::unparse(const Location &P) { | 
|  | return json::obj{ | 
|  | {"uri", P.uri}, | 
|  | {"range", P.range}, | 
|  | }; | 
|  | } | 
|  |  | 
|  | llvm::Optional<TextDocumentItem> | 
|  | TextDocumentItem::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | TextDocumentItem Result; | 
|  | if (auto U = O->getString("uri")) | 
|  | Result.uri = URI::parse(*U); | 
|  | if (auto L = O->getString("languageId")) | 
|  | Result.languageId = *L; | 
|  | if (auto V = O->getInteger("version")) | 
|  | Result.version = *V; | 
|  | if (auto T = O->getString("text")) | 
|  | Result.text = *T; | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | llvm::Optional<Metadata> Metadata::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | Metadata Result; | 
|  | if (auto *Flags = O->getArray("extraFlags")) | 
|  | for (auto &F : *Flags) { | 
|  | if (auto S = F.asString()) | 
|  | Result.extraFlags.push_back(*S); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | llvm::Optional<TextEdit> TextEdit::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | TextEdit Result; | 
|  | if (auto *R = O->get("range")) { | 
|  | if (auto Parsed = Range::parse(*R)) | 
|  | Result.range = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | if (auto T = O->getString("newText")) | 
|  | Result.newText = *T; | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | json::Expr TextEdit::unparse(const TextEdit &P) { | 
|  | return json::obj{ | 
|  | {"range", P.range}, | 
|  | {"newText", P.newText}, | 
|  | }; | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | TraceLevel getTraceLevel(llvm::StringRef TraceLevelStr) { | 
|  | if (TraceLevelStr == "off") | 
|  | return TraceLevel::Off; | 
|  | else if (TraceLevelStr == "messages") | 
|  | return TraceLevel::Messages; | 
|  | else if (TraceLevelStr == "verbose") | 
|  | return TraceLevel::Verbose; | 
|  | return TraceLevel::Off; | 
|  | } | 
|  | } // namespace | 
|  |  | 
|  | llvm::Optional<InitializeParams> | 
|  | InitializeParams::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | InitializeParams Result; | 
|  | if (auto P = O->getInteger("processId")) | 
|  | Result.processId = *P; | 
|  | if (auto R = O->getString("rootPath")) | 
|  | Result.rootPath = *R; | 
|  | if (auto R = O->getString("rootUri")) | 
|  | Result.rootUri = URI::parse(*R); | 
|  | if (auto T = O->getString("trace")) | 
|  | Result.trace = getTraceLevel(*T); | 
|  | // initializationOptions, capabilities unused | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | llvm::Optional<DidOpenTextDocumentParams> | 
|  | DidOpenTextDocumentParams::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | DidOpenTextDocumentParams Result; | 
|  | if (auto *D = O->get("textDocument")) { | 
|  | if (auto Parsed = TextDocumentItem::parse(*D)) | 
|  | Result.textDocument = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | if (auto *M = O->get("metadata")) { | 
|  | if (auto Parsed = Metadata::parse(*M)) | 
|  | Result.metadata = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | llvm::Optional<DidCloseTextDocumentParams> | 
|  | DidCloseTextDocumentParams::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | DidCloseTextDocumentParams Result; | 
|  | if (auto *D = O->get("textDocument")) { | 
|  | if (auto Parsed = TextDocumentIdentifier::parse(*D)) | 
|  | Result.textDocument = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | llvm::Optional<DidChangeTextDocumentParams> | 
|  | DidChangeTextDocumentParams::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | DidChangeTextDocumentParams Result; | 
|  | if (auto *D = O->get("textDocument")) { | 
|  | if (auto Parsed = TextDocumentIdentifier::parse(*D)) | 
|  | Result.textDocument = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | if (auto *A = O->getArray("contentChanges")) | 
|  | for (auto &E : *A) { | 
|  | if (auto Parsed = TextDocumentContentChangeEvent::parse(E)) | 
|  | Result.contentChanges.push_back(std::move(*Parsed)); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | llvm::Optional<FileEvent> FileEvent::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | FileEvent Result; | 
|  | if (auto U = O->getString("uri")) | 
|  | Result.uri = URI::parse(*U); | 
|  | if (auto T = O->getInteger("type")) { | 
|  | if (*T < static_cast<int>(FileChangeType::Created) || | 
|  | *T > static_cast<int>(FileChangeType::Deleted)) | 
|  | return llvm::None; | 
|  | Result.type = static_cast<FileChangeType>(*T); | 
|  | } | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | llvm::Optional<DidChangeWatchedFilesParams> | 
|  | DidChangeWatchedFilesParams::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | DidChangeWatchedFilesParams Result; | 
|  | if (auto *C = O->getArray("changes")) | 
|  | for (auto &E : *C) { | 
|  | if (auto Parsed = FileEvent::parse(E)) | 
|  | Result.changes.push_back(std::move(*Parsed)); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | llvm::Optional<TextDocumentContentChangeEvent> | 
|  | TextDocumentContentChangeEvent::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | TextDocumentContentChangeEvent Result; | 
|  | if (auto T = O->getString("text")) | 
|  | Result.text = *T; | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | llvm::Optional<FormattingOptions> | 
|  | FormattingOptions::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | FormattingOptions Result; | 
|  | if (auto T = O->getInteger("tabSize")) | 
|  | Result.tabSize = *T; | 
|  | if (auto I = O->getBoolean("insertSpaces")) | 
|  | Result.insertSpaces = *I; | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | json::Expr FormattingOptions::unparse(const FormattingOptions &P) { | 
|  | return json::obj{ | 
|  | {"tabSize", P.tabSize}, | 
|  | {"insertSpaces", P.insertSpaces}, | 
|  | }; | 
|  | } | 
|  |  | 
|  | llvm::Optional<DocumentRangeFormattingParams> | 
|  | DocumentRangeFormattingParams::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | DocumentRangeFormattingParams Result; | 
|  | if (auto *D = O->get("textDocument")) { | 
|  | if (auto Parsed = TextDocumentIdentifier::parse(*D)) | 
|  | Result.textDocument = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | if (auto *R = O->get("range")) { | 
|  | if (auto Parsed = Range::parse(*R)) | 
|  | Result.range = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | if (auto *F = O->get("options")) { | 
|  | if (auto Parsed = FormattingOptions::parse(*F)) | 
|  | Result.options = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | llvm::Optional<DocumentOnTypeFormattingParams> | 
|  | DocumentOnTypeFormattingParams::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | DocumentOnTypeFormattingParams Result; | 
|  | if (auto Ch = O->getString("ch")) | 
|  | Result.ch = *Ch; | 
|  | if (auto *D = O->get("textDocument")) { | 
|  | if (auto Parsed = TextDocumentIdentifier::parse(*D)) | 
|  | Result.textDocument = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | if (auto *P = O->get("position")) { | 
|  | if (auto Parsed = Position::parse(*P)) | 
|  | Result.position = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | if (auto *F = O->get("options")) { | 
|  | if (auto Parsed = FormattingOptions::parse(*F)) | 
|  | Result.options = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | llvm::Optional<DocumentFormattingParams> | 
|  | DocumentFormattingParams::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | DocumentFormattingParams Result; | 
|  | if (auto *D = O->get("textDocument")) { | 
|  | if (auto Parsed = TextDocumentIdentifier::parse(*D)) | 
|  | Result.textDocument = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | if (auto *F = O->get("options")) { | 
|  | if (auto Parsed = FormattingOptions::parse(*F)) | 
|  | Result.options = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | llvm::Optional<Diagnostic> Diagnostic::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | Diagnostic Result; | 
|  | if (auto *R = O->get("range")) { | 
|  | if (auto Parsed = Range::parse(*R)) | 
|  | Result.range = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | if (auto S = O->getInteger("severity")) | 
|  | Result.severity = *S; | 
|  | if (auto M = O->getString("message")) | 
|  | Result.message = *M; | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | llvm::Optional<CodeActionContext> | 
|  | CodeActionContext::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | CodeActionContext Result; | 
|  | if (auto *D = O->getArray("diagnostics")) | 
|  | for (auto &E : *D) { | 
|  | if (auto Parsed = Diagnostic::parse(E)) | 
|  | Result.diagnostics.push_back(std::move(*Parsed)); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | llvm::Optional<CodeActionParams> | 
|  | CodeActionParams::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | CodeActionParams Result; | 
|  | if (auto *D = O->get("textDocument")) { | 
|  | if (auto Parsed = TextDocumentIdentifier::parse(*D)) | 
|  | Result.textDocument = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | if (auto *R = O->get("range")) { | 
|  | if (auto Parsed = Range::parse(*R)) | 
|  | Result.range = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | if (auto *R = O->get("context")) { | 
|  | if (auto Parsed = CodeActionContext::parse(*R)) | 
|  | Result.context = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | llvm::Optional<std::map<std::string, std::vector<TextEdit>>> | 
|  | parseWorkspaceEditChange(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | std::map<std::string, std::vector<TextEdit>> Result; | 
|  | for (const auto &KV : *O) { | 
|  | auto &Values = Result[StringRef(KV.first)]; | 
|  | if (auto *Edits = KV.second.asArray()) | 
|  | for (auto &Edit : *Edits) { | 
|  | if (auto Parsed = TextEdit::parse(Edit)) | 
|  | Values.push_back(std::move(*Parsed)); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | llvm::Optional<WorkspaceEdit> WorkspaceEdit::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | WorkspaceEdit Result; | 
|  | if (auto *C = O->get("changes")) { | 
|  | if (auto Parsed = parseWorkspaceEditChange(*C)) | 
|  | Result.changes = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | const std::string ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND = | 
|  | "clangd.applyFix"; | 
|  |  | 
|  | llvm::Optional<ExecuteCommandParams> | 
|  | ExecuteCommandParams::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | ExecuteCommandParams Result; | 
|  | if (auto Command = O->getString("command")) | 
|  | Result.command = *Command; | 
|  | auto Args = O->getArray("arguments"); | 
|  |  | 
|  | if (Result.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND) { | 
|  | if (!Args || Args->size() != 1) | 
|  | return llvm::None; | 
|  | if (auto Parsed = WorkspaceEdit::parse(Args->front())) | 
|  | Result.workspaceEdit = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } else | 
|  | return llvm::None; // Unrecognized command. | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | json::Expr WorkspaceEdit::unparse(const WorkspaceEdit &WE) { | 
|  | if (!WE.changes) | 
|  | return json::obj{}; | 
|  | json::obj FileChanges; | 
|  | for (auto &Change : *WE.changes) | 
|  | FileChanges[Change.first] = json::ary(Change.second); | 
|  | return json::obj{{"changes", std::move(FileChanges)}}; | 
|  | } | 
|  |  | 
|  | json::Expr | 
|  | ApplyWorkspaceEditParams::unparse(const ApplyWorkspaceEditParams &Params) { | 
|  | return json::obj{{"edit", Params.edit}}; | 
|  | } | 
|  |  | 
|  | llvm::Optional<TextDocumentPositionParams> | 
|  | TextDocumentPositionParams::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | TextDocumentPositionParams Result; | 
|  | if (auto *D = O->get("textDocument")) { | 
|  | if (auto Parsed = TextDocumentIdentifier::parse(*D)) | 
|  | Result.textDocument = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | if (auto *P = O->get("position")) { | 
|  | if (auto Parsed = Position::parse(*P)) | 
|  | Result.position = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | json::Expr CompletionItem::unparse(const CompletionItem &CI) { | 
|  | assert(!CI.label.empty() && "completion item label is required"); | 
|  | json::obj Result{{"label", CI.label}}; | 
|  | if (CI.kind != CompletionItemKind::Missing) | 
|  | Result["kind"] = static_cast<int>(CI.kind); | 
|  | if (!CI.detail.empty()) | 
|  | Result["detail"] = CI.detail; | 
|  | if (!CI.documentation.empty()) | 
|  | Result["documentation"] = CI.documentation; | 
|  | if (!CI.sortText.empty()) | 
|  | Result["sortText"] = CI.sortText; | 
|  | if (!CI.filterText.empty()) | 
|  | Result["filterText"] = CI.filterText; | 
|  | if (!CI.insertText.empty()) | 
|  | Result["insertText"] = CI.insertText; | 
|  | if (CI.insertTextFormat != InsertTextFormat::Missing) | 
|  | Result["insertTextFormat"] = static_cast<int>(CI.insertTextFormat); | 
|  | if (CI.textEdit) | 
|  | Result["textEdit"] = *CI.textEdit; | 
|  | if (!CI.additionalTextEdits.empty()) | 
|  | Result["additionalTextEdits"] = json::ary(CI.additionalTextEdits); | 
|  | return std::move(Result); | 
|  | } | 
|  |  | 
|  | bool clangd::operator<(const CompletionItem &L, const CompletionItem &R) { | 
|  | return (L.sortText.empty() ? L.label : L.sortText) < | 
|  | (R.sortText.empty() ? R.label : R.sortText); | 
|  | } | 
|  |  | 
|  | json::Expr CompletionList::unparse(const CompletionList &L) { | 
|  | return json::obj{ | 
|  | {"isIncomplete", L.isIncomplete}, | 
|  | {"items", json::ary(L.items)}, | 
|  | }; | 
|  | } | 
|  |  | 
|  | json::Expr ParameterInformation::unparse(const ParameterInformation &PI) { | 
|  | assert(!PI.label.empty() && "parameter information label is required"); | 
|  | json::obj Result{{"label", PI.label}}; | 
|  | if (!PI.documentation.empty()) | 
|  | Result["documentation"] = PI.documentation; | 
|  | return std::move(Result); | 
|  | } | 
|  |  | 
|  | json::Expr SignatureInformation::unparse(const SignatureInformation &SI) { | 
|  | assert(!SI.label.empty() && "signature information label is required"); | 
|  | json::obj Result{ | 
|  | {"label", SI.label}, | 
|  | {"parameters", json::ary(SI.parameters)}, | 
|  | }; | 
|  | if (!SI.documentation.empty()) | 
|  | Result["documentation"] = SI.documentation; | 
|  | return std::move(Result); | 
|  | } | 
|  |  | 
|  | json::Expr SignatureHelp::unparse(const SignatureHelp &SH) { | 
|  | assert(SH.activeSignature >= 0 && | 
|  | "Unexpected negative value for number of active signatures."); | 
|  | assert(SH.activeParameter >= 0 && | 
|  | "Unexpected negative value for active parameter index"); | 
|  | return json::obj{ | 
|  | {"activeSignature", SH.activeSignature}, | 
|  | {"activeParameter", SH.activeParameter}, | 
|  | {"signatures", json::ary(SH.signatures)}, | 
|  | }; | 
|  | } | 
|  |  | 
|  | llvm::Optional<RenameParams> RenameParams::parse(const json::Expr &Params) { | 
|  | const json::obj *O = Params.asObject(); | 
|  | if (!O) | 
|  | return None; | 
|  |  | 
|  | RenameParams Result; | 
|  | if (auto *D = O->get("textDocument")) { | 
|  | if (auto Parsed = TextDocumentIdentifier::parse(*D)) | 
|  | Result.textDocument = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | if (auto *P = O->get("position")) { | 
|  | if (auto Parsed = Position::parse(*P)) | 
|  | Result.position = std::move(*Parsed); | 
|  | else | 
|  | return llvm::None; | 
|  | } | 
|  | if (auto N = O->getString("newName")) | 
|  | Result.newName = *N; | 
|  | return Result; | 
|  | } |