1 /* ide-lsp-search-provider.c
2 *
3 * Copyright 2020 Günther Wagner <info@gunibert.de>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: GPL-3.0-or-later
19 */
20
21 #define G_LOG_DOMAIN "ide-lsp-search-provider"
22
23 #include "config.h"
24
25 #include <jsonrpc-glib.h>
26
27 #include <libide-search.h>
28
29 #include "ide-lsp-client.h"
30 #include "ide-lsp-util.h"
31 #include "ide-lsp-search-provider.h"
32 #include "ide-lsp-search-result.h"
33
34 typedef struct
35 {
36 IdeLspClient *client;
37 } IdeLspSearchProviderPrivate;
38
39 static void provider_iface_init (IdeSearchProviderInterface *iface);
40
41 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (IdeLspSearchProvider, ide_lsp_search_provider, IDE_TYPE_OBJECT,
42 G_ADD_PRIVATE (IdeLspSearchProvider)
43 G_IMPLEMENT_INTERFACE (IDE_TYPE_SEARCH_PROVIDER, provider_iface_init))
44
45 enum {
46 PROP_0,
47 PROP_CLIENT,
48 N_PROPS
49 };
50
51 static GParamSpec *properties [N_PROPS];
52
53 /**
54 * ide_lsp_search_provider_get_client:
55 * @self: An #IdeLspSearchProvider
56 *
57 * Gets the client for the search provider.
58 *
59 * Returns: (transfer none) (nullable): An #IdeLspClient or %NULL
60 */
61 IdeLspClient *
ide_lsp_search_provider_get_client(IdeLspSearchProvider * self)62 ide_lsp_search_provider_get_client (IdeLspSearchProvider *self)
63 {
64 IdeLspSearchProviderPrivate *priv = ide_lsp_search_provider_get_instance_private (self);
65
66 g_return_val_if_fail (IDE_IS_LSP_SEARCH_PROVIDER (self), NULL);
67
68 return priv->client;
69 }
70
71 void
ide_lsp_search_provider_set_client(IdeLspSearchProvider * self,IdeLspClient * client)72 ide_lsp_search_provider_set_client (IdeLspSearchProvider *self,
73 IdeLspClient *client)
74 {
75 IdeLspSearchProviderPrivate *priv = ide_lsp_search_provider_get_instance_private (self);
76
77 g_return_if_fail (IDE_IS_LSP_SEARCH_PROVIDER (self));
78 g_return_if_fail (!client || IDE_IS_LSP_CLIENT (client));
79
80 if (g_set_object (&priv->client, client))
81 g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CLIENT]);
82 }
83
84 static void
ide_lsp_search_provider_finalize(GObject * object)85 ide_lsp_search_provider_finalize (GObject *object)
86 {
87 IdeLspSearchProvider *self = (IdeLspSearchProvider *)object;
88 IdeLspSearchProviderPrivate *priv = ide_lsp_search_provider_get_instance_private (self);
89
90 g_clear_object (&priv->client);
91
92 G_OBJECT_CLASS (ide_lsp_search_provider_parent_class)->finalize (object);
93 }
94
95 static void
ide_lsp_search_provider_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)96 ide_lsp_search_provider_get_property (GObject *object,
97 guint prop_id,
98 GValue *value,
99 GParamSpec *pspec)
100 {
101 IdeLspSearchProvider *self = IDE_LSP_SEARCH_PROVIDER (object);
102
103 switch (prop_id)
104 {
105 case PROP_CLIENT:
106 g_value_set_object (value, ide_lsp_search_provider_get_client (self));
107 break;
108
109 default:
110 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
111 }
112 }
113
114 static void
ide_lsp_search_provider_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)115 ide_lsp_search_provider_set_property (GObject *object,
116 guint prop_id,
117 const GValue *value,
118 GParamSpec *pspec)
119 {
120 IdeLspSearchProvider *self = IDE_LSP_SEARCH_PROVIDER (object);
121
122 switch (prop_id)
123 {
124 case PROP_CLIENT:
125 ide_lsp_search_provider_set_client (self, g_value_get_object (value));
126 break;
127
128 default:
129 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
130 }
131 }
132
133 static void
ide_lsp_search_provider_class_init(IdeLspSearchProviderClass * klass)134 ide_lsp_search_provider_class_init (IdeLspSearchProviderClass *klass)
135 {
136 GObjectClass *object_class = G_OBJECT_CLASS (klass);
137
138 object_class->finalize = ide_lsp_search_provider_finalize;
139 object_class->get_property = ide_lsp_search_provider_get_property;
140 object_class->set_property = ide_lsp_search_provider_set_property;
141
142 properties[PROP_CLIENT] =
143 g_param_spec_object ("client",
144 "client",
145 "The Language Server client",
146 IDE_TYPE_LSP_CLIENT,
147 (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
148
149 g_object_class_install_properties (object_class, N_PROPS, properties);
150 }
151
152 static void
ide_lsp_search_provider_init(IdeLspSearchProvider * self)153 ide_lsp_search_provider_init (IdeLspSearchProvider *self)
154 {
155 }
156
157 static void
ide_lsp_search_provider_search_cb(GObject * object,GAsyncResult * res,gpointer user_data)158 ide_lsp_search_provider_search_cb (GObject *object,
159 GAsyncResult *res,
160 gpointer user_data)
161 {
162 IdeLspClient *client = (IdeLspClient *)object;
163 g_autoptr(IdeTask) task = user_data;
164 g_autoptr(GVariant) result = NULL;
165 g_autoptr(GVariantIter) iter = NULL;
166 g_autoptr(GError) error = NULL;
167 GVariant *symbol_information;
168 GPtrArray *ar;
169
170 IDE_ENTRY;
171
172 ar = g_ptr_array_new_with_free_func (g_object_unref);
173
174 if (!ide_lsp_client_call_finish (client, res, &result, &error))
175 {
176 ide_task_return_error (task, g_steal_pointer (&error));
177 IDE_EXIT;
178 }
179
180 iter = g_variant_iter_new (result);
181
182 while (g_variant_iter_loop (iter, "v", &symbol_information))
183 {
184 g_autoptr(GFile) gfile = NULL;
185 g_autoptr(IdeLocation) location = NULL;
186 g_autofree gchar *base = NULL;
187 const gchar *title;
188 const gchar *uri;
189 gint64 kind;
190 gint64 line, character;
191 IdeSymbolKind symbol_kind;
192 const gchar *icon_name;
193
194 JSONRPC_MESSAGE_PARSE (symbol_information,
195 "name", JSONRPC_MESSAGE_GET_STRING (&title),
196 "kind", JSONRPC_MESSAGE_GET_INT64 (&kind),
197 "location", "{",
198 "uri", JSONRPC_MESSAGE_GET_STRING (&uri),
199 "range", "{",
200 "start", "{",
201 "line", JSONRPC_MESSAGE_GET_INT64 (&line),
202 "character", JSONRPC_MESSAGE_GET_INT64 (&character),
203 "}",
204 "}",
205 "}");
206
207 symbol_kind = ide_lsp_decode_symbol_kind (kind);
208 icon_name = ide_symbol_kind_get_icon_name (symbol_kind);
209
210 gfile = g_file_new_for_uri (uri);
211 location = ide_location_new (gfile, line, character);
212 base = g_file_get_basename (gfile);
213
214 g_ptr_array_add (ar, ide_lsp_search_result_new (title, base, location, icon_name));
215 }
216
217 ide_task_return_pointer (task,
218 g_steal_pointer (&ar),
219 g_ptr_array_unref);
220
221 IDE_EXIT;
222 }
223
224 static void
ide_lsp_search_provider_search_async(IdeSearchProvider * provider,const gchar * query,guint max_results,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)225 ide_lsp_search_provider_search_async (IdeSearchProvider *provider,
226 const gchar *query,
227 guint max_results,
228 GCancellable *cancellable,
229 GAsyncReadyCallback callback,
230 gpointer user_data)
231 {
232 IdeLspSearchProvider *self = (IdeLspSearchProvider *)provider;
233 IdeLspSearchProviderPrivate *priv = ide_lsp_search_provider_get_instance_private (self);
234 g_autoptr(IdeTask) task = NULL;
235 g_autoptr(GVariant) params = NULL;
236
237 IDE_ENTRY;
238
239 g_return_if_fail (IDE_IS_MAIN_THREAD ());
240 g_return_if_fail (IDE_IS_LSP_SEARCH_PROVIDER (self));
241 g_return_if_fail (query != NULL);
242 g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
243
244 task = ide_task_new (self, cancellable, callback, user_data);
245 ide_task_set_source_tag (task, ide_lsp_search_provider_search_async);
246
247 if (priv->client == NULL)
248 {
249 ide_task_return_new_error (task,
250 G_IO_ERROR,
251 G_IO_ERROR_NOT_SUPPORTED,
252 "Cannot query, client not available");
253 IDE_EXIT;
254 }
255
256 params = JSONRPC_MESSAGE_NEW ("query", JSONRPC_MESSAGE_PUT_STRING (query));
257
258 ide_lsp_client_call_async (priv->client,
259 "workspace/symbol",
260 params,
261 cancellable,
262 ide_lsp_search_provider_search_cb,
263 g_steal_pointer (&task));
264
265 IDE_EXIT;
266 }
267
268 static GPtrArray *
ide_lsp_search_provider_search_finish(IdeSearchProvider * provider,GAsyncResult * result,GError ** error)269 ide_lsp_search_provider_search_finish (IdeSearchProvider *provider,
270 GAsyncResult *result,
271 GError **error)
272 {
273 g_autoptr(GPtrArray) ret = NULL;
274
275 g_assert (IDE_IS_SEARCH_PROVIDER (provider));
276 g_assert (IDE_IS_TASK (result));
277
278 if ((ret = ide_task_propagate_pointer (IDE_TASK (result), error)))
279 return IDE_PTR_ARRAY_STEAL_FULL (&ret);
280
281 return NULL;
282 }
283
284 static void
provider_iface_init(IdeSearchProviderInterface * iface)285 provider_iface_init (IdeSearchProviderInterface *iface)
286 {
287 iface->search_async = ide_lsp_search_provider_search_async;
288 iface->search_finish = ide_lsp_search_provider_search_finish;
289 }
290