From 8fb0fb816c45ea7fad3ac4732b72d42401d184b4 Mon Sep 17 00:00:00 2001 From: Jacob Dufault Date: Sat, 4 Mar 2017 18:16:23 -0800 Subject: [PATCH] wip --- command_line.cc | 274 +++++--- ipc.cc | 41 +- ipc.h | 70 +- language_server_api.h | 1458 +++++++++++++++++++++++++++++++++++++++++ platform_win.cc | 4 +- query.h | 3 +- serializer.cc | 2 +- 7 files changed, 1727 insertions(+), 125 deletions(-) create mode 100644 language_server_api.h diff --git a/command_line.cc b/command_line.cc index 7ea6dd05..118a0451 100644 --- a/command_line.cc +++ b/command_line.cc @@ -1,11 +1,21 @@ #include #include #include +#include #include "compilation_database_loader.h" #include "indexer.h" #include "ipc.h" #include "query.h" +#include "language_server_api.h" + +#include +#include + +#ifdef _WIN32 +#include +#include +#endif bool ParsePreferredSymbolLocation(const std::string& content, PreferredSymbolLocation* obj) { #define PARSE_AS(name, string) \ @@ -96,7 +106,7 @@ indexer.exe --index-file /work2/chrome/src/chrome/foo.cc */ -struct IpcMessage_IsAlive : public BaseIpcMessage { +struct IpcMessage_IsAlive : public BaseIpcMessage { static IpcMessageId id; }; @@ -109,7 +119,12 @@ IpcMessageId IpcMessage_IsAlive::id = "IsAlive"; -struct IpcMessage_DocumentSymbolsRequest : public BaseIpcMessage { + + + + + +struct IpcMessage_DocumentSymbolsRequest : public BaseIpcMessage { std::string document; // BaseIpcMessage: @@ -123,58 +138,8 @@ struct IpcMessage_DocumentSymbolsRequest : public BaseIpcMessage { }; IpcMessageId IpcMessage_DocumentSymbolsRequest::id = "IpcMessage_DocumentSymbolsRequest"; - -// Keep all types in the language_server_api namespace in sync with language server spec. -namespace language_server_api { - using DocumentUri = std::string; // TODO - - struct Position { - // Note: these are 0-based. - int line; - int character; - }; - - struct Range { - Position start; - Position end; - }; - - struct Location { - DocumentUri uri; - Range range; - }; - - enum class SymbolKind : int { - File = 1, - Module = 2, - Namespace = 3, - Package = 4, - Class = 5, - Method = 6, - Property = 7, - Field = 8, - Constructor = 9, - Enum = 10, - Interface = 11, - Function = 12, - Variable = 13, - Constant = 14, - String = 15, - Number = 16, - Boolean = 17, - Array = 18 - }; - - struct SymbolInfo { - std::string name; - SymbolKind kind; - Location location; - std::string containerName; - }; -} - -struct IpcMessage_DocumentSymbolsResponse : public BaseIpcMessage { - std::vector symbols; +struct IpcMessage_DocumentSymbolsResponse : public BaseIpcMessage { + std::vector symbols; // BaseIpcMessage: static IpcMessageId id; @@ -185,22 +150,22 @@ IpcMessageId IpcMessage_DocumentSymbolsResponse::id = "IpcMessage_DocumentSymbol - -void IndexerServerMain() { +void QueryDbMain() { IpcServer ipc("languageserver"); while (true) { - std::vector> messages = ipc.TakeMessages(); + std::vector> messages = ipc.TakeMessages(); - std::cout << "Server has " << messages.size() << " messages" << std::endl; for (auto& message : messages) { - if (message->runtime_id == IpcMessage_IsAlive::id) { + std::cout << "Processing message " << message->runtime_id() << " (hash " << message->hashed_runtime_id() << ")" << std::endl; + + if (message->runtime_id() == IpcMessage_IsAlive::id) { IpcMessage_IsAlive response; ipc.SendToClient(0, &response); // todo: make non-blocking break; } else { - std::cerr << "Unhandled IPC message with kind " << message->runtime_id << " (hash " << message->hashed_runtime_id << ")" << std::endl; + std::cerr << "Unhandled IPC message with kind " << message->runtime_id() << " (hash " << message->hashed_runtime_id() << ")" << std::endl; exit(1); break; } @@ -226,21 +191,130 @@ void LanguageServerStdinToServerDispatcher(IpcClient& ipc) { } } + +void ParseRpc(const std::string& method, const rapidjson::GenericValue>& params) { +} + +std::unique_ptr ParseMessage() { + int content_length = -1; + int iteration = 0; + while (true) { + if (++iteration > 10) { + assert(false && "bad parser state"); + exit(1); + } + + std::string line; + std::getline(std::cin, line); + //std::cin >> line; + + if (line.compare(0, 14, "Content-Length") == 0) { + content_length = atoi(line.c_str() + 16); + } + + if (line == "\r") + break; + } + + assert(content_length >= 0); + + std::string content; + content.reserve(content_length); + for (int i = 0; i < content_length; ++i) { + char c; + std::cin >> c; + content += c; + } + + rapidjson::Document document; + document.Parse(content.c_str(), content_length); + assert(!document.HasParseError()); + + return language_server_api::MessageRegistry::instance()->Parse(document); + + /* + std::string id; + if (document["id"].IsString()) + id = document["id"].GetString(); + else + id = std::to_string(document["id"].GetInt()); + std::string method = document["method"].GetString(); + auto& params = document["params"]; + + + // Send initialize response. + { + std::string content = + R"foo({ + "jsonrpc": "2.0", + "id": 0, + "result": { + "capabilities": { + "documentSymbolProvider": true + } + } + })foo"; + std::cout << "Content-Length: " << content.size(); + std::cout << (char)13 << char(10) << char(13) << char(10); + std::cout << content; + } + */ +} + + + // Main loop for the language server. |ipc| is connected to // a server. -void LanguageServerLoop(IpcClient& ipc) { +void LanguageServerLoop(IpcClient* ipc) { + using namespace language_server_api; + while (true) { - std::string input; - std::cin >> input; + std::unique_ptr message = ParseMessage(); - std::cout << "got input " << input << std::endl << std::endl; + // Message parsing can fail if we don't recognize the method. + if (!message) + continue; - if (input == "references") { + std::cerr << "[info]: Got message of type " << MethodIdToString(message->method_id) << std::endl; + switch (message->method_id) { + case MethodId::Initialize: + { + // TODO: response should take id as input. + // TODO: message should not have top-level id. + auto response = Out_InitializeResponse(); + response.id = message->id.value(); + response.result.capabilities.documentSymbolProvider = true; + response.Send(); + break; + } + case MethodId::TextDocumentDocumentSymbol: + { + auto response = Out_DocumentSymbolResponse(); + response.id = message->id.value(); + + for (int i = 0; i < 2500; ++i) { + SymbolInformation info; + info.containerName = "fooContainer"; + info.kind = language_server_api::SymbolKind::Field; + info.location.range.start.line = 5; + info.location.range.end.character = 20; + info.location.range.end.line = 5; + info.location.range.end.character = 25; + info.name = "Foobar"; + response.result.push_back(info); + } + + response.Send(); + break; + } } } } + + + void LanguageServerMain() { IpcClient ipc("languageserver", 0); @@ -255,23 +329,44 @@ void LanguageServerMain() { std::this_thread::sleep_for(std::chrono::milliseconds(20)); // Check if we got an IsAlive message back. - std::vector> messages = ipc.TakeMessages(); + std::vector> messages = ipc.TakeMessages(); bool has_server = false; for (auto& message : messages) { - if (message->runtime_id == IpcMessage_IsAlive::id) { + if (message->runtime_id() == IpcMessage_IsAlive::id) { has_server = true; break; } } // No server is running. Start it. - if (!has_server) { - std::cerr << "Unable to detect running indexer server" << std::endl; - exit(1); - } + //if (!has_server) { + // std::cerr << "Unable to detect running indexer server" << std::endl; + // exit(1); + //} - std::cout << "Found indexer server" << std::endl; - LanguageServerLoop(ipc); + std::thread stdio_reader(&LanguageServerLoop, &ipc); + + //std::cout << "Found indexer server" << std::endl; + //LanguageServerLoop(ipc); + + // TODO: This is used for debugging, so we can attach to the client. + + + //std::cout << "garbagelkadklasldk" << std::endl; + + bool should_break = true; + while (should_break) + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + //std::this_thread::sleep_for(std::chrono::seconds(4)); + + + //std::cout.flush(); + /* + language_server_api::ShowMessageOutNotification show; + show.type = language_server_api::MessageType::Info; + show.message = "hello"; + show.Send(); + */ } @@ -348,19 +443,40 @@ void IpcMessage_CreateIndex::Deserialize(Reader& reader) { #endif int main(int argc, char** argv) { + // We need to write to stdout in binary mode because in Windows, writing + // \n will implicitly write \r\n. Language server API will ignore a + // \r\r\n split request. +#ifdef _WIN32 + _setmode(_fileno(stdout), O_BINARY); + _setmode(_fileno(stdin), O_BINARY); +#endif + + std::cerr << "Starting language server" << std::endl; + IpcRegistry::instance()->Register(); IpcRegistry::instance()->Register(); IpcRegistry::instance()->Register(); - - if (argc == 2) - LanguageServerMain(); - else - IndexerServerMain(); - return 0; + language_server_api::MessageRegistry::instance()->Register(); + language_server_api::MessageRegistry::instance()->Register(); + language_server_api::MessageRegistry::instance()->Register(); + language_server_api::MessageRegistry::instance()->Register(); std::unordered_map options = ParseOptions(argc, argv); + if (HasOption(options, "--language-server")) { + LanguageServerMain(); + return 0; + } + if (HasOption(options, "--querydb")) { + QueryDbMain(); + return 0; + } + + + LanguageServerMain(); + return 0; + if (argc == 1 || options.find("--help") != options.end()) { std::cout << R"help(clang-indexer help: diff --git a/ipc.cc b/ipc.cc index c77b025d..88265021 100644 --- a/ipc.cc +++ b/ipc.cc @@ -24,27 +24,14 @@ void JsonMessage::SetPayload(size_t payload_size, const char* payload) { memcpy(payload_dest, payload, payload_size); } -BaseIpcMessage::BaseIpcMessage() { - assert(!runtime_id_.empty() && "Message is not registered using IpcRegistry::RegisterAllocator"); +void BaseIpcMessageElided::Serialize(Writer& writer) {} - runtime_id = runtime_id_; - hashed_runtime_id = hashed_runtime_id_; -} - -BaseIpcMessage::~BaseIpcMessage() {} - -void BaseIpcMessage::Serialize(Writer& writer) {} - -void BaseIpcMessage::Deserialize(Reader& reader) {} - -IpcMessageId BaseIpcMessage::runtime_id_; - -int BaseIpcMessage::hashed_runtime_id_ = -1; +void BaseIpcMessageElided::Deserialize(Reader& reader) {} IpcRegistry* IpcRegistry::instance_ = nullptr; -std::unique_ptr IpcRegistry::Allocate(int id) { - return std::unique_ptr((*allocators)[id]()); +std::unique_ptr IpcRegistry::Allocate(int id) { + return std::unique_ptr((*allocators)[id]()); } IpcDirectionalChannel::IpcDirectionalChannel(const std::string& name) { @@ -57,7 +44,7 @@ IpcDirectionalChannel::~IpcDirectionalChannel() { delete[] local_block; } -void IpcDirectionalChannel::PushMessage(BaseIpcMessage* message) { +void IpcDirectionalChannel::PushMessage(BaseIpcMessageElided* message) { rapidjson::StringBuffer output; rapidjson::PrettyWriter writer(output); writer.SetFormatOptions( @@ -65,6 +52,8 @@ void IpcDirectionalChannel::PushMessage(BaseIpcMessage* message) { writer.SetIndent(' ', 2); message->Serialize(writer); + //std::cout << "Sending message with id " << message->runtime_id() << " (hash " << message->hashed_runtime_id() << ")" << std::endl; + size_t payload_size = strlen(output.GetString()); assert(payload_size < shmem_size && "Increase shared memory size, payload will never fit"); @@ -86,7 +75,7 @@ void IpcDirectionalChannel::PushMessage(BaseIpcMessage* message) { if ((*shared->shared_bytes_used + sizeof(JsonMessage) + payload_size) >= shmem_size) continue; - get_free_message()->message_id = message->hashed_runtime_id; + get_free_message()->message_id = message->hashed_runtime_id(); get_free_message()->SetPayload(payload_size, output.GetString()); *shared->shared_bytes_used += sizeof(JsonMessage) + get_free_message()->payload_size; @@ -97,7 +86,7 @@ void IpcDirectionalChannel::PushMessage(BaseIpcMessage* message) { } -std::vector> IpcDirectionalChannel::TakeMessages() { +std::vector> IpcDirectionalChannel::TakeMessages() { size_t remaining_bytes = 0; // Move data from shared memory into a local buffer. Do this // before parsing the blocks so that other processes can begin @@ -111,11 +100,11 @@ std::vector> IpcDirectionalChannel::TakeMessages get_free_message()->message_id = -1; } - std::vector> result; + std::vector> result; char* message = local_block; while (remaining_bytes > 0) { - std::unique_ptr base_message = IpcRegistry::instance()->Allocate(as_message(message)->message_id); + std::unique_ptr base_message = IpcRegistry::instance()->Allocate(as_message(message)->message_id); rapidjson::Document document; document.Parse(as_message(message)->payload(), as_message(message)->payload_size); @@ -138,7 +127,7 @@ std::vector> IpcDirectionalChannel::TakeMessages IpcServer::IpcServer(const std::string& name) : name_(name), server_(NameToServerName(name)) {} -void IpcServer::SendToClient(int client_id, BaseIpcMessage* message) { +void IpcServer::SendToClient(int client_id, BaseIpcMessageElided* message) { // Find or create the client. auto it = clients_.find(client_id); if (it == clients_.end()) @@ -147,17 +136,17 @@ void IpcServer::SendToClient(int client_id, BaseIpcMessage* message) { clients_[client_id]->PushMessage(message); } -std::vector> IpcServer::TakeMessages() { +std::vector> IpcServer::TakeMessages() { return server_.TakeMessages(); } IpcClient::IpcClient(const std::string& name, int client_id) : server_(NameToServerName(name)), client_(NameToClientName(name, client_id)) {} -void IpcClient::SendToServer(BaseIpcMessage* message) { +void IpcClient::SendToServer(BaseIpcMessageElided* message) { server_.PushMessage(message); } -std::vector> IpcClient::TakeMessages() { +std::vector> IpcClient::TakeMessages() { return client_.TakeMessages(); } diff --git a/ipc.h b/ipc.h index ad9302b6..bad1c9ce 100644 --- a/ipc.h +++ b/ipc.h @@ -31,9 +31,17 @@ struct JsonMessage { using IpcMessageId = std::string; +struct BaseIpcMessageElided { + virtual IpcMessageId runtime_id() const = 0; + virtual int hashed_runtime_id() const = 0; + + virtual void Serialize(Writer& writer); + virtual void Deserialize(Reader& reader); +}; + // Usage: // -// class IpcMessage_Foo : public BaseIpcMessage { +// class IpcMessage_Foo : public BaseIpcMessage { // static IpcMessageId id; // // // BaseIpcMessage: @@ -45,23 +53,29 @@ using IpcMessageId = std::string; // main() { // IpcRegistry::instance()->Register(); // } -struct BaseIpcMessage { +// +// Note: This is a template so that the statics are stored separately +// per type. +template +struct BaseIpcMessage : BaseIpcMessageElided { BaseIpcMessage(); virtual ~BaseIpcMessage(); - virtual void Serialize(Writer& writer); - virtual void Deserialize(Reader& reader); - - IpcMessageId runtime_id; - int hashed_runtime_id = -1; - // Populated by IpcRegistry::RegisterAllocator. static IpcMessageId runtime_id_; static int hashed_runtime_id_; + + // BaseIpcMessageElided: + IpcMessageId runtime_id() const override { + return runtime_id_; + } + int hashed_runtime_id() const override { + return hashed_runtime_id_; + } }; struct IpcRegistry { - using Allocator = std::function; + using Allocator = std::function; // Use unique_ptrs so we can initialize on first use // (static init order might not be right). @@ -71,7 +85,7 @@ struct IpcRegistry { template void Register(); - std::unique_ptr Allocate(int id); + std::unique_ptr Allocate(int id); static IpcRegistry* instance() { if (!instance_) @@ -115,8 +129,8 @@ struct IpcDirectionalChannel { explicit IpcDirectionalChannel(const std::string& name); ~IpcDirectionalChannel(); - void PushMessage(BaseIpcMessage* message); - std::vector> TakeMessages(); + void PushMessage(BaseIpcMessageElided* message); + std::vector> TakeMessages(); private: JsonMessage* get_free_message() { @@ -134,8 +148,8 @@ private: struct IpcServer { IpcServer(const std::string& name); - void SendToClient(int client_id, BaseIpcMessage* message); - std::vector> TakeMessages(); + void SendToClient(int client_id, BaseIpcMessageElided* message); + std::vector> TakeMessages(); private: std::string name_; @@ -146,10 +160,34 @@ private: struct IpcClient { IpcClient(const std::string& name, int client_id); - void SendToServer(BaseIpcMessage* message); - std::vector> TakeMessages(); + void SendToServer(BaseIpcMessageElided* message); + std::vector> TakeMessages(); private: IpcDirectionalChannel server_; IpcDirectionalChannel client_; }; + + + + + + + + + + + +template +BaseIpcMessage::BaseIpcMessage() { + assert(!runtime_id_.empty() && "Message is not registered using IpcRegistry::RegisterAllocator"); +} + +template +BaseIpcMessage::~BaseIpcMessage() {} + +template +IpcMessageId BaseIpcMessage::runtime_id_; + +template +int BaseIpcMessage::hashed_runtime_id_ = -1; diff --git a/language_server_api.h b/language_server_api.h new file mode 100644 index 00000000..f764a8a8 --- /dev/null +++ b/language_server_api.h @@ -0,0 +1,1458 @@ +#pragma once + +#include +#include +#include + +#include +#include "optional.h" + +using std::experimental::optional; +using std::experimental::nullopt; + +namespace language_server_api { + + // TODO: Cleanup serializer.h/cc as well. + +#define SERIALIZE_MEMBER(name) \ + SerializeMember(writer, #name, value.name) +#define SERIALIZE_MEMBER2(name, value) \ + SerializeMember(writer, #name, value) +#define DESERIALIZE_MEMBER(name) \ + DeserializeMember(reader, #name, value.name) + + using Reader = rapidjson::GenericValue>; + using Writer = rapidjson::Writer; + + // Special templates used by (DE)SERIALIZE_MEMBER macros. + template + void SerializeMember(Writer& writer, const char* name, const T& value) { + writer.Key(name); + Serialize(writer, value); + } + template + void SerializeMember(Writer& writer, const char* name, const optional& value) { + if (value.has_value()) { + writer.Key(name); + Serialize(writer, value.value()); + } + } + template + void DeserializeMember(const Reader& reader, const char* name, T& value) { + auto it = reader.FindMember(name); + if (it != reader.MemberEnd()) + Deserialize(it->value, value); + } + + + + + // Start normal serialization routines. + void Serialize(Writer& writer, int value) { + writer.Int(value); + } + + void Serialize(Writer& writer, const std::string& value) { + writer.String(value.c_str(), value.size()); + } + + template + void Serialize(Writer& writer, const std::vector& values) { + writer.StartArray(); + for (const auto& value : values) + Serialize(writer, value); + writer.EndArray(); + } + + + void Deserialize(const Reader& reader, int& value) { + value = reader.GetInt(); + } + + void Deserialize(const Reader& reader, bool& value) { + value = reader.GetBool(); + } + + void Deserialize(const Reader& reader, std::string& value) { + value = std::string(reader.GetString()); + } + + template + void Deserialize(const Reader& reader, std::vector& value) { + for (const auto& entry : reader.GetArray()) { + T entry_value; + Deserialize(entry, entry_value); + value.push_back(entry_value); + } + } + + template + void Deserialize(const Reader& reader, optional& value) { + T real_value; + Deserialize(reader, real_value); + value = real_value; + } + + + + + +#if false + void Deserialize(const Reader& reader, DocumentUri& output) { + //static var driveLetterPathRe = ~/^\/[a-zA-Z]:/; + //static var uriRe = ~/^(([^:\/?#]+?):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/; + + /** ported from VSCode sources **/ + public static function toFsPath(uri:DocumentUri) :FsPath{ + if (!uriRe.match(uri.toString()) || uriRe.matched(2) != "file") + throw 'Invalid uri: $uri'; + + var path = uriRe.matched(5).urlDecode(); + if (driveLetterPathRe.match(path)) + return new FsPath(path.charAt(1).toLowerCase() + path.substr(2)); + else + return new FsPath(path); + } + } +#endif + + + + + + + + + + + + + + + + + + + + + + + + + + + ///////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + ///////////////////////////// OUTGOING MESSAGES ///////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + + struct RequestId { + optional id0; + optional id1; + }; + + void Serialize(Writer& writer, const RequestId& value) { + if (value.id0) { + Serialize(writer, value.id0.value()); + } + else { + assert(value.id1.has_value()); + Serialize(writer, value.id1.value()); + } + } + + void Deserialize(const Reader& reader, RequestId& id) { + if (reader.IsInt()) + id.id0 = reader.GetInt(); + else if (reader.IsString()) + id.id1 = std::string(reader.GetString()); + else + std::cerr << "Unable to deserialize id" << std::endl; + } + + + + struct OutMessage { + // Write out the body of the message. The writer expects object key/value + // pairs. + virtual void WriteMessageBody(Writer& writer) = 0; + + // Send the message to the language client by writing it to stdout. + void Send() { + rapidjson::StringBuffer output; + Writer writer(output); + writer.StartObject(); + writer.Key("jsonrpc"); + writer.String("2.0"); + WriteMessageBody(writer); + writer.EndObject(); + + std::cout << "Content-Length: " << output.GetSize(); + std::cout << (char)13 << char(10) << char(13) << char(10); + std::cout << output.GetString(); + std::cout.flush(); + } + }; + + struct OutRequestMessage : OutMessage { + RequestId id; + + virtual std::string Method() = 0; + virtual void SerializeParams(Writer& writer) = 0; + + // Message: + void WriteMessageBody(Writer& writer) override { + auto& value = *this; + + SERIALIZE_MEMBER(id); + SERIALIZE_MEMBER2("method", Method()); + + writer.Key("params"); + SerializeParams(writer); + } + }; + + + struct ResponseError { + struct Data { + virtual void Write(Writer& writer) = 0; + }; + + enum class ErrorCodes : int { + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603, + serverErrorStart = -32099, + serverErrorEnd = -32000, + ServerNotInitialized = -32002, + UnknownErrorCode = -32001 + }; + + ErrorCodes code; + // Short description. + std::string message; + std::unique_ptr data; + + void Write(Writer& writer) { + auto& value = *this; + + writer.StartObject(); + SERIALIZE_MEMBER2(code, static_cast(code)); + SERIALIZE_MEMBER(message); + if (data) { + writer.Key("data"); + data->Write(writer); + } + writer.EndObject(); + } + }; + + struct OutResponseMessage : OutMessage { + RequestId id; + + virtual optional Error() { + return nullopt; + } + virtual void WriteResult(Writer& writer) = 0; + + // Message: + void WriteMessageBody(Writer& writer) override { + auto& value = *this; + + SERIALIZE_MEMBER(id); + + optional error = Error(); + if (error) { + writer.Key("error"); + error->Write(writer); + } + else { + writer.Key("result"); + WriteResult(writer); + } + } + }; + + struct OutNotificationMessage : OutMessage { + virtual std::string Method() = 0; + virtual void SerializeParams(Writer& writer) = 0; + + // Message: + void WriteMessageBody(Writer& writer) override { + writer.Key("method"); + std::string method = Method(); + writer.Key(method.c_str(), method.size()); + + writer.Key("params"); + SerializeParams(writer); + } + }; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ///////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + ///////////////////////////// INCOMING MESSAGES ///////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + + enum class MethodId { + CancelRequest, + Initialize, + Initialized, + TextDocumentDocumentSymbol, + }; + + const char* MethodIdToString(MethodId id) { + switch (id) { + case MethodId::CancelRequest: + return "$/cancelRequest"; + case MethodId::Initialize: + return "initialize"; + case MethodId::Initialized: + return "initialized"; + case MethodId::TextDocumentDocumentSymbol: + return "textDocument/documentSymbol"; + default: + exit(1); + } + } + + struct InMessage; + + struct MessageRegistry { + static MessageRegistry* instance_; + static MessageRegistry* instance(); + + using Allocator = std::function(optional id, const Reader& params)>; + std::unordered_map allocators; + + template + void Register() { + std::string method_name = MethodIdToString(T::kMethod); + allocators[method_name] = [](optional id, const Reader& params) { + return MakeUnique(id, params); + }; + } + + std::unique_ptr Parse(const Reader& reader) { + std::string jsonrpc = reader["jsonrpc"].GetString(); + if (jsonrpc != "2.0") + exit(1); + + optional id; + if (reader.FindMember("id") != reader.MemberEnd()) + Deserialize(reader["id"], id); + + std::string method; + Deserialize(reader["method"], method); + + if (allocators.find(method) == allocators.end()) { + std::cerr << "Unable to find registered handler for method \"" << method << "\"" << std::endl; + return nullptr; + } + + Allocator& allocator = allocators[method]; + + + // We run the allocator with actual params object or a null + // params object if there are no params. Unifying the two ifs is + // tricky because the second allocator param is a reference. + if (reader.FindMember("params") != reader.MemberEnd()) { + const Reader& params = reader["params"]; + return allocator(id, params); + } + else { + Reader params; + params.SetNull(); + return allocator(id, params); + } + } + }; + + MessageRegistry* MessageRegistry::instance_ = nullptr; + MessageRegistry* MessageRegistry::instance() { + if (!instance_) + instance_ = new MessageRegistry(); + + return instance_; + } + + + struct InMessage { + const MethodId method_id; + optional id; + + InMessage(MethodId method_id, optional id, const Reader& reader) + // We verify there are no duplicate hashes inside of MessageRegistry. + : method_id(method_id), id(id) {} + }; + + struct InRequestMessage : public InMessage { + InRequestMessage(MethodId method, optional id, const Reader& reader) + : InMessage(method, id, reader) {} + }; + + struct InNotificationMessage : public InMessage { + InNotificationMessage(MethodId method, optional id, const Reader& reader) + : InMessage(method, id, reader) {} + }; + + + + + + + + + + + + + + + struct In_CancelRequest : public InNotificationMessage { + static const MethodId kMethod = MethodId::CancelRequest; + + In_CancelRequest(optional id, const Reader& reader) + : InNotificationMessage(kMethod, id, reader) {} + }; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ///////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + ////////////////////////////// PRIMITIVE TYPES ////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + + // Keep all types in the language_server_api namespace in sync with language server spec. + // TODO + struct DocumentUri { + std::string path; + }; + + void Serialize(Writer& writer, const DocumentUri& value) { + Serialize(writer, value.path); + } + + void Deserialize(const Reader& reader, DocumentUri& value) { + Deserialize(reader, value.path); + } + + + struct Position { + // Note: these are 0-based. + int line = 0; + int character = 0; + }; + + void Serialize(Writer& writer, const Position& value) { + writer.StartObject(); + SERIALIZE_MEMBER(line); + SERIALIZE_MEMBER(character); + writer.EndObject(); + } + + void Deserialize(const Reader& reader, Position& value) { + DESERIALIZE_MEMBER(line); + DESERIALIZE_MEMBER(character); + } + + + struct Range { + Position start; + Position end; + }; + + void Serialize(Writer& writer, const Range& value) { + writer.StartObject(); + SERIALIZE_MEMBER(start); + SERIALIZE_MEMBER(end); + writer.EndObject(); + } + + void Deserialize(const Reader& reader, Range& value) { + DESERIALIZE_MEMBER(start); + DESERIALIZE_MEMBER(end); + } + + struct Location { + DocumentUri uri; + Range range; + }; + + void Serialize(Writer& writer, const Location& value) { + writer.StartObject(); + SERIALIZE_MEMBER(uri); + SERIALIZE_MEMBER(range); + writer.EndObject(); + } + + void Deserialize(const Reader& reader, Location& value) { + DESERIALIZE_MEMBER(uri); + DESERIALIZE_MEMBER(range); + } + + enum class SymbolKind : int { + File = 1, + Module = 2, + Namespace = 3, + Package = 4, + Class = 5, + Method = 6, + Property = 7, + Field = 8, + Constructor = 9, + Enum = 10, + Interface = 11, + Function = 12, + Variable = 13, + Constant = 14, + String = 15, + Number = 16, + Boolean = 17, + Array = 18 + }; + + void Serialize(Writer& writer, const SymbolKind& value) { + writer.Int(static_cast(value)); + } + + void Deserialize(const Reader& reader, SymbolKind& value) { + value = static_cast(reader.GetInt()); + } + + + struct SymbolInformation { + std::string name; + SymbolKind kind; + Location location; + std::string containerName; + }; + + void Serialize(Writer& writer, const SymbolInformation& value) { + writer.StartObject(); + SERIALIZE_MEMBER(name); + SERIALIZE_MEMBER(kind); + SERIALIZE_MEMBER(location); + SERIALIZE_MEMBER(containerName); + writer.EndObject(); + } + + void Deserialize(const Reader& reader, SymbolInformation& value) { + DESERIALIZE_MEMBER(name); + DESERIALIZE_MEMBER(kind); + DESERIALIZE_MEMBER(location); + DESERIALIZE_MEMBER(containerName); + } + + + struct Command { + // Title of the command (ie, 'save') + std::string title; + // Actual command identifier. + std::string command; + // Arguments to run the command with. Could be JSON objects. + std::vector arguments; + }; + + void Serialize(Writer& writer, const Command& value) { + writer.StartObject(); + SERIALIZE_MEMBER(title); + SERIALIZE_MEMBER(command); + SERIALIZE_MEMBER(arguments); + writer.EndObject(); + } + + void Deserialize(const Reader& reader, Command& value) { + DESERIALIZE_MEMBER(title); + DESERIALIZE_MEMBER(command); + DESERIALIZE_MEMBER(arguments); + } + + // TODO: TextDocumentEdit + // TODO: WorkspaceEdit + + struct TextDocumentIdentifier { + DocumentUri uri; + }; + + void Serialize(Writer& writer, const TextDocumentIdentifier& value) { + writer.StartObject(); + SERIALIZE_MEMBER(uri); + writer.EndObject(); + } + + void Deserialize(const Reader& reader, TextDocumentIdentifier& value) { + DESERIALIZE_MEMBER(uri); + } + + // TODO: TextDocumentItem + // TODO: VersionedTextDocumentIdentifier + // TODO: TextDocumentPositionParams + // TODO: DocumentFilter + // TODO: DocumentSelector + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ///////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + ////////////////////////////// INITIALIZATION /////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + + // Workspace specific client capabilities. + struct WorkspaceClientCapabilites { + // The client supports applying batch edits to the workspace. + optional applyEdit; + + struct WorkspaceEdit { + // The client supports versioned document changes in `WorkspaceEdit`s + optional documentChanges; + }; + + // Capabilities specific to `WorkspaceEdit`s + optional workspaceEdit; + + + struct GenericDynamicReg { + // Did foo notification supports dynamic registration. + optional dynamicRegistration; + }; + + + // Capabilities specific to the `workspace/didChangeConfiguration` notification. + optional didChangeConfiguration; + + // Capabilities specific to the `workspace/didChangeWatchedFiles` notification. + optional didChangeWatchedFiles; + + // Capabilities specific to the `workspace/symbol` request. + optional symbol; + + // Capabilities specific to the `workspace/executeCommand` request. + optional executeCommand; + }; + + void Deserialize(const Reader& reader, WorkspaceClientCapabilites::WorkspaceEdit& value) { + DESERIALIZE_MEMBER(documentChanges); + } + + void Deserialize(const Reader& reader, WorkspaceClientCapabilites::GenericDynamicReg& value) { + DESERIALIZE_MEMBER(dynamicRegistration); + } + + void Deserialize(const Reader& reader, WorkspaceClientCapabilites& value) { + DESERIALIZE_MEMBER(applyEdit); + DESERIALIZE_MEMBER(workspaceEdit); + DESERIALIZE_MEMBER(didChangeConfiguration); + DESERIALIZE_MEMBER(didChangeWatchedFiles); + DESERIALIZE_MEMBER(symbol); + DESERIALIZE_MEMBER(executeCommand); + } + + // Text document specific client capabilities. + struct TextDocumentClientCapabilities { + struct Synchronization { + // Whether text document synchronization supports dynamic registration. + optional dynamicRegistration; + + // The client supports sending will save notifications. + optional willSave; + + // The client supports sending a will save request and + // waits for a response providing text edits which will + // be applied to the document before it is saved. + optional willSaveWaitUntil; + + // The client supports did save notifications. + optional didSave; + }; + + Synchronization synchronization; + + struct Completion { + // Whether completion supports dynamic registration. + optional dynamicRegistration; + + struct CompletionItem { + // Client supports snippets as insert text. + // + // A snippet can define tab stops and placeholders with `$1`, `$2` + // and `${3:foo}`. `$0` defines the final tab stop, it defaults to + // the end of the snippet. Placeholders with equal identifiers are linked, + // that is typing in one will update others too. + optional snippetSupport; + }; + + // The client supports the following `CompletionItem` specific + // capabilities. + optional completionItem; + }; + // Capabilities specific to the `textDocument/completion` + optional completion; + + struct GenericDynamicReg { + // Whether foo supports dynamic registration. + optional dynamicRegistration; + }; + + // Capabilities specific to the `textDocument/hover` + optional hover; + + // Capabilities specific to the `textDocument/signatureHelp` + optional signatureHelp; + + // Capabilities specific to the `textDocument/references` + optional references; + + // Capabilities specific to the `textDocument/documentHighlight` + optional documentHighlight; + + // Capabilities specific to the `textDocument/documentSymbol` + optional documentSymbol; + + // Capabilities specific to the `textDocument/formatting` + optional formatting; + + // Capabilities specific to the `textDocument/rangeFormatting` + optional rangeFormatting; + + // Capabilities specific to the `textDocument/onTypeFormatting` + optional onTypeFormatting; + + // Capabilities specific to the `textDocument/definition` + optional definition; + + // Capabilities specific to the `textDocument/codeAction` + optional codeAction; + + // Capabilities specific to the `textDocument/codeLens` + optional codeLens; + + // Capabilities specific to the `textDocument/documentLink` + optional documentLink; + + // Capabilities specific to the `textDocument/rename` + optional rename; + }; + + void Deserialize(const Reader& reader, TextDocumentClientCapabilities::Synchronization& value) { + DESERIALIZE_MEMBER(dynamicRegistration); + DESERIALIZE_MEMBER(willSave); + DESERIALIZE_MEMBER(willSaveWaitUntil); + DESERIALIZE_MEMBER(didSave); + } + + void Deserialize(const Reader& reader, TextDocumentClientCapabilities::Completion& value) { + DESERIALIZE_MEMBER(dynamicRegistration); + DESERIALIZE_MEMBER(completionItem); + } + + void Deserialize(const Reader& reader, TextDocumentClientCapabilities::Completion::CompletionItem& value) { + DESERIALIZE_MEMBER(snippetSupport); + } + + void Deserialize(const Reader& reader, TextDocumentClientCapabilities::GenericDynamicReg& value) { + DESERIALIZE_MEMBER(dynamicRegistration); + } + + void Deserialize(const Reader& reader, TextDocumentClientCapabilities& value) { + DESERIALIZE_MEMBER(synchronization); + DESERIALIZE_MEMBER(completion); + DESERIALIZE_MEMBER(hover); + DESERIALIZE_MEMBER(signatureHelp); + DESERIALIZE_MEMBER(references); + DESERIALIZE_MEMBER(documentHighlight); + DESERIALIZE_MEMBER(documentSymbol); + DESERIALIZE_MEMBER(formatting); + DESERIALIZE_MEMBER(rangeFormatting); + DESERIALIZE_MEMBER(onTypeFormatting); + DESERIALIZE_MEMBER(definition); + DESERIALIZE_MEMBER(codeAction); + DESERIALIZE_MEMBER(codeLens); + DESERIALIZE_MEMBER(documentLink); + DESERIALIZE_MEMBER(rename); + } + + struct ClientCapabilities { + // Workspace specific client capabilities. + optional workspace; + + // Text document specific client capabilities. + optional textDocument; + + /** + * Experimental client capabilities. + */ + // experimental?: any; // TODO + }; + + void Deserialize(const Reader& reader, ClientCapabilities& value) { + DESERIALIZE_MEMBER(workspace); + DESERIALIZE_MEMBER(textDocument); + } + + struct InitializeParams { + // The process Id of the parent process that started + // the server. Is null if the process has not been started by another process. + // If the parent process is not alive then the server should exit (see exit notification) its process. + optional processId; + + // The rootPath of the workspace. Is null + // if no folder is open. + // + // @deprecated in favour of rootUri. + optional rootPath; + + // The rootUri of the workspace. Is null if no + // folder is open. If both `rootPath` and `rootUri` are set + // `rootUri` wins. + optional rootUri; + + // User provided initialization options. + // initializationOptions?: any; // TODO + + // The capabilities provided by the client (editor or tool) + ClientCapabilities capabilities; + + enum class Trace { + // NOTE: serialized as a string, one of 'off' | 'messages' | 'verbose'; + Off, // off + Messages, // messages + Verbose // verbose + }; + + // The initial trace setting. If omitted trace is disabled ('off'). + Trace trace = Trace::Off; + }; + + void Deserialize(const Reader& reader, InitializeParams::Trace& value) { + std::string v = reader.GetString(); + if (v == "off") + value = InitializeParams::Trace::Off; + else if (v == "messages") + value = InitializeParams::Trace::Messages; + else if (v == "verbose") + value = InitializeParams::Trace::Verbose; + } + + void Deserialize(const Reader& reader, InitializeParams& value) { + DESERIALIZE_MEMBER(processId); + DESERIALIZE_MEMBER(rootPath); + DESERIALIZE_MEMBER(rootUri); + DESERIALIZE_MEMBER(capabilities); + DESERIALIZE_MEMBER(trace); + } + + +#if false + /** + * Known error codes for an `InitializeError`; + */ + export namespace InitializeError { + /** + * If the protocol version provided by the client can't be handled by the server. + * @deprecated This initialize error got replaced by client capabilities. There is + * no version handshake in version 3.0x + */ + export const unknownProtocolVersion : number = 1; + } +#endif + + struct InitializeError { + // Indicates whether the client should retry to send the + // initilize request after showing the message provided + // in the ResponseError. + bool retry; + }; + + void Serialize(Writer& writer, const InitializeError& value) { + writer.StartObject(); + SERIALIZE_MEMBER(retry); + writer.EndObject(); + } + + // Defines how the host (editor) should sync document changes to the language server. + enum class TextDocumentSyncKind { + // Documents should not be synced at all. + None = 0, + + // Documents are synced by always sending the full content + // of the document. + Full = 1, + + // Documents are synced by sending the full content on open. + // After that only incremental updates to the document are + // send. + Incremental = 2 + }; + + void Serialize(Writer& writer, const TextDocumentSyncKind& value) { + writer.Int(static_cast(value)); + } + + // Completion options. + struct CompletionOptions { + // The server provides support to resolve additional + // information for a completion item. + bool resolveProvider = false; + + // The characters that trigger completion automatically. + std::vector triggerCharacters; + }; + + void Serialize(Writer& writer, const CompletionOptions& value) { + writer.StartObject(); + SERIALIZE_MEMBER(resolveProvider); + SERIALIZE_MEMBER(triggerCharacters); + writer.EndObject(); + } + + // Signature help options. + struct SignatureHelpOptions { + // The characters that trigger signature help automatically. + std::vector triggerCharacters; + }; + + void Serialize(Writer& writer, const SignatureHelpOptions& value) { + writer.StartObject(); + SERIALIZE_MEMBER(triggerCharacters); + writer.EndObject(); + } + + // Code Lens options. + struct CodeLensOptions { + // Code lens has a resolve provider as well. + bool resolveProvider = false; + }; + + void Serialize(Writer& writer, const CodeLensOptions& value) { + writer.StartObject(); + SERIALIZE_MEMBER(resolveProvider); + writer.EndObject(); + } + + // Format document on type options + struct DocumentOnTypeFormattingOptions { + // A character on which formatting should be triggered, like `}`. + std::string firstTriggerCharacter; + + // More trigger characters. + std::vector moreTriggerCharacter; + }; + + void Serialize(Writer& writer, const DocumentOnTypeFormattingOptions& value) { + writer.StartObject(); + SERIALIZE_MEMBER(firstTriggerCharacter); + SERIALIZE_MEMBER(moreTriggerCharacter); + writer.EndObject(); + } + + // Document link options + struct DocumentLinkOptions { + // Document links have a resolve provider as well. + bool resolveProvider = false; + }; + + void Serialize(Writer& writer, const DocumentLinkOptions& value) { + writer.StartObject(); + SERIALIZE_MEMBER(resolveProvider); + writer.EndObject(); + } + + // Execute command options. + struct ExecuteCommandOptions { + // The commands to be executed on the server + std::vector commands; + }; + + void Serialize(Writer& writer, const ExecuteCommandOptions& value) { + writer.StartObject(); + SERIALIZE_MEMBER(commands); + writer.EndObject(); + } + + // Save options. + struct SaveOptions { + // The client is supposed to include the content on save. + bool includeText = false; + }; + + void Serialize(Writer& writer, const SaveOptions& value) { + writer.StartObject(); + SERIALIZE_MEMBER(includeText); + writer.EndObject(); + } + + struct TextDocumentSyncOptions { + // Open and close notifications are sent to the server. + bool openClose = false; + // Change notificatins are sent to the server. See TextDocumentSyncKind.None, TextDocumentSyncKind.Full + // and TextDocumentSyncKindIncremental. + optional change; + // Will save notifications are sent to the server. + bool willSave = false; + // Will save wait until requests are sent to the server. + bool willSaveWaitUntil = false; + // Save notifications are sent to the server. + optional save; + }; + + void Serialize(Writer& writer, const TextDocumentSyncOptions& value) { + writer.StartObject(); + SERIALIZE_MEMBER(openClose); + SERIALIZE_MEMBER(change); + SERIALIZE_MEMBER(willSave); + SERIALIZE_MEMBER(willSaveWaitUntil); + SERIALIZE_MEMBER(save); + writer.EndObject(); + } + + struct ServerCapabilities { + // Defines how text documents are synced. Is either a detailed structure defining each notification or + // for backwards compatibility the TextDocumentSyncKind number. + optional textDocumentSync; + // The server provides hover support. + bool hoverProvider = false; + // The server provides completion support. + optional completionProvider; + // The server provides signature help support. + optional signatureHelpProvider; + // The server provides goto definition support. + bool definitionProvider = false; + // The server provides find references support. + bool referencesProvider = false; + // The server provides document highlight support. + bool documentHighlightProvider = false; + // The server provides document symbol support. + bool documentSymbolProvider = false; + // The server provides workspace symbol support. + bool workspaceSymbolProvider = false; + // The server provides code actions. + bool codeActionProvider = false; + // The server provides code lens. + optional codeLensProvider; + // The server provides document formatting. + bool documentFormattingProvider = false; + // The server provides document range formatting. + bool documentRangeFormattingProvider = false; + // The server provides document formatting on typing. + optional documentOnTypeFormattingProvider; + // The server provides rename support. + bool renameProvider = false; + // The server provides document link support. + optional documentLinkProvider; + // The server provides execute command support. + optional executeCommandProvider; + }; + + void Serialize(Writer& writer, const ServerCapabilities& value) { + writer.StartObject(); + SERIALIZE_MEMBER(textDocumentSync); + SERIALIZE_MEMBER(hoverProvider); + SERIALIZE_MEMBER(completionProvider); + SERIALIZE_MEMBER(signatureHelpProvider); + SERIALIZE_MEMBER(definitionProvider); + SERIALIZE_MEMBER(referencesProvider); + SERIALIZE_MEMBER(documentHighlightProvider); + SERIALIZE_MEMBER(documentSymbolProvider); + SERIALIZE_MEMBER(workspaceSymbolProvider); + SERIALIZE_MEMBER(codeActionProvider); + SERIALIZE_MEMBER(codeLensProvider); + SERIALIZE_MEMBER(documentFormattingProvider); + SERIALIZE_MEMBER(documentRangeFormattingProvider); + SERIALIZE_MEMBER(documentOnTypeFormattingProvider); + SERIALIZE_MEMBER(renameProvider); + SERIALIZE_MEMBER(documentLinkProvider); + SERIALIZE_MEMBER(executeCommandProvider); + writer.EndObject(); + } + + struct InitializeResult { + // The capabilities the language server provides. + ServerCapabilities capabilities; + }; + + void Serialize(Writer& writer, const InitializeResult& value) { + writer.StartObject(); + SERIALIZE_MEMBER(capabilities); + writer.EndObject(); + } + + + struct In_InitializeRequest : public InRequestMessage { + const static MethodId kMethod = MethodId::Initialize; + InitializeParams params; + + In_InitializeRequest(optional id, const Reader& reader) + : InRequestMessage(kMethod, id, reader) { + auto type = reader.GetType(); + Deserialize(reader, params); + std::cerr << "done" << std::endl; + } + }; + + struct Out_InitializeResponse : public OutResponseMessage { + InitializeResult result; + + // OutResponseMessage: + void WriteResult(Writer& writer) override { + Serialize(writer, result); + } + }; + + struct In_InitializedNotification : public InNotificationMessage { + const static MethodId kMethod = MethodId::Initialized; + + In_InitializedNotification(optional id, const Reader& reader) + : InNotificationMessage(kMethod, id, reader) {} + }; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + struct DocumentSymbolParams { + TextDocumentIdentifier textDocument; + }; + + void Deserialize(const Reader& reader, DocumentSymbolParams& value) { + DESERIALIZE_MEMBER(textDocument); + } + + struct In_DocumentSymbolRequest : public InRequestMessage { + const static MethodId kMethod = MethodId::TextDocumentDocumentSymbol; + + DocumentSymbolParams params; + + In_DocumentSymbolRequest(optional id, const Reader& reader) + : InRequestMessage(kMethod, id, reader) { + Deserialize(reader, params); + } + }; + + + struct Out_DocumentSymbolResponse : public OutResponseMessage { + std::vector result; + + // OutResponseMessage: + void WriteResult(Writer& writer) override { + Serialize(writer, result); + } + }; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + enum class MessageType { + Error = 1, + Warning = 2, + Info = 3, + Log = 4 + }; + + void Serialize(Writer& writer, MessageType value) { + writer.Int(static_cast(value)); + } + + struct ShowMessageOutNotification : public OutNotificationMessage { + MessageType type = MessageType::Error; + std::string message; + + // OutNotificationMessage: + std::string Method() override { + return "window/showMessage"; + } + void SerializeParams(Writer& writer) override { + auto& value = *this; + + writer.StartObject(); + SERIALIZE_MEMBER(type); + SERIALIZE_MEMBER(message); + writer.EndObject(); + } + }; + + struct LogMessageOutNotification : public OutNotificationMessage { + MessageType type = MessageType::Error; + std::string message; + + // OutNotificationMessage: + std::string Method() override { + return "window/logMessage"; + } + void SerializeParams(Writer& writer) override { + auto& value = *this; + + writer.StartObject(); + SERIALIZE_MEMBER(type); + SERIALIZE_MEMBER(message); + writer.EndObject(); + } + }; + + +#undef SERIALIZE_MEMBER +#undef SERIALIZE_MEMBER2 +#undef DESERIALIZE_MEMBER + + + +} \ No newline at end of file diff --git a/platform_win.cc b/platform_win.cc index a4206f52..25e57104 100644 --- a/platform_win.cc +++ b/platform_win.cc @@ -1,10 +1,11 @@ -#if false #include "platform.h" #include #include #include +#include "utils.h" + struct PlatformMutexWin : public PlatformMutex { HANDLE raw_mutex = INVALID_HANDLE_VALUE; @@ -70,4 +71,3 @@ std::unique_ptr CreatePlatformScopedMutexLock(PlatformM std::unique_ptr CreatePlatformSharedMemory(const std::string& name) { return MakeUnique(name); } -#endif diff --git a/query.h b/query.h index 773ee0bf..e76641a2 100644 --- a/query.h +++ b/query.h @@ -166,6 +166,7 @@ struct SymbolIdx { // TODO: Instead of passing to/from json, we can probably bass the IndexedFile type almost directly as // a raw memory dump - the type has almost zero pointers inside of it. We could do a little bit of fixup // so that passing from a separate process to the main db is really fast (no need to go through JSON). +/* namespace foo2 { using Usr = size_t; struct UsrTable { @@ -174,7 +175,7 @@ namespace foo2 { const char* usrs[]; }; } - +*/ struct IndexUpdate { // File updates. diff --git a/serializer.cc b/serializer.cc index 8dadc72e..7dcd96ce 100644 --- a/serializer.cc +++ b/serializer.cc @@ -230,7 +230,7 @@ void Deserialize(const Reader& reader, IndexedFile* file) { std::string Serialize(IndexedFile* file) { rapidjson::StringBuffer output; - rapidjson::PrettyWriter writer(output); + Writer writer(output); writer.SetFormatOptions( rapidjson::PrettyFormatOptions::kFormatSingleLineArray); writer.SetIndent(' ', 2);