[gnome-builder] monitor Cargo.toml file and restart in case of a change



commit 1d11a736d0e4f218ed844a695cdcfca60bc1efde
Author: Günther Wagner <info gunibert de>
Date:   Sun May 10 17:37:13 2020 +0200

    monitor Cargo.toml file and restart in case of a change

 doc/help/plugins/langserv.rst                     | 74 +++++++++++++++++++++++
 src/plugins/rust-analyzer/rust-analyzer-service.c | 55 +++++++++++++++++
 2 files changed, 129 insertions(+)
---
diff --git a/doc/help/plugins/langserv.rst b/doc/help/plugins/langserv.rst
index ed99dbe5d..63692234f 100644
--- a/doc/help/plugins/langserv.rst
+++ b/doc/help/plugins/langserv.rst
@@ -1,3 +1,77 @@
 ############################
 Integrating Language Servers
 ############################
+
+In order to integrate a language server with **GNOME Builder** you have to create an 
``Ide.SubprocessLauncher``
+to startup the language server.
+
+This subprocess should be restarted if it breaks therefore we have to wrap this
+``Ide.SubprocessLauncher`` in a ``Ide.SubprocessSupervisor`` to monitor the
+external process. After the subprocess is started we connect ``stdin`` and ``stdout``
+to our ``Ide.LspClient`` for dispatching of messages between client and server.
+
+.. code-block:: python3
+
+   class LSPService(Ide.Object):
+      _has_started = False
+      _client = None
+
+      @GObject.Property(type=Ide.LspClient)
+      def client(self):
+         return self._client = value
+
+      @client.setter
+      def client(self, value):
+         self._client = value
+         self.notify('client')
+
+      def start(self):
+         if not self._has_started:
+            self._has_started = True
+            launcher = Ide.SubprocessLauncher()
+            launcher.set_flags(Gio.SubprocessFlags.STDIN_PIPE | Gio.SubprocessFlags.STDOUT_PIPE)
+            launcher.push_argv("my_language_server_executable")
+
+            supervisor = Ide.SubprocessSupervisor()
+            supervisor.connect('spawned', lsp_spawned)
+            supervisor.set_launcher(launcher)
+            supervisor.start()
+
+      def lsp_spawned(self, supervisor, subprocess):
+         stdin = subprocess.get_stdin_pipe()
+         stdout = subprocess.get_stdout_pipe()
+         io_stream = Gio.SimpleIOStream.new(stdout, stdin)
+
+         client = Ide.LspClient.new(io_stream)
+         self.append(client)
+         client.add_language('my_language')
+         client.start()
+         self.client(client)
+
+
+As a language server handles several parts of an IDE we have to create according
+extensions for code completion, diagnostics or hover content. As we want to make
+sure that the corresponding service is only started once we use the builtin object
+system (``Ide.Context.ensure_child_type(type)``.
+
+.. code-block:: python3
+
+   class MyLspCompletionProvider(Ide.LspCompletionProvider, Ide.CompletionProvider):
+
+      def do_load(self, context):
+         service = context.ensure_child_typed(LSPService)
+         service.start()
+         service.bind_property('client', self, 'client', GObject.BindingFlags.SYNC_CREATE)
+
+      def do_get_priority(self, context):
+         return 0
+
+.. code-block:: python3
+
+   class MyLspDiagnosticProvider(Ide.LspDiagnosticProvider, Ide.DiagnosticProvider):
+
+      def do_load(self):
+         context = self.get_context()
+         service = context.ensure_child_typed(LSPService)
+         service.start()
+         service.bind_property('client', self, 'client', GObject.BindingFlags.SYNC_CREATE)
diff --git a/src/plugins/rust-analyzer/rust-analyzer-service.c 
b/src/plugins/rust-analyzer/rust-analyzer-service.c
index efe69bffd..96c286305 100644
--- a/src/plugins/rust-analyzer/rust-analyzer-service.c
+++ b/src/plugins/rust-analyzer/rust-analyzer-service.c
@@ -33,6 +33,7 @@ struct _RustAnalyzerService
   IdeObject parent_instance;
   IdeLspClient  *client;
   IdeSubprocessSupervisor *supervisor;
+  GFileMonitor *cargo_monitor;
 
   ServiceState state;
 };
@@ -53,6 +54,23 @@ rust_analyzer_service_new (void)
   return g_object_new (RUST_TYPE_ANALYZER_SERVICE, NULL);
 }
 
+static void
+_cargo_toml_changed_cb (GFileMonitor      *monitor,
+                        GFile             *file,
+                        GFile             *other_file,
+                        GFileMonitorEvent  event_type,
+                        gpointer           user_data)
+{
+  RustAnalyzerService *self = RUST_ANALYZER_SERVICE (user_data);
+
+  if (self->supervisor != NULL)
+    {
+      IdeSubprocess *subprocess = ide_subprocess_supervisor_get_subprocess (self->supervisor);
+      if (subprocess != NULL)
+        ide_subprocess_force_exit (subprocess);
+    }
+}
+
 static void
 rust_analyzer_service_finalize (GObject *object)
 {
@@ -99,6 +117,42 @@ rust_analyzer_service_set_property (GObject      *object,
     }
 }
 
+static void
+rust_analyzer_service_set_parent (IdeObject *object,
+                                  IdeObject *parent)
+{
+  RustAnalyzerService *self = RUST_ANALYZER_SERVICE (object);
+
+  IdeContext *context = NULL;
+  g_autoptr(GFile) workdir = NULL;
+  g_autoptr(GFile) cargo_toml = NULL;
+
+  g_return_if_fail (RUST_IS_ANALYZER_SERVICE (object));
+  g_return_if_fail (parent != NULL);
+
+  context = ide_object_get_context (object);
+  workdir = ide_context_ref_workdir (context);
+  cargo_toml = g_file_get_child (workdir, "Cargo.toml");
+
+  if (g_file_query_exists (cargo_toml, NULL))
+    {
+      GError *error = NULL;
+
+      if (self->cargo_monitor != NULL)
+        return;
+
+      self->cargo_monitor = g_file_monitor (cargo_toml, G_FILE_MONITOR_NONE, NULL, &error);
+      if (error != NULL)
+        {
+          g_warning ("%s", error->message);
+          return;
+        }
+      g_file_monitor_set_rate_limit (self->cargo_monitor, 5 * 1000); // 5 Seconds
+      g_signal_connect (self->cargo_monitor, "changed", G_CALLBACK (_cargo_toml_changed_cb), self);
+    }
+
+}
+
 static void
 rust_analyzer_service_destroy (IdeObject *object)
 {
@@ -124,6 +178,7 @@ rust_analyzer_service_class_init (RustAnalyzerServiceClass *klass)
   object_class->get_property = rust_analyzer_service_get_property;
   object_class->set_property = rust_analyzer_service_set_property;
 
+  i_class->parent_set = rust_analyzer_service_set_parent;
   i_class->destroy = rust_analyzer_service_destroy;
 
   properties [PROP_CLIENT] =


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