1
/* ide-gca-diagnostic-provider.c
3
* Copyright (C) 2015 Christian Hergert <christian@hergert.me>
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.
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.
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/>.
19
#define G_LOG_DOMAIN "ide-gca-diagnostic-provider"
21
#include <gca-diagnostics.h>
22
#include <glib/gi18n.h>
24
#include "ide-context.h"
25
#include "ide-diagnostics.h"
27
#include "ide-gca-diagnostic-provider.h"
28
#include "ide-gca-service.h"
29
#include "ide-internal.h"
30
#include "ide-source-location.h"
31
#include "ide-source-range.h"
32
#include "ide-unsaved-file.h"
33
#include "ide-unsaved-files.h"
35
#include "gca-structs.h"
37
struct _IdeGcaDiagnosticProvider
39
IdeObject parent_instance;
40
GHashTable *document_cache;
45
GTask *task; /* Integrity check backpointer */
46
IdeUnsavedFile *unsaved_file;
51
static void diagnostic_provider_iface_init (IdeDiagnosticProviderInterface *iface);
53
G_DEFINE_TYPE_EXTENDED (IdeGcaDiagnosticProvider, ide_gca_diagnostic_provider, IDE_TYPE_OBJECT, 0,
54
G_IMPLEMENT_INTERFACE (IDE_TYPE_DIAGNOSTIC_PROVIDER,
55
diagnostic_provider_iface_init))
58
diagnose_state_free (gpointer data)
60
DiagnoseState *state = data;
64
g_clear_object (&state->file);
65
g_free (state->language_id);
66
g_clear_pointer (&state->unsaved_file, ide_unsaved_file_unref);
67
g_slice_free (DiagnoseState, state);
71
static IdeDiagnosticSeverity
72
get_severity (guint val)
76
case GCA_SEVERITY_INFO:
77
return IDE_DIAGNOSTIC_NOTE;
79
case GCA_SEVERITY_WARNING:
80
return IDE_DIAGNOSTIC_WARNING;
82
case GCA_SEVERITY_DEPRECATED:
83
return IDE_DIAGNOSTIC_DEPRECATED;
85
case GCA_SEVERITY_ERROR:
86
return IDE_DIAGNOSTIC_ERROR;
88
case GCA_SEVERITY_FATAL:
89
return IDE_DIAGNOSTIC_FATAL;
91
case GCA_SEVERITY_NONE:
93
return IDE_DIAGNOSTIC_IGNORED;
98
static IdeDiagnostics *
99
variant_to_diagnostics (DiagnoseState *state,
112
ar = g_ptr_array_new ();
113
g_ptr_array_set_free_func (ar, (GDestroyNotify)ide_diagnostic_unref);
115
g_variant_iter_init (&iter, variant);
117
while (g_variant_iter_loop (&iter, "(ua((x(xx)(xx))s)a(x(xx)(xx))s)",
120
IdeDiagnosticSeverity severity;
122
gint64 x1, x2, x3, x4, x5;
125
severity = get_severity (a);
127
while (g_variant_iter_next (b, "((x(xx)(xx))s)",
128
&x1, &x2, &x3, &x4, &x5, &e))
131
* TODO: Add fixits back after we plumb them into IdeDiagnostic.
134
GcaFixit fixit = {{ 0 }};
136
fixit.range.file = x1;
137
fixit.range.begin.line = x2 - 1;
138
fixit.range.begin.column = x3 - 1;
139
fixit.range.end.line = x4 - 1;
140
fixit.range.end.column = x5 - 1;
141
fixit.value = g_strdup (e);
143
g_array_append_val (diag.fixits, fixit);
147
diag = ide_diagnostic_new (severity, d, NULL);
149
while (g_variant_iter_next (c, "(x(xx)(xx))", &x1, &x2, &x3, &x4, &x5))
151
IdeSourceRange *range;
152
IdeSourceLocation *begin;
153
IdeSourceLocation *end;
154
IdeFile *file = NULL;
159
* Not always true, but we can cheat for now and claim it is within
160
* the file we just parsed.
164
begin = ide_source_location_new (file, x2 - 1, x3 - 1, 0);
165
end = ide_source_location_new (file, x4 - 1, x5 - 1, 0);
167
range = ide_source_range_new (begin, end);
168
ide_diagnostic_take_range (diag, range);
170
ide_source_location_unref (begin);
171
ide_source_location_unref (end);
174
g_ptr_array_add (ar, diag);
177
return ide_diagnostics_new (ar);
181
diagnostics_cb (GObject *object,
182
GAsyncResult *result,
185
GcaDiagnostics *proxy = (GcaDiagnostics *)object;
186
g_autoptr(GTask) task = user_data;
187
g_autoptr(GVariant) var = NULL;
188
GError *error = NULL;
189
IdeDiagnostics *diagnostics;
190
DiagnoseState *state;
192
g_assert (G_IS_TASK (task));
193
g_assert (G_IS_ASYNC_RESULT (result));
195
if (!gca_diagnostics_call_diagnostics_finish (proxy, &var, result, &error))
197
g_task_return_error (task, error);
201
state = g_task_get_task_data (task);
202
g_assert (state->task == task);
204
diagnostics = variant_to_diagnostics (state, var);
206
g_task_return_pointer (task, diagnostics,
207
(GDestroyNotify)ide_diagnostics_unref);
211
get_diag_proxy_cb (GObject *object,
212
GAsyncResult *result,
215
g_autoptr(GTask) task = user_data;
216
IdeGcaDiagnosticProvider *self;
217
GcaDiagnostics *proxy;
218
GError *error = NULL;
221
g_assert (G_IS_TASK (task));
222
g_assert (G_IS_ASYNC_RESULT (result));
224
self = g_task_get_source_object (task);
226
proxy = gca_diagnostics_proxy_new_finish (result, &error);
230
g_task_return_error (task, error);
234
path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (proxy));
235
g_hash_table_replace (self->document_cache, g_strdup (path), proxy);
237
gca_diagnostics_call_diagnostics (proxy,
238
g_task_get_cancellable (task),
240
g_object_ref (task));
244
parse_cb (GObject *object,
245
GAsyncResult *result,
248
GcaService *proxy = (GcaService *)object;
249
IdeGcaDiagnosticProvider *self;
250
DiagnoseState *state;
251
g_autoptr(GTask) task = user_data;
252
GcaDiagnostics *doc_proxy;
254
GError *error = NULL;
255
g_autofree gchar *document_path = NULL;
257
g_assert (GCA_IS_SERVICE (proxy));
258
g_assert (G_IS_TASK (task));
260
self = g_task_get_source_object (task);
261
state = g_task_get_task_data (task);
263
ret = gca_service_call_parse_finish (proxy, &document_path, result, &error);
267
g_task_return_error (task, error);
271
doc_proxy = g_hash_table_lookup (self->document_cache, document_path);
275
g_autofree gchar *well_known_name = NULL;
276
GDBusConnection *conn;
278
well_known_name = g_strdup_printf ("org.gnome.CodeAssist.v1.%s",
280
conn = g_dbus_proxy_get_connection (G_DBUS_PROXY (proxy));
282
gca_diagnostics_proxy_new (conn,
283
G_DBUS_PROXY_FLAGS_NONE,
286
g_task_get_cancellable (task),
288
g_object_ref (task));
292
gca_diagnostics_call_diagnostics (doc_proxy,
293
g_task_get_cancellable (task),
295
g_object_ref (task));
299
get_proxy_cb (GObject *object,
300
GAsyncResult *result,
303
g_autoptr(GTask) task = user_data;
304
IdeGcaService *service = (IdeGcaService *)object;
305
DiagnoseState *state;
307
const gchar *temp_path;
308
GError *error = NULL;
310
g_autofree gchar *path = NULL;
311
GVariant *cursor = NULL;
312
GVariant *options = NULL;
314
g_assert (G_IS_TASK (task));
315
g_assert (IDE_IS_GCA_SERVICE (service));
317
state = g_task_get_task_data (task);
318
g_assert (state->task == task);
320
proxy = ide_gca_service_get_proxy_finish (service, result, &error);
324
g_task_return_error (task, error);
328
gfile = ide_file_get_file (state->file);
329
temp_path = path = g_file_get_path (gfile);
333
g_task_return_new_error (task,
335
G_IO_ERROR_NOT_SUPPORTED,
336
_("Code assistance requires a local file."));
340
if (state->unsaved_file)
342
if (!ide_unsaved_file_persist (state->unsaved_file,
343
g_task_get_cancellable (task),
346
g_task_return_error (task, error);
350
temp_path = ide_unsaved_file_get_temp_path (state->unsaved_file);
353
/* TODO: Plumb support for cursors down to this level? */
354
cursor = g_variant_new ("(xx)", (gint64)0, (gint64)0);
355
options = g_variant_new ("a{sv}", 0);
357
gca_service_call_parse (proxy,
362
g_task_get_cancellable (task),
364
g_object_ref (task));
367
g_clear_object (&proxy);
371
ide_gca_diagnostic_provider_diagnose_async (IdeDiagnosticProvider *provider,
373
GCancellable *cancellable,
374
GAsyncReadyCallback callback,
377
IdeGcaDiagnosticProvider *self = (IdeGcaDiagnosticProvider *)provider;
378
g_autoptr(GTask) task = NULL;
379
IdeGcaService *service;
380
DiagnoseState *state;
381
GtkSourceLanguage *language;
383
IdeUnsavedFiles *files;
384
const gchar *language_id;
387
g_return_if_fail (IDE_IS_GCA_DIAGNOSTIC_PROVIDER (self));
389
task = g_task_new (self, cancellable, callback, user_data);
391
language = ide_file_get_language (file);
392
language_id = gtk_source_language_get_id (language);
396
g_task_return_new_error (task,
398
G_IO_ERROR_NOT_SUPPORTED,
399
"No language specified, code assistance not supported.");
403
context = ide_object_get_context (IDE_OBJECT (provider));
404
service = ide_context_get_service_typed (context, IDE_TYPE_GCA_SERVICE);
405
files = ide_context_get_unsaved_files (context);
406
gfile = ide_file_get_file (file);
408
state = g_slice_new0 (DiagnoseState);
410
state->language_id = g_strdup (language_id);
411
state->file = g_object_ref (file);
412
state->unsaved_file = ide_unsaved_files_get_unsaved_file (files, gfile);
414
g_task_set_task_data (task, state, diagnose_state_free);
416
ide_gca_service_get_proxy_async (service, language_id, cancellable,
417
get_proxy_cb, g_object_ref (task));
420
static IdeDiagnostics *
421
ide_gca_diagnostic_provider_diagnose_finish (IdeDiagnosticProvider *self,
422
GAsyncResult *result,
425
GTask *task = (GTask *)result;
427
g_return_val_if_fail (IDE_IS_GCA_DIAGNOSTIC_PROVIDER (self), NULL);
428
g_return_val_if_fail (G_IS_TASK (task), NULL);
430
return g_task_propagate_pointer (task, error);
434
ide_gca_diagnostic_provider_finalize (GObject *object)
436
IdeGcaDiagnosticProvider *self = (IdeGcaDiagnosticProvider *)object;
438
g_clear_pointer (&self->document_cache, g_hash_table_unref);
440
G_OBJECT_CLASS (ide_gca_diagnostic_provider_parent_class)->finalize (object);
444
diagnostic_provider_iface_init (IdeDiagnosticProviderInterface *iface)
446
iface->diagnose_async = ide_gca_diagnostic_provider_diagnose_async;
447
iface->diagnose_finish = ide_gca_diagnostic_provider_diagnose_finish;
451
ide_gca_diagnostic_provider_class_init (IdeGcaDiagnosticProviderClass *klass)
453
GObjectClass *object_class = G_OBJECT_CLASS (klass);
455
object_class->finalize = ide_gca_diagnostic_provider_finalize;
459
ide_gca_diagnostic_provider_init (IdeGcaDiagnosticProvider *self)
461
self->document_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
462
g_free, g_object_unref);