From b882025e3d4f50758ad753c1abe3f8885efccfd9 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
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<TextDocumentContentChangeEvent> 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

