Commit b727ef21 authored by Günther Wagner's avatar Günther Wagner

rust-analyzer: added search provider for symbols

parent 774b12b5
......@@ -11,6 +11,8 @@ plugins_sources += files([
'rust-analyzer-hover-provider.c',
'rust-analyzer-rename-provider.c',
'rust-analyzer-transfer.c',
'rust-analyzer-search-provider.c',
'rust-analyzer-search-result.c',
'rust-analyzer-workbench-addin.c',
])
......
/* rust-analyzer-search-provider.c
*
* Copyright 2020 Günther Wagner <info@gunibert.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "rust-analyzer-search-provider"
#include "rust-analyzer-search-provider.h"
#include <libide-search.h>
#include <jsonrpc-glib.h>
#include <libide-lsp.h>
#include "rust-analyzer-service.h"
#include "rust-analyzer-search-result.h"
struct _RustAnalyzerSearchProvider
{
IdeObject parent_instance;
IdeLspClient *client;
};
static void provider_iface_init (IdeSearchProviderInterface *iface);
G_DEFINE_TYPE_WITH_CODE (RustAnalyzerSearchProvider,
rust_analyzer_search_provider,
IDE_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (IDE_TYPE_SEARCH_PROVIDER, provider_iface_init))
enum {
PROP_0,
PROP_CLIENT,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
RustAnalyzerSearchProvider *
rust_analyzer_search_provider_new (void)
{
return g_object_new (RUST_TYPE_ANALYZER_SEARCH_PROVIDER, NULL);
}
static void
rust_analyzer_search_provider_finalize (GObject *object)
{
RustAnalyzerSearchProvider *self = (RustAnalyzerSearchProvider *)object;
g_clear_object (&self->client);
G_OBJECT_CLASS (rust_analyzer_search_provider_parent_class)->finalize (object);
}
static void
rust_analyzer_search_provider_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
RustAnalyzerSearchProvider *self = RUST_ANALYZER_SEARCH_PROVIDER (object);
switch (prop_id)
{
case PROP_CLIENT:
g_value_set_object (value, rust_analyzer_search_provider_get_client (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
rust_analyzer_search_provider_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
RustAnalyzerSearchProvider *self = RUST_ANALYZER_SEARCH_PROVIDER (object);
switch (prop_id)
{
case PROP_CLIENT:
rust_analyzer_search_provider_set_client (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
rust_analyzer_search_provider_class_init (RustAnalyzerSearchProviderClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = rust_analyzer_search_provider_finalize;
object_class->get_property = rust_analyzer_search_provider_get_property;
object_class->set_property = rust_analyzer_search_provider_set_property;
properties[PROP_CLIENT] =
g_param_spec_object ("client",
"client",
"The Language Server client",
IDE_TYPE_LSP_CLIENT,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
rust_analyzer_search_provider_init (RustAnalyzerSearchProvider *self)
{
}
static void
rust_analyzer_search_provider_search_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
IdeLspClient *client = (IdeLspClient *) source_object;
GPtrArray *ar;
g_autoptr(IdeTask) task = user_data;
g_autoptr(GVariant) result = NULL;
g_autoptr(GVariantIter) iter = NULL;
GVariant *symbol_information;
GError *error = NULL;
IDE_ENTRY;
ar = g_ptr_array_new ();
ide_lsp_client_call_finish (client, res, &result, &error);
if (error != NULL)
{
ide_task_return_error (task, error);
return;
}
iter = g_variant_iter_new (result);
while (g_variant_iter_loop (iter, "v", &symbol_information))
{
g_autoptr(GFile) gfile = NULL;
g_autoptr(IdeLocation) location = NULL;
const gchar *title;
const gchar *uri;
gint64 kind;
gint64 line, character;
IdeSymbolKind symbol_kind;
const gchar *icon_name;
JSONRPC_MESSAGE_PARSE (symbol_information,
"name", JSONRPC_MESSAGE_GET_STRING (&title),
"kind", JSONRPC_MESSAGE_GET_INT64 (&kind),
"location", "{",
"uri", JSONRPC_MESSAGE_GET_STRING (&uri),
"range", "{",
"start", "{",
"line", JSONRPC_MESSAGE_GET_INT64 (&line),
"character", JSONRPC_MESSAGE_GET_INT64 (&character),
"}",
"}",
"}");
symbol_kind = ide_lsp_decode_symbol_kind (kind);
icon_name = ide_symbol_kind_get_icon_name (symbol_kind);
gfile = g_file_new_for_uri (uri);
location = ide_location_new (gfile, line, character);
g_ptr_array_add (ar, rust_analyzer_search_result_new (title, g_file_get_basename (gfile), location, icon_name));
}
ide_task_return_pointer (task,
g_steal_pointer(&ar),
g_ptr_array_unref);
IDE_EXIT;
}
static void
rust_analyzer_search_provider_search_async (IdeSearchProvider *provider,
const gchar *query,
guint max_results,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
RustAnalyzerSearchProvider *self = (RustAnalyzerSearchProvider *)provider;
g_autoptr(IdeTask) task = NULL;
g_autoptr(GVariant) params = NULL;
IDE_ENTRY;
g_return_if_fail (IDE_IS_MAIN_THREAD ());
g_return_if_fail (RUST_IS_ANALYZER_SEARCH_PROVIDER (self));
g_return_if_fail (query != NULL);
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
task = ide_task_new (self, cancellable, callback, user_data);
ide_task_set_source_tag (task, rust_analyzer_search_provider_search_async);
params = JSONRPC_MESSAGE_NEW ("query", JSONRPC_MESSAGE_PUT_STRING (query));
ide_lsp_client_call_async (self->client,
"workspace/symbol",
params,
cancellable,
rust_analyzer_search_provider_search_cb,
g_steal_pointer (&task));
IDE_EXIT;
}
static GPtrArray *
rust_analyzer_search_provider_search_finish (IdeSearchProvider *provider,
GAsyncResult *result,
GError **error)
{
return ide_task_propagate_pointer (IDE_TASK (result), error);
}
static void
provider_iface_init (IdeSearchProviderInterface *iface)
{
iface->search_async = rust_analyzer_search_provider_search_async;
iface->search_finish = rust_analyzer_search_provider_search_finish;
}
IdeLspClient *
rust_analyzer_search_provider_get_client (RustAnalyzerSearchProvider *self)
{
g_return_val_if_fail (RUST_IS_ANALYZER_SEARCH_PROVIDER (self), NULL);
return self->client;
}
void
rust_analyzer_search_provider_set_client (RustAnalyzerSearchProvider *self,
IdeLspClient *client)
{
g_return_if_fail (RUST_IS_ANALYZER_SEARCH_PROVIDER (self));
g_return_if_fail (!client || IDE_IS_LSP_CLIENT (client));
if (self->client != NULL)
g_clear_pointer (&self->client, g_object_unref);
self->client = g_object_ref (client);
}
/* rust-analyzer-search-provider.h
*
* Copyright 2020 Günther Wagner <info@gunibert.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <libide-lsp.h>
G_BEGIN_DECLS
#define RUST_TYPE_ANALYZER_SEARCH_PROVIDER (rust_analyzer_search_provider_get_type())
G_DECLARE_FINAL_TYPE (RustAnalyzerSearchProvider, rust_analyzer_search_provider, RUST, ANALYZER_SEARCH_PROVIDER, IdeObject)
RustAnalyzerSearchProvider *rust_analyzer_search_provider_new (void);
IdeLspClient *rust_analyzer_search_provider_get_client (RustAnalyzerSearchProvider *self);
void rust_analyzer_search_provider_set_client (RustAnalyzerSearchProvider *self,
IdeLspClient *client);
G_END_DECLS
/* rust-analyzer-search-result.c
*
* Copyright 2020 Günther Wagner <info@gunibert.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "rust-analyzer-search-result.h"
#include <libide-editor.h>
struct _RustAnalyzerSearchResult
{
IdeSearchResult parent_instance;
IdeLocation *location;
};
G_DEFINE_TYPE (RustAnalyzerSearchResult, rust_analyzer_search_result, IDE_TYPE_SEARCH_RESULT)
enum {
PROP_0,
PROP_LOCATION,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
RustAnalyzerSearchResult *
rust_analyzer_search_result_new (const gchar *title,
const gchar *subtitle,
IdeLocation *location,
const gchar *icon_name)
{
return g_object_new (RUST_TYPE_ANALYZER_SEARCH_RESULT,
"title", title,
"subtitle", subtitle,
"location", location,
"icon-name", icon_name,
// place search results before the other search providers
"priority", -1,
NULL);
}
static void
rust_analyzer_search_result_finalize (GObject *object)
{
RustAnalyzerSearchResult *self = (RustAnalyzerSearchResult *)object;
g_clear_object (&self->location);
G_OBJECT_CLASS (rust_analyzer_search_result_parent_class)->finalize (object);
}
static void
rust_analyzer_search_result_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
RustAnalyzerSearchResult *self = RUST_ANALYZER_SEARCH_RESULT (object);
switch (prop_id)
{
case PROP_LOCATION:
g_value_set_object (value, self->location);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
rust_analyzer_search_result_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
RustAnalyzerSearchResult *self = RUST_ANALYZER_SEARCH_RESULT (object);
switch (prop_id)
{
case PROP_LOCATION:
self->location = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
rust_analyzer_search_result_activate (IdeSearchResult *result,
GtkWidget *last_focus)
{
RustAnalyzerSearchResult *self = (RustAnalyzerSearchResult *)result;
IdeWorkspace *workspace;
IdeSurface *editor;
g_assert (RUST_IS_ANALYZER_SEARCH_RESULT (self));
g_assert (GTK_IS_WIDGET (last_focus));
if (!last_focus)
return;
if ((workspace = ide_widget_get_workspace (last_focus)) &&
(editor = ide_workspace_get_surface_by_name (workspace, "editor")))
ide_editor_surface_focus_location (IDE_EDITOR_SURFACE (editor), self->location);
}
static void
rust_analyzer_search_result_class_init (RustAnalyzerSearchResultClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
IdeSearchResultClass *search_class = IDE_SEARCH_RESULT_CLASS (klass);
object_class->finalize = rust_analyzer_search_result_finalize;
object_class->get_property = rust_analyzer_search_result_get_property;
object_class->set_property = rust_analyzer_search_result_set_property;
search_class->activate = rust_analyzer_search_result_activate;
properties [PROP_LOCATION] =
g_param_spec_object ("location",
"location",
"Location of the symbol",
IDE_TYPE_LOCATION,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
rust_analyzer_search_result_init (RustAnalyzerSearchResult *self)
{
}
/* rust-analyzer-search-result.h
*
* Copyright 2020 Günther Wagner <info@gunibert.de>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <libide-code.h>
#include <libide-search.h>
G_BEGIN_DECLS
#define RUST_TYPE_ANALYZER_SEARCH_RESULT (rust_analyzer_search_result_get_type())
G_DECLARE_FINAL_TYPE (RustAnalyzerSearchResult, rust_analyzer_search_result, RUST, ANALYZER_SEARCH_RESULT, IdeSearchResult)
RustAnalyzerSearchResult *rust_analyzer_search_result_new (const gchar *title,
const gchar *subtitle,
IdeLocation *location,
const gchar *icon_name);
G_END_DECLS
......@@ -28,6 +28,8 @@
#include <libide-core.h>
#include <jsonrpc-glib.h>
#include <glib/gi18n.h>
#include <libide-search.h>
#include "rust-analyzer-search-provider.h"
struct _RustAnalyzerService
{
......@@ -35,6 +37,7 @@ struct _RustAnalyzerService
IdeLspClient *client;
IdeSubprocessSupervisor *supervisor;
GFileMonitor *cargo_monitor;
RustAnalyzerSearchProvider *search_provider;
ServiceState state;
};
......@@ -74,14 +77,15 @@ _cargo_toml_changed_cb (GFileMonitor *monitor,
}
}
static void
rust_analyzer_service_finalize (GObject *object)
static IdeSearchEngine *
_get_search_engine (RustAnalyzerService *self)
{
RustAnalyzerService *self = (RustAnalyzerService *)object;
IdeContext *context = NULL;
g_clear_object (&self->client);
g_assert (RUST_IS_ANALYZER_SERVICE (self));
G_OBJECT_CLASS (rust_analyzer_service_parent_class)->finalize (object);
context = ide_object_get_context (IDE_OBJECT (self));
return ide_object_get_child_typed (IDE_OBJECT (context), IDE_TYPE_SEARCH_ENGINE);
}
static void
......@@ -162,6 +166,7 @@ static void
rust_analyzer_service_destroy (IdeObject *object)
{
RustAnalyzerService *self = RUST_ANALYZER_SERVICE (object);
IdeSearchEngine *search_engine = NULL;
if (self->supervisor != NULL)
{
......@@ -170,6 +175,13 @@ rust_analyzer_service_destroy (IdeObject *object)
ide_subprocess_supervisor_stop (supervisor);
}
g_clear_object (&self->client);
search_engine = _get_search_engine (self);
if (search_engine != NULL)
ide_search_engine_remove_provider (search_engine, IDE_SEARCH_PROVIDER (self->search_provider));
g_clear_object (&self->search_provider);
IDE_OBJECT_CLASS (rust_analyzer_service_parent_class)->destroy (object);
}
......@@ -179,7 +191,6 @@ 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;
......@@ -254,6 +265,16 @@ rust_analyzer_service_lsp_started (IdeSubprocessSupervisor *supervisor,
ide_object_append (IDE_OBJECT (self), IDE_OBJECT (client));
ide_lsp_client_add_language (client, "rust");
ide_lsp_client_start (client);
// register SearchProvider
if (self->search_provider == NULL)
{
IdeSearchEngine *search_engine = _get_search_engine (self);
self->search_provider = rust_analyzer_search_provider_new ();
ide_search_engine_add_provider (search_engine, IDE_SEARCH_PROVIDER (self->search_provider));
}
rust_analyzer_search_provider_set_client (self->search_provider, client);
}
static gboolean
......
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