From b882025e3d4f50758ad753c1abe3f8885efccfd9 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Wed, 17 May 2017 21:42:18 -0400 Subject: [PATCH 07/31] FIXME: tweaks to lsp --- gcc/json-rpc.c | 2 + gcc/lsp.c | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 142 insertions(+), 15 deletions(-) diff --git a/gcc/json-rpc.c b/gcc/json-rpc.c index 3656a87..f437814 100644 --- a/gcc/json-rpc.c +++ b/gcc/json-rpc.c @@ -68,6 +68,8 @@ jsonrpc::make_method_not_found (const json::value *id, const char *method) json::value * jsonrpc::make_invalid_params (const json::value *id, const char *msg) { + if (msg == NULL) + msg = "invalid params"; return make_failure (INVALID_PARAMS, msg, id); } diff --git a/gcc/lsp.c b/gcc/lsp.c index 02d59ad..682efcf 100644 --- a/gcc/lsp.c +++ b/gcc/lsp.c @@ -24,18 +24,122 @@ along with GCC; see the file COPYING3. If not see #include "json-rpc.h" #include "selftest.h" -#if CHECKING_P +using namespace jsonrpc; -namespace selftest { +typedef const char *DocumentUri; -/* Selftests. */ -using namespace jsonrpc; +/* Interfaces from the protocol specification (which uses camel case). */ + +// Exceptions would be nicer than passing around the out_err + +// TODO: autogenerate the interface binding/marshalling/demarshalling code +// from an interface description. + +struct TextDocumentItem +{ + static TextDocumentItem from_json (const json::value *params, + json::value *&out_err) + { + TextDocumentItem result; + const json::object *obj = params->as_object (); + if (!obj) + { + out_err = make_invalid_params (NULL, NULL); + return result; + } + + result.uri = obj->get ("uri")->as_string ()->get_string (); + result.languageId = obj->get ("languageId")->as_string ()->get_string (); + result.version = obj->get ("version")->as_number ()->get (); + result.text = obj->get ("text")->as_string ()->get_string (); + return result; + } + + DocumentUri uri; + const char *languageId; + double version; + const char *text; +}; + +struct DidOpenTextDocumentParams +{ + static DidOpenTextDocumentParams from_json (const json::value *params, + json::value *&out_err) + { + DidOpenTextDocumentParams result; + const json::object *obj = params->as_object (); + if (!obj) + { + out_err = make_invalid_params (NULL, NULL); + return result; + } + // FIXME: error-handling + result.textDocument + = TextDocumentItem::from_json (obj->get ("textDocument"), out_err); + return result; + } + + TextDocumentItem textDocument; +}; + +struct DidChangeTextDocumentParams +{ + public: + static DidChangeTextDocumentParams from_json (const json::value *params, + json::value *&out_err) + { + DidChangeTextDocumentParams result; + // FIXME + return result; + } + + private: +#if 0 + VersionedTextDocumentIdentifier textDocument; + auto_vec contentChanges; +#endif +}; + +/* An abstract base class for implementing the LSP as vfunc calls. */ + +class lsp_server +{ + public: + virtual ~lsp_server () {} + + virtual void + do_text_document_did_open (const DidOpenTextDocumentParams &p) = 0; + + virtual void + do_text_document_did_change (const DidChangeTextDocumentParams &p) = 0; +}; + +/* A concrete subclass of lsp_server that implements everything as a no-op. */ + +class noop_lsp_server : public lsp_server +{ + void + do_text_document_did_open (const DidOpenTextDocumentParams &p) OVERRIDE + { + // no-op + } + + void + do_text_document_did_change (const DidChangeTextDocumentParams &p) OVERRIDE + { + // no-op + } +}; -class lsp_server : public jsonrpc::server +/* A jsonrpc::server subclass that decodes incoming JSON-RPC requests + and dispatches them to an lsp_server instance as vfunc calls. */ + +class json_lsp_server : public jsonrpc::server { public: - lsp_server (bool verbose) : server (verbose) {} + json_lsp_server (bool verbose, lsp_server &inner) + : server (verbose), m_inner (inner) {} json::value * dispatch (const char *method, const json::value *params, @@ -64,29 +168,50 @@ class lsp_server : public jsonrpc::server json::value * do_text_document_did_open (const json::value *params) { - // params ought to be a "DidOpenTextDocumentParams", - // which is a "textDocument" holding a TextDocumentItem - // FIXME: for now, ignore params + json::value *err = NULL; + DidOpenTextDocumentParams p + = DidOpenTextDocumentParams::from_json (params, err); + if (err) + return err; // though we ought not to return non-NULL for a notification + + m_inner.do_text_document_did_open (p); return NULL; // notification, so no response } json::value * do_text_document_did_change (const json::value *params) { - // params ought to be a "DidChangeTextDocumentParams" - // FIXME: for now, ignore params + json::value *err = NULL; + DidChangeTextDocumentParams p + = DidChangeTextDocumentParams::from_json (params, err); + if (err) + return err; // though we ought not to return non-NULL for a notification + + m_inner.do_text_document_did_change (p); + return NULL; // notification, so no response } + + private: + lsp_server &m_inner; }; + +#if CHECKING_P + +namespace selftest { + +/* Selftests. */ + static void test_simple () { - lsp_server s (true); + noop_lsp_server noop; + json_lsp_server js (true, noop); const char *init_request = ("{\"jsonrpc\": \"2.0\", \"method\": \"initialize\"," " \"params\": [42, 23], \"id\": 1}"); // FIXME - json::value *init_response = s.handle_request_string (init_request); + json::value *init_response = js.handle_request_string (init_request); //const json::value *result = assert_is_success (response, 1); //ASSERT_EQ (19, result->as_number ()->get ()); delete init_response; @@ -99,7 +224,7 @@ test_simple () " \"languageId\": \"c++\"," " \"version\": 0," " \"text\": \"/* Initial content. */\"}}}"); - json::value *did_open_response = s.handle_request_string (did_open_note); + json::value *did_open_response = js.handle_request_string (did_open_note); delete did_open_response; const char *did_change_note @@ -108,7 +233,7 @@ test_simple () " \"textDocument\": {\"version\": 1}," " \"contentChanges\": [{\"text\": \"/* Hello world. */\"}]" "}}"); // FIXME - json::value *did_change_response = s.handle_request_string (did_change_note); + json::value *did_change_response = js.handle_request_string (did_change_note); delete did_change_response; } -- 1.8.5.3