Commit 1d11a736 authored by Günther Wagner's avatar Günther Wagner Committed by Christian Hergert

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

parent 362c718b
############################
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)
......@@ -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] =
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment