//===--- 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/raw_ostream.h" using namespace clang::clangd; llvm::Optional TextDocumentIdentifier::parse(llvm::yaml::MappingNode *Params) { TextDocumentIdentifier Result; for (auto &NextKeyValue : *Params) { auto *KeyString = dyn_cast(NextKeyValue.getKey()); if (!KeyString) return llvm::None; llvm::SmallString<10> KeyStorage; StringRef KeyValue = KeyString->getValue(KeyStorage); auto *Value = dyn_cast_or_null(NextKeyValue.getValue()); if (!Value) return llvm::None; llvm::SmallString<10> Storage; if (KeyValue == "uri") { Result.uri = Value->getValue(Storage); } else if (KeyValue == "version") { // FIXME: parse version, but only for VersionedTextDocumentIdentifiers. } else { return llvm::None; } } return Result; } llvm::Optional Position::parse(llvm::yaml::MappingNode *Params) { Position Result; for (auto &NextKeyValue : *Params) { auto *KeyString = dyn_cast(NextKeyValue.getKey()); if (!KeyString) return llvm::None; llvm::SmallString<10> KeyStorage; StringRef KeyValue = KeyString->getValue(KeyStorage); auto *Value = dyn_cast_or_null(NextKeyValue.getValue()); if (!Value) return llvm::None; llvm::SmallString<10> Storage; if (KeyValue == "line") { long long Val; if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val)) return llvm::None; Result.line = Val; } else if (KeyValue == "character") { long long Val; if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val)) return llvm::None; Result.character = Val; } else { return llvm::None; } } return Result; } std::string Position::unparse(const Position &P) { std::string Result; llvm::raw_string_ostream(Result) << llvm::format(R"({"line": %d, "character": %d})", P.line, P.character); return Result; } llvm::Optional Range::parse(llvm::yaml::MappingNode *Params) { Range Result; for (auto &NextKeyValue : *Params) { auto *KeyString = dyn_cast(NextKeyValue.getKey()); if (!KeyString) return llvm::None; llvm::SmallString<10> KeyStorage; StringRef KeyValue = KeyString->getValue(KeyStorage); auto *Value = dyn_cast_or_null(NextKeyValue.getValue()); if (!Value) return llvm::None; llvm::SmallString<10> Storage; if (KeyValue == "start") { auto Parsed = Position::parse(Value); if (!Parsed) return llvm::None; Result.start = std::move(*Parsed); } else if (KeyValue == "end") { auto Parsed = Position::parse(Value); if (!Parsed) return llvm::None; Result.end = std::move(*Parsed); } else { return llvm::None; } } return Result; } std::string Range::unparse(const Range &P) { std::string Result; llvm::raw_string_ostream(Result) << llvm::format( R"({"start": %s, "end": %s})", Position::unparse(P.start).c_str(), Position::unparse(P.end).c_str()); return Result; } llvm::Optional TextDocumentItem::parse(llvm::yaml::MappingNode *Params) { TextDocumentItem Result; for (auto &NextKeyValue : *Params) { auto *KeyString = dyn_cast(NextKeyValue.getKey()); if (!KeyString) return llvm::None; llvm::SmallString<10> KeyStorage; StringRef KeyValue = KeyString->getValue(KeyStorage); auto *Value = dyn_cast_or_null(NextKeyValue.getValue()); if (!Value) return llvm::None; llvm::SmallString<10> Storage; if (KeyValue == "uri") { Result.uri = Value->getValue(Storage); } else if (KeyValue == "languageId") { Result.languageId = Value->getValue(Storage); } else if (KeyValue == "version") { long long Val; if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val)) return llvm::None; Result.version = Val; } else if (KeyValue == "text") { Result.text = Value->getValue(Storage); } else { return llvm::None; } } return Result; } llvm::Optional TextEdit::parse(llvm::yaml::MappingNode *Params) { TextEdit Result; for (auto &NextKeyValue : *Params) { auto *KeyString = dyn_cast(NextKeyValue.getKey()); if (!KeyString) return llvm::None; llvm::SmallString<10> KeyStorage; StringRef KeyValue = KeyString->getValue(KeyStorage); auto *Value = NextKeyValue.getValue(); llvm::SmallString<10> Storage; if (KeyValue == "range") { auto *Map = dyn_cast(Value); if (!Map) return llvm::None; auto Parsed = Range::parse(Map); if (!Parsed) return llvm::None; Result.range = std::move(*Parsed); } else if (KeyValue == "newText") { auto *Node = dyn_cast(Value); if (!Node) return llvm::None; Result.newText = Node->getValue(Storage); } else { return llvm::None; } } return Result; } std::string TextEdit::unparse(const TextEdit &P) { std::string Result; llvm::raw_string_ostream(Result) << llvm::format( R"({"range": %s, "newText": "%s"})", Range::unparse(P.range).c_str(), llvm::yaml::escape(P.newText).c_str()); return Result; } llvm::Optional DidOpenTextDocumentParams::parse(llvm::yaml::MappingNode *Params) { DidOpenTextDocumentParams Result; for (auto &NextKeyValue : *Params) { auto *KeyString = dyn_cast(NextKeyValue.getKey()); if (!KeyString) return llvm::None; llvm::SmallString<10> KeyStorage; StringRef KeyValue = KeyString->getValue(KeyStorage); auto *Value = dyn_cast_or_null(NextKeyValue.getValue()); if (!Value) return llvm::None; llvm::SmallString<10> Storage; if (KeyValue == "textDocument") { auto Parsed = TextDocumentItem::parse(Value); if (!Parsed) return llvm::None; Result.textDocument = std::move(*Parsed); } else { return llvm::None; } } return Result; } llvm::Optional DidChangeTextDocumentParams::parse(llvm::yaml::MappingNode *Params) { DidChangeTextDocumentParams Result; for (auto &NextKeyValue : *Params) { auto *KeyString = dyn_cast(NextKeyValue.getKey()); if (!KeyString) return llvm::None; llvm::SmallString<10> KeyStorage; StringRef KeyValue = KeyString->getValue(KeyStorage); auto *Value = NextKeyValue.getValue(); llvm::SmallString<10> Storage; if (KeyValue == "textDocument") { auto *Map = dyn_cast(Value); if (!Map) return llvm::None; auto Parsed = TextDocumentIdentifier::parse(Map); if (!Parsed) return llvm::None; Result.textDocument = std::move(*Parsed); } else if (KeyValue == "contentChanges") { auto *Seq = dyn_cast(Value); if (!Seq) return llvm::None; for (auto &Item : *Seq) { auto *I = dyn_cast(&Item); if (!I) return llvm::None; auto Parsed = TextDocumentContentChangeEvent::parse(I); if (!Parsed) return llvm::None; Result.contentChanges.push_back(std::move(*Parsed)); } } else { return llvm::None; } } return Result; } llvm::Optional TextDocumentContentChangeEvent::parse(llvm::yaml::MappingNode *Params) { TextDocumentContentChangeEvent Result; for (auto &NextKeyValue : *Params) { auto *KeyString = dyn_cast(NextKeyValue.getKey()); if (!KeyString) return llvm::None; llvm::SmallString<10> KeyStorage; StringRef KeyValue = KeyString->getValue(KeyStorage); auto *Value = dyn_cast_or_null(NextKeyValue.getValue()); if (!Value) return llvm::None; llvm::SmallString<10> Storage; if (KeyValue == "text") { Result.text = Value->getValue(Storage); } else { return llvm::None; } } return Result; } llvm::Optional FormattingOptions::parse(llvm::yaml::MappingNode *Params) { FormattingOptions Result; for (auto &NextKeyValue : *Params) { auto *KeyString = dyn_cast(NextKeyValue.getKey()); if (!KeyString) return llvm::None; llvm::SmallString<10> KeyStorage; StringRef KeyValue = KeyString->getValue(KeyStorage); auto *Value = dyn_cast_or_null(NextKeyValue.getValue()); if (!Value) return llvm::None; llvm::SmallString<10> Storage; if (KeyValue == "tabSize") { long long Val; if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val)) return llvm::None; Result.tabSize = Val; } else if (KeyValue == "insertSpaces") { long long Val; StringRef Str = Value->getValue(Storage); if (llvm::getAsSignedInteger(Str, 0, Val)) { if (Str == "true") Val = 1; else if (Str == "false") Val = 0; else return llvm::None; } Result.insertSpaces = Val; } else { return llvm::None; } } return Result; } std::string FormattingOptions::unparse(const FormattingOptions &P) { std::string Result; llvm::raw_string_ostream(Result) << llvm::format( R"({"tabSize": %d, "insertSpaces": %d})", P.tabSize, P.insertSpaces); return Result; } llvm::Optional DocumentRangeFormattingParams::parse(llvm::yaml::MappingNode *Params) { DocumentRangeFormattingParams Result; for (auto &NextKeyValue : *Params) { auto *KeyString = dyn_cast(NextKeyValue.getKey()); if (!KeyString) return llvm::None; llvm::SmallString<10> KeyStorage; StringRef KeyValue = KeyString->getValue(KeyStorage); auto *Value = dyn_cast_or_null(NextKeyValue.getValue()); if (!Value) return llvm::None; llvm::SmallString<10> Storage; if (KeyValue == "textDocument") { auto Parsed = TextDocumentIdentifier::parse(Value); if (!Parsed) return llvm::None; Result.textDocument = std::move(*Parsed); } else if (KeyValue == "range") { auto Parsed = Range::parse(Value); if (!Parsed) return llvm::None; Result.range = std::move(*Parsed); } else if (KeyValue == "options") { auto Parsed = FormattingOptions::parse(Value); if (!Parsed) return llvm::None; Result.options = std::move(*Parsed); } else { return llvm::None; } } return Result; } llvm::Optional DocumentOnTypeFormattingParams::parse(llvm::yaml::MappingNode *Params) { DocumentOnTypeFormattingParams Result; for (auto &NextKeyValue : *Params) { auto *KeyString = dyn_cast(NextKeyValue.getKey()); if (!KeyString) return llvm::None; llvm::SmallString<10> KeyStorage; StringRef KeyValue = KeyString->getValue(KeyStorage); if (KeyValue == "ch") { auto *ScalarValue = dyn_cast_or_null(NextKeyValue.getValue()); if (!ScalarValue) return llvm::None; llvm::SmallString<10> Storage; Result.ch = ScalarValue->getValue(Storage); continue; } auto *Value = dyn_cast_or_null(NextKeyValue.getValue()); if (!Value) return llvm::None; if (KeyValue == "textDocument") { auto Parsed = TextDocumentIdentifier::parse(Value); if (!Parsed) return llvm::None; Result.textDocument = std::move(*Parsed); } else if (KeyValue == "position") { auto Parsed = Position::parse(Value); if (!Parsed) return llvm::None; Result.position = std::move(*Parsed); } else if (KeyValue == "options") { auto Parsed = FormattingOptions::parse(Value); if (!Parsed) return llvm::None; Result.options = std::move(*Parsed); } else { return llvm::None; } } return Result; } llvm::Optional DocumentFormattingParams::parse(llvm::yaml::MappingNode *Params) { DocumentFormattingParams Result; for (auto &NextKeyValue : *Params) { auto *KeyString = dyn_cast(NextKeyValue.getKey()); if (!KeyString) return llvm::None; llvm::SmallString<10> KeyStorage; StringRef KeyValue = KeyString->getValue(KeyStorage); auto *Value = dyn_cast_or_null(NextKeyValue.getValue()); if (!Value) return llvm::None; llvm::SmallString<10> Storage; if (KeyValue == "textDocument") { auto Parsed = TextDocumentIdentifier::parse(Value); if (!Parsed) return llvm::None; Result.textDocument = std::move(*Parsed); } else if (KeyValue == "options") { auto Parsed = FormattingOptions::parse(Value); if (!Parsed) return llvm::None; Result.options = std::move(*Parsed); } else { return llvm::None; } } return Result; }