llvm-project/clang-tools-extra/clangd/Protocol.cpp

1022 lines
31 KiB
C++
Raw Normal View History

//===--- Protocol.cpp - Language Server Protocol Implementation -----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains the serialization code for the LSP structs.
//
//===----------------------------------------------------------------------===//
#include "Protocol.h"
#include "Logger.h"
#include "URI.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
namespace clang {
namespace clangd {
char LSPError::ID;
URIForFile URIForFile::canonicalize(llvm::StringRef AbsPath,
llvm::StringRef TUPath) {
assert(llvm::sys::path::is_absolute(AbsPath) && "the path is relative");
auto Resolved = URI::resolvePath(AbsPath, TUPath);
if (!Resolved) {
elog("URIForFile: failed to resolve path {0} with TU path {1}: "
"{2}.\nUsing unresolved path.",
AbsPath, TUPath, Resolved.takeError());
return URIForFile(AbsPath);
}
return URIForFile(std::move(*Resolved));
}
llvm::Expected<URIForFile> URIForFile::fromURI(const URI &U,
llvm::StringRef HintPath) {
auto Resolved = URI::resolve(U, HintPath);
if (!Resolved)
return Resolved.takeError();
return URIForFile(std::move(*Resolved));
}
bool fromJSON(const llvm::json::Value &E, URIForFile &R) {
if (auto S = E.getAsString()) {
auto Parsed = URI::parse(*S);
if (!Parsed) {
elog("Failed to parse URI {0}: {1}", *S, Parsed.takeError());
return false;
}
if (Parsed->scheme() != "file" && Parsed->scheme() != "test") {
elog("Clangd only supports 'file' URI scheme for workspace files: {0}",
*S);
return false;
}
// "file" and "test" schemes do not require hint path.
auto U = URIForFile::fromURI(*Parsed, /*HintPath=*/"");
if (!U) {
elog("{0}", U.takeError());
return false;
}
R = std::move(*U);
return true;
}
return false;
}
llvm::json::Value toJSON(const URIForFile &U) { return U.uri(); }
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const URIForFile &U) {
return OS << U.uri();
}
llvm::json::Value toJSON(const TextDocumentIdentifier &R) {
return llvm::json::Object{{"uri", R.uri}};
}
bool fromJSON(const llvm::json::Value &Params, TextDocumentIdentifier &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("uri", R.uri);
}
bool fromJSON(const llvm::json::Value &Params, Position &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("line", R.line) && O.map("character", R.character);
}
llvm::json::Value toJSON(const Position &P) {
return llvm::json::Object{
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
{"line", P.line},
{"character", P.character},
};
}
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Position &P) {
return OS << P.line << ':' << P.character;
}
bool fromJSON(const llvm::json::Value &Params, Range &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("start", R.start) && O.map("end", R.end);
}
llvm::json::Value toJSON(const Range &P) {
return llvm::json::Object{
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
{"start", P.start},
{"end", P.end},
};
}
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Range &R) {
return OS << R.start << '-' << R.end;
}
llvm::json::Value toJSON(const Location &P) {
return llvm::json::Object{
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
{"uri", P.uri},
{"range", P.range},
};
}
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Location &L) {
return OS << L.range << '@' << L.uri;
}
bool fromJSON(const llvm::json::Value &Params, TextDocumentItem &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("uri", R.uri) && O.map("languageId", R.languageId) &&
O.map("version", R.version) && O.map("text", R.text);
}
bool fromJSON(const llvm::json::Value &Params, TextEdit &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("range", R.range) && O.map("newText", R.newText);
}
llvm::json::Value toJSON(const TextEdit &P) {
return llvm::json::Object{
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
{"range", P.range},
{"newText", P.newText},
};
}
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const TextEdit &TE) {
OS << TE.range << " => \"";
llvm::printEscapedString(TE.newText, OS);
return OS << '"';
}
bool fromJSON(const llvm::json::Value &E, TraceLevel &Out) {
if (auto S = E.getAsString()) {
if (*S == "off") {
Out = TraceLevel::Off;
return true;
} else if (*S == "messages") {
Out = TraceLevel::Messages;
return true;
} else if (*S == "verbose") {
Out = TraceLevel::Verbose;
return true;
}
}
return false;
}
bool fromJSON(const llvm::json::Value &E, SymbolKind &Out) {
if (auto T = E.getAsInteger()) {
if (*T < static_cast<int>(SymbolKind::File) ||
*T > static_cast<int>(SymbolKind::TypeParameter))
return false;
Out = static_cast<SymbolKind>(*T);
return true;
}
return false;
}
bool fromJSON(const llvm::json::Value &E, SymbolKindBitset &Out) {
if (auto *A = E.getAsArray()) {
for (size_t I = 0; I < A->size(); ++I) {
SymbolKind KindOut;
if (fromJSON((*A)[I], KindOut))
Out.set(size_t(KindOut));
}
return true;
}
return false;
}
SymbolKind adjustKindToCapability(SymbolKind Kind,
SymbolKindBitset &SupportedSymbolKinds) {
auto KindVal = static_cast<size_t>(Kind);
if (KindVal >= SymbolKindMin && KindVal <= SupportedSymbolKinds.size() &&
SupportedSymbolKinds[KindVal])
return Kind;
switch (Kind) {
// Provide some fall backs for common kinds that are close enough.
case SymbolKind::Struct:
return SymbolKind::Class;
case SymbolKind::EnumMember:
return SymbolKind::Enum;
default:
return SymbolKind::String;
}
}
SymbolKind indexSymbolKindToSymbolKind(index::SymbolKind Kind) {
switch (Kind) {
case index::SymbolKind::Unknown:
return SymbolKind::Variable;
case index::SymbolKind::Module:
return SymbolKind::Module;
case index::SymbolKind::Namespace:
return SymbolKind::Namespace;
case index::SymbolKind::NamespaceAlias:
return SymbolKind::Namespace;
case index::SymbolKind::Macro:
return SymbolKind::String;
case index::SymbolKind::Enum:
return SymbolKind::Enum;
case index::SymbolKind::Struct:
return SymbolKind::Struct;
case index::SymbolKind::Class:
return SymbolKind::Class;
case index::SymbolKind::Protocol:
return SymbolKind::Interface;
case index::SymbolKind::Extension:
return SymbolKind::Interface;
case index::SymbolKind::Union:
return SymbolKind::Class;
case index::SymbolKind::TypeAlias:
return SymbolKind::Class;
case index::SymbolKind::Function:
return SymbolKind::Function;
case index::SymbolKind::Variable:
return SymbolKind::Variable;
case index::SymbolKind::Field:
return SymbolKind::Field;
case index::SymbolKind::EnumConstant:
return SymbolKind::EnumMember;
case index::SymbolKind::InstanceMethod:
case index::SymbolKind::ClassMethod:
case index::SymbolKind::StaticMethod:
return SymbolKind::Method;
case index::SymbolKind::InstanceProperty:
case index::SymbolKind::ClassProperty:
case index::SymbolKind::StaticProperty:
return SymbolKind::Property;
case index::SymbolKind::Constructor:
case index::SymbolKind::Destructor:
return SymbolKind::Method;
case index::SymbolKind::ConversionFunction:
return SymbolKind::Function;
case index::SymbolKind::Parameter:
return SymbolKind::Variable;
case index::SymbolKind::Using:
return SymbolKind::Namespace;
}
llvm_unreachable("invalid symbol kind");
}
bool fromJSON(const llvm::json::Value &Params, ClientCapabilities &R) {
const llvm::json::Object *O = Params.getAsObject();
if (!O)
return false;
if (auto *TextDocument = O->getObject("textDocument")) {
if (auto *Diagnostics = TextDocument->getObject("publishDiagnostics")) {
if (auto CategorySupport = Diagnostics->getBoolean("categorySupport"))
R.DiagnosticCategory = *CategorySupport;
if (auto CodeActions = Diagnostics->getBoolean("codeActionsInline"))
R.DiagnosticFixes = *CodeActions;
if (auto RelatedInfo = Diagnostics->getBoolean("relatedInformation"))
R.DiagnosticRelatedInformation = *RelatedInfo;
}
if (auto *Completion = TextDocument->getObject("completion")) {
if (auto *Item = Completion->getObject("completionItem")) {
if (auto SnippetSupport = Item->getBoolean("snippetSupport"))
R.CompletionSnippets = *SnippetSupport;
}
if (auto *ItemKind = Completion->getObject("completionItemKind")) {
if (auto *ValueSet = ItemKind->get("valueSet")) {
R.CompletionItemKinds.emplace();
if (!fromJSON(*ValueSet, *R.CompletionItemKinds))
return false;
}
}
}
if (auto *CodeAction = TextDocument->getObject("codeAction")) {
if (CodeAction->getObject("codeActionLiteralSupport"))
R.CodeActionStructure = true;
}
if (auto *DocumentSymbol = TextDocument->getObject("documentSymbol")) {
if (auto HierarchicalSupport =
DocumentSymbol->getBoolean("hierarchicalDocumentSymbolSupport"))
R.HierarchicalDocumentSymbol = *HierarchicalSupport;
}
if (auto *Hover = TextDocument->getObject("hover")) {
if (auto *ContentFormat = Hover->getArray("contentFormat")) {
for (const auto &Format : *ContentFormat) {
MarkupKind K = MarkupKind::PlainText;
if (fromJSON(Format, K)) {
R.HoverContentFormat = K;
break;
}
}
}
}
}
if (auto *Workspace = O->getObject("workspace")) {
if (auto *Symbol = Workspace->getObject("symbol")) {
if (auto *SymbolKind = Symbol->getObject("symbolKind")) {
if (auto *ValueSet = SymbolKind->get("valueSet")) {
R.WorkspaceSymbolKinds.emplace();
if (!fromJSON(*ValueSet, *R.WorkspaceSymbolKinds))
return false;
}
}
}
}
if (auto *OffsetEncoding = O->get("offsetEncoding")) {
R.offsetEncoding.emplace();
if (!fromJSON(*OffsetEncoding, *R.offsetEncoding))
return false;
}
return true;
}
bool fromJSON(const llvm::json::Value &Params, InitializeParams &R) {
llvm::json::ObjectMapper O(Params);
if (!O)
return false;
// We deliberately don't fail if we can't parse individual fields.
// Failing to handle a slightly malformed initialize would be a disaster.
O.map("processId", R.processId);
O.map("rootUri", R.rootUri);
O.map("rootPath", R.rootPath);
O.map("capabilities", R.capabilities);
O.map("trace", R.trace);
O.map("initializationOptions", R.initializationOptions);
return true;
}
bool fromJSON(const llvm::json::Value &Params, DidOpenTextDocumentParams &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("textDocument", R.textDocument);
}
bool fromJSON(const llvm::json::Value &Params, DidCloseTextDocumentParams &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("textDocument", R.textDocument);
}
bool fromJSON(const llvm::json::Value &Params, DidChangeTextDocumentParams &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("textDocument", R.textDocument) &&
O.map("contentChanges", R.contentChanges) &&
O.map("wantDiagnostics", R.wantDiagnostics);
}
bool fromJSON(const llvm::json::Value &E, FileChangeType &Out) {
if (auto T = E.getAsInteger()) {
if (*T < static_cast<int>(FileChangeType::Created) ||
*T > static_cast<int>(FileChangeType::Deleted))
return false;
Out = static_cast<FileChangeType>(*T);
return true;
}
return false;
}
bool fromJSON(const llvm::json::Value &Params, FileEvent &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("uri", R.uri) && O.map("type", R.type);
}
bool fromJSON(const llvm::json::Value &Params, DidChangeWatchedFilesParams &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("changes", R.changes);
}
bool fromJSON(const llvm::json::Value &Params,
TextDocumentContentChangeEvent &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("range", R.range) && O.map("rangeLength", R.rangeLength) &&
O.map("text", R.text);
}
bool fromJSON(const llvm::json::Value &Params, FormattingOptions &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("tabSize", R.tabSize) &&
O.map("insertSpaces", R.insertSpaces);
}
llvm::json::Value toJSON(const FormattingOptions &P) {
return llvm::json::Object{
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
{"tabSize", P.tabSize},
{"insertSpaces", P.insertSpaces},
};
}
bool fromJSON(const llvm::json::Value &Params,
DocumentRangeFormattingParams &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("textDocument", R.textDocument) &&
O.map("range", R.range) && O.map("options", R.options);
}
bool fromJSON(const llvm::json::Value &Params,
DocumentOnTypeFormattingParams &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("textDocument", R.textDocument) &&
O.map("position", R.position) && O.map("ch", R.ch) &&
O.map("options", R.options);
}
bool fromJSON(const llvm::json::Value &Params, DocumentFormattingParams &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("textDocument", R.textDocument) &&
O.map("options", R.options);
}
bool fromJSON(const llvm::json::Value &Params, DocumentSymbolParams &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("textDocument", R.textDocument);
}
llvm::json::Value toJSON(const DiagnosticRelatedInformation &DRI) {
return llvm::json::Object{
{"location", DRI.location},
{"message", DRI.message},
};
}
llvm::json::Value toJSON(const Diagnostic &D) {
llvm::json::Object Diag{
{"range", D.range},
{"severity", D.severity},
{"message", D.message},
};
if (D.category)
Diag["category"] = *D.category;
if (D.codeActions)
Diag["codeActions"] = D.codeActions;
if (!D.code.empty())
Diag["code"] = D.code;
if (!D.source.empty())
Diag["source"] = D.source;
if (D.relatedInformation)
Diag["relatedInformation"] = *D.relatedInformation;
return std::move(Diag);
}
bool fromJSON(const llvm::json::Value &Params, Diagnostic &R) {
llvm::json::ObjectMapper O(Params);
if (!O || !O.map("range", R.range) || !O.map("message", R.message))
return false;
O.map("severity", R.severity);
O.map("category", R.category);
O.map("code", R.code);
O.map("source", R.source);
return true;
}
bool fromJSON(const llvm::json::Value &Params, CodeActionContext &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("diagnostics", R.diagnostics);
}
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Diagnostic &D) {
OS << D.range << " [";
switch (D.severity) {
case 1:
OS << "error";
break;
case 2:
OS << "warning";
break;
case 3:
OS << "note";
break;
case 4:
OS << "remark";
break;
default:
OS << "diagnostic";
break;
}
return OS << '(' << D.severity << "): " << D.message << "]";
}
bool fromJSON(const llvm::json::Value &Params, CodeActionParams &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("textDocument", R.textDocument) &&
O.map("range", R.range) && O.map("context", R.context);
}
bool fromJSON(const llvm::json::Value &Params, WorkspaceEdit &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("changes", R.changes);
}
const llvm::StringLiteral ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND =
"clangd.applyFix";
const llvm::StringLiteral ExecuteCommandParams::CLANGD_APPLY_TWEAK =
"clangd.applyTweak";
bool fromJSON(const llvm::json::Value &Params, ExecuteCommandParams &R) {
llvm::json::ObjectMapper O(Params);
if (!O || !O.map("command", R.command))
return false;
auto Args = Params.getAsObject()->getArray("arguments");
if (R.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND) {
return Args && Args->size() == 1 &&
fromJSON(Args->front(), R.workspaceEdit);
}
if (R.command == ExecuteCommandParams::CLANGD_APPLY_TWEAK)
return Args && Args->size() == 1 && fromJSON(Args->front(), R.tweakArgs);
return false; // Unrecognized command.
}
llvm::json::Value toJSON(const SymbolInformation &P) {
return llvm::json::Object{
{"name", P.name},
{"kind", static_cast<int>(P.kind)},
{"location", P.location},
{"containerName", P.containerName},
};
}
llvm::raw_ostream &operator<<(llvm::raw_ostream &O,
const SymbolInformation &SI) {
O << SI.containerName << "::" << SI.name << " - " << toJSON(SI);
return O;
}
bool operator==(const SymbolDetails &LHS, const SymbolDetails &RHS) {
return LHS.name == RHS.name && LHS.containerName == RHS.containerName &&
LHS.USR == RHS.USR && LHS.ID == RHS.ID;
}
llvm::json::Value toJSON(const SymbolDetails &P) {
llvm::json::Object Result{{"name", llvm::json::Value(nullptr)},
{"containerName", llvm::json::Value(nullptr)},
{"usr", llvm::json::Value(nullptr)},
{"id", llvm::json::Value(nullptr)}};
if (!P.name.empty())
Result["name"] = P.name;
if (!P.containerName.empty())
Result["containerName"] = P.containerName;
if (!P.USR.empty())
Result["usr"] = P.USR;
if (P.ID.hasValue())
Result["id"] = P.ID.getValue().str();
// Older clang cannot compile 'return Result', even though it is legal.
return llvm::json::Value(std::move(Result));
}
llvm::raw_ostream &operator<<(llvm::raw_ostream &O, const SymbolDetails &S) {
if (!S.containerName.empty()) {
O << S.containerName;
llvm::StringRef ContNameRef;
if (!ContNameRef.endswith("::")) {
O << " ";
}
}
O << S.name << " - " << toJSON(S);
return O;
}
bool fromJSON(const llvm::json::Value &Params, WorkspaceSymbolParams &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("query", R.query);
}
llvm::json::Value toJSON(const Command &C) {
auto Cmd = llvm::json::Object{{"title", C.title}, {"command", C.command}};
if (C.workspaceEdit)
Cmd["arguments"] = {*C.workspaceEdit};
if (C.tweakArgs)
Cmd["arguments"] = {*C.tweakArgs};
return std::move(Cmd);
}
const llvm::StringLiteral CodeAction::QUICKFIX_KIND = "quickfix";
const llvm::StringLiteral CodeAction::REFACTOR_KIND = "refactor";
llvm::json::Value toJSON(const CodeAction &CA) {
auto CodeAction = llvm::json::Object{{"title", CA.title}};
if (CA.kind)
CodeAction["kind"] = *CA.kind;
if (CA.diagnostics)
CodeAction["diagnostics"] = llvm::json::Array(*CA.diagnostics);
if (CA.edit)
CodeAction["edit"] = *CA.edit;
if (CA.command)
CodeAction["command"] = *CA.command;
return std::move(CodeAction);
}
llvm::raw_ostream &operator<<(llvm::raw_ostream &O, const DocumentSymbol &S) {
return O << S.name << " - " << toJSON(S);
}
llvm::json::Value toJSON(const DocumentSymbol &S) {
llvm::json::Object Result{{"name", S.name},
{"kind", static_cast<int>(S.kind)},
{"range", S.range},
{"selectionRange", S.selectionRange}};
if (!S.detail.empty())
Result["detail"] = S.detail;
if (!S.children.empty())
Result["children"] = S.children;
if (S.deprecated)
Result["deprecated"] = true;
// Older gcc cannot compile 'return Result', even though it is legal.
return llvm::json::Value(std::move(Result));
}
llvm::json::Value toJSON(const WorkspaceEdit &WE) {
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
if (!WE.changes)
return llvm::json::Object{};
llvm::json::Object FileChanges;
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
for (auto &Change : *WE.changes)
FileChanges[Change.first] = llvm::json::Array(Change.second);
return llvm::json::Object{{"changes", std::move(FileChanges)}};
}
bool fromJSON(const llvm::json::Value &Params, TweakArgs &A) {
llvm::json::ObjectMapper O(Params);
return O && O.map("file", A.file) && O.map("selection", A.selection) &&
O.map("tweakID", A.tweakID);
}
llvm::json::Value toJSON(const TweakArgs &A) {
return llvm::json::Object{
{"tweakID", A.tweakID}, {"selection", A.selection}, {"file", A.file}};
}
llvm::json::Value toJSON(const ApplyWorkspaceEditParams &Params) {
return llvm::json::Object{{"edit", Params.edit}};
}
bool fromJSON(const llvm::json::Value &Params, TextDocumentPositionParams &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("textDocument", R.textDocument) &&
O.map("position", R.position);
}
bool fromJSON(const llvm::json::Value &Params, CompletionContext &R) {
llvm::json::ObjectMapper O(Params);
if (!O)
return false;
int TriggerKind;
if (!O.map("triggerKind", TriggerKind))
return false;
R.triggerKind = static_cast<CompletionTriggerKind>(TriggerKind);
if (auto *TC = Params.getAsObject()->get("triggerCharacter"))
return fromJSON(*TC, R.triggerCharacter);
return true;
}
bool fromJSON(const llvm::json::Value &Params, CompletionParams &R) {
if (!fromJSON(Params, static_cast<TextDocumentPositionParams &>(R)))
return false;
if (auto *Context = Params.getAsObject()->get("context"))
return fromJSON(*Context, R.context);
return true;
}
static llvm::StringRef toTextKind(MarkupKind Kind) {
switch (Kind) {
case MarkupKind::PlainText:
return "plaintext";
case MarkupKind::Markdown:
return "markdown";
}
llvm_unreachable("Invalid MarkupKind");
}
bool fromJSON(const llvm::json::Value &V, MarkupKind &K) {
auto Str = V.getAsString();
if (!Str) {
elog("Failed to parse markup kind: expected a string");
return false;
}
if (*Str == "plaintext")
K = MarkupKind::PlainText;
else if (*Str == "markdown")
K = MarkupKind::Markdown;
else {
elog("Unknown markup kind: {0}", *Str);
return false;
}
return true;
}
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, MarkupKind K) {
return OS << toTextKind(K);
}
llvm::json::Value toJSON(const MarkupContent &MC) {
if (MC.value.empty())
return nullptr;
return llvm::json::Object{
{"kind", toTextKind(MC.kind)},
{"value", MC.value},
};
}
llvm::json::Value toJSON(const Hover &H) {
llvm::json::Object Result{{"contents", toJSON(H.contents)}};
if (H.range.hasValue())
Result["range"] = toJSON(*H.range);
return std::move(Result);
}
bool fromJSON(const llvm::json::Value &E, CompletionItemKind &Out) {
if (auto T = E.getAsInteger()) {
if (*T < static_cast<int>(CompletionItemKind::Text) ||
*T > static_cast<int>(CompletionItemKind::TypeParameter))
return false;
Out = static_cast<CompletionItemKind>(*T);
return true;
}
return false;
}
CompletionItemKind
adjustKindToCapability(CompletionItemKind Kind,
CompletionItemKindBitset &SupportedCompletionItemKinds) {
auto KindVal = static_cast<size_t>(Kind);
if (KindVal >= CompletionItemKindMin &&
KindVal <= SupportedCompletionItemKinds.size() &&
SupportedCompletionItemKinds[KindVal])
return Kind;
switch (Kind) {
// Provide some fall backs for common kinds that are close enough.
case CompletionItemKind::Folder:
return CompletionItemKind::File;
case CompletionItemKind::EnumMember:
return CompletionItemKind::Enum;
case CompletionItemKind::Struct:
return CompletionItemKind::Class;
default:
return CompletionItemKind::Text;
}
}
bool fromJSON(const llvm::json::Value &E, CompletionItemKindBitset &Out) {
if (auto *A = E.getAsArray()) {
for (size_t I = 0; I < A->size(); ++I) {
CompletionItemKind KindOut;
if (fromJSON((*A)[I], KindOut))
Out.set(size_t(KindOut));
}
return true;
}
return false;
}
llvm::json::Value toJSON(const CompletionItem &CI) {
assert(!CI.label.empty() && "completion item label is required");
llvm::json::Object Result{{"label", CI.label}};
if (CI.kind != CompletionItemKind::Missing)
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
Result["kind"] = static_cast<int>(CI.kind);
if (!CI.detail.empty())
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
Result["detail"] = CI.detail;
if (!CI.documentation.empty())
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
Result["documentation"] = CI.documentation;
if (!CI.sortText.empty())
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
Result["sortText"] = CI.sortText;
if (!CI.filterText.empty())
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
Result["filterText"] = CI.filterText;
if (!CI.insertText.empty())
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
Result["insertText"] = CI.insertText;
if (CI.insertTextFormat != InsertTextFormat::Missing)
Result["insertTextFormat"] = static_cast<int>(CI.insertTextFormat);
if (CI.textEdit)
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
Result["textEdit"] = *CI.textEdit;
if (!CI.additionalTextEdits.empty())
Result["additionalTextEdits"] = llvm::json::Array(CI.additionalTextEdits);
if (CI.deprecated)
Result["deprecated"] = CI.deprecated;
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
return std::move(Result);
}
llvm::raw_ostream &operator<<(llvm::raw_ostream &O, const CompletionItem &I) {
O << I.label << " - " << toJSON(I);
return O;
}
bool operator<(const CompletionItem &L, const CompletionItem &R) {
return (L.sortText.empty() ? L.label : L.sortText) <
(R.sortText.empty() ? R.label : R.sortText);
}
llvm::json::Value toJSON(const CompletionList &L) {
return llvm::json::Object{
{"isIncomplete", L.isIncomplete},
{"items", llvm::json::Array(L.items)},
};
}
llvm::json::Value toJSON(const ParameterInformation &PI) {
assert(!PI.label.empty() && "parameter information label is required");
llvm::json::Object Result{{"label", PI.label}};
if (!PI.documentation.empty())
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
Result["documentation"] = PI.documentation;
return std::move(Result);
}
llvm::json::Value toJSON(const SignatureInformation &SI) {
assert(!SI.label.empty() && "signature information label is required");
llvm::json::Object Result{
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
{"label", SI.label},
{"parameters", llvm::json::Array(SI.parameters)},
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
};
if (!SI.documentation.empty())
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
Result["documentation"] = SI.documentation;
return std::move(Result);
}
llvm::raw_ostream &operator<<(llvm::raw_ostream &O,
const SignatureInformation &I) {
O << I.label << " - " << toJSON(I);
return O;
}
llvm::json::Value toJSON(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 llvm::json::Object{
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
{"activeSignature", SH.activeSignature},
{"activeParameter", SH.activeParameter},
{"signatures", llvm::json::Array(SH.signatures)},
Adds a json::Expr type to represent intermediate JSON expressions. Summary: This form can be created with a nice clang-format-friendly literal syntax, and gets escaping right. It knows how to call unparse() on our Protocol types. All the places where we pass around JSON internally now use this type. Object properties are sorted (stored as std::map) and so serialization is canonicalized, with optional prettyprinting (triggered by a -pretty flag). This makes the lit tests much nicer to read and somewhat nicer to debug. (Unfortunately the completion tests use CHECK-DAG, which only has line-granularity, so pretty-printing is disabled there. In future we could make completion ordering deterministic, or switch to unittests). Compared to the current approach, it has some efficiencies like avoiding copies of string literals used as object keys, but is probably slower overall. I think the code/test quality benefits are worth it. This patch doesn't attempt to do anything about JSON *parsing*. It takes direction from the proposal in this doc[1], but is limited in scope and visibility, for now. I am of half a mind just to use Expr as the target of a parser, and maybe do a little string deduplication, but not bother with clever memory allocation. That would be simple, and fast enough for clangd... [1] https://docs.google.com/document/d/1OEF9IauWwNuSigZzvvbjc1cVS1uGHRyGTXaoy3DjqM4/edit +cc d0k so he can tell me not to use std::map. Reviewers: ioeric, malaperle Subscribers: bkramer, ilya-biryukov, mgorny, klimek Differential Revision: https://reviews.llvm.org/D39435 llvm-svn: 317486
2017-11-06 23:40:30 +08:00
};
}
bool fromJSON(const llvm::json::Value &Params, RenameParams &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("textDocument", R.textDocument) &&
O.map("position", R.position) && O.map("newName", R.newName);
}
llvm::json::Value toJSON(const DocumentHighlight &DH) {
return llvm::json::Object{
{"range", toJSON(DH.range)},
{"kind", static_cast<int>(DH.kind)},
};
}
llvm::json::Value toJSON(const FileStatus &FStatus) {
return llvm::json::Object{
{"uri", FStatus.uri},
{"state", FStatus.state},
};
}
llvm::raw_ostream &operator<<(llvm::raw_ostream &O,
const DocumentHighlight &V) {
O << V.range;
if (V.kind == DocumentHighlightKind::Read)
O << "(r)";
if (V.kind == DocumentHighlightKind::Write)
O << "(w)";
return O;
}
bool fromJSON(const llvm::json::Value &Params,
DidChangeConfigurationParams &CCP) {
llvm::json::ObjectMapper O(Params);
return O && O.map("settings", CCP.settings);
}
bool fromJSON(const llvm::json::Value &Params,
ClangdCompileCommand &CDbUpdate) {
llvm::json::ObjectMapper O(Params);
return O && O.map("workingDirectory", CDbUpdate.workingDirectory) &&
O.map("compilationCommand", CDbUpdate.compilationCommand);
}
bool fromJSON(const llvm::json::Value &Params, ConfigurationSettings &S) {
llvm::json::ObjectMapper O(Params);
if (!O)
return true; // 'any' type in LSP.
O.map("compilationDatabaseChanges", S.compilationDatabaseChanges);
return true;
}
bool fromJSON(const llvm::json::Value &Params, InitializationOptions &Opts) {
llvm::json::ObjectMapper O(Params);
if (!O)
return true; // 'any' type in LSP.
fromJSON(Params, Opts.ConfigSettings);
O.map("compilationDatabasePath", Opts.compilationDatabasePath);
O.map("fallbackFlags", Opts.fallbackFlags);
O.map("clangdFileStatus", Opts.FileStatus);
return true;
}
bool fromJSON(const llvm::json::Value &E, TypeHierarchyDirection &Out) {
auto T = E.getAsInteger();
if (!T)
return false;
if (*T < static_cast<int>(TypeHierarchyDirection::Children) ||
*T > static_cast<int>(TypeHierarchyDirection::Both))
return false;
Out = static_cast<TypeHierarchyDirection>(*T);
return true;
}
bool fromJSON(const llvm::json::Value &Params, TypeHierarchyParams &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("textDocument", R.textDocument) &&
O.map("position", R.position) && O.map("resolve", R.resolve) &&
O.map("direction", R.direction);
}
llvm::raw_ostream &operator<<(llvm::raw_ostream &O,
const TypeHierarchyItem &I) {
return O << I.name << " - " << toJSON(I);
}
llvm::json::Value toJSON(const TypeHierarchyItem &I) {
llvm::json::Object Result{{"name", I.name},
{"kind", static_cast<int>(I.kind)},
{"range", I.range},
{"selectionRange", I.selectionRange},
{"uri", I.uri}};
if (I.detail)
Result["detail"] = I.detail;
if (I.deprecated)
Result["deprecated"] = I.deprecated;
if (I.parents)
Result["parents"] = I.parents;
if (I.children)
Result["children"] = I.children;
return std::move(Result);
}
bool fromJSON(const llvm::json::Value &Params, TypeHierarchyItem &I) {
llvm::json::ObjectMapper O(Params);
// Required fields.
if (!(O && O.map("name", I.name) && O.map("kind", I.kind) &&
O.map("uri", I.uri) && O.map("range", I.range) &&
O.map("selectionRange", I.selectionRange))) {
return false;
}
// Optional fields.
O.map("detail", I.detail);
O.map("deprecated", I.deprecated);
O.map("parents", I.parents);
O.map("children", I.children);
return true;
}
bool fromJSON(const llvm::json::Value &Params, ReferenceParams &R) {
TextDocumentPositionParams &Base = R;
return fromJSON(Params, Base);
}
static const char *toString(OffsetEncoding OE) {
switch (OE) {
case OffsetEncoding::UTF8:
return "utf-8";
case OffsetEncoding::UTF16:
return "utf-16";
case OffsetEncoding::UTF32:
return "utf-32";
case OffsetEncoding::UnsupportedEncoding:
return "unknown";
}
llvm_unreachable("Unknown clang.clangd.OffsetEncoding");
}
llvm::json::Value toJSON(const OffsetEncoding &OE) { return toString(OE); }
bool fromJSON(const llvm::json::Value &V, OffsetEncoding &OE) {
auto Str = V.getAsString();
if (!Str)
return false;
OE = llvm::StringSwitch<OffsetEncoding>(*Str)
.Case("utf-8", OffsetEncoding::UTF8)
.Case("utf-16", OffsetEncoding::UTF16)
.Case("utf-32", OffsetEncoding::UTF32)
.Default(OffsetEncoding::UnsupportedEncoding);
return true;
}
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, OffsetEncoding Enc) {
return OS << toString(Enc);
}
} // namespace clangd
} // namespace clang