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

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1265 lines
39 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 "URI.h"
#include "support/Logger.h"
#include "clang/Basic/LLVM.h"
#include "clang/Index/IndexSymbol.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(std::string(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);
}
llvm::json::Value toJSON(const VersionedTextDocumentIdentifier &R) {
auto Result = toJSON(static_cast<const TextDocumentIdentifier &>(R));
Result.getAsObject()->try_emplace("version", R.version);
return Result;
}
bool fromJSON(const llvm::json::Value &Params,
VersionedTextDocumentIdentifier &R) {
llvm::json::ObjectMapper O(Params);
return fromJSON(Params, static_cast<TextDocumentIdentifier &>(R)) && O &&
O.map("version", R.version);
}
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::Constructor;
case index::SymbolKind::ConversionFunction:
return SymbolKind::Function;
case index::SymbolKind::Parameter:
case index::SymbolKind::NonTypeTemplateParm:
return SymbolKind::Variable;
case index::SymbolKind::Using:
return SymbolKind::Namespace;
case index::SymbolKind::TemplateTemplateParm:
case index::SymbolKind::TemplateTypeParm:
return SymbolKind::TypeParameter;
}
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 *SemanticHighlighting =
TextDocument->getObject("semanticHighlightingCapabilities")) {
if (auto SemanticHighlightingSupport =
SemanticHighlighting->getBoolean("semanticHighlighting"))
R.TheiaSemanticHighlighting = *SemanticHighlightingSupport;
}
if (TextDocument->getObject("semanticTokens"))
R.SemanticTokens = true;
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 DocumentationFormat = Item->getArray("documentationFormat")) {
for (const auto &Format : *DocumentationFormat) {
if (fromJSON(Format, R.CompletionDocumentationFormat))
break;
}
}
}
if (auto *ItemKind = Completion->getObject("completionItemKind")) {
if (auto *ValueSet = ItemKind->get("valueSet")) {
R.CompletionItemKinds.emplace();
if (!fromJSON(*ValueSet, *R.CompletionItemKinds))
return false;
}
}
if (auto EditsNearCursor = Completion->getBoolean("editsNearCursor"))
R.CompletionFixes = *EditsNearCursor;
}
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) {
if (fromJSON(Format, R.HoverContentFormat))
break;
}
}
}
if (auto *Help = TextDocument->getObject("signatureHelp")) {
R.HasSignatureHelp = true;
if (auto *Info = Help->getObject("signatureInformation")) {
if (auto *Parameter = Info->getObject("parameterInformation")) {
if (auto OffsetSupport = Parameter->getBoolean("labelOffsetSupport"))
R.OffsetsInSignatureHelp = *OffsetSupport;
}
}
}
if (auto *Rename = TextDocument->getObject("rename")) {
if (auto RenameSupport = Rename->getBoolean("prepareSupport"))
R.RenamePrepareSupport = *RenameSupport;
}
}
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 *Window = O->getObject("window")) {
if (auto WorkDoneProgress = Window->getBoolean("workDoneProgress"))
R.WorkDoneProgress = *WorkDoneProgress;
if (auto Implicit = Window->getBoolean("implicitWorkDoneProgressCreate"))
R.ImplicitProgressCreation = *Implicit;
}
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;
}
llvm::json::Value toJSON(const WorkDoneProgressCreateParams &P) {
return llvm::json::Object{{"token", P.token}};
}
llvm::json::Value toJSON(const WorkDoneProgressBegin &P) {
llvm::json::Object Result{
{"kind", "begin"},
{"title", P.title},
};
if (P.cancellable)
Result["cancellable"] = true;
if (P.percentage)
Result["percentage"] = 0;
// FIXME: workaround for older gcc/clang
return std::move(Result);
}
llvm::json::Value toJSON(const WorkDoneProgressReport &P) {
llvm::json::Object Result{{"kind", "report"}};
if (P.cancellable)
Result["cancellable"] = *P.cancellable;
if (P.message)
Result["message"] = *P.message;
if (P.percentage)
Result["percentage"] = *P.percentage;
// FIXME: workaround for older gcc/clang
return std::move(Result);
}
llvm::json::Value toJSON(const WorkDoneProgressEnd &P) {
llvm::json::Object Result{{"kind", "end"}};
if (P.message)
Result["message"] = *P.message;
// FIXME: workaround for older gcc/clang
return std::move(Result);
}
llvm::json::Value toJSON(const MessageType &R) {
return static_cast<int64_t>(R);
}
llvm::json::Value toJSON(const ShowMessageParams &R) {
return llvm::json::Object{{"type", R.type}, {"message", R.message}};
}
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, DidSaveTextDocumentParams &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);
if (!O)
return false;
O.map("forceRebuild", R.forceRebuild); // Optional clangd extension.
return 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,
DocumentRangeFormattingParams &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("textDocument", R.textDocument) && O.map("range", R.range);
}
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);
}
bool fromJSON(const llvm::json::Value &Params, DocumentFormattingParams &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("textDocument", R.textDocument);
}
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;
// FIXME: workaround for older gcc/clang
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;
}
llvm::json::Value toJSON(const PublishDiagnosticsParams &PDP) {
llvm::json::Object Result{
{"uri", PDP.uri},
{"diagnostics", PDP.diagnostics},
};
if (PDP.version)
Result["version"] = PDP.version;
return std::move(Result);
}
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();
// FIXME: workaround for older gcc/clang
return 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";
const llvm::StringLiteral CodeAction::INFO_KIND = "info";
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;
// FIXME: workaround for older gcc/clang
return 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 &Response,
ApplyWorkspaceEditResponse &R) {
llvm::json::ObjectMapper O(Response);
if (!O || !O.map("applied", R.applied))
return false;
O.map("failureReason", R.failureReason);
return true;
}
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)
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;
Result["score"] = CI.score;
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.labelOffsets.hasValue() || !PI.labelString.empty()) &&
"parameter information label is required");
llvm::json::Object Result;
if (PI.labelOffsets)
Result["label"] =
llvm::json::Array({PI.labelOffsets->first, PI.labelOffsets->second});
else
Result["label"] = PI.labelString;
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},
};
}
constexpr unsigned SemanticTokenEncodingSize = 5;
static llvm::json::Value encodeTokens(llvm::ArrayRef<SemanticToken> Toks) {
llvm::json::Array Result;
for (const auto &Tok : Toks) {
Result.push_back(Tok.deltaLine);
Result.push_back(Tok.deltaStart);
Result.push_back(Tok.length);
Result.push_back(Tok.tokenType);
Result.push_back(Tok.tokenModifiers);
}
assert(Result.size() == SemanticTokenEncodingSize * Toks.size());
return std::move(Result);
}
bool operator==(const SemanticToken &L, const SemanticToken &R) {
return std::tie(L.deltaLine, L.deltaStart, L.length, L.tokenType,
L.tokenModifiers) == std::tie(R.deltaLine, R.deltaStart,
R.length, R.tokenType,
R.tokenModifiers);
}
llvm::json::Value toJSON(const SemanticTokens &Tokens) {
return llvm::json::Object{{"resultId", Tokens.resultId},
{"data", encodeTokens(Tokens.tokens)}};
}
llvm::json::Value toJSON(const SemanticTokensEdit &Edit) {
return llvm::json::Object{
{"start", SemanticTokenEncodingSize * Edit.startToken},
{"deleteCount", SemanticTokenEncodingSize * Edit.deleteTokens},
{"data", encodeTokens(Edit.tokens)}};
}
llvm::json::Value toJSON(const SemanticTokensOrDelta &TE) {
llvm::json::Object Result{{"resultId", TE.resultId}};
if (TE.edits)
Result["edits"] = *TE.edits;
if (TE.tokens)
Result["data"] = encodeTokens(*TE.tokens);
return std::move(Result);
}
bool fromJSON(const llvm::json::Value &Params, SemanticTokensParams &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("textDocument", R.textDocument);
}
bool fromJSON(const llvm::json::Value &Params, SemanticTokensDeltaParams &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("textDocument", R.textDocument) &&
O.map("previousResultId", R.previousResultId);
}
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;
if (I.data)
Result["data"] = I.data;
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);
O.map("data", I.data);
return true;
}
bool fromJSON(const llvm::json::Value &Params,
ResolveTypeHierarchyItemParams &P) {
llvm::json::ObjectMapper O(Params);
return O && O.map("item", P.item) && O.map("resolve", P.resolve) &&
O.map("direction", P.direction);
}
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);
}
bool operator==(const TheiaSemanticHighlightingInformation &Lhs,
const TheiaSemanticHighlightingInformation &Rhs) {
return Lhs.Line == Rhs.Line && Lhs.Tokens == Rhs.Tokens;
}
llvm::json::Value
toJSON(const TheiaSemanticHighlightingInformation &Highlighting) {
return llvm::json::Object{{"line", Highlighting.Line},
{"tokens", Highlighting.Tokens},
{"isInactive", Highlighting.IsInactive}};
}
llvm::json::Value toJSON(const TheiaSemanticHighlightingParams &Highlighting) {
return llvm::json::Object{
{"textDocument", Highlighting.TextDocument},
{"lines", std::move(Highlighting.Lines)},
};
}
bool fromJSON(const llvm::json::Value &Params, SelectionRangeParams &P) {
llvm::json::ObjectMapper O(Params);
return O && O.map("textDocument", P.textDocument) &&
O.map("positions", P.positions);
}
llvm::json::Value toJSON(const SelectionRange &Out) {
if (Out.parent) {
return llvm::json::Object{{"range", Out.range},
{"parent", toJSON(*Out.parent)}};
}
return llvm::json::Object{{"range", Out.range}};
}
bool fromJSON(const llvm::json::Value &Params, DocumentLinkParams &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("textDocument", R.textDocument);
}
llvm::json::Value toJSON(const DocumentLink &DocumentLink) {
return llvm::json::Object{
{"range", DocumentLink.range},
{"target", DocumentLink.target},
};
}
bool fromJSON(const llvm::json::Value &Params, FoldingRangeParams &R) {
llvm::json::ObjectMapper O(Params);
return O && O.map("textDocument", R.textDocument);
}
llvm::json::Value toJSON(const FoldingRange &Range) {
llvm::json::Object Result{
{"startLine", Range.startLine},
{"endLine", Range.endLine},
};
if (Range.startCharacter)
Result["startCharacter"] = Range.startCharacter;
if (Range.endCharacter)
Result["endCharacter"] = Range.endCharacter;
if (Range.kind)
Result["kind"] = *Range.kind;
return Result;
}
} // namespace clangd
} // namespace clang