diff --git a/src/plugins/rust-analyzer/meson.build b/src/plugins/rust-analyzer/meson.build index 3d7097d3cb9eabe18f04bfa21c771fd57cb52916..7f7ce0f7eca5db352bc3a903bb432e2cfb8bbb7a 100644 --- a/src/plugins/rust-analyzer/meson.build +++ b/src/plugins/rust-analyzer/meson.build @@ -1,13 +1,21 @@ if get_option('plugin_rust_analyzer') -install_data('rust_analyzer_plugin.py', install_dir: plugindir) - -configure_file( - input: 'rust-analyzer.plugin', - output: 'rust-analyzer.plugin', - configuration: config_h, - install: true, - install_dir: plugindir, +plugins_sources += files([ + 'rust-analyzer.c', + 'rust-analyzer-service.c', + 'rust-analyzer-completion-provider.c', + 'rust-analyzer-symbol-resolver.c', + 'rust-analyzer-diagnostic-provider.c', + 'rust-analyzer-transfer.c', + 'rust-analyzer-workbench-addin.c', +]) + +plugin_rust_analyzer_resources = gnome.compile_resources( + 'rust-analyzer-resources', + 'rust-analyzer.gresource.xml', + c_name: 'rust_analyzer' ) +plugins_sources += plugin_rust_analyzer_resources + endif diff --git a/src/plugins/rust-analyzer/rust-analyzer-completion-provider.c b/src/plugins/rust-analyzer/rust-analyzer-completion-provider.c new file mode 100644 index 0000000000000000000000000000000000000000..6b88d081c0a34d42ecf8c9a8e5c6b35a3b45bccb --- /dev/null +++ b/src/plugins/rust-analyzer/rust-analyzer-completion-provider.c @@ -0,0 +1,67 @@ +/* rust-analyzer-completion-provider.c + * + * Copyright 2020 Günther Wagner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "rust-analyzer-completion-provider.h" +#include "rust-analyzer-service.h" + +struct _RustAnalyzerCompletionProvider +{ + IdeLspCompletionProvider parent_instance; +}; + +static void provider_iface_init (IdeCompletionProviderInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (RustAnalyzerCompletionProvider, + rust_analyzer_completion_provider, + IDE_TYPE_LSP_COMPLETION_PROVIDER, + G_IMPLEMENT_INTERFACE (IDE_TYPE_COMPLETION_PROVIDER, provider_iface_init)) + +static void +rust_analyzer_completion_provider_class_init (RustAnalyzerCompletionProviderClass *klass) +{ +} + +static void +rust_analyzer_completion_provider_init (RustAnalyzerCompletionProvider *self) +{ +} + +static void +rust_analyzer_completion_provider_load (IdeCompletionProvider *self, + IdeContext *context) +{ + RustAnalyzerService *service = ide_object_ensure_child_typed (IDE_OBJECT (context), RUST_TYPE_ANALYZER_SERVICE); + g_object_bind_property (service, "client", self, "client", G_BINDING_SYNC_CREATE); + rust_analyzer_service_ensure_started (service); +} + +static gint +rust_analyzer_completion_provider_get_priority (IdeCompletionProvider *provider, + IdeCompletionContext *context) +{ + return -1000; +} + +static void +provider_iface_init (IdeCompletionProviderInterface *iface) +{ + iface->load = rust_analyzer_completion_provider_load; + iface->get_priority = rust_analyzer_completion_provider_get_priority; +} diff --git a/src/plugins/rust-analyzer/rust-analyzer-completion-provider.h b/src/plugins/rust-analyzer/rust-analyzer-completion-provider.h new file mode 100644 index 0000000000000000000000000000000000000000..f06faaff5e52faa2df4fdefa83b0212897f915b0 --- /dev/null +++ b/src/plugins/rust-analyzer/rust-analyzer-completion-provider.h @@ -0,0 +1,32 @@ +/* rust-analyzer-completion-provider.h + * + * Copyright 2020 Günther Wagner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include +#include + +G_BEGIN_DECLS + +#define RUST_TYPE_ANALYZER_COMPLETION_PROVIDER (rust_analyzer_completion_provider_get_type()) + +G_DECLARE_FINAL_TYPE (RustAnalyzerCompletionProvider, rust_analyzer_completion_provider, RUST, ANALYZER_COMPLETION_PROVIDER, IdeLspCompletionProvider) + +G_END_DECLS diff --git a/src/plugins/rust-analyzer/rust-analyzer-diagnostic-provider.c b/src/plugins/rust-analyzer/rust-analyzer-diagnostic-provider.c new file mode 100644 index 0000000000000000000000000000000000000000..dd5e16c781b5bd4693a1fb9d44c0767f63e7bc73 --- /dev/null +++ b/src/plugins/rust-analyzer/rust-analyzer-diagnostic-provider.c @@ -0,0 +1,59 @@ +/* rust-analyzer-diagnostic-provider.c + * + * Copyright 2020 Günther Wagner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "rust-analyzer-diagnostic-provider.h" +#include "rust-analyzer-service.h" + +struct _RustAnalyzerDiagnosticProvider +{ + IdeLspDiagnosticProvider parent_instance; +}; + +static void provider_iface_init (IdeDiagnosticProviderInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (RustAnalyzerDiagnosticProvider, + rust_analyzer_diagnostic_provider, + IDE_TYPE_LSP_DIAGNOSTIC_PROVIDER, + G_IMPLEMENT_INTERFACE (IDE_TYPE_DIAGNOSTIC_PROVIDER, provider_iface_init)) + +static void +rust_analyzer_diagnostic_provider_class_init (RustAnalyzerDiagnosticProviderClass *klass) +{ +} + +static void +rust_analyzer_diagnostic_provider_init (RustAnalyzerDiagnosticProvider *self) +{ +} + +static void +rust_analyzer_diagnostic_provider_load (IdeDiagnosticProvider *self) +{ + IdeContext *context = ide_object_get_context (IDE_OBJECT (self)); + RustAnalyzerService *service = ide_object_ensure_child_typed (IDE_OBJECT (context), RUST_TYPE_ANALYZER_SERVICE); + rust_analyzer_service_ensure_started (service); + g_object_bind_property (service, "client", self, "client", G_BINDING_SYNC_CREATE); +} + +static void +provider_iface_init (IdeDiagnosticProviderInterface *iface) +{ + iface->load = rust_analyzer_diagnostic_provider_load; +} diff --git a/src/plugins/rust-analyzer/rust-analyzer-diagnostic-provider.h b/src/plugins/rust-analyzer/rust-analyzer-diagnostic-provider.h new file mode 100644 index 0000000000000000000000000000000000000000..5da85ef38781732e54182f75d7e1d178d9821f2f --- /dev/null +++ b/src/plugins/rust-analyzer/rust-analyzer-diagnostic-provider.h @@ -0,0 +1,31 @@ +/* rust-analyzer-diagnostic-provider.h + * + * Copyright 2020 Günther Wagner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define RUST_TYPE_ANALYZER_DIAGNOSTIC_PROVIDER (rust_analyzer_diagnostic_provider_get_type()) + +G_DECLARE_FINAL_TYPE (RustAnalyzerDiagnosticProvider, rust_analyzer_diagnostic_provider, RUST, ANALYZER_DIAGNOSTIC_PROVIDER, IdeLspDiagnosticProvider) + +G_END_DECLS diff --git a/src/plugins/rust-analyzer/rust-analyzer-service.c b/src/plugins/rust-analyzer/rust-analyzer-service.c new file mode 100644 index 0000000000000000000000000000000000000000..d9aa76715e89ba2153216f6ddc5ed67f8397d102 --- /dev/null +++ b/src/plugins/rust-analyzer/rust-analyzer-service.c @@ -0,0 +1,295 @@ +/* rust-analyzer-service.c + * + * Copyright 2020 Günther Wagner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "rust-analyzer-service" + +#include "rust-analyzer-service.h" +#include "rust-analyzer-transfer.h" +#include +#include +#include +#include + +struct _RustAnalyzerService +{ + IdeObject parent_instance; + IdeLspClient *client; + IdeSubprocessSupervisor *supervisor; + + ServiceState state; +}; + +G_DEFINE_TYPE (RustAnalyzerService, rust_analyzer_service, IDE_TYPE_OBJECT) + +enum { + PROP_0, + PROP_CLIENT, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + +RustAnalyzerService * +rust_analyzer_service_new (void) +{ + return g_object_new (RUST_TYPE_ANALYZER_SERVICE, NULL); +} + +static void +rust_analyzer_service_finalize (GObject *object) +{ + RustAnalyzerService *self = (RustAnalyzerService *)object; + + g_clear_object (&self->client); + + G_OBJECT_CLASS (rust_analyzer_service_parent_class)->finalize (object); +} + +static void +rust_analyzer_service_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + RustAnalyzerService *self = RUST_ANALYZER_SERVICE (object); + + switch (prop_id) + { + case PROP_CLIENT: + g_value_set_object (value, rust_analyzer_service_get_client (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +rust_analyzer_service_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + RustAnalyzerService *self = RUST_ANALYZER_SERVICE (object); + + switch (prop_id) + { + case PROP_CLIENT: + rust_analyzer_service_set_client (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +rust_analyzer_service_destroy (IdeObject *object) +{ + RustAnalyzerService *self = RUST_ANALYZER_SERVICE (object); + + if (self->supervisor != NULL) + { + g_autoptr(IdeSubprocessSupervisor) supervisor = g_steal_pointer (&self->supervisor); + + ide_subprocess_supervisor_stop (supervisor); + } + + IDE_OBJECT_CLASS (rust_analyzer_service_parent_class)->destroy (object); +} + +static void +rust_analyzer_service_class_init (RustAnalyzerServiceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + IdeObjectClass *i_class = IDE_OBJECT_CLASS (klass); + + object_class->finalize = rust_analyzer_service_finalize; + object_class->get_property = rust_analyzer_service_get_property; + object_class->set_property = rust_analyzer_service_set_property; + + i_class->destroy = rust_analyzer_service_destroy; + + properties [PROP_CLIENT] = + g_param_spec_object ("client", + "Client", + "The Language Server client", + IDE_TYPE_LSP_CLIENT, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +rust_analyzer_service_init (RustAnalyzerService *self) +{ + self->client = NULL; + self->state = RUST_ANALYZER_SERVICE_INIT; +} + +IdeLspClient * +rust_analyzer_service_get_client (RustAnalyzerService *self) +{ + g_return_val_if_fail (RUST_IS_ANALYZER_SERVICE (self), NULL); + + return self->client; +} + +void +rust_analyzer_service_set_client (RustAnalyzerService *self, + IdeLspClient *client) +{ + g_return_if_fail (RUST_IS_ANALYZER_SERVICE (self)); + g_return_if_fail (!client || IDE_IS_LSP_CLIENT (client)); + + if (g_set_object (&self->client, client)) + { + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CLIENT]); + } +} + +void +rust_analyzer_service_lsp_started (IdeSubprocessSupervisor *supervisor, + IdeSubprocess *subprocess, + gpointer user_data) +{ + g_autoptr(GIOStream) io_stream = NULL; + GInputStream *input; + GOutputStream *output; + IdeLspClient *client = NULL; + + RustAnalyzerService *self = RUST_ANALYZER_SERVICE (user_data); + + input = ide_subprocess_get_stdout_pipe (subprocess); + output = ide_subprocess_get_stdin_pipe (subprocess); + io_stream = g_simple_io_stream_new (input, output); + + if (self->client != NULL) + { + ide_lsp_client_stop (self->client); + ide_object_destroy (IDE_OBJECT (self->client)); + } + + client = ide_lsp_client_new (io_stream); + rust_analyzer_service_set_client (self, client); + ide_object_append (IDE_OBJECT (self), IDE_OBJECT (client)); + ide_lsp_client_add_language (client, "rust"); + ide_lsp_client_start (client); +} + +static gboolean +rust_analyzer_service_check_rust_analyzer_bin (RustAnalyzerService *self) +{ + // Check if `rust-analyzer` can be found on PATH or if there is an executable + // in typical location + g_autoptr(GFile) rust_analyzer_bin_file = NULL; + g_autofree gchar *rust_analyzer_bin = NULL; + g_autoptr(GFileInfo) file_info = NULL; + + rust_analyzer_bin = g_find_program_in_path ("rust-analyzer"); + if (rust_analyzer_bin == NULL) + { + g_autofree gchar *path = NULL; + const gchar *homedir = g_get_home_dir (); + + path = g_build_path (G_DIR_SEPARATOR_S, homedir, ".cargo", "bin", "rust-analyzer", NULL); + rust_analyzer_bin_file = g_file_new_for_path (path); + } + else + { + rust_analyzer_bin_file = g_file_new_for_path (rust_analyzer_bin); + } + + if (!g_file_query_exists (rust_analyzer_bin_file, NULL)) + { + return FALSE; + } + + file_info = g_file_query_info (rust_analyzer_bin_file, + "*", + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + + if (ide_str_equal0 ("application/x-sharedlib", g_file_info_get_content_type (file_info))) + return TRUE; + + return FALSE; +} + +void +rust_analyzer_service_ensure_started (RustAnalyzerService *self) +{ + if (self->state == RUST_ANALYZER_SERVICE_INIT) + { + if (!rust_analyzer_service_check_rust_analyzer_bin (self)) + { + g_autoptr(IdeNotification) notification = NULL; + IdeContext *context = NULL; + + self->state = RUST_ANALYZER_SERVICE_OFFER_DOWNLOAD; + + notification = ide_notification_new (); + ide_notification_set_id (notification, "org.gnome-builder.rust-analyzer"); + ide_notification_set_title (notification, "Your computer is missing the Rust Analyzer Language Server"); + ide_notification_set_body (notification, "The Language Server is necessary to provide IDE features like completion or diagnostic"); + ide_notification_set_icon_name (notification, "dialog-warning-symbolic"); + ide_notification_add_button (notification, "Install Language Server", NULL, "win.install-rust-analyzer"); + ide_notification_set_urgent (notification, TRUE); + context = ide_object_get_context (IDE_OBJECT (self)); + ide_notification_attach (notification, IDE_OBJECT (context)); + } + else + self->state = RUST_ANALYZER_SERVICE_READY; + } + else if (self->state == RUST_ANALYZER_SERVICE_READY) + { + g_autofree gchar *newpath = NULL; + g_autoptr(IdeSubprocessLauncher) launcher = NULL; + IdeContext *context = NULL; + GFile *workdir = NULL; + const gchar *oldpath = NULL; + + launcher = ide_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE | G_SUBPROCESS_FLAGS_STDIN_PIPE); + ide_subprocess_launcher_set_run_on_host (launcher, TRUE); + ide_subprocess_launcher_set_clear_env (launcher, TRUE); + + context = ide_object_get_context (IDE_OBJECT (self)); + workdir = ide_context_ref_workdir (context); + ide_subprocess_launcher_set_cwd (launcher, g_file_get_path (workdir)); + oldpath = g_getenv ("PATH"); + newpath = g_strdup_printf ("%s/%s:%s", g_get_home_dir (), ".cargo/bin", oldpath); + ide_subprocess_launcher_setenv (launcher, "PATH", newpath, TRUE); + + ide_subprocess_launcher_push_argv (launcher, "rust-analyzer"); + + self->supervisor = ide_subprocess_supervisor_new (); + g_signal_connect (self->supervisor, "spawned", G_CALLBACK (rust_analyzer_service_lsp_started), self); + ide_subprocess_supervisor_set_launcher (self->supervisor, launcher); + ide_subprocess_supervisor_start (self->supervisor); + self->state = RUST_ANALYZER_SERVICE_LSP_STARTED; + } +} + +void +rust_analyzer_service_set_state (RustAnalyzerService *self, + ServiceState state) +{ + g_return_if_fail (RUST_IS_ANALYZER_SERVICE (self)); + + self->state = state; +} diff --git a/src/plugins/rust-analyzer/rust-analyzer-service.h b/src/plugins/rust-analyzer/rust-analyzer-service.h new file mode 100644 index 0000000000000000000000000000000000000000..6c3ffae6609bcf31a506d36c22f5474f60ff41f3 --- /dev/null +++ b/src/plugins/rust-analyzer/rust-analyzer-service.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +G_BEGIN_DECLS + +#define RUST_TYPE_ANALYZER_SERVICE (rust_analyzer_service_get_type()) + +G_DECLARE_FINAL_TYPE (RustAnalyzerService, rust_analyzer_service, RUST, ANALYZER_SERVICE, IdeObject) + +typedef enum { + RUST_ANALYZER_SERVICE_INIT, + RUST_ANALYZER_SERVICE_OFFER_DOWNLOAD, + RUST_ANALYZER_SERVICE_READY, + RUST_ANALYZER_SERVICE_LSP_STARTED, +} ServiceState; + +RustAnalyzerService *rust_analyzer_service_new (void); +IdeLspClient *rust_analyzer_service_get_client (RustAnalyzerService *self); +void rust_analyzer_service_set_client (RustAnalyzerService *self, + IdeLspClient *client); +void rust_analyzer_service_ensure_started (RustAnalyzerService *self); +void rust_analyzer_service_set_state (RustAnalyzerService *self, + ServiceState state); + +G_END_DECLS diff --git a/src/plugins/rust-analyzer/rust-analyzer-symbol-resolver.c b/src/plugins/rust-analyzer/rust-analyzer-symbol-resolver.c new file mode 100644 index 0000000000000000000000000000000000000000..980138b8921f3c08ea7cd431dfe3f41cdff1be3c --- /dev/null +++ b/src/plugins/rust-analyzer/rust-analyzer-symbol-resolver.c @@ -0,0 +1,59 @@ +/* rust-analyzer-symbol-resolver.c + * + * Copyright 2020 Günther Wagner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "rust-analyzer-symbol-resolver.h" +#include "rust-analyzer-service.h" + +struct _RustAnalyzerSymbolResolver +{ + IdeLspSymbolResolver parent_instance; +}; + +static void symbol_iface_init (IdeSymbolResolverInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (RustAnalyzerSymbolResolver, + rust_analyzer_symbol_resolver, + IDE_TYPE_LSP_SYMBOL_RESOLVER, + G_IMPLEMENT_INTERFACE (IDE_TYPE_SYMBOL_RESOLVER, symbol_iface_init)) + +static void +rust_analyzer_symbol_resolver_class_init (RustAnalyzerSymbolResolverClass *klass) +{ +} + +static void +rust_analyzer_symbol_resolver_init (RustAnalyzerSymbolResolver *self) +{ +} + +static void +rust_analyzer_symbol_resolver_load (IdeSymbolResolver *self) +{ + IdeContext *context = ide_object_get_context (IDE_OBJECT (self)); + RustAnalyzerService *service = ide_object_ensure_child_typed (IDE_OBJECT (context), RUST_TYPE_ANALYZER_SERVICE); + rust_analyzer_service_ensure_started (service); + g_object_bind_property (service, "client", self, "client", G_BINDING_SYNC_CREATE); +} + +static void +symbol_iface_init (IdeSymbolResolverInterface *iface) +{ + iface->load = rust_analyzer_symbol_resolver_load; +} diff --git a/src/plugins/rust-analyzer/rust-analyzer-symbol-resolver.h b/src/plugins/rust-analyzer/rust-analyzer-symbol-resolver.h new file mode 100644 index 0000000000000000000000000000000000000000..12d8bd507bec41ae060da2bd390bd62cd969d552 --- /dev/null +++ b/src/plugins/rust-analyzer/rust-analyzer-symbol-resolver.h @@ -0,0 +1,31 @@ +/* rust-analyzer-symbol-resolver.h + * + * Copyright 2020 Günther Wagner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define RUST_TYPE_ANALYZER_SYMBOL_RESOLVER (rust_analyzer_symbol_resolver_get_type()) + +G_DECLARE_FINAL_TYPE (RustAnalyzerSymbolResolver, rust_analyzer_symbol_resolver, RUST, ANALYZER_SYMBOL_RESOLVER, IdeLspSymbolResolver) + +G_END_DECLS diff --git a/src/plugins/rust-analyzer/rust-analyzer-transfer.c b/src/plugins/rust-analyzer/rust-analyzer-transfer.c new file mode 100644 index 0000000000000000000000000000000000000000..46b7bc7cc639f90baed0d410b42f97a540722203 --- /dev/null +++ b/src/plugins/rust-analyzer/rust-analyzer-transfer.c @@ -0,0 +1,155 @@ +/* rust-analyzer-transfer.c + * + * Copyright 2020 Günther Wagner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "rust-analyzer-transfer.h" +#include +#include +#include +#include +#include +#include + +struct _RustAnalyzerTransfer +{ + IdeTransfer parent_instance; +}; + +G_DEFINE_TYPE (RustAnalyzerTransfer, rust_analyzer_transfer, IDE_TYPE_TRANSFER) + +RustAnalyzerTransfer * +rust_analyzer_transfer_new (void) +{ + return g_object_new (RUST_TYPE_ANALYZER_TRANSFER, NULL); +} + +typedef struct { + gchar buffer[6*1024]; + gsize count; + guint64 total_bytes; + gchar *filepath; + GOutputStream *filestream; + IdeTransfer *transfer; + IdeTask *task; +} DownloadData; + +static void +_downloaded_chunk (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + g_autofree gchar *statusmsg = NULL; + GInputStream *stream = G_INPUT_STREAM (source_object); + DownloadData *data = user_data; + + gsize count = g_input_stream_read_finish (stream, result, NULL); + if (count == -1 || count == 0) + { + g_output_stream_close (data->filestream, NULL, NULL); + g_input_stream_close (stream, NULL, NULL); + ide_task_return_boolean (data->task, TRUE); + g_object_unref (data->task); + g_chmod (data->filepath, S_IRWXU); + g_free (data->filepath); + g_slice_free (DownloadData, data); + return; + } + + data->count += count; + statusmsg = g_strdup_printf ("%.2f MB / %.2f MB", data->count / 1048576., data->total_bytes / 1048576.); + ide_transfer_set_status (data->transfer, statusmsg); + ide_transfer_set_progress (data->transfer, (gdouble) data->count / data->total_bytes); + + g_output_stream_write_all (data->filestream, &data->buffer, count, NULL, ide_task_get_cancellable (data->task), NULL); + g_input_stream_read_async (stream, &data->buffer, sizeof (data->buffer), G_PRIORITY_DEFAULT, ide_task_get_cancellable (data->task), _downloaded_chunk, data); +} + +static void +_download_lsp (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(IdeTask) task = IDE_TASK (user_data); + g_autoptr(GFile) file = NULL; + SoupRequest *request = SOUP_REQUEST (source_object); + GInputStream *stream = NULL; + + stream = soup_request_send_finish (request, result, NULL); + + DownloadData *data = g_slice_new0 (DownloadData); + data->filepath = g_build_filename (g_get_home_dir (), ".cargo", "bin", "rust-analyzer", NULL); + file = g_file_new_for_path (data->filepath); + data->transfer = IDE_TRANSFER (ide_task_get_task_data (task)); + data->filestream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL)); // handle error + data->total_bytes = soup_request_get_content_length (request); + data->task = g_steal_pointer (&task); + + g_input_stream_read_async (stream, &data->buffer, sizeof (data->buffer), G_PRIORITY_DEFAULT, ide_task_get_cancellable (data->task), _downloaded_chunk, data); +} + +static void +rust_analyzer_transfer_execute_async (IdeTransfer *transfer, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + RustAnalyzerTransfer *self = RUST_ANALYZER_TRANSFER (transfer); + g_autoptr(IdeTask) task = NULL; + g_autoptr(SoupSession) session = NULL; + g_autoptr(SoupRequest) request = NULL; + + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + task = ide_task_new (self, cancellable, callback, user_data); + ide_task_set_source_tag (task, rust_analyzer_transfer_execute_async); + ide_task_set_task_data (task, transfer, g_object_unref); + + session = soup_session_new (); + + request = soup_session_request (session, "https://github.com/rust-analyzer/rust-analyzer/releases/download/nightly/rust-analyzer-linux", NULL); + + soup_request_send_async (request, NULL, _download_lsp, g_steal_pointer (&task)); +} + +static gboolean +rust_analyzer_transfer_execute_finish (IdeTransfer *transfer, + GAsyncResult *result, + GError **error) +{ + gboolean ret; + + ret = ide_task_propagate_boolean (IDE_TASK (result), error); + + return ret; +} + +static void +rust_analyzer_transfer_class_init (RustAnalyzerTransferClass *klass) +{ + IdeTransferClass *transfer_class = IDE_TRANSFER_CLASS (klass); + + transfer_class->execute_async = rust_analyzer_transfer_execute_async; + transfer_class->execute_finish = rust_analyzer_transfer_execute_finish; +} + +static void +rust_analyzer_transfer_init (RustAnalyzerTransfer *self) +{ + ide_transfer_set_title (IDE_TRANSFER (self), "Installing Rust Analyzer..."); +} diff --git a/src/plugins/rust-analyzer/rust-analyzer-transfer.h b/src/plugins/rust-analyzer/rust-analyzer-transfer.h new file mode 100644 index 0000000000000000000000000000000000000000..7d45fccc2e8354d0279390bc951cefbc800693f8 --- /dev/null +++ b/src/plugins/rust-analyzer/rust-analyzer-transfer.h @@ -0,0 +1,33 @@ +/* rust-analyzer-transfer.h + * + * Copyright 2020 Günther Wagner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define RUST_TYPE_ANALYZER_TRANSFER (rust_analyzer_transfer_get_type()) + +G_DECLARE_FINAL_TYPE (RustAnalyzerTransfer, rust_analyzer_transfer, RUST, ANALYZER_TRANSFER, IdeTransfer) + +RustAnalyzerTransfer *rust_analyzer_transfer_new (void); + +G_END_DECLS diff --git a/src/plugins/rust-analyzer/rust-analyzer-workbench-addin.c b/src/plugins/rust-analyzer/rust-analyzer-workbench-addin.c new file mode 100644 index 0000000000000000000000000000000000000000..375f38aadfd515f2df4a7eec0e855cd6c15e3c2c --- /dev/null +++ b/src/plugins/rust-analyzer/rust-analyzer-workbench-addin.c @@ -0,0 +1,139 @@ +/* rust-analyzer-workbench-addin.c + * + * Copyright 2020 Günther Wagner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "rust-analyzer-workbench-addin.h" +#include +#include +#include "rust-analyzer-transfer.h" +#include "rust-analyzer-service.h" + +struct _RustAnalyzerWorkbenchAddin +{ + IdeObject parent_instance; +}; + +static void provider_iface_init (IdeWorkbenchAddinInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (RustAnalyzerWorkbenchAddin, rust_analyzer_workbench_addin, IDE_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (IDE_TYPE_WORKBENCH_ADDIN, provider_iface_init)) + +static void +rust_analyzer_workbench_addin_class_init (RustAnalyzerWorkbenchAddinClass *klass) +{ +} + +static void +rust_analyzer_workbench_addin_init (RustAnalyzerWorkbenchAddin *self) +{ +} + +static void +rust_analyzer_workbench_addin_load (IdeWorkbenchAddin *addin, + IdeWorkbench *workbench) +{ +} + +static void +rust_analyzer_workbench_addin_unload (IdeWorkbenchAddin *addin, + IdeWorkbench *workbench) +{ +} + +static void +rust_analyzer_service_downloaded_lsp (GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + RustAnalyzerService *service = NULL; + IdeContext *context = IDE_CONTEXT (user_data); + + g_return_if_fail (IDE_IS_CONTEXT (context)); + + service = ide_object_ensure_child_typed (IDE_OBJECT (context), RUST_TYPE_ANALYZER_SERVICE); + rust_analyzer_service_set_state (service, RUST_ANALYZER_SERVICE_READY); + rust_analyzer_service_ensure_started (service); +} + +static void +rust_analyzer_workbench_addin_remove_lsp (IdeTransfer *transfer, + gpointer user_data) +{ + g_autofree gchar *rust_analyzer_path = NULL; + g_autoptr(GFile) rust_analyzer_bin = NULL; + + rust_analyzer_path = g_build_filename (g_get_home_dir (), ".cargo", "bin", "rust-analyzer", NULL); + rust_analyzer_bin = g_file_new_for_path (rust_analyzer_path); + g_file_trash (rust_analyzer_bin, NULL, NULL); +} + +static void +rust_analyzer_workbench_addin_install_rust_analyzer (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + g_autoptr(RustAnalyzerTransfer) transfer = NULL; + IdeNotifications *notifications = NULL; + IdeNotification *notification = NULL; + IdeTransferManager *transfer_manager = NULL; + + IdeContext *context = IDE_CONTEXT (user_data); + + notifications = ide_object_get_child_typed (IDE_OBJECT (context), IDE_TYPE_NOTIFICATIONS); + notification = ide_notifications_find_by_id (notifications, "org.gnome-builder.rust-analyzer"); + + if (notification != NULL) + ide_notification_withdraw (notification); + + transfer_manager = ide_transfer_manager_get_default (); + transfer = rust_analyzer_transfer_new (); + g_signal_connect (transfer, "cancelled", G_CALLBACK (rust_analyzer_workbench_addin_remove_lsp), NULL); + + notification = ide_transfer_create_notification (IDE_TRANSFER (transfer)); + ide_notification_attach (notification, IDE_OBJECT (context)); + + ide_transfer_manager_execute_async (transfer_manager, + IDE_TRANSFER (transfer), + g_cancellable_new (), + rust_analyzer_service_downloaded_lsp, + context); +} + +static void +rust_analyzer_workbench_addin_workspace_added (IdeWorkbenchAddin *addin, + IdeWorkspace *workspace) +{ + GSimpleAction *install_rust_analyzer = NULL; + + install_rust_analyzer = g_simple_action_new ("install-rust-analyzer", NULL); + g_simple_action_set_enabled (install_rust_analyzer, TRUE); + g_signal_connect (install_rust_analyzer, + "activate", + G_CALLBACK (rust_analyzer_workbench_addin_install_rust_analyzer), + ide_workspace_get_context (workspace)); + g_action_map_add_action (G_ACTION_MAP (workspace), G_ACTION (install_rust_analyzer)); +} + +static void +provider_iface_init (IdeWorkbenchAddinInterface *iface) +{ + iface->load = rust_analyzer_workbench_addin_load; + iface->unload = rust_analyzer_workbench_addin_unload; + iface->workspace_added = rust_analyzer_workbench_addin_workspace_added; +} diff --git a/src/plugins/rust-analyzer/rust-analyzer-workbench-addin.h b/src/plugins/rust-analyzer/rust-analyzer-workbench-addin.h new file mode 100644 index 0000000000000000000000000000000000000000..f5eb0befd3a0875ae767e72b2597cea1f4fb25d0 --- /dev/null +++ b/src/plugins/rust-analyzer/rust-analyzer-workbench-addin.h @@ -0,0 +1,31 @@ +/* rust-analyzer-workbench-addin.h + * + * Copyright 2020 Günther Wagner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define RUST_TYPE_ANALYZER_WORKBENCH_ADDIN (rust_analyzer_workbench_addin_get_type()) + +G_DECLARE_FINAL_TYPE (RustAnalyzerWorkbenchAddin, rust_analyzer_workbench_addin, RUST, ANALYZER_WORKBENCH_ADDIN, IdeObject) + +G_END_DECLS diff --git a/src/plugins/rust-analyzer/rust-analyzer.c b/src/plugins/rust-analyzer/rust-analyzer.c new file mode 100644 index 0000000000000000000000000000000000000000..c9b38d35f9f847358f53510ee3f0fc76a325500a --- /dev/null +++ b/src/plugins/rust-analyzer/rust-analyzer.c @@ -0,0 +1,44 @@ +/* rust-analyzer.c + * + * Copyright 2020 Günther Wagner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include +#include "rust-analyzer-completion-provider.h" +#include "rust-analyzer-symbol-resolver.h" +#include "rust-analyzer-diagnostic-provider.h" +#include "rust-analyzer-workbench-addin.h" + +_IDE_EXTERN void +_rust_analyzer_register_types (PeasObjectModule *module) +{ + peas_object_module_register_extension_type (module, + IDE_TYPE_WORKBENCH_ADDIN, + RUST_TYPE_ANALYZER_WORKBENCH_ADDIN); + peas_object_module_register_extension_type (module, + IDE_TYPE_COMPLETION_PROVIDER, + RUST_TYPE_ANALYZER_COMPLETION_PROVIDER); + peas_object_module_register_extension_type (module, + IDE_TYPE_SYMBOL_RESOLVER, + RUST_TYPE_ANALYZER_SYMBOL_RESOLVER); + peas_object_module_register_extension_type (module, + IDE_TYPE_DIAGNOSTIC_PROVIDER, + RUST_TYPE_ANALYZER_DIAGNOSTIC_PROVIDER); +} diff --git a/src/plugins/rust-analyzer/rust-analyzer.gresource.xml b/src/plugins/rust-analyzer/rust-analyzer.gresource.xml new file mode 100644 index 0000000000000000000000000000000000000000..79bbf331a3ed545f18791629f3beb496ac3f87fb --- /dev/null +++ b/src/plugins/rust-analyzer/rust-analyzer.gresource.xml @@ -0,0 +1,6 @@ + + + + rust-analyzer.plugin + + diff --git a/src/plugins/rust-analyzer/rust-analyzer.plugin b/src/plugins/rust-analyzer/rust-analyzer.plugin index 231d28819a19daac2808bda1fda11adfcc9f6fdd..2d56ac03dfa0ef6a146288bbef68918740fb6ad5 100644 --- a/src/plugins/rust-analyzer/rust-analyzer.plugin +++ b/src/plugins/rust-analyzer/rust-analyzer.plugin @@ -3,14 +3,13 @@ Authors=Günther Wagner Builtin=true Copyright=Copyright © 2020 Günther Wagner Description=Provides auto-completion, diagnostics, and other IDE features -Loader=python3 -Module=rust_analyzer_plugin -Name=Rust Analyzer Language Server Integration +Module=rust-analyzer +Embedded=_rust_analyzer_register_types +Name=Rust Analyzer Language Server Integration2 X-Completion-Provider-Languages=rust X-Diagnostic-Provider-Languages=rust -X-Formatter-Languages=rust -X-Highlighter-Languages=rust -X-Hover-Provider-Languages=rust -X-Rename-Provider-Languages=rust X-Symbol-Resolver-Languages=rust -X-Builder-ABI=@PACKAGE_ABI@ +#X-Formatter-Languages=rust +#X-Highlighter-Languages=rust +#X-Hover-Provider-Languages=rust +#X-Rename-Provider-Languages=rust diff --git a/src/plugins/rust-analyzer/rust_analyzer_plugin.py b/src/plugins/rust-analyzer/rust_analyzer_plugin.py deleted file mode 100644 index eeec191b1ad659ac614332381a2dcafc7c923929..0000000000000000000000000000000000000000 --- a/src/plugins/rust-analyzer/rust_analyzer_plugin.py +++ /dev/null @@ -1,254 +0,0 @@ -#!/usr/bin/env python - -# rust_langserv_plugin.py -# -# Copyright 2016 Christian Hergert -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -""" -This plugin provides integration with the Rust Language Server. -It builds off the generic language service components in libide -by bridging them to our supervised Rust Language Server. -""" - -import gi -import os - -from gi.repository import GLib -from gi.repository import Gio -from gi.repository import GObject -from gi.repository import Ide - -DEV_MODE = False - -class RustAnalyzerService(Ide.Object): - _client = None - _has_started = False - _supervisor = None - _monitor = None - - @classmethod - def from_context(klass, context): - return context.ensure_child_typed(RustAnalyzerService) - - @GObject.Property(type=Ide.LspClient) - def client(self): - return self._client - - @client.setter - def client(self, value): - self._client = value - self.notify('client') - - def do_parent_set(self, parent): - """ - After the context has been loaded, we want to watch the project - Cargo.toml for changes if we find one. That will allow us to - restart the process as necessary to pick up changes. - """ - if parent is None: - return - - context = self.get_context() - workdir = context.ref_workdir() - cargo_toml = workdir.get_child('Cargo.toml') - - if cargo_toml.query_exists(): - try: - self._monitor = cargo_toml.monitor(0, None) - self._monitor.set_rate_limit(5 * 1000) # 5 Seconds - self._monitor.connect('changed', self._monitor_changed_cb) - except Exception as ex: - Ide.debug('Failed to monitor Cargo.toml for changes:', repr(ex)) - - def _monitor_changed_cb(self, monitor, file, other_file, event_type): - """ - This method is called when Cargo.toml has changed. We need to - cancel any supervised process and force the language server to - restart. Otherwise, we risk it not picking up necessary changes. - """ - if self._supervisor is not None: - subprocess = self._supervisor.get_subprocess() - if subprocess is not None: - subprocess.force_exit() - - def do_stop(self): - """ - Stops the Rust Analyzer Language Server upon request to shutdown the - RustAnalyzerService. - """ - if self._monitor is not None: - monitor, self._monitor = self._monitor, None - if monitor is not None: - monitor.cancel() - - if self._supervisor is not None: - supervisor, self._supervisor = self._supervisor, None - supervisor.stop() - - def _ensure_started(self): - """ - Start the rust service which provides communication with the - Rust Analyzer Language Server. We supervise our own instance of the - language server and restart it as necessary using the - Ide.SubprocessSupervisor. - - Various extension points (diagnostics, symbol providers, etc) use - the RustAnalyzerService to access the rust components they need. - """ - # To avoid starting the `rust-analyzer` process unconditionally at startup, - # we lazily start it when the first provider tries to bind a client - # to its :client property. - if not self._has_started: - self._has_started = True - - # Setup a launcher to spawn the rust language server - launcher = self._create_launcher() - launcher.set_clear_env(False) - sysroot = self._discover_sysroot() - if sysroot: - launcher.setenv("SYS_ROOT", sysroot, True) - launcher.setenv("LD_LIBRARY_PATH", os.path.join(sysroot, "lib"), True) - if DEV_MODE: - launcher.setenv('RUST_LOG', 'debug', True) - - # Locate the directory of the project and run rls from there. - workdir = self.get_context().ref_workdir() - launcher.set_cwd(workdir.get_path()) - - # If rls was installed with Cargo, try to discover that - # to save the user having to update PATH. - path_to_rust_analyzer_bin = os.path.expanduser("~/.cargo/bin/rust-analyzer") - if os.path.exists(path_to_rust_analyzer_bin): - old_path = os.getenv('PATH') - new_path = os.path.expanduser('~/.cargo/bin') - if old_path is not None: - new_path += os.path.pathsep + old_path - launcher.setenv('PATH', new_path, True) - else: - path_to_rust_analyzer_bin = "rust-analyzer" - - # Setup our Argv. We want to communicate over STDIN/STDOUT, - # so it does not require any command line options. - launcher.push_argv(path_to_rust_analyzer_bin) - - # Spawn our peer process and monitor it for - # crashes. We may need to restart it occasionally. - self._supervisor = Ide.SubprocessSupervisor() - self._supervisor.connect('spawned', self._rls_spawned) - self._supervisor.set_launcher(launcher) - self._supervisor.start() - - def _rls_spawned(self, supervisor, subprocess): - """ - This callback is executed when the `rls` process is spawned. - We can use the stdin/stdout to create a channel for our - LspClient. - """ - stdin = subprocess.get_stdin_pipe() - stdout = subprocess.get_stdout_pipe() - io_stream = Gio.SimpleIOStream.new(stdout, stdin) - - if self._client: - self._client.stop() - self._client.destroy() - - self._client = Ide.LspClient.new(io_stream) - self.append(self._client) - self._client.add_language('rust') - self._client.start() - self.notify('client') - - def _create_launcher(self): - """ - Creates a launcher to be used by the rust service. This needs - to be run on the host because we do not currently bundle rust - inside our flatpak. - - In the future, we might be able to rely on the runtime for - the tooling. Maybe even the program if flatpak-builder has - prebuilt our dependencies. - """ - flags = Gio.SubprocessFlags.STDIN_PIPE | Gio.SubprocessFlags.STDOUT_PIPE - if not DEV_MODE: - flags |= Gio.SubprocessFlags.STDERR_SILENCE - launcher = Ide.SubprocessLauncher() - launcher.set_flags(flags) - launcher.set_cwd(GLib.get_home_dir()) - launcher.set_run_on_host(True) - return launcher - - def _discover_sysroot(self): - """ - The Rust Language Server needs to know where the sysroot is of - the Rust installation we are using. This is simple enough to - get, by using `rust --print sysroot` as the rust-language-server - documentation suggests. - """ - for rustc in ['rustc', os.path.expanduser('~/.cargo/bin/rustc')]: - try: - launcher = self._create_launcher() - launcher.push_args([rustc, '--print', 'sysroot']) - subprocess = launcher.spawn() - _, stdout, _ = subprocess.communicate_utf8() - return stdout.strip() - except: - pass - - @classmethod - def bind_client(klass, provider): - """ - This helper tracks changes to our client as it might happen when - our `rls` process has crashed. - """ - context = provider.get_context() - self = RustAnalyzerService.from_context(context) - self._ensure_started() - self.bind_property('client', provider, 'client', GObject.BindingFlags.SYNC_CREATE) - -class RustAnalyzerDiagnosticProvider(Ide.LspDiagnosticProvider, Ide.DiagnosticProvider): - def do_load(self): - RustAnalyzerService.bind_client(self) - -class RustAnalyzerCompletionProvider(Ide.LspCompletionProvider, Ide.CompletionProvider): - def do_load(self, context): - RustAnalyzerService.bind_client(self) - - def do_get_priority(self, context): - # This provider only activates when it is very likely that we - # want the results. So use high priority (negative is better). - return -1000 - -class RustAnalyzerRenameProvider(Ide.LspRenameProvider, Ide.RenameProvider): - def do_load(self): - RustAnalyzerService.bind_client(self) - -class RustAnalyzerSymbolResolver(Ide.LspSymbolResolver, Ide.SymbolResolver): - def do_load(self): - RustAnalyzerService.bind_client(self) - -class RustAnalyzerHighlighter(Ide.LspHighlighter, Ide.Highlighter): - def do_load(self): - RustAnalyzerService.bind_client(self) - -class RustAnalyzerFormatter(Ide.LspFormatter, Ide.Formatter): - def do_load(self): - RustAnalyzerService.bind_client(self) - -class RustAnalyzerHoverProvider(Ide.LspHoverProvider, Ide.HoverProvider): - def do_prepare(self): - self.props.category = 'Rust' - self.props.priority = 200 - RustAnalyzerService.bind_client(self)