[gnome-builder] monitor Cargo.toml file and restart in case of a change
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] monitor Cargo.toml file and restart in case of a change
- Date: Mon, 11 May 2020 23:42:24 +0000 (UTC)
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]