[gnome-builder/wip/chergert/langserv] langserv-client: implement diagnostics parsing



commit 4f3c36135a2d68d16480b34e4f6bde0c4a5a4f0f
Author: Christian Hergert <chergert redhat com>
Date:   Sun Oct 23 20:10:18 2016 -0700

    langserv-client: implement diagnostics parsing
    
    This will now parse the results from various notifications from the
    language server.

 libide/langserv/ide-langserv-client.c |  256 ++++++++++++++++++++++++++++++--
 libide/langserv/ide-langserv-client.h |   37 +++--
 2 files changed, 263 insertions(+), 30 deletions(-)
---
diff --git a/libide/langserv/ide-langserv-client.c b/libide/langserv/ide-langserv-client.c
index cdf51ac..6c2989a 100644
--- a/libide/langserv/ide-langserv-client.c
+++ b/libide/langserv/ide-langserv-client.c
@@ -30,6 +30,8 @@
 #include "buffers/ide-buffer-manager.h"
 #include "diagnostics/ide-diagnostic.h"
 #include "diagnostics/ide-diagnostics.h"
+#include "diagnostics/ide-source-location.h"
+#include "diagnostics/ide-source-range.h"
 #include "langserv/ide-langserv-client.h"
 #include "projects/ide-project.h"
 #include "vcs/ide-vcs.h"
@@ -40,7 +42,7 @@ typedef struct
   EggSignalGroup *project_signals;
   JsonrpcClient  *rpc_client;
   GIOStream      *io_stream;
-  GHashTable     *diagnostics_by_uri;
+  GHashTable     *diagnostics_by_file;
 } IdeLangservClientPrivate;
 
 G_DEFINE_TYPE_WITH_PRIVATE (IdeLangservClient, ide_langserv_client, IDE_TYPE_OBJECT)
@@ -52,6 +54,13 @@ enum {
 };
 
 enum {
+  SEVERITY_ERROR       = 1,
+  SEVERITY_WARNING     = 2,
+  SEVERITY_INFORMATION = 3,
+  SEVERITY_HINT        = 4,
+};
+
+enum {
   PROP_0,
   PROP_IO_STREAM,
   N_PROPS
@@ -66,6 +75,21 @@ static GParamSpec *properties [N_PROPS];
 static guint signals [N_SIGNALS];
 
 static void
+ide_langserv_client_clear_diagnostics (IdeLangservClient *self,
+                                       const gchar       *uri)
+{
+  IdeLangservClientPrivate *priv = ide_langserv_client_get_instance_private (self);
+  g_autoptr(GFile) file = NULL;
+
+  g_assert (IDE_IS_LANGSERV_CLIENT (self));
+  g_assert (uri != NULL);
+
+  file = g_file_new_for_uri (uri);
+
+  g_hash_table_remove (priv->diagnostics_by_file, file);
+}
+
+static void
 ide_langserv_client_buffer_loaded (IdeLangservClient *self,
                                    IdeBuffer         *buffer,
                                    IdeBufferManager  *buffer_manager)
@@ -86,7 +110,10 @@ ide_langserv_client_buffer_loaded (IdeLangservClient *self,
     "}"
   );
 
-  jsonrpc_client_notification_async (priv->rpc_client, "textDocument/didOpen", params, NULL, NULL, NULL);
+  jsonrpc_client_notification_async (priv->rpc_client,
+                                     "textDocument/didOpen",
+                                     g_steal_pointer (&params),
+                                     NULL, NULL, NULL);
 }
 
 static void
@@ -110,7 +137,10 @@ ide_langserv_client_buffer_unloaded (IdeLangservClient *self,
     "}"
   );
 
-  jsonrpc_client_notification_async (priv->rpc_client, "textDocument/didClose", params, NULL, NULL, NULL);
+  jsonrpc_client_notification_async (priv->rpc_client,
+                                     "textDocument/didClose",
+                                     g_steal_pointer (&params),
+                                     NULL, NULL, NULL);
 }
 
 static void
@@ -173,7 +203,12 @@ ide_langserv_client_project_file_trashed (IdeLangservClient *self,
     "]"
   );
 
-  jsonrpc_client_notification_async (priv->rpc_client, "workspace/didChangeWatchedFiles", params, NULL, 
NULL, NULL);
+  jsonrpc_client_notification_async (priv->rpc_client,
+                                     "workspace/didChangeWatchedFiles",
+                                     g_steal_pointer (&params),
+                                     NULL, NULL, NULL);
+
+  ide_langserv_client_clear_diagnostics (self, uri);
 
   IDE_EXIT;
 }
@@ -212,33 +247,140 @@ ide_langserv_client_project_file_renamed (IdeLangservClient *self,
     "]"
   );
 
-  jsonrpc_client_notification_async (priv->rpc_client, "workspace/didChangeWatchedFiles", params, NULL, 
NULL, NULL);
+  jsonrpc_client_notification_async (priv->rpc_client,
+                                     "workspace/didChangeWatchedFiles",
+                                     g_steal_pointer (&params),
+                                     NULL, NULL, NULL);
+
+  ide_langserv_client_clear_diagnostics (self, src_uri);
 
   IDE_EXIT;
 }
 
+static IdeDiagnostics *
+ide_langserv_client_translate_diagnostics (IdeLangservClient *self,
+                                           IdeFile           *file,
+                                           JsonArray         *diagnostics)
+{
+  g_autoptr(GPtrArray) ar = NULL;
+  guint length;
+
+  g_assert (IDE_IS_LANGSERV_CLIENT (self));
+  g_assert (diagnostics != NULL);
+
+  length = json_array_get_length (diagnostics);
+
+  ar = g_ptr_array_sized_new (length);
+  g_ptr_array_set_free_func (ar, (GDestroyNotify)ide_diagnostic_unref);
+
+  for (guint i = 0; i < length; i++)
+    {
+      JsonNode *node = json_array_get_element (diagnostics, i);
+      g_autoptr(IdeSourceLocation) begin_loc = NULL;
+      g_autoptr(IdeSourceLocation) end_loc = NULL;
+      g_autoptr(IdeDiagnostic) diag = NULL;
+      const gchar *message = NULL;
+      const gchar *source = NULL;
+      JsonNode *range = NULL;
+      gint severity = 0;
+      gboolean success;
+      struct {
+        gint line;
+        gint column;
+      } begin, end;
+
+      /* Mandatory fields */
+      if (!JCON_EXTRACT (node,
+                         "range", JCONE_NODE (range),
+                         "message", JCONE_STRING (message)))
+        continue;
+
+      /* Optional Fields */
+      JCON_EXTRACT (node, "severity", JCONE_INT (severity));
+      JCON_EXTRACT (node, "source", JCONE_STRING (source));
+
+      /* Extract location information */
+      success = JCON_EXTRACT (range,
+        "start", "{",
+          "line", JCONE_INT (begin.line),
+          "character", JCONE_INT (begin.column),
+        "}",
+        "end", "{",
+          "line", JCONE_INT (end.line),
+          "character", JCONE_INT (end.column),
+        "}"
+      );
+
+      if (!success)
+        continue;
+
+      begin_loc = ide_source_location_new (file, begin.line, begin.column, 0);
+      end_loc = ide_source_location_new (file, end.line, end.column, 0);
+
+      switch (severity)
+        {
+        case SEVERITY_ERROR:
+          severity = IDE_DIAGNOSTIC_ERROR;
+          break;
+
+        case SEVERITY_WARNING:
+          severity = IDE_DIAGNOSTIC_WARNING;
+          break;
+
+        case SEVERITY_INFORMATION:
+        case SEVERITY_HINT:
+        default:
+          severity = IDE_DIAGNOSTIC_NOTE;
+          break;
+        }
+
+      diag = ide_diagnostic_new (severity, message, begin_loc);
+      ide_diagnostic_take_range (diag, ide_source_range_new (begin_loc, end_loc));
+
+      g_ptr_array_add (ar, g_steal_pointer (&diag));
+    }
+
+  return ide_diagnostics_new (g_steal_pointer (&ar));
+}
+
 static void
 ide_langserv_client_text_document_publish_diagnostics (IdeLangservClient *self,
                                                        JsonNode          *params)
 {
+  IdeLangservClientPrivate *priv = ide_langserv_client_get_instance_private (self);
+  JsonArray *json_diagnostics = NULL;
   const gchar *uri = NULL;
-  JsonArray *diagnostics = NULL;
   gboolean success;
 
   IDE_ENTRY;
 
+  g_print ("================================================== diagnostics\n");
+
   g_assert (IDE_IS_LANGSERV_CLIENT (self));
   g_assert (params != NULL);
 
   success = JCON_EXTRACT (params,
     "uri", JCONE_STRING (uri),
-    "diagnostics", JCONE_ARRAY (diagnostics)
+    "diagnostics", JCONE_ARRAY (json_diagnostics)
   );
 
-  if (!success)
-    IDE_EXIT;
+  if (success)
+    {
+      g_autoptr(IdeFile) ifile = NULL;
+      g_autoptr(GFile) file = NULL;
 
-  IDE_TRACE_MSG ("Diagnostics received for %s", uri);
+      IDE_TRACE_MSG ("Diagnostics received for %s", uri);
+
+      file = g_file_new_for_uri (uri);
+      ifile = g_object_new (IDE_TYPE_FILE,
+                            "file", file,
+                            "context", ide_object_get_context (IDE_OBJECT (self)),
+                            NULL);
+
+      g_hash_table_insert (priv->diagnostics_by_file,
+                           g_file_new_for_uri (uri),
+                           ide_langserv_client_translate_diagnostics (self, ifile, json_diagnostics));
+    }
 
   IDE_EXIT;
 }
@@ -296,7 +438,7 @@ ide_langserv_client_finalize (GObject *object)
   IdeLangservClient *self = (IdeLangservClient *)object;
   IdeLangservClientPrivate *priv = ide_langserv_client_get_instance_private (self);
 
-  g_clear_pointer (&priv->diagnostics_by_uri, g_hash_table_unref);
+  g_clear_pointer (&priv->diagnostics_by_file, g_hash_table_unref);
   g_clear_object (&priv->rpc_client);
   g_clear_object (&priv->buffer_manager_signals);
   g_clear_object (&priv->project_signals);
@@ -381,10 +523,10 @@ ide_langserv_client_init (IdeLangservClient *self)
 {
   IdeLangservClientPrivate *priv = ide_langserv_client_get_instance_private (self);
 
-  priv->diagnostics_by_uri = g_hash_table_new_full (g_str_hash,
-                                                    g_str_equal,
-                                                    g_free,
-                                                    (GDestroyNotify)ide_diagnostics_unref);
+  priv->diagnostics_by_file = g_hash_table_new_full ((GHashFunc)g_file_hash,
+                                                     (GEqualFunc)g_file_equal,
+                                                     g_object_unref,
+                                                     (GDestroyNotify)ide_diagnostics_unref);
 
   priv->buffer_manager_signals = egg_signal_group_new (IDE_TYPE_BUFFER_MANAGER);
 
@@ -516,7 +658,10 @@ ide_langserv_client_start (IdeLangservClient *self)
     "capabilities", "{", "}"
   );
 
-  jsonrpc_client_call_async (priv->rpc_client, "initialize", params, NULL,
+  jsonrpc_client_call_async (priv->rpc_client,
+                             "initialize",
+                             g_steal_pointer (&params),
+                             NULL,
                              ide_langserv_client_initialize_cb,
                              g_object_ref (self));
 
@@ -567,6 +712,17 @@ ide_langserv_client_call_cb (GObject      *object,
   IDE_EXIT;
 }
 
+/**
+ * ide_langserv_client_call_async:
+ * @self: An #IdeLangservClient
+ * @method: the method to call
+ * @params: (nullable) (transfer full): An #JsonNode or %NULL
+ * @cancellable: (nullable): A cancellable or %NULL
+ * @callback: the callback to receive the result, or %NULL
+ * @user_data: user data for @callback
+ *
+ * Asynchronously queries the Language Server using the JSON-RPC protocol.
+ */
 void
 ide_langserv_client_call_async (IdeLangservClient   *self,
                                 const gchar         *method,
@@ -624,3 +780,71 @@ ide_langserv_client_call_finish (IdeLangservClient  *self,
 
   IDE_RETURN (ret);
 }
+
+void
+ide_langserv_client_get_diagnostics_async (IdeLangservClient   *self,
+                                           GFile               *file,
+                                           GCancellable        *cancellable,
+                                           GAsyncReadyCallback  callback,
+                                           gpointer             user_data)
+{
+  IdeLangservClientPrivate *priv = ide_langserv_client_get_instance_private (self);
+  g_autoptr(GTask) task = NULL;
+  IdeDiagnostics *diagnostics;
+
+  g_return_if_fail (IDE_IS_LANGSERV_CLIENT (self));
+  g_return_if_fail (G_IS_FILE (file));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_langserv_client_get_diagnostics_async);
+
+  diagnostics = g_hash_table_lookup (priv->diagnostics_by_file, file);
+
+  if (diagnostics != NULL)
+    g_task_return_pointer (task,
+                           ide_diagnostics_ref (diagnostics),
+                           (GDestroyNotify)ide_diagnostics_unref);
+  else
+    g_task_return_pointer (task, NULL, NULL);
+}
+
+/**
+ * ide_langserv_client_get_diagnostics_finish:
+ * @self: A #IdeLangservClient
+ * @result: A #GAsyncResult
+ * @diagnostics: (nullable) (out): A location for a #IdeDiagnostics or %NULL
+ * @error: A location for a #GError or %NULL
+ *
+ * Completes a request to ide_langserv_client_get_diagnostics_async().
+ *
+ * Returns: %TRUE if successful and @diagnostics is set, otherwise %FALSE
+ *   and @error is set.
+ */
+gboolean
+ide_langserv_client_get_diagnostics_finish (IdeLangservClient  *self,
+                                            GAsyncResult       *result,
+                                            IdeDiagnostics    **diagnostics,
+                                            GError            **error)
+{
+  g_autoptr(IdeDiagnostics) local_diagnostics = NULL;
+  g_autoptr(GError) local_error = NULL;
+  gboolean ret;
+
+  g_return_val_if_fail (IDE_IS_LANGSERV_CLIENT (self), FALSE);
+  g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+  local_diagnostics = g_task_propagate_pointer (G_TASK (result), &local_error);
+  ret = local_error == NULL;
+
+  if (ret == TRUE && local_diagnostics == NULL)
+    local_diagnostics = ide_diagnostics_new (NULL);
+
+  if (local_diagnostics != NULL && diagnostics != NULL)
+    *diagnostics = g_steal_pointer (&local_diagnostics);
+
+  if (local_error)
+    g_propagate_error (error, local_error);
+
+  return ret;
+}
diff --git a/libide/langserv/ide-langserv-client.h b/libide/langserv/ide-langserv-client.h
index dac9a35..c5d5caa 100644
--- a/libide/langserv/ide-langserv-client.h
+++ b/libide/langserv/ide-langserv-client.h
@@ -47,20 +47,29 @@ struct _IdeLangservClientClass
   gpointer _reserved8;
 };
 
-IdeLangservClient *ide_langserv_client_new         (IdeContext           *context,
-                                                    GIOStream            *io_stream);
-void               ide_langserv_client_start       (IdeLangservClient    *self);
-void               ide_langserv_client_stop        (IdeLangservClient    *self);
-void               ide_langserv_client_call_async  (IdeLangservClient    *self,
-                                                    const gchar          *method,
-                                                    JsonNode             *params,
-                                                    GCancellable         *cancellable,
-                                                    GAsyncReadyCallback   callback,
-                                                    gpointer              user_data);
-gboolean           ide_langserv_client_call_finish (IdeLangservClient    *self,
-                                                    GAsyncResult         *result,
-                                                    JsonNode            **return_value,
-                                                    GError              **error);
+IdeLangservClient *ide_langserv_client_new                    (IdeContext           *context,
+                                                              GIOStream            *io_stream);
+void               ide_langserv_client_start                  (IdeLangservClient    *self);
+void               ide_langserv_client_stop                   (IdeLangservClient    *self);
+void               ide_langserv_client_call_async             (IdeLangservClient    *self,
+                                                               const gchar          *method,
+                                                               JsonNode             *params,
+                                                               GCancellable         *cancellable,
+                                                               GAsyncReadyCallback   callback,
+                                                               gpointer              user_data);
+gboolean           ide_langserv_client_call_finish            (IdeLangservClient    *self,
+                                                               GAsyncResult         *result,
+                                                               JsonNode            **return_value,
+                                                               GError              **error);
+void               ide_langserv_client_get_diagnostics_async  (IdeLangservClient    *self,
+                                                               GFile                *file,
+                                                               GCancellable         *cancellable,
+                                                               GAsyncReadyCallback   callback,
+                                                               gpointer              user_data);
+gboolean           ide_langserv_client_get_diagnostics_finish (IdeLangservClient    *self,
+                                                               GAsyncResult         *result,
+                                                               IdeDiagnostics      **diagnostics,
+                                                               GError              **error);
 
 G_END_DECLS
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]