1 /* ide-diagnostic.c
2 *
3 * Copyright 2018-2019 Christian Hergert <chergert@redhat.com>
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-diagnostic"
22
23 #include "config.h"
24
25 #include "ide-code-enums.h"
26 #include "ide-diagnostic.h"
27 #include "ide-location.h"
28 #include "ide-range.h"
29 #include "ide-text-edit.h"
30
31 typedef struct
32 {
33 IdeDiagnosticSeverity severity;
34 guint hash;
35 gchar *text;
36 IdeLocation *location;
37 GPtrArray *ranges;
38 GPtrArray *fixits;
39 } IdeDiagnosticPrivate;
40
41 enum {
42 PROP_0,
43 PROP_LOCATION,
44 PROP_SEVERITY,
45 PROP_TEXT,
46 PROP_DISPLAY_TEXT,
47 N_PROPS
48 };
49
G_DEFINE_TYPE_WITH_PRIVATE(IdeDiagnostic,ide_diagnostic,IDE_TYPE_OBJECT)50 G_DEFINE_TYPE_WITH_PRIVATE (IdeDiagnostic, ide_diagnostic, IDE_TYPE_OBJECT)
51
52 static GParamSpec *properties [N_PROPS];
53
54 static void
55 ide_diagnostic_set_location (IdeDiagnostic *self,
56 IdeLocation *location)
57 {
58 IdeDiagnosticPrivate *priv = ide_diagnostic_get_instance_private (self);
59
60 g_return_if_fail (IDE_IS_DIAGNOSTIC (self));
61
62 g_set_object (&priv->location, location);
63 }
64
65 static void
ide_diagnostic_set_text(IdeDiagnostic * self,const gchar * text)66 ide_diagnostic_set_text (IdeDiagnostic *self,
67 const gchar *text)
68 {
69 IdeDiagnosticPrivate *priv = ide_diagnostic_get_instance_private (self);
70
71 g_return_if_fail (IDE_IS_DIAGNOSTIC (self));
72
73 priv->text = g_strdup (text);
74 }
75
76 static void
ide_diagnostic_set_severity(IdeDiagnostic * self,IdeDiagnosticSeverity severity)77 ide_diagnostic_set_severity (IdeDiagnostic *self,
78 IdeDiagnosticSeverity severity)
79 {
80 IdeDiagnosticPrivate *priv = ide_diagnostic_get_instance_private (self);
81
82 g_return_if_fail (IDE_IS_DIAGNOSTIC (self));
83
84 priv->severity = severity;
85 }
86
87 static void
ide_diagnostic_finalize(GObject * object)88 ide_diagnostic_finalize (GObject *object)
89 {
90 IdeDiagnostic *self = (IdeDiagnostic *)object;
91 IdeDiagnosticPrivate *priv = ide_diagnostic_get_instance_private (self);
92
93 g_clear_pointer (&priv->text, g_free);
94 g_clear_pointer (&priv->ranges, g_ptr_array_unref);
95 g_clear_pointer (&priv->fixits, g_ptr_array_unref);
96 g_clear_object (&priv->location);
97
98 G_OBJECT_CLASS (ide_diagnostic_parent_class)->finalize (object);
99 }
100
101 static void
ide_diagnostic_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)102 ide_diagnostic_get_property (GObject *object,
103 guint prop_id,
104 GValue *value,
105 GParamSpec *pspec)
106 {
107 IdeDiagnostic *self = IDE_DIAGNOSTIC (object);
108
109 switch (prop_id)
110 {
111 case PROP_LOCATION:
112 g_value_set_object (value, ide_diagnostic_get_location (self));
113 break;
114
115 case PROP_SEVERITY:
116 g_value_set_enum (value, ide_diagnostic_get_severity (self));
117 break;
118
119 case PROP_DISPLAY_TEXT:
120 g_value_take_string (value, ide_diagnostic_get_text_for_display (self));
121 break;
122
123 case PROP_TEXT:
124 g_value_set_string (value, ide_diagnostic_get_text (self));
125 break;
126
127 default:
128 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
129 }
130 }
131
132 static void
ide_diagnostic_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)133 ide_diagnostic_set_property (GObject *object,
134 guint prop_id,
135 const GValue *value,
136 GParamSpec *pspec)
137 {
138 IdeDiagnostic *self = IDE_DIAGNOSTIC (object);
139
140 switch (prop_id)
141 {
142 case PROP_LOCATION:
143 ide_diagnostic_set_location (self, g_value_get_object (value));
144 break;
145
146 case PROP_SEVERITY:
147 ide_diagnostic_set_severity (self, g_value_get_enum (value));
148 break;
149
150 case PROP_TEXT:
151 ide_diagnostic_set_text (self, g_value_get_string (value));
152 break;
153
154 default:
155 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
156 }
157 }
158
159 static void
ide_diagnostic_class_init(IdeDiagnosticClass * klass)160 ide_diagnostic_class_init (IdeDiagnosticClass *klass)
161 {
162 GObjectClass *object_class = G_OBJECT_CLASS (klass);
163
164 object_class->finalize = ide_diagnostic_finalize;
165 object_class->get_property = ide_diagnostic_get_property;
166 object_class->set_property = ide_diagnostic_set_property;
167
168 properties [PROP_LOCATION] =
169 g_param_spec_object ("location",
170 "Location",
171 "The location of the diagnostic",
172 IDE_TYPE_LOCATION,
173 (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
174
175 properties [PROP_SEVERITY] =
176 g_param_spec_enum ("severity",
177 "Severity",
178 "The severity of the diagnostic",
179 IDE_TYPE_DIAGNOSTIC_SEVERITY,
180 IDE_DIAGNOSTIC_IGNORED,
181 (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
182
183 properties [PROP_TEXT] =
184 g_param_spec_string ("text",
185 "Text",
186 "The text of the diagnostic",
187 NULL,
188 (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
189
190 properties [PROP_DISPLAY_TEXT] =
191 g_param_spec_string ("display-text",
192 "Display Text",
193 "The text formatted for display",
194 NULL,
195 (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
196
197 g_object_class_install_properties (object_class, N_PROPS, properties);
198 }
199
200 static void
ide_diagnostic_init(IdeDiagnostic * self)201 ide_diagnostic_init (IdeDiagnostic *self)
202 {
203 }
204
205 /**
206 * ide_diagnostic_get_location:
207 * @self: a #IdeDiagnostic
208 *
209 * Gets the location of the diagnostic.
210 *
211 * See also: ide_diagnostic_get_range().
212 *
213 * Returns: (transfer none) (nullable): an #IdeLocation or %NULL
214 *
215 * Since: 3.32
216 */
217 IdeLocation *
ide_diagnostic_get_location(IdeDiagnostic * self)218 ide_diagnostic_get_location (IdeDiagnostic *self)
219 {
220 IdeDiagnosticPrivate *priv = ide_diagnostic_get_instance_private (self);
221
222 g_return_val_if_fail (IDE_IS_DIAGNOSTIC (self), NULL);
223
224 if (priv->location != NULL)
225 return priv->location;
226
227 if (priv->ranges != NULL && priv->ranges->len > 0)
228 {
229 IdeRange *range = g_ptr_array_index (priv->ranges, 0);
230 return ide_range_get_begin (range);
231 }
232
233 return NULL;
234 }
235
236 /**
237 * ide_diagnostic_get_file:
238 * @self: a #IdeDiagnostic
239 *
240 * Gets the file containing the diagnostic, if any.
241 *
242 * Returns: (transfer none) (nullable): an #IdeLocation or %NULL
243 *
244 * Since: 3.32
245 */
246 GFile *
ide_diagnostic_get_file(IdeDiagnostic * self)247 ide_diagnostic_get_file (IdeDiagnostic *self)
248 {
249 IdeLocation *location;
250
251 g_return_val_if_fail (IDE_IS_DIAGNOSTIC (self), NULL);
252
253 if ((location = ide_diagnostic_get_location (self)))
254 return ide_location_get_file (location);
255
256 return NULL;
257 }
258
259 /**
260 * ide_diagnostic_get_text_for_display:
261 * @self: an #IdeDiagnostic
262 *
263 * This creates a new string that is formatted using the diagnostics
264 * line number, column, severity, and message text in the format
265 * "line:column: severity: message".
266 *
267 * This can be convenient when wanting to quickly display a
268 * diagnostic such as in a tooltip.
269 *
270 * Returns: (transfer full): string containing the text formatted for
271 * display.
272 *
273 * Since: 3.32
274 */
275 gchar *
ide_diagnostic_get_text_for_display(IdeDiagnostic * self)276 ide_diagnostic_get_text_for_display (IdeDiagnostic *self)
277 {
278 IdeDiagnosticPrivate *priv = ide_diagnostic_get_instance_private (self);
279 IdeLocation *location;
280 const gchar *severity;
281 guint line = 0;
282 guint column = 0;
283
284 g_return_val_if_fail (IDE_IS_DIAGNOSTIC (self), NULL);
285
286 severity = ide_diagnostic_severity_to_string (priv->severity);
287 location = ide_diagnostic_get_location (self);
288
289 if (location != NULL)
290 {
291 line = ide_location_get_line (location) + 1;
292 column = ide_location_get_line_offset (location) + 1;
293 }
294
295 return g_strdup_printf ("%u:%u: %s: %s", line, column, severity, priv->text);
296 }
297
298 /**
299 * ide_diagnostic_severity_to_string:
300 * @severity: a #IdeDiagnosticSeverity
301 *
302 * Returns a string suitable to represent the diagnsotic severity.
303 *
304 * Returns: a string
305 *
306 * Since: 3.32
307 */
308 const gchar *
ide_diagnostic_severity_to_string(IdeDiagnosticSeverity severity)309 ide_diagnostic_severity_to_string (IdeDiagnosticSeverity severity)
310 {
311 switch (severity)
312 {
313 case IDE_DIAGNOSTIC_IGNORED:
314 return "ignored";
315
316 case IDE_DIAGNOSTIC_NOTE:
317 return "note";
318
319 case IDE_DIAGNOSTIC_UNUSED:
320 return "unused";
321
322 case IDE_DIAGNOSTIC_DEPRECATED:
323 return "deprecated";
324
325 case IDE_DIAGNOSTIC_WARNING:
326 return "warning";
327
328 case IDE_DIAGNOSTIC_ERROR:
329 return "error";
330
331 case IDE_DIAGNOSTIC_FATAL:
332 return "fatal";
333
334 default:
335 return "unknown";
336 }
337 }
338
339 guint
ide_diagnostic_get_n_ranges(IdeDiagnostic * self)340 ide_diagnostic_get_n_ranges (IdeDiagnostic *self)
341 {
342 IdeDiagnosticPrivate *priv = ide_diagnostic_get_instance_private (self);
343
344 g_return_val_if_fail (IDE_IS_DIAGNOSTIC (self), 0);
345
346 return priv->ranges ? priv->ranges->len : 0;
347 }
348
349 /**
350 * ide_diagnostic_get_range:
351 *
352 * Retrieves the range found at @index. It is a programming error to call this
353 * function with a value greater or equal to ide_diagnostic_get_n_ranges().
354 *
355 * Returns: (transfer none) (nullable): An #IdeRange
356 *
357 * Since: 3.32
358 */
359 IdeRange *
ide_diagnostic_get_range(IdeDiagnostic * self,guint index)360 ide_diagnostic_get_range (IdeDiagnostic *self,
361 guint index)
362 {
363 IdeDiagnosticPrivate *priv = ide_diagnostic_get_instance_private (self);
364
365 g_return_val_if_fail (IDE_IS_DIAGNOSTIC (self), NULL);
366
367 if (priv->ranges)
368 {
369 if (index < priv->ranges->len)
370 return g_ptr_array_index (priv->ranges, index);
371 }
372
373 return NULL;
374 }
375
376 guint
ide_diagnostic_get_n_fixits(IdeDiagnostic * self)377 ide_diagnostic_get_n_fixits (IdeDiagnostic *self)
378 {
379 IdeDiagnosticPrivate *priv = ide_diagnostic_get_instance_private (self);
380
381 g_return_val_if_fail (IDE_IS_DIAGNOSTIC (self), 0);
382
383 return (priv->fixits != NULL) ? priv->fixits->len : 0;
384 }
385
386 /**
387 * ide_diagnostic_get_fixit:
388 * @self: an #IdeDiagnostic.
389 * @index: The index of the fixit.
390 *
391 * Gets the fixit denoted by @index. This value should be less than the value
392 * returned from ide_diagnostic_get_n_fixits().
393 *
394 * Returns: (transfer none) (nullable): An #IdeTextEdit
395 *
396 * Since: 3.32
397 */
398 IdeTextEdit *
ide_diagnostic_get_fixit(IdeDiagnostic * self,guint index)399 ide_diagnostic_get_fixit (IdeDiagnostic *self,
400 guint index)
401 {
402 IdeDiagnosticPrivate *priv = ide_diagnostic_get_instance_private (self);
403
404 g_return_val_if_fail (self, NULL);
405 g_return_val_if_fail (IDE_IS_DIAGNOSTIC (self), NULL);
406 g_return_val_if_fail (priv->fixits, NULL);
407
408 if (priv->fixits != NULL)
409 {
410 if (index < priv->fixits->len)
411 return g_ptr_array_index (priv->fixits, index);
412 }
413
414 return NULL;
415 }
416
417 gint
ide_diagnostic_compare(IdeDiagnostic * a,IdeDiagnostic * b)418 ide_diagnostic_compare (IdeDiagnostic *a,
419 IdeDiagnostic *b)
420 {
421 IdeDiagnosticPrivate *priv_a = ide_diagnostic_get_instance_private (a);
422 IdeDiagnosticPrivate *priv_b = ide_diagnostic_get_instance_private (b);
423 gint ret;
424
425 g_assert (IDE_IS_DIAGNOSTIC (a));
426 g_assert (IDE_IS_DIAGNOSTIC (b));
427
428 /* Severity is 0..N where N is more important. So reverse comparator. */
429 if (0 != (ret = (gint)priv_b->severity - (gint)priv_a->severity))
430 return ret;
431
432 if (priv_a->location && priv_b->location)
433 {
434 if (0 != (ret = ide_location_compare (priv_a->location, priv_b->location)))
435 return ret;
436 }
437
438 return g_strcmp0 (priv_a->text, priv_b->text);
439 }
440
441 const gchar *
ide_diagnostic_get_text(IdeDiagnostic * self)442 ide_diagnostic_get_text (IdeDiagnostic *self)
443 {
444 IdeDiagnosticPrivate *priv = ide_diagnostic_get_instance_private (self);
445
446 g_return_val_if_fail (IDE_IS_DIAGNOSTIC (self), NULL);
447
448 return priv->text;
449 }
450
451 IdeDiagnosticSeverity
ide_diagnostic_get_severity(IdeDiagnostic * self)452 ide_diagnostic_get_severity (IdeDiagnostic *self)
453 {
454 IdeDiagnosticPrivate *priv = ide_diagnostic_get_instance_private (self);
455
456 g_return_val_if_fail (IDE_IS_DIAGNOSTIC (self), 0);
457
458 return priv->severity;
459 }
460
461 /**
462 * ide_diagnostic_add_range:
463 * @self: a #IdeDiagnostic
464 * @range: an #IdeRange
465 *
466 * Adds a source range to the diagnostic.
467 *
468 * Since: 3.32
469 */
470 void
ide_diagnostic_add_range(IdeDiagnostic * self,IdeRange * range)471 ide_diagnostic_add_range (IdeDiagnostic *self,
472 IdeRange *range)
473 {
474 IdeDiagnosticPrivate *priv = ide_diagnostic_get_instance_private (self);
475
476 g_return_if_fail (IDE_IS_DIAGNOSTIC (self));
477 g_return_if_fail (IDE_IS_RANGE (range));
478
479 if (priv->ranges == NULL)
480 priv->ranges = g_ptr_array_new_with_free_func (g_object_unref);
481 g_ptr_array_add (priv->ranges, g_object_ref (range));
482 }
483
484 /**
485 * ide_diagnostic_take_range:
486 * @self: a #IdeDiagnostic
487 * @range: (transfer full): an #IdeRange
488 *
489 * Adds a source range to the diagnostic, but does not increment the
490 * reference count of @range.
491 *
492 * Since: 3.32
493 */
494 void
ide_diagnostic_take_range(IdeDiagnostic * self,IdeRange * range)495 ide_diagnostic_take_range (IdeDiagnostic *self,
496 IdeRange *range)
497 {
498 IdeDiagnosticPrivate *priv = ide_diagnostic_get_instance_private (self);
499
500 g_return_if_fail (IDE_IS_DIAGNOSTIC (self));
501 g_return_if_fail (IDE_IS_RANGE (range));
502
503 if (priv->ranges == NULL)
504 priv->ranges = g_ptr_array_new_with_free_func (g_object_unref);
505 g_ptr_array_add (priv->ranges, g_steal_pointer (&range));
506 }
507
508 /**
509 * ide_diagnostic_add_fixit:
510 * @self: a #IdeDiagnostic
511 * @fixit: an #IdeTextEdit
512 *
513 * Adds a source fixit to the diagnostic.
514 *
515 * Since: 3.32
516 */
517 void
ide_diagnostic_add_fixit(IdeDiagnostic * self,IdeTextEdit * fixit)518 ide_diagnostic_add_fixit (IdeDiagnostic *self,
519 IdeTextEdit *fixit)
520 {
521 IdeDiagnosticPrivate *priv = ide_diagnostic_get_instance_private (self);
522
523 g_return_if_fail (IDE_IS_DIAGNOSTIC (self));
524 g_return_if_fail (IDE_IS_TEXT_EDIT (fixit));
525
526 if (priv->fixits == NULL)
527 priv->fixits = g_ptr_array_new_with_free_func (g_object_unref);
528 g_ptr_array_add (priv->fixits, g_object_ref (fixit));
529 }
530
531 /**
532 * ide_diagnostic_take_fixit:
533 * @self: a #IdeDiagnostic
534 * @fixit: (transfer full): an #IdeTextEdit
535 *
536 * Adds a source fixit to the diagnostic, but does not increment the
537 * reference count of @fixit.
538 *
539 * Since: 3.32
540 */
541 void
ide_diagnostic_take_fixit(IdeDiagnostic * self,IdeTextEdit * fixit)542 ide_diagnostic_take_fixit (IdeDiagnostic *self,
543 IdeTextEdit *fixit)
544 {
545 IdeDiagnosticPrivate *priv = ide_diagnostic_get_instance_private (self);
546
547 g_return_if_fail (IDE_IS_DIAGNOSTIC (self));
548 g_return_if_fail (IDE_IS_TEXT_EDIT (fixit));
549
550 if (priv->fixits == NULL)
551 priv->fixits = g_ptr_array_new_with_free_func (g_object_unref);
552 g_ptr_array_add (priv->fixits, g_steal_pointer (&fixit));
553 }
554
555 IdeDiagnostic *
ide_diagnostic_new(IdeDiagnosticSeverity severity,const gchar * message,IdeLocation * location)556 ide_diagnostic_new (IdeDiagnosticSeverity severity,
557 const gchar *message,
558 IdeLocation *location)
559 {
560 return g_object_new (IDE_TYPE_DIAGNOSTIC,
561 "severity", severity,
562 "location", location,
563 "text", message,
564 NULL);
565 }
566
567 guint
ide_diagnostic_hash(IdeDiagnostic * self)568 ide_diagnostic_hash (IdeDiagnostic *self)
569 {
570 IdeDiagnosticPrivate *priv = ide_diagnostic_get_instance_private (self);
571
572 g_return_val_if_fail (IDE_IS_DIAGNOSTIC (self), 0);
573
574 if (priv->hash == 0)
575 {
576 guint hash = g_str_hash (priv->text ?: "");
577 if (priv->location)
578 hash ^= ide_location_hash (priv->location);
579 if (priv->fixits)
580 hash ^= g_int_hash (&priv->fixits->len);
581 if (priv->ranges)
582 hash ^= g_int_hash (&priv->ranges->len);
583 priv->hash = hash;
584 }
585
586 return priv->hash;
587 }
588
589 /**
590 * ide_diagnostic_to_variant:
591 * @self: a #IdeDiagnostic
592 *
593 * Creates a #GVariant to represent the diagnostic. This can be useful when
594 * working in subprocesses to serialize the diagnostic.
595 *
596 * This function will never return a floating variant.
597 *
598 * Returns: (transfer full): a #GVariant
599 *
600 * Since: 3.32
601 */
602 GVariant *
ide_diagnostic_to_variant(IdeDiagnostic * self)603 ide_diagnostic_to_variant (IdeDiagnostic *self)
604 {
605 IdeDiagnosticPrivate *priv = ide_diagnostic_get_instance_private (self);
606 GVariantDict dict;
607
608 g_return_val_if_fail (self != NULL, NULL);
609 g_return_val_if_fail (IDE_IS_DIAGNOSTIC (self), NULL);
610
611 g_variant_dict_init (&dict, NULL);
612
613 g_variant_dict_insert (&dict, "text", "s", priv->text ?: "");
614 g_variant_dict_insert (&dict, "severity", "u", priv->severity);
615
616 if (priv->location != NULL)
617 {
618 g_autoptr(GVariant) vloc = ide_location_to_variant (priv->location);
619
620 if (vloc != NULL)
621 g_variant_dict_insert_value (&dict, "location", vloc);
622 }
623
624 if (priv->ranges != NULL && priv->ranges->len > 0)
625 {
626 GVariantBuilder builder;
627
628 g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
629
630 for (guint i = 0; i < priv->ranges->len; i++)
631 {
632 IdeRange *range = g_ptr_array_index (priv->ranges, i);
633 g_autoptr(GVariant) vrange = ide_range_to_variant (range);
634
635 g_variant_builder_add_value (&builder, vrange);
636 }
637
638 g_variant_dict_insert_value (&dict, "ranges", g_variant_builder_end (&builder));
639 }
640
641 if (priv->fixits != NULL && priv->fixits->len > 0)
642 {
643 GVariantBuilder builder;
644
645 g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
646
647 for (guint i = 0; i < priv->fixits->len; i++)
648 {
649 IdeTextEdit *fixit = g_ptr_array_index (priv->fixits, i);
650 g_autoptr(GVariant) vfixit = ide_text_edit_to_variant (fixit);
651
652 g_variant_builder_add_value (&builder, vfixit);
653 }
654
655 g_variant_dict_insert_value (&dict, "fixits", g_variant_builder_end (&builder));
656 }
657
658 return g_variant_take_ref (g_variant_dict_end (&dict));
659 }
660
661 /**
662 * ide_diagnostic_new_from_variant:
663 * @variant: (nullable): a #GVariant or %NULL
664 *
665 * Creates a new #GVariant using the data contained in @variant.
666 *
667 * If @variant is %NULL or Upon failure, %NULL is returned.
668 *
669 * Returns: (nullable) (transfer full): a #IdeDiagnostic or %NULL
670 *
671 * Since: 3.32
672 */
673 IdeDiagnostic *
ide_diagnostic_new_from_variant(GVariant * variant)674 ide_diagnostic_new_from_variant (GVariant *variant)
675 {
676 g_autoptr(IdeLocation) loc = NULL;
677 g_autoptr(GVariant) vloc = NULL;
678 g_autoptr(GVariant) unboxed = NULL;
679 g_autoptr(GVariant) ranges = NULL;
680 g_autoptr(GVariant) fixits = NULL;
681 IdeDiagnostic *self;
682 GVariantDict dict;
683 GVariantIter iter;
684 const gchar *text;
685 guint32 severity;
686
687 if (variant == NULL)
688 return NULL;
689
690 if (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARIANT))
691 variant = unboxed = g_variant_get_variant (variant);
692
693 if (!g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT))
694 return NULL;
695
696 g_variant_dict_init (&dict, variant);
697
698 if (!g_variant_dict_lookup (&dict, "text", "&s", &text))
699 text = NULL;
700
701 if (!g_variant_dict_lookup (&dict, "severity", "u", &severity))
702 severity = 0;
703
704 if ((vloc = g_variant_dict_lookup_value (&dict, "location", NULL)))
705 loc = ide_location_new_from_variant (vloc);
706
707 if (!(self = ide_diagnostic_new (severity, text, loc)))
708 goto failure;
709
710 /* Ranges */
711 if ((ranges = g_variant_dict_lookup_value (&dict, "ranges", NULL)))
712 {
713 GVariant *vrange;
714
715 g_variant_iter_init (&iter, ranges);
716
717 while ((vrange = g_variant_iter_next_value (&iter)))
718 {
719 IdeRange *range;
720
721 if ((range = ide_range_new_from_variant (vrange)))
722 ide_diagnostic_take_range (self, g_steal_pointer (&range));
723
724 g_variant_unref (vrange);
725 }
726 }
727
728 /* Fixits */
729 if ((fixits = g_variant_dict_lookup_value (&dict, "fixits", NULL)))
730 {
731 GVariant *vfixit;
732
733 g_variant_iter_init (&iter, fixits);
734
735 while ((vfixit = g_variant_iter_next_value (&iter)))
736 {
737 IdeTextEdit *fixit;
738
739 if ((fixit = ide_text_edit_new_from_variant (vfixit)))
740 ide_diagnostic_take_fixit (self, g_steal_pointer (&fixit));
741
742 g_variant_unref (vfixit);
743 }
744 }
745
746 failure:
747
748 g_variant_dict_clear (&dict);
749
750 return self;
751 }
752