[gnome-builder/wip/highlight: 2/2] highlight: add basic, incremental semantic highlighting engine.



commit b09da39d5c61196dfa2402d63b009e026e60a10a
Author: Christian Hergert <christian hergert me>
Date:   Wed Mar 25 15:16:00 2015 -0700

    highlight: add basic, incremental semantic highlighting engine.
    
    This will make incremental progress through the buffer and invalidate
    regions based on changes to the buffer.
    
    There are likely bugs, and some iteration will be needed.

 libide/Makefile.am            |    3 +
 libide/ide-buffer.c           |   31 ++
 libide/ide-enums.c.template   |    3 +-
 libide/ide-highlight-engine.c |  630 +++++++++++++++++++++++++++++++++++++++++
 libide/ide-highlight-engine.h |   39 +++
 libide/ide-highlighter.c      |   21 ++
 libide/ide-highlighter.h      |   28 ++
 7 files changed, 754 insertions(+), 1 deletions(-)
---
diff --git a/libide/Makefile.am b/libide/Makefile.am
index f329ccc..75a5230 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -231,6 +231,8 @@ libide_1_0_la_SOURCES = \
        libide/ide-async-helper.h \
        libide/ide-battery-monitor.c \
        libide/ide-battery-monitor.h \
+       libide/ide-highlight-engine.c \
+       libide/ide-highlight-engine.h \
        libide/ide-internal.h \
        libide/ide-line-change-gutter-renderer.c \
        libide/ide-line-change-gutter-renderer.h \
@@ -346,6 +348,7 @@ endif
 libide_1_0_la_type_headers = \
        libide/ide-buffer.h \
        libide/ide-diagnostic.h \
+       libide/ide-highlighter.h \
        libide/ide-source-view.h \
        $(NULL)
 
diff --git a/libide/ide-buffer.c b/libide/ide-buffer.c
index 8408ba8..4c4cd66 100644
--- a/libide/ide-buffer.c
+++ b/libide/ide-buffer.c
@@ -30,6 +30,8 @@
 #include "ide-diagnostics.h"
 #include "ide-file.h"
 #include "ide-file-settings.h"
+#include "ide-highlighter.h"
+#include "ide-highlight-engine.h"
 #include "ide-language.h"
 #include "ide-source-location.h"
 #include "ide-source-range.h"
@@ -53,6 +55,7 @@ typedef struct
   IdeFile                *file;
   GBytes                 *content;
   IdeBufferChangeMonitor *change_monitor;
+  IdeHighlightEngine     *highlight_engine;
   gchar                  *title;
 
   gulong                  change_monitor_changed_handler;
@@ -494,6 +497,27 @@ ide_buffer_queue_diagnose (IdeBuffer *self)
 }
 
 static void
+ide_buffer_reload_highlighter (IdeBuffer *self)
+{
+  IdeBufferPrivate *priv = ide_buffer_get_instance_private (self);
+  IdeHighlighter *highlighter = NULL;
+
+  g_assert (IDE_IS_BUFFER (self));
+
+  if (priv->file != NULL)
+    {
+      IdeLanguage *language;
+
+      language = ide_file_get_language (priv->file);
+      if (language != NULL)
+        highlighter = ide_language_get_highlighter (language);
+    }
+
+  if (priv->highlight_engine != NULL)
+    ide_highlight_engine_set_highlighter (priv->highlight_engine, highlighter);
+}
+
+static void
 ide_buffer__change_monitor_changed_cb (IdeBuffer              *self,
                                        IdeBufferChangeMonitor *monitor)
 {
@@ -688,6 +712,7 @@ static void
 ide_buffer_constructed (GObject *object)
 {
   IdeBuffer *self = (IdeBuffer *)object;
+  IdeBufferPrivate *priv = ide_buffer_get_instance_private (self);
 #if GTK_CHECK_VERSION(3, 16, 1)
   GdkRGBA warning_rgba;
 #endif
@@ -710,6 +735,10 @@ ide_buffer_constructed (GObject *object)
   gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (self), TAG_NOTE,
                               "underline", PANGO_UNDERLINE_SINGLE,
                               NULL);
+
+  priv->highlight_engine = ide_highlight_engine_new (self);
+
+  ide_buffer_reload_highlighter (self);
 }
 
 static void
@@ -737,6 +766,7 @@ ide_buffer_dispose (GObject *object)
   g_clear_pointer (&priv->content, g_bytes_unref);
   g_clear_pointer (&priv->title, g_free);
   g_clear_object (&priv->file);
+  g_clear_object (&priv->highlight_engine);
 
   G_OBJECT_CLASS (ide_buffer_parent_class)->dispose (object);
 
@@ -1052,6 +1082,7 @@ ide_buffer_set_file (IdeBuffer *self,
                                     ide_buffer__file_load_settings_cb,
                                     g_object_ref (self));
       ide_buffer_reload_change_monitor (self);
+      ide_buffer_reload_highlighter (self);
       ide_buffer_update_title (self);
       g_object_notify_by_pspec (G_OBJECT (self), gParamSpecs [PROP_FILE]);
       g_object_notify_by_pspec (G_OBJECT (self), gParamSpecs [PROP_TITLE]);
diff --git a/libide/ide-enums.c.template b/libide/ide-enums.c.template
index cd1a4ff..b60ca20 100644
--- a/libide/ide-enums.c.template
+++ b/libide/ide-enums.c.template
@@ -6,8 +6,9 @@
 #include "ide-enums.h"
 
 #include "ide-buffer.h"
-#include "ide-source-view.h"
 #include "ide-diagnostic.h"
+#include "ide-highlighter.h"
+#include "ide-source-view.h"
 
 /*** END file-header ***/
 
diff --git a/libide/ide-highlight-engine.c b/libide/ide-highlight-engine.c
new file mode 100644
index 0000000..4cb38ef
--- /dev/null
+++ b/libide/ide-highlight-engine.c
@@ -0,0 +1,630 @@
+/* ide-highlight-engine.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser 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/>.
+ */
+
+#define G_LOG_DOMAIN "ide-highlight-engine"
+
+#include <glib/gi18n.h>
+
+#include "ide-debug.h"
+#include "ide-highlight-engine.h"
+#include "ide-types.h"
+
+#define HIGHLIGHT_QUANTA_USEC 1000
+#define WORK_TIMEOUT_MSEC     50
+
+struct _IdeHighlightEngine
+{
+  GObject         parent_instance;
+
+  IdeBuffer      *buffer;
+  IdeHighlighter *highlighter;
+
+  GtkTextTag     *tags[IDE_HIGHLIGHT_KIND_LAST];
+
+  GtkTextMark    *invalid_begin;
+  GtkTextMark    *invalid_end;
+
+  guint           work_timeout;
+};
+
+G_DEFINE_TYPE (IdeHighlightEngine, ide_highlight_engine, G_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_BUFFER,
+  PROP_HIGHLIGHTER,
+  LAST_PROP
+};
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+static GtkTextTag *
+create_tag_from_style (IdeHighlightEngine *self,
+                       const gchar        *style_name)
+{
+  GtkSourceStyleScheme *style_scheme;
+  GtkSourceStyle *style;
+  GtkTextTag *tag;
+  g_autofree gchar *foreground = NULL;
+  g_autofree gchar *background = NULL;
+  gboolean foreground_set = FALSE;
+  gboolean background_set = FALSE;
+  gboolean bold = FALSE;
+  gboolean bold_set = FALSE;
+  gboolean underline = FALSE;
+  gboolean underline_set = FALSE;
+  gboolean italic = FALSE;
+  gboolean italic_set = FALSE;
+
+  g_assert (IDE_IS_HIGHLIGHT_ENGINE (self));
+  g_assert (self->buffer != NULL);
+  g_assert (IDE_IS_BUFFER (self->buffer));
+
+  tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (self->buffer), "", NULL);
+
+  style_scheme = gtk_source_buffer_get_style_scheme (GTK_SOURCE_BUFFER (self->buffer));
+  if (style_scheme == NULL)
+    return tag;
+
+  style = gtk_source_style_scheme_get_style (style_scheme, style_name);
+  if (style == NULL)
+    return tag;
+
+  g_object_get (style,
+                "background", &background,
+                "background-set", &background_set,
+                "foreground", &foreground,
+                "foreground-set", &foreground_set,
+                "bold", &bold,
+                "bold-set", &bold_set,
+                "underline", &underline,
+                "underline-set", &underline_set,
+                "italic", &italic,
+                "italic-set", &italic_set,
+                NULL);
+
+  if (background_set)
+    g_object_set (tag, "background", background, NULL);
+
+  if (foreground_set)
+    g_object_set (tag, "foreground", foreground, NULL);
+
+  if (bold_set && bold)
+    g_object_set (tag, "weight", PANGO_WEIGHT_BOLD, NULL);
+
+  if (italic_set && italic)
+    g_object_set (tag, "style", PANGO_STYLE_ITALIC, NULL);
+
+  if (underline_set && underline)
+    g_object_set (tag, "underline", PANGO_UNDERLINE_SINGLE, NULL);
+
+  return tag;
+}
+
+static GtkTextTag *
+get_kind_tag (IdeHighlightEngine *self,
+              IdeHighlightKind    kind)
+{
+  const gchar *name = NULL;
+
+  g_assert (IDE_IS_HIGHLIGHT_ENGINE (self));
+
+  switch (kind)
+    {
+    case IDE_HIGHLIGHT_KIND_TYPE_NAME:
+    case IDE_HIGHLIGHT_KIND_FUNCTION_NAME:
+    case IDE_HIGHLIGHT_KIND_CLASS_NAME:
+    case IDE_HIGHLIGHT_KIND_MACRO_NAME:
+      name = "def:type";
+      break;
+
+    case IDE_HIGHLIGHT_KIND_NONE:
+    default:
+      return NULL;
+    }
+
+    if ((self->tags [kind] == NULL) && (name != NULL))
+      self->tags [kind] = create_tag_from_style (self, name);
+
+    return self->tags [kind];
+}
+
+static gboolean
+ide_highlight_engine_tick (IdeHighlightEngine *self)
+{
+  GtkTextBuffer *buffer;
+  GtkTextIter iter;
+  GtkTextIter begin;
+  GtkTextIter end;
+  guint64 quanta_expiration;
+  IdeHighlightKind kind;
+
+  IDE_PROBE;
+
+  g_assert (IDE_IS_HIGHLIGHT_ENGINE (self));
+  g_assert (self->buffer != NULL);
+  g_assert (self->highlighter != NULL);
+  g_assert (self->invalid_begin != NULL);
+  g_assert (self->invalid_end != NULL);
+
+  quanta_expiration = g_get_monotonic_time () + HIGHLIGHT_QUANTA_USEC;
+
+  buffer = GTK_TEXT_BUFFER (self->buffer);
+
+  do
+    {
+      GtkTextTag *tag;
+      GtkTextIter invalid_begin;
+      GtkTextIter invalid_end;
+
+      gtk_text_buffer_get_iter_at_mark (buffer, &invalid_begin, self->invalid_begin);
+      gtk_text_buffer_get_iter_at_mark (buffer, &invalid_end, self->invalid_end);
+
+      IDE_TRACE_MSG ("Highlight Range (%u:%u <=> %u:%u)",
+                     gtk_text_iter_get_line (&invalid_begin),
+                     gtk_text_iter_get_line_offset (&invalid_begin),
+                     gtk_text_iter_get_line (&invalid_end),
+                     gtk_text_iter_get_line_offset (&invalid_end));
+
+      if (gtk_text_iter_compare (&invalid_begin, &invalid_end) >= 0)
+        IDE_GOTO (up_to_date);
+
+      iter = invalid_begin;
+
+      kind = ide_highlighter_next (self->highlighter, &iter, &invalid_end, &begin, &end);
+
+      if (kind == IDE_HIGHLIGHT_KIND_NONE)
+        IDE_GOTO (up_to_date);
+
+      IDE_TRACE_MSG ("Found tag of kind: %d\n", kind);
+
+      tag = get_kind_tag (self, kind);
+
+      gtk_text_buffer_apply_tag (buffer, tag, &begin, &end);
+      gtk_text_buffer_move_mark (buffer, self->invalid_begin, &end);
+    }
+  while (g_get_monotonic_time () < quanta_expiration);
+
+  return TRUE;
+
+up_to_date:
+  gtk_text_buffer_get_start_iter (buffer, &begin);
+  gtk_text_buffer_move_mark (buffer, self->invalid_begin, &begin);
+  gtk_text_buffer_move_mark (buffer, self->invalid_end, &begin);
+
+  return FALSE;
+}
+
+static gboolean
+ide_highlight_engine_work_timeout_handler (gpointer data)
+{
+  IdeHighlightEngine *self = data;
+
+  g_assert (IDE_IS_HIGHLIGHT_ENGINE (self));
+
+  if (ide_highlight_engine_tick (self))
+    return G_SOURCE_CONTINUE;
+
+  self->work_timeout = 0;
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+ide_highlight_engine_queue_work (IdeHighlightEngine *self)
+{
+  g_assert (IDE_IS_HIGHLIGHT_ENGINE (self));
+
+  if (self->work_timeout != 0)
+    return;
+
+  self->work_timeout = g_timeout_add (WORK_TIMEOUT_MSEC,
+                                      ide_highlight_engine_work_timeout_handler,
+                                      self);
+}
+
+static void
+ide_highlight_engine_reload (IdeHighlightEngine *self)
+{
+  GtkTextBuffer *buffer;
+  GtkTextIter begin;
+  GtkTextIter end;
+  gsize i;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_HIGHLIGHT_ENGINE (self));
+
+  if ((self->buffer == NULL) || (self->highlighter == NULL))
+    IDE_EXIT;
+
+  buffer = GTK_TEXT_BUFFER (self->buffer);
+
+  gtk_text_buffer_get_bounds (buffer, &begin, &end);
+
+  /*
+   * Invalidate the whole buffer.
+   */
+  gtk_text_buffer_move_mark (buffer, self->invalid_begin, &begin);
+  gtk_text_buffer_move_mark (buffer, self->invalid_end, &end);
+
+  /*
+   * Remove our highlight tags from the buffer.
+   */
+  for (i = 0; i < G_N_ELEMENTS (self->tags); i++)
+    if (self->tags [i] != NULL)
+      gtk_text_buffer_remove_tag (buffer, self->tags [i], &begin, &end);
+
+  ide_highlight_engine_queue_work (self);
+
+  IDE_EXIT;
+}
+
+static void
+ide_highlight_engine__buffer_insert_text_cb (IdeHighlightEngine *self,
+                                             GtkTextIter        *location,
+                                             gchar              *text,
+                                             gint                len,
+                                             IdeBuffer          *buffer)
+{
+  GtkTextBuffer *text_buffer = (GtkTextBuffer *)buffer;
+  GtkTextIter begin;
+  GtkTextIter end;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_HIGHLIGHT_ENGINE (self));
+  g_assert (location);
+  g_assert (text);
+  g_assert (IDE_IS_BUFFER (buffer));
+  g_assert (GTK_IS_TEXT_BUFFER (text_buffer));
+
+  gtk_text_buffer_get_iter_at_mark (text_buffer, &begin, self->invalid_begin);
+  gtk_text_buffer_get_iter_at_mark (text_buffer, &end, self->invalid_end);
+
+  if (gtk_text_iter_equal (&begin, &end))
+    {
+      begin = *location;
+      end = *location;
+
+      gtk_text_iter_backward_lines (&begin, 2);
+      gtk_text_iter_forward_lines (&end, 2);
+      gtk_text_buffer_move_mark (text_buffer, self->invalid_begin, &begin);
+      gtk_text_buffer_move_mark (text_buffer, self->invalid_end, &end);
+    }
+  else
+    {
+      if (gtk_text_iter_compare (location, &begin) < 0)
+        gtk_text_buffer_move_mark (text_buffer, self->invalid_begin, location);
+      if (gtk_text_iter_compare (location, &end) > 0)
+        gtk_text_buffer_move_mark (text_buffer, self->invalid_end, location);
+    }
+
+  ide_highlight_engine_queue_work (self);
+
+  IDE_EXIT;
+}
+
+static void
+ide_highlight_engine__buffer_delete_range_cb (IdeHighlightEngine *self,
+                                              GtkTextIter        *range_begin,
+                                              GtkTextIter        *range_end,
+                                              IdeBuffer          *buffer)
+{
+  GtkTextBuffer *text_buffer = (GtkTextBuffer *)buffer;
+  GtkTextIter begin;
+  GtkTextIter end;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_HIGHLIGHT_ENGINE (self));
+  g_assert (range_begin);
+  g_assert (range_end);
+  g_assert (IDE_IS_BUFFER (buffer));
+  g_assert (GTK_IS_TEXT_BUFFER (text_buffer));
+
+  gtk_text_buffer_get_iter_at_mark (text_buffer, &begin, self->invalid_begin);
+  gtk_text_buffer_get_iter_at_mark (text_buffer, &end, self->invalid_end);
+
+  if (gtk_text_iter_equal (&begin, &end))
+    {
+      begin = *range_begin;
+      end = *range_end;
+
+      gtk_text_iter_backward_lines (&begin, 2);
+      gtk_text_iter_forward_lines (&end, 2);
+      gtk_text_buffer_move_mark (text_buffer, self->invalid_begin, &begin);
+      gtk_text_buffer_move_mark (text_buffer, self->invalid_end, &end);
+    }
+  else
+    {
+      if (gtk_text_iter_compare (range_begin, &begin) < 0)
+        gtk_text_buffer_move_mark (text_buffer, self->invalid_begin, range_begin);
+      if (gtk_text_iter_compare (range_end, &end) > 0)
+        gtk_text_buffer_move_mark (text_buffer, self->invalid_end, range_end);
+    }
+
+  ide_highlight_engine_queue_work (self);
+
+  IDE_EXIT;
+}
+
+static void
+ide_highlight_engine_connect_buffer (IdeHighlightEngine *self,
+                                     IdeBuffer          *buffer)
+{
+  GtkTextBuffer *text_buffer = (GtkTextBuffer *)buffer;
+  GtkTextIter begin;
+  GtkTextIter end;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_HIGHLIGHT_ENGINE (self));
+  g_assert (IDE_IS_BUFFER (buffer));
+
+  gtk_text_buffer_get_bounds (text_buffer, &begin, &end);
+
+  self->invalid_begin = gtk_text_buffer_create_mark (text_buffer, NULL, &begin, TRUE);
+  self->invalid_end = gtk_text_buffer_create_mark (text_buffer, NULL, &end, FALSE);
+
+  g_signal_connect_object (buffer,
+                           "insert-text",
+                           G_CALLBACK (ide_highlight_engine__buffer_insert_text_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (buffer,
+                           "delete-range",
+                           G_CALLBACK (ide_highlight_engine__buffer_delete_range_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  ide_highlight_engine_reload (self);
+
+  IDE_EXIT;
+}
+
+static void
+ide_highlight_engine_disconnect_buffer (IdeHighlightEngine *self,
+                                        IdeBuffer          *buffer)
+{
+  GtkTextBuffer *text_buffer = (GtkTextBuffer *)buffer;
+  GtkTextTagTable *tag_table;
+  GtkTextIter begin;
+  GtkTextIter end;
+  gsize i;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_HIGHLIGHT_ENGINE (self));
+  g_assert (IDE_IS_BUFFER (buffer));
+
+  if (self->work_timeout)
+    {
+      g_source_remove (self->work_timeout);
+      self->work_timeout = 0;
+    }
+
+  g_signal_handlers_disconnect_by_func (buffer,
+                                        G_CALLBACK (ide_highlight_engine__buffer_delete_range_cb),
+                                        self);
+
+  g_signal_handlers_disconnect_by_func (buffer,
+                                        G_CALLBACK (ide_highlight_engine__buffer_insert_text_cb),
+                                        self);
+
+  tag_table = gtk_text_buffer_get_tag_table (text_buffer);
+
+  gtk_text_buffer_delete_mark (text_buffer, self->invalid_begin);
+  gtk_text_buffer_delete_mark (text_buffer, self->invalid_end);
+
+  self->invalid_begin = NULL;
+  self->invalid_end = NULL;
+
+  gtk_text_buffer_get_bounds (text_buffer, &begin, &end);
+
+  for (i = 0; i < G_N_ELEMENTS (self->tags); i++)
+    {
+      if (self->tags [i] != NULL)
+        {
+          gtk_text_buffer_remove_tag (text_buffer, self->tags [i], &begin, &end);
+          gtk_text_tag_table_remove (tag_table, self->tags [i]);
+          self->tags [i] = NULL;
+        }
+    }
+
+  IDE_EXIT;
+}
+
+static void
+ide_highlight_engine_set_buffer (IdeHighlightEngine *self,
+                                 IdeBuffer          *buffer)
+{
+  g_return_if_fail (IDE_IS_HIGHLIGHT_ENGINE (self));
+  g_return_if_fail (IDE_IS_BUFFER (buffer));
+
+  if (self->buffer != buffer)
+    {
+      if (self->buffer != NULL)
+        {
+          ide_highlight_engine_disconnect_buffer (self, self->buffer);
+          ide_clear_weak_pointer (&self->buffer);
+        }
+
+      if (buffer != NULL)
+        {
+          ide_set_weak_pointer (&self->buffer, buffer);
+          ide_highlight_engine_connect_buffer (self, self->buffer);
+        }
+
+      g_object_notify_by_pspec (G_OBJECT (self), gParamSpecs [PROP_BUFFER]);
+    }
+}
+
+static void
+ide_highlight_engine_dispose (GObject *object)
+{
+  IdeHighlightEngine *self = (IdeHighlightEngine *)object;
+
+  ide_highlight_engine_set_buffer (self, NULL);
+
+  G_OBJECT_CLASS (ide_highlight_engine_parent_class)->dispose (object);
+}
+
+static void
+ide_highlight_engine_finalize (GObject *object)
+{
+  IdeHighlightEngine *self = (IdeHighlightEngine *)object;
+
+  g_clear_object (&self->highlighter);
+  ide_clear_weak_pointer (&self->buffer);
+
+  G_OBJECT_CLASS (ide_highlight_engine_parent_class)->finalize (object);
+}
+
+static void
+ide_highlight_engine_get_property (GObject    *object,
+                                   guint       prop_id,
+                                   GValue     *value,
+                                   GParamSpec *pspec)
+{
+  IdeHighlightEngine *self = IDE_HIGHLIGHT_ENGINE (object);
+
+  switch (prop_id)
+    {
+    case PROP_BUFFER:
+      g_value_set_object (value, ide_highlight_engine_get_buffer (self));
+      break;
+
+    case PROP_HIGHLIGHTER:
+      g_value_set_object (value, ide_highlight_engine_get_highlighter (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_highlight_engine_set_property (GObject      *object,
+                                   guint         prop_id,
+                                   const GValue *value,
+                                   GParamSpec   *pspec)
+{
+  IdeHighlightEngine *self = IDE_HIGHLIGHT_ENGINE (object);
+
+  switch (prop_id)
+    {
+    case PROP_BUFFER:
+      ide_highlight_engine_set_buffer (self, g_value_get_object (value));
+      break;
+
+    case PROP_HIGHLIGHTER:
+      ide_highlight_engine_set_highlighter (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_highlight_engine_class_init (IdeHighlightEngineClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = ide_highlight_engine_dispose;
+  object_class->finalize = ide_highlight_engine_finalize;
+  object_class->get_property = ide_highlight_engine_get_property;
+  object_class->set_property = ide_highlight_engine_set_property;
+
+  gParamSpecs [PROP_BUFFER] =
+    g_param_spec_object ("buffer",
+                         _("Buffer"),
+                         _("The buffer to highlight."),
+                         IDE_TYPE_BUFFER,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_BUFFER, gParamSpecs [PROP_BUFFER]);
+
+  gParamSpecs [PROP_HIGHLIGHTER] =
+    g_param_spec_object ("highlighter",
+                         _("Highlighter"),
+                         _("The highlighter to use for type information."),
+                         IDE_TYPE_HIGHLIGHTER,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_HIGHLIGHTER, gParamSpecs [PROP_HIGHLIGHTER]);
+}
+
+static void
+ide_highlight_engine_init (IdeHighlightEngine *self)
+{
+}
+
+IdeHighlightEngine *
+ide_highlight_engine_new (IdeBuffer *buffer)
+{
+  return g_object_new (IDE_TYPE_HIGHLIGHT_ENGINE,
+                       "buffer", buffer,
+                       NULL);
+}
+
+/**
+ * ide_highlight_engine_get_highlighter:
+ * @self: A #IdeHighlightEngine.
+ *
+ * Gets the IdeHighlightEngine:highlighter property.
+ *
+ * Returns: (transfer none): An #IdeHighlighter.
+ */
+IdeHighlighter *
+ide_highlight_engine_get_highlighter (IdeHighlightEngine *self)
+{
+  g_return_val_if_fail (IDE_IS_HIGHLIGHT_ENGINE (self), NULL);
+
+  return self->highlighter;
+}
+
+void
+ide_highlight_engine_set_highlighter (IdeHighlightEngine *self,
+                                      IdeHighlighter     *highlighter)
+{
+  g_return_if_fail (IDE_IS_HIGHLIGHT_ENGINE (self));
+  g_return_if_fail (!highlighter || IDE_IS_HIGHLIGHTER (highlighter));
+
+  if (g_set_object (&self->highlighter, highlighter))
+    {
+      ide_highlight_engine_reload (self);
+      g_object_notify_by_pspec (G_OBJECT (self), gParamSpecs [PROP_HIGHLIGHTER]);
+    }
+}
+
+/**
+ * ide_highlight_engine_get_buffer:
+ * @self: A #IdeHighlightEngine.
+ *
+ * Gets the IdeHighlightEngine:buffer property.
+ *
+ * Returns: (transfer none): An #IdeBuffer.
+ */
+IdeBuffer *
+ide_highlight_engine_get_buffer (IdeHighlightEngine *self)
+{
+  g_return_val_if_fail (IDE_IS_HIGHLIGHT_ENGINE (self), NULL);
+
+  return self->buffer;
+}
diff --git a/libide/ide-highlight-engine.h b/libide/ide-highlight-engine.h
new file mode 100644
index 0000000..1e2d316
--- /dev/null
+++ b/libide/ide-highlight-engine.h
@@ -0,0 +1,39 @@
+/* ide-highlight-engine.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser 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/>.
+ */
+
+#ifndef IDE_HIGHLIGHT_ENGINE_H
+#define IDE_HIGHLIGHT_ENGINE_H
+
+#include "ide-buffer.h"
+#include "ide-highlighter.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_HIGHLIGHT_ENGINE (ide_highlight_engine_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeHighlightEngine, ide_highlight_engine, IDE, HIGHLIGHT_ENGINE, GObject)
+
+IdeHighlightEngine *ide_highlight_engine_new             (IdeBuffer          *buffer);
+IdeBuffer          *ide_highlight_engine_get_buffer      (IdeHighlightEngine *self);
+IdeHighlighter     *ide_highlight_engine_get_highlighter (IdeHighlightEngine *self);
+void                ide_highlight_engine_set_highlighter (IdeHighlightEngine *self,
+                                                          IdeHighlighter     *highlighter);
+
+G_END_DECLS
+
+#endif /* IDE_HIGHLIGHT_ENGINE_H */
diff --git a/libide/ide-highlighter.c b/libide/ide-highlighter.c
index aa8fa9d..6b54162 100644
--- a/libide/ide-highlighter.c
+++ b/libide/ide-highlighter.c
@@ -29,3 +29,24 @@ static void
 ide_highlighter_init (IdeHighlighter *self)
 {
 }
+
+IdeHighlightKind
+ide_highlighter_next (IdeHighlighter    *self,
+                      const GtkTextIter *range_begin,
+                      const GtkTextIter *range_end,
+                      GtkTextIter       *match_begin,
+                      GtkTextIter       *match_end)
+{
+  g_return_val_if_fail (IDE_IS_HIGHLIGHTER (self), 0);
+  g_return_val_if_fail (range_begin, 0);
+  g_return_val_if_fail (range_end, 0);
+  g_return_val_if_fail (match_begin, 0);
+  g_return_val_if_fail (match_end, 0);
+
+  if (IDE_HIGHLIGHTER_GET_CLASS (self)->next)
+    return IDE_HIGHLIGHTER_GET_CLASS (self)->next (self,
+                                                   range_begin, range_end,
+                                                   match_begin, match_end);
+
+  return IDE_HIGHLIGHT_KIND_NONE;
+}
diff --git a/libide/ide-highlighter.h b/libide/ide-highlighter.h
index b866b41..b65d78c 100644
--- a/libide/ide-highlighter.h
+++ b/libide/ide-highlighter.h
@@ -19,7 +19,11 @@
 #ifndef IDE_HIGHLIGHTER_H
 #define IDE_HIGHLIGHTER_H
 
+#include <gtk/gtk.h>
+
+#include "ide-buffer.h"
 #include "ide-object.h"
+#include "ide-source-view.h"
 
 G_BEGIN_DECLS
 
@@ -27,11 +31,35 @@ G_BEGIN_DECLS
 
 G_DECLARE_DERIVABLE_TYPE (IdeHighlighter, ide_highlighter, IDE, HIGHLIGHTER, IdeObject)
 
+typedef enum
+{
+  IDE_HIGHLIGHT_KIND_NONE,
+
+  IDE_HIGHLIGHT_KIND_TYPE_NAME,
+  IDE_HIGHLIGHT_KIND_CLASS_NAME,
+  IDE_HIGHLIGHT_KIND_FUNCTION_NAME,
+  IDE_HIGHLIGHT_KIND_MACRO_NAME,
+
+  IDE_HIGHLIGHT_KIND_LAST
+} IdeHighlightKind;
+
 struct _IdeHighlighterClass
 {
   IdeObjectClass parent;
+
+  IdeHighlightKind (*next) (IdeHighlighter    *self,
+                            const GtkTextIter *range_begin,
+                            const GtkTextIter *range_end,
+                            GtkTextIter       *match_begin,
+                            GtkTextIter       *match_end);
 };
 
+IdeHighlightKind ide_highlighter_next (IdeHighlighter    *self,
+                                       const GtkTextIter *range_begin,
+                                       const GtkTextIter *range_end,
+                                       GtkTextIter       *match_begin,
+                                       GtkTextIter       *match_end);
+
 G_END_DECLS
 
 #endif /* IDE_HIGHLIGHTER_H */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]