[gnome-builder] libide: run diagnostician when changes are made
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] libide: run diagnostician when changes are made
- Date: Mon, 23 Mar 2015 23:43:04 +0000 (UTC)
commit 77458219c36a392b5992ce37ebd720bf82a7cedd
Author: Christian Hergert <christian hergert me>
Date: Wed Feb 25 18:28:15 2015 -0800
libide: run diagnostician when changes are made
If show-diagnostics is set, then run the diagnostician when buffer changes
are made. Try to avoid parsing too frequently by using a delay timeout.
libide/ide-buffer.c | 357 ++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 356 insertions(+), 1 deletions(-)
---
diff --git a/libide/ide-buffer.c b/libide/ide-buffer.c
index 5d3090a..fd5cd02 100644
--- a/libide/ide-buffer.c
+++ b/libide/ide-buffer.c
@@ -22,8 +22,21 @@
#include "ide-buffer.h"
#include "ide-context.h"
+#include "ide-diagnostic.h"
+#include "ide-diagnostician.h"
+#include "ide-diagnostics.h"
#include "ide-file.h"
#include "ide-file-settings.h"
+#include "ide-language.h"
+#include "ide-source-location.h"
+#include "ide-source-range.h"
+#include "ide-unsaved-files.h"
+
+#define DEFAULT_DIAGNOSE_TIMEOUT_MSEC 333
+
+#define TAG_ERROR "diagnostician::error"
+#define TAG_WARNING "diagnostician::warning"
+#define TAG_NOTE "diagnostician::note"
typedef struct _IdeBufferClass
{
@@ -35,8 +48,13 @@ struct _IdeBuffer
GtkSourceBuffer parent_instance;
IdeContext *context;
+ IdeDiagnostics *diagnostics;
IdeFile *file;
+ guint diagnose_timeout;
+
+ guint diagnostics_dirty : 1;
+ guint in_diagnose : 1;
guint highlight_diagnostics : 1;
};
@@ -50,9 +68,35 @@ enum {
LAST_PROP
};
+static void ide_buffer_queue_diagnose (IdeBuffer *self);
+
static GParamSpec *gParamSpecs [LAST_PROP];
static void
+ide_buffer_get_iter_at_location (IdeBuffer *self,
+ GtkTextIter *iter,
+ IdeSourceLocation *location)
+{
+ guint line;
+ guint line_offset;
+
+ g_assert (IDE_IS_BUFFER (self));
+ g_assert (iter);
+ g_assert (location);
+
+ line = ide_source_location_get_line (location);
+ line_offset = ide_source_location_get_line_offset (location);
+
+ gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (self), iter, line);
+
+ while (line_offset && !gtk_text_iter_ends_line (iter))
+ {
+ gtk_text_iter_forward_char (iter);
+ line_offset--;
+ }
+}
+
+static void
ide_buffer_set_context (IdeBuffer *self,
IdeContext *context)
{
@@ -64,6 +108,184 @@ ide_buffer_set_context (IdeBuffer *self,
}
static void
+ide_buffer_sync_to_unsaved_files (IdeBuffer *self)
+{
+ IdeUnsavedFiles *unsaved_files;
+ GtkTextBuffer *buffer = (GtkTextBuffer *)self;
+ gchar *text;
+ GFile *gfile;
+ GBytes *content;
+ GtkTextIter begin;
+ GtkTextIter end;
+
+ g_assert (IDE_IS_BUFFER (self));
+
+ if (!self->context || !self->file)
+ return;
+
+ gfile = ide_file_get_file (self->file);
+ if (!gfile)
+ return;
+
+ gtk_text_buffer_get_bounds (buffer, &begin, &end);
+ text = gtk_text_buffer_get_text (buffer, &begin, &end, TRUE);
+ content = g_bytes_new_take (text, strlen (text));
+
+ unsaved_files = ide_context_get_unsaved_files (self->context);
+ ide_unsaved_files_update (unsaved_files, gfile, content);
+
+ g_bytes_unref (content);
+}
+
+static void
+ide_buffer_clear_diagnostics (IdeBuffer *self)
+{
+ GtkTextBuffer *buffer = (GtkTextBuffer *)self;
+ GtkTextIter begin;
+ GtkTextIter end;
+
+ g_assert (IDE_IS_BUFFER (self));
+
+ gtk_text_buffer_get_bounds (buffer, &begin, &end);
+
+ gtk_text_buffer_remove_tag_by_name (buffer, TAG_NOTE, &begin, &end);
+ gtk_text_buffer_remove_tag_by_name (buffer, TAG_WARNING, &begin, &end);
+ gtk_text_buffer_remove_tag_by_name (buffer, TAG_ERROR, &begin, &end);
+}
+
+static void
+ide_buffer_highlight_diagnostic (IdeBuffer *self,
+ IdeDiagnostic *diagnostic)
+{
+ IdeDiagnosticSeverity severity;
+ const gchar *tag_name = NULL;
+ IdeSourceLocation *location;
+ gsize num_ranges;
+ gsize i;
+
+ g_assert (IDE_IS_BUFFER (self));
+ g_assert (diagnostic);
+
+ severity = ide_diagnostic_get_severity (diagnostic);
+
+ switch (severity)
+ {
+ case IDE_DIAGNOSTIC_NOTE:
+ tag_name = TAG_NOTE;
+ break;
+
+ case IDE_DIAGNOSTIC_WARNING:
+ tag_name = TAG_WARNING;
+ break;
+
+ case IDE_DIAGNOSTIC_ERROR:
+ case IDE_DIAGNOSTIC_FATAL:
+ tag_name = TAG_ERROR;
+ break;
+
+ case IDE_DIAGNOSTIC_IGNORED:
+ default:
+ return;
+ }
+
+ if ((location = ide_diagnostic_get_location (diagnostic)))
+ {
+ IdeFile *file;
+ GtkTextIter iter1;
+ GtkTextIter iter2;
+
+ file = ide_source_location_get_file (location);
+
+ if (file && self->file && !ide_file_equal (file, self->file))
+ {
+ /* Ignore? */
+ }
+
+ ide_buffer_get_iter_at_location (self, &iter1, location);
+ gtk_text_iter_assign (&iter2, &iter1);
+ if (!gtk_text_iter_ends_line (&iter2))
+ gtk_text_iter_forward_to_line_end (&iter2);
+ else
+ gtk_text_iter_backward_char (&iter1);
+
+ gtk_text_buffer_apply_tag_by_name (GTK_TEXT_BUFFER (self), tag_name, &iter1, &iter2);
+ }
+
+ num_ranges = ide_diagnostic_get_num_ranges (diagnostic);
+
+ for (i = 0; i < num_ranges; i++)
+ {
+ IdeSourceRange *range;
+ IdeSourceLocation *begin;
+ IdeSourceLocation *end;
+ IdeFile *file;
+ GtkTextIter iter1;
+ GtkTextIter iter2;
+
+ range = ide_diagnostic_get_range (diagnostic, i);
+ begin = ide_source_range_get_begin (range);
+ end = ide_source_range_get_end (range);
+
+ file = ide_source_location_get_file (begin);
+
+ if (file && self->file && !ide_file_equal (file, self->file))
+ {
+ /* Ignore */
+ }
+
+ ide_buffer_get_iter_at_location (self, &iter1, begin);
+ ide_buffer_get_iter_at_location (self, &iter2, end);
+
+ if (gtk_text_iter_equal (&iter1, &iter2))
+ {
+ if (!gtk_text_iter_ends_line (&iter2))
+ gtk_text_iter_forward_char (&iter2);
+ else
+ gtk_text_iter_backward_char (&iter1);
+ }
+
+ gtk_text_buffer_apply_tag_by_name (GTK_TEXT_BUFFER (self), tag_name, &iter1, &iter2);
+ }
+}
+
+static void
+ide_buffer_highlight_diagnostics (IdeBuffer *self,
+ IdeDiagnostics *diagnostics)
+{
+ gsize size;
+ gsize i;
+
+ g_assert (IDE_IS_BUFFER (self));
+ g_assert (diagnostics);
+
+ size = ide_diagnostics_get_size (diagnostics);
+
+ for (i = 0; i < size; i++)
+ {
+ IdeDiagnostic *diagnostic;
+
+ diagnostic = ide_diagnostics_index (diagnostics, i);
+ ide_buffer_highlight_diagnostic (self, diagnostic);
+ }
+}
+
+static void
+ide_buffer_set_diagnostics (IdeBuffer *self,
+ IdeDiagnostics *diagnostics)
+{
+ g_assert (IDE_IS_BUFFER (self));
+
+ if (diagnostics != self->diagnostics)
+ {
+ g_clear_pointer (&self->diagnostics, ide_diagnostics_unref);
+ self->diagnostics = diagnostics ? ide_diagnostics_ref (diagnostics) : NULL;
+ ide_buffer_clear_diagnostics (self);
+ if (diagnostics)
+ ide_buffer_highlight_diagnostics (self, diagnostics);
+ }
+}
+
+static void
ide_buffer__file_load_settings_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
@@ -88,12 +310,140 @@ ide_buffer__file_load_settings_cb (GObject *object,
}
static void
+ide_buffer__diagnostician_diagnose_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeDiagnostician *diagnostician = (IdeDiagnostician *)object;
+ g_autoptr(IdeBuffer) self = user_data;
+ g_autoptr(IdeDiagnostics) diagnostics = NULL;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (IDE_IS_DIAGNOSTICIAN (diagnostician));
+ g_assert (IDE_IS_BUFFER (self));
+
+ self->in_diagnose = FALSE;
+
+ diagnostics = ide_diagnostician_diagnose_finish (diagnostician, result, &error);
+
+ if (error)
+ g_message ("%s", error->message);
+
+ ide_buffer_set_diagnostics (self, diagnostics);
+
+ if (self->diagnostics_dirty)
+ ide_buffer_queue_diagnose (self);
+}
+
+static gboolean
+ide_buffer__diagnose_timeout_cb (gpointer user_data)
+{
+ IdeBuffer *self = user_data;
+
+ g_assert (IDE_IS_BUFFER (self));
+
+ self->diagnose_timeout = 0;
+
+ if (self->file)
+ {
+ IdeLanguage *language;
+
+ language = ide_file_get_language (self->file);
+
+ if (language)
+ {
+ IdeDiagnostician *diagnostician;
+
+ diagnostician = ide_language_get_diagnostician (language);
+
+ if (diagnostician)
+ {
+ self->diagnostics_dirty = FALSE;
+ self->in_diagnose = TRUE;
+
+ ide_buffer_sync_to_unsaved_files (self);
+ ide_diagnostician_diagnose_async (diagnostician, self->file, NULL,
+ ide_buffer__diagnostician_diagnose_cb,
+ g_object_ref (self));
+ }
+ }
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+ide_buffer_queue_diagnose (IdeBuffer *self)
+{
+ g_assert (IDE_IS_BUFFER (self));
+
+ self->diagnostics_dirty = TRUE;
+
+ if (self->diagnose_timeout != 0)
+ {
+ g_source_remove (self->diagnose_timeout);
+ self->diagnose_timeout = 0;
+ }
+
+ self->diagnose_timeout = g_timeout_add (DEFAULT_DIAGNOSE_TIMEOUT_MSEC,
+ ide_buffer__diagnose_timeout_cb,
+ self);
+}
+
+static void
+ide_buffer_changed (GtkTextBuffer *buffer)
+{
+ IdeBuffer *self = (IdeBuffer *)buffer;
+
+ GTK_TEXT_BUFFER_CLASS (ide_buffer_parent_class)->changed (buffer);
+
+ self->diagnostics_dirty = TRUE;
+
+ if (self->highlight_diagnostics && !self->in_diagnose)
+ ide_buffer_queue_diagnose (self);
+}
+
+static void
+ide_buffer_constructed (GObject *object)
+{
+ IdeBuffer *self = (IdeBuffer *)object;
+
+ G_OBJECT_CLASS (ide_buffer_parent_class)->constructed (object);
+
+ gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (self), TAG_ERROR,
+ "underline", PANGO_UNDERLINE_ERROR,
+ NULL);
+ gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (self), TAG_WARNING,
+ "underline", PANGO_UNDERLINE_ERROR,
+ NULL);
+ gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (self), TAG_NOTE,
+ "underline", PANGO_UNDERLINE_SINGLE,
+ NULL);
+}
+
+static void
+ide_buffer_dispose (GObject *object)
+{
+ IdeBuffer *self = (IdeBuffer *)object;
+
+ if (self->diagnose_timeout)
+ {
+ g_source_remove (self->diagnose_timeout);
+ self->diagnose_timeout = 0;
+ }
+
+ g_clear_pointer (&self->diagnostics, ide_diagnostics_unref);
+ g_clear_object (&self->file);
+
+ G_OBJECT_CLASS (ide_buffer_parent_class)->dispose (object);
+}
+
+static void
ide_buffer_finalize (GObject *object)
{
IdeBuffer *self = (IdeBuffer *)object;
ide_clear_weak_pointer (&self->context);
- g_clear_object (&self->file);
G_OBJECT_CLASS (ide_buffer_parent_class)->finalize (object);
}
@@ -156,11 +506,16 @@ static void
ide_buffer_class_init (IdeBufferClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkTextBufferClass *text_buffer_class = GTK_TEXT_BUFFER_CLASS (klass);
+ object_class->constructed = ide_buffer_constructed;
+ object_class->dispose = ide_buffer_dispose;
object_class->finalize = ide_buffer_finalize;
object_class->get_property = ide_buffer_get_property;
object_class->set_property = ide_buffer_set_property;
+ text_buffer_class->changed = ide_buffer_changed;
+
gParamSpecs [PROP_CONTEXT] =
g_param_spec_object ("context",
_("Context"),
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]