[gtksourceview] Implement private class GtkSourceMarksSequence



commit 4add97f5d782f888a77b395e3a704a82a53aa189
Author: Sébastien Wilmet <swilmet gnome org>
Date:   Sun Sep 8 15:25:55 2013 +0200

    Implement private class GtkSourceMarksSequence
    
    And use it for GtkSourceMarks. It will be useful for the code folding
    too.
    
    The unit tests are in the yellow (>= 75%).

 docs/reference/Makefile.am             |    1 +
 gtksourceview/Makefile.am              |    2 +
 gtksourceview/gtksourcebuffer.c        |  490 +++++++--------------------
 gtksourceview/gtksourcemark.c          |   24 +-
 gtksourceview/gtksourcemarkssequence.c |  569 ++++++++++++++++++++++++++++++++
 gtksourceview/gtksourcemarkssequence.h |   96 ++++++
 gtksourceview/gtksourcetypes-private.h |    1 +
 tests/test-mark.c                      |  126 ++++++--
 8 files changed, 908 insertions(+), 401 deletions(-)
---
diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am
index 8acf0b3..1987d7f 100644
--- a/docs/reference/Makefile.am
+++ b/docs/reference/Makefile.am
@@ -36,6 +36,7 @@ IGNORE_HFILES =                                       \
        gtksourcegutterrenderermarks.h          \
        gtksourcegutterrenderer-private.h       \
        gtksourcelanguage-private.h             \
+       gtksourcemarkssequence.h                \
        gtksourcepixbufhelper.h                 \
        gtksourceregex.h                        \
        gtksourcestyle-private.h                \
diff --git a/gtksourceview/Makefile.am b/gtksourceview/Makefile.am
index 735bfb6..e27d220 100644
--- a/gtksourceview/Makefile.am
+++ b/gtksourceview/Makefile.am
@@ -61,6 +61,7 @@ libgtksourceview_private_headers = \
        gtksourcegutterrenderermarks.h          \
        gtksourcegutterrenderer-private.h       \
        gtksourcelanguage-private.h             \
+       gtksourcemarkssequence.h                \
        gtksourcepixbufhelper.h                 \
        gtksourceregex.h                        \
        gtksourcestyle-private.h                \
@@ -79,6 +80,7 @@ libgtksourceview_private_c_files = \
        gtksourcegutterrenderermarks.c  \
        gtksourcelanguage-parser-1.c    \
        gtksourcelanguage-parser-2.c    \
+       gtksourcemarkssequence.c        \
        gtksourcepixbufhelper.c         \
        gtksourceregex.c                \
        gtksourceundomanager.c          \
diff --git a/gtksourceview/gtksourcebuffer.c b/gtksourceview/gtksourcebuffer.c
index 78b2919..0f061f6 100644
--- a/gtksourceview/gtksourcebuffer.c
+++ b/gtksourceview/gtksourcebuffer.c
@@ -32,18 +32,19 @@
 
 #include "gtksourcebuffer.h"
 #include "gtksourcebuffer-private.h"
-#include "gtksourceview-i18n.h"
 #include "gtksourcelanguage.h"
 #include "gtksourcelanguage-private.h"
 #include "gtksourceundomanager.h"
-#include "gtksourceview-marshal.h"
+#include "gtksourceundomanagerdefault.h"
 #include "gtksourcestylescheme.h"
 #include "gtksourcestyleschememanager.h"
 #include "gtksourcestyle-private.h"
-#include "gtksourceundomanagerdefault.h"
-#include "gtksourceview-typebuiltins.h"
 #include "gtksourcemark.h"
+#include "gtksourcemarkssequence.h"
 #include "gtksourcesearchcontext.h"
+#include "gtksourceview-i18n.h"
+#include "gtksourceview-marshal.h"
+#include "gtksourceview-typebuiltins.h"
 
 /**
  * SECTION:buffer
@@ -169,7 +170,9 @@ struct _GtkSourceBufferPrivate
        GtkTextMark           *bracket_mark_match;
        GtkSourceBracketMatchType bracket_match;
 
-       GArray                *source_marks;
+       /* Hash table: category -> MarksSequence */
+       GHashTable             *source_marks;
+       GtkSourceMarksSequence *all_source_marks;
 
        GtkSourceLanguage     *language;
 
@@ -191,7 +194,6 @@ G_DEFINE_TYPE_WITH_PRIVATE (GtkSourceBuffer, gtk_source_buffer, GTK_TYPE_TEXT_BU
 
 static guint    buffer_signals[LAST_SIGNAL];
 
-static void     gtk_source_buffer_finalize             (GObject                 *object);
 static void     gtk_source_buffer_dispose              (GObject                 *object);
 static void      gtk_source_buffer_set_property         (GObject                 *object,
                                                         guint                    prop_id,
@@ -265,7 +267,6 @@ gtk_source_buffer_class_init (GtkSourceBufferClass *klass)
        tb_class        = GTK_TEXT_BUFFER_CLASS (klass);
 
        object_class->constructed  = gtk_source_buffer_constructed;
-       object_class->finalize     = gtk_source_buffer_finalize;
        object_class->dispose      = gtk_source_buffer_dispose;
        object_class->get_property = gtk_source_buffer_get_property;
        object_class->set_property = gtk_source_buffer_set_property;
@@ -515,7 +516,13 @@ gtk_source_buffer_init (GtkSourceBuffer *buffer)
        priv->bracket_mark_match = NULL;
        priv->bracket_match = GTK_SOURCE_BRACKET_MATCH_NONE;
 
-       priv->source_marks = g_array_new (FALSE, FALSE, sizeof (GtkSourceMark *));
+       priv->source_marks = g_hash_table_new_full (g_str_hash,
+                                                   g_str_equal,
+                                                   (GDestroyNotify)g_free,
+                                                   (GDestroyNotify)g_object_unref);
+
+       priv->all_source_marks = _gtk_source_marks_sequence_new (GTK_TEXT_BUFFER (buffer));
+
        priv->style_scheme = _gtk_source_style_scheme_get_default ();
 
        if (priv->style_scheme != NULL)
@@ -525,23 +532,6 @@ gtk_source_buffer_init (GtkSourceBuffer *buffer)
 }
 
 static void
-gtk_source_buffer_finalize (GObject *object)
-{
-       GtkSourceBuffer *buffer;
-
-       g_return_if_fail (object != NULL);
-       g_return_if_fail (GTK_SOURCE_IS_BUFFER (object));
-
-       buffer = GTK_SOURCE_BUFFER (object);
-       g_return_if_fail (buffer->priv != NULL);
-
-       if (buffer->priv->source_marks)
-               g_array_free (buffer->priv->source_marks, TRUE);
-
-       G_OBJECT_CLASS (gtk_source_buffer_parent_class)->finalize (object);
-}
-
-static void
 gtk_source_buffer_dispose (GObject *object)
 {
        GtkSourceBuffer *buffer;
@@ -578,6 +568,14 @@ gtk_source_buffer_dispose (GObject *object)
        g_list_free (buffer->priv->search_contexts);
        buffer->priv->search_contexts = NULL;
 
+       g_clear_object (&buffer->priv->all_source_marks);
+
+       if (buffer->priv->source_marks != NULL)
+       {
+               g_hash_table_unref (buffer->priv->source_marks);
+               buffer->priv->source_marks = NULL;
+       }
+
        G_OBJECT_CLASS (gtk_source_buffer_parent_class)->dispose (object);
 }
 
@@ -1688,168 +1686,6 @@ gtk_source_buffer_get_style_scheme (GtkSourceBuffer *buffer)
        return buffer->priv->style_scheme;
 }
 
-/* Source Marks functionality */
-
-/* At the moment this is pretty dumb (O(N)), if it is a performance
- * problem we should change data struct.
- * Since it's used from mark_set when the mark was moved, we cannot bsearch.
- * Returns TRUE if the mark was found and removed */
-static gboolean
-source_mark_remove (GtkSourceBuffer *buffer, GtkSourceMark *mark)
-{
-       guint i;
-
-       for (i = 0; i < buffer->priv->source_marks->len; ++i)
-       {
-               GtkSourceMark *m;
-
-               m = g_array_index (buffer->priv->source_marks, GtkSourceMark *, i);
-               if (mark == m)
-               {
-                       g_array_remove_index (buffer->priv->source_marks, i);
-                       g_object_unref (m);
-
-                       return TRUE;
-               }
-       }
-
-       return FALSE;
-}
-
-/* Performs a binary search among the source marks in @buffer for the
- * position of the @iter.  Returns the index of the mark at the specified
- * position or nearest before or after depending on @before.
- *
- * Return value: an index in the source marks array or -1 if the array is
- * empty or if there is no mark before/after the specified position
- */
-static gint
-source_mark_bsearch (GtkSourceBuffer *buffer, GtkTextIter *iter, gboolean before)
-{
-       GArray *marks = buffer->priv->source_marks;
-       GtkSourceMark *check;
-       GtkTextIter check_iter, found_iter;
-       gint cmp, i, min, max;
-
-       if (marks->len == 0)
-               return -1;
-
-       i = min = 0;
-       max = marks->len - 1;
-       while (max >= min)
-       {
-               i = (min + max) >> 1;
-               check = g_array_index (marks, GtkSourceMark *, i);
-               gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
-                                                 &check_iter,
-                                                 GTK_TEXT_MARK (check));
-               cmp = gtk_text_iter_compare (iter, &check_iter);
-               if (cmp < 0)
-               {
-                       max = i - 1;
-               }
-               else if (cmp > 0)
-               {
-                       min = i + 1;
-               }
-               else
-                       break;
-       }
-
-       if (before)
-       {
-               /* if the binary search match is after the specified iter, go back */
-               while (cmp < 0 && i >= 0)
-               {
-                       if (i == 0)
-                               return -1;
-
-                       i--;
-                       check = g_array_index (marks, GtkSourceMark *, i);
-                       gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
-                                                         &check_iter,
-                                                         GTK_TEXT_MARK (check));
-                       cmp = gtk_text_iter_compare (iter, &check_iter);
-               }
-
-               /* if there are many marks at the given iter, return the last */
-               found_iter = check_iter;
-               while (i < marks->len - 1)
-               {
-                       check = g_array_index (marks, GtkSourceMark *, i + 1);
-                       gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
-                                                         &check_iter,
-                                                         GTK_TEXT_MARK (check));
-                       cmp = gtk_text_iter_compare (&found_iter, &check_iter);
-                       if (cmp != 0)
-                       {
-                               break;
-                       }
-                       i++;
-               }
-       }
-       else
-       {
-               /* if the binary search match is before the specified iter, go forward */
-               while (cmp > 0 && i < marks->len)
-               {
-                       if (i == marks->len - 1)
-                               return -1;
-
-                       i++;
-                       check = g_array_index (marks, GtkSourceMark *, i);
-                       gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
-                                                         &check_iter,
-                                                         GTK_TEXT_MARK (check));
-                       cmp = gtk_text_iter_compare (iter, &check_iter);
-               }
-
-               /* if there are many marks at the given iter, return the first */
-               found_iter = check_iter;
-               while (i > 0)
-               {
-                       check = g_array_index (marks, GtkSourceMark *, i - 1);
-                       gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
-                                                         &check_iter,
-                                                         GTK_TEXT_MARK (check));
-                       cmp = gtk_text_iter_compare (&found_iter, &check_iter);
-                       if (cmp != 0)
-                       {
-                               break;
-                       }
-                       i--;
-               }
-       }
-
-       return i;
-}
-
-static void
-source_mark_insert (GtkSourceBuffer *buffer, GtkSourceMark *mark)
-{
-       GtkTextIter iter;
-       gint idx;
-
-       gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
-                                         &iter,
-                                         GTK_TEXT_MARK (mark));
-
-       idx = source_mark_bsearch (buffer, &iter, TRUE);
-       if (idx >= 0)
-       {
-               /* if the mark we found is at same iter or before
-                * put our mark after that */
-               idx++;
-       }
-       else
-       {
-               idx = 0;
-       }
-
-       g_object_ref (mark);
-       g_array_insert_val (buffer->priv->source_marks, idx, mark);
-}
-
 static void
 gtk_source_buffer_real_apply_tag (GtkTextBuffer     *buffer,
                                   GtkTextTag        *tag,
@@ -1872,18 +1708,39 @@ gtk_source_buffer_real_apply_tag (GtkTextBuffer     *buffer,
 }
 
 static void
+add_source_mark (GtkSourceBuffer *buffer,
+                GtkSourceMark   *mark)
+{
+       const gchar *category;
+       GtkSourceMarksSequence *seq;
+
+       _gtk_source_marks_sequence_add (buffer->priv->all_source_marks,
+                                       GTK_TEXT_MARK (mark));
+
+       category = gtk_source_mark_get_category (mark);
+       seq = g_hash_table_lookup (buffer->priv->source_marks, category);
+
+       if (seq == NULL)
+       {
+               seq = _gtk_source_marks_sequence_new (GTK_TEXT_BUFFER (buffer));
+
+               g_hash_table_insert (buffer->priv->source_marks,
+                                    g_strdup (category),
+                                    seq);
+       }
+
+       _gtk_source_marks_sequence_add (seq, GTK_TEXT_MARK (mark));
+}
+
+static void
 gtk_source_buffer_real_mark_set        (GtkTextBuffer     *buffer,
                                 const GtkTextIter *location,
                                 GtkTextMark       *mark)
 {
        if (GTK_SOURCE_IS_MARK (mark))
        {
-               /* for now we simply remove and reinsert at
-                * the right place every time */
-               source_mark_remove (GTK_SOURCE_BUFFER (buffer),
-                                   GTK_SOURCE_MARK (mark));
-               source_mark_insert (GTK_SOURCE_BUFFER (buffer),
-                                   GTK_SOURCE_MARK (mark));
+               add_source_mark (GTK_SOURCE_BUFFER (buffer),
+                                GTK_SOURCE_MARK (mark));
 
                g_signal_emit_by_name (buffer, "source_mark_updated", mark);
        }
@@ -1899,18 +1756,29 @@ gtk_source_buffer_real_mark_set (GtkTextBuffer     *buffer,
 
 static void
 gtk_source_buffer_real_mark_deleted (GtkTextBuffer *buffer,
-                                    GtkTextMark *mark)
+                                    GtkTextMark   *mark)
 {
        if (GTK_SOURCE_IS_MARK (mark))
        {
-               source_mark_remove (GTK_SOURCE_BUFFER (buffer),
-                                   GTK_SOURCE_MARK (mark));
+               GtkSourceBuffer *source_buffer = GTK_SOURCE_BUFFER (buffer);
+               const gchar *category;
+               GtkSourceMarksSequence *seq;
+
+               category = gtk_source_mark_get_category (GTK_SOURCE_MARK (mark));
+               seq = g_hash_table_lookup (source_buffer->priv->source_marks, category);
+
+               if (_gtk_source_marks_sequence_is_empty (seq))
+               {
+                       g_hash_table_remove (source_buffer->priv->source_marks, category);
+               }
 
                g_signal_emit_by_name (buffer, "source_mark_updated", mark);
        }
 
        if (GTK_TEXT_BUFFER_CLASS (gtk_source_buffer_parent_class)->mark_deleted != NULL)
+       {
                GTK_TEXT_BUFFER_CLASS (gtk_source_buffer_parent_class)->mark_deleted (buffer, mark);
+       }
 }
 
 static void
@@ -1974,32 +1842,13 @@ gtk_source_buffer_create_source_mark (GtkSourceBuffer   *buffer,
        return mark;
 }
 
-static gint
-get_mark_index (GtkSourceBuffer *buffer,
-               GtkSourceMark   *mark)
+static GtkSourceMarksSequence *
+get_marks_sequence (GtkSourceBuffer *buffer,
+                   const gchar     *category)
 {
-       GtkTextIter iter;
-       gint idx;
-
-       /* TODO: we could speed this up by caching the current
-        * position in the mark and invalidating the cache when
-        * the marks array changes. For now we always lookup. */
-       gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
-                                         &iter,
-                                         GTK_TEXT_MARK (mark));
-
-       idx = source_mark_bsearch (buffer, &iter, FALSE);
-
-       /* the array should already contain @mark */
-       g_assert (idx >= 0);
-
-       /* move up to our mark among the ones at this position */
-       while (mark != g_array_index (buffer->priv->source_marks, GtkSourceMark *, idx))
-       {
-               ++idx;
-       }
-
-       return idx;
+       return category == NULL ?
+               buffer->priv->all_source_marks :
+               g_hash_table_lookup (buffer->priv->source_marks, category);
 }
 
 GtkSourceMark *
@@ -2007,25 +1856,21 @@ _gtk_source_buffer_source_mark_next (GtkSourceBuffer *buffer,
                                     GtkSourceMark   *mark,
                                     const gchar     *category)
 {
-       gint idx;
+       GtkSourceMarksSequence *seq;
+       GtkTextMark *next_mark;
 
        g_return_val_if_fail (GTK_SOURCE_IS_BUFFER (buffer), NULL);
 
-       idx = get_mark_index (buffer, mark);
+       seq = get_marks_sequence (buffer, category);
 
-       while ((guint) ++idx < buffer->priv->source_marks->len)
+       if (seq == NULL)
        {
-               GtkSourceMark *ret;
-
-               ret = g_array_index (buffer->priv->source_marks, GtkSourceMark *, idx);
-               if (category == NULL ||
-                   0 == strcmp (category, gtk_source_mark_get_category (ret)))
-               {
-                       return ret;
-               }
+               return NULL;
        }
 
-       return NULL;
+       next_mark = _gtk_source_marks_sequence_next (seq, GTK_TEXT_MARK (mark));
+
+       return next_mark == NULL ? NULL : GTK_SOURCE_MARK (next_mark);
 }
 
 GtkSourceMark *
@@ -2033,25 +1878,21 @@ _gtk_source_buffer_source_mark_prev (GtkSourceBuffer *buffer,
                                     GtkSourceMark   *mark,
                                     const gchar     *category)
 {
-       gint idx;
+       GtkSourceMarksSequence *seq;
+       GtkTextMark *prev_mark;
 
        g_return_val_if_fail (GTK_SOURCE_IS_BUFFER (buffer), NULL);
 
-       idx = get_mark_index (buffer, mark);
+       seq = get_marks_sequence (buffer, category);
 
-       while (--idx >= 0)
+       if (seq == NULL)
        {
-               GtkSourceMark *ret;
-
-               ret = g_array_index (buffer->priv->source_marks, GtkSourceMark *, idx);
-               if (category == NULL ||
-                   0 == strcmp (category, gtk_source_mark_get_category (ret)))
-               {
-                       return ret;
-               }
+               return NULL;
        }
 
-       return NULL;
+       prev_mark = _gtk_source_marks_sequence_prev (seq, GTK_TEXT_MARK (mark));
+
+       return prev_mark == NULL ? NULL : GTK_SOURCE_MARK (prev_mark);
 }
 
 /**
@@ -2073,39 +1914,19 @@ gtk_source_buffer_forward_iter_to_source_mark (GtkSourceBuffer *buffer,
                                               GtkTextIter     *iter,
                                               const gchar     *category)
 {
-       GtkTextIter i;
-       gint idx;
+       GtkSourceMarksSequence *seq;
 
        g_return_val_if_fail (GTK_SOURCE_IS_BUFFER (buffer), FALSE);
        g_return_val_if_fail (iter != NULL, FALSE);
 
-       i = *iter;
-
-       idx = source_mark_bsearch (buffer, &i, FALSE);
-       if (idx < 0)
-               return FALSE;
+       seq = get_marks_sequence (buffer, category);
 
-       while ((guint) idx < buffer->priv->source_marks->len)
+       if (seq == NULL)
        {
-               GtkSourceMark *mark;
-
-               mark = g_array_index (buffer->priv->source_marks, GtkSourceMark *, idx);
-               if (category == NULL ||
-                   0 == strcmp (category, gtk_source_mark_get_category (mark)))
-               {
-                       /* update the iter */
-                       gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer), &i, GTK_TEXT_MARK (mark));
-                       if (gtk_text_iter_compare (&i, iter) > 0)
-                       {
-                               *iter = i;
-                               return TRUE;
-                       }
-               }
-
-               ++idx;
+               return FALSE;
        }
 
-       return FALSE;
+       return _gtk_source_marks_sequence_forward_iter (seq, iter);
 }
 
 /**
@@ -2127,39 +1948,19 @@ gtk_source_buffer_backward_iter_to_source_mark (GtkSourceBuffer *buffer,
                                                GtkTextIter     *iter,
                                                const gchar     *category)
 {
-       GtkTextIter i;
-       gint idx;
+       GtkSourceMarksSequence *seq;
 
        g_return_val_if_fail (GTK_SOURCE_IS_BUFFER (buffer), FALSE);
        g_return_val_if_fail (iter != NULL, FALSE);
 
-       i = *iter;
-
-       idx = source_mark_bsearch (buffer, &i, TRUE);
-       if (idx < 0)
-               return FALSE;
+       seq = get_marks_sequence (buffer, category);
 
-       while (idx >= 0)
+       if (seq == NULL)
        {
-               GtkSourceMark *mark;
-
-               mark = g_array_index (buffer->priv->source_marks, GtkSourceMark *, idx);
-               if (category == NULL ||
-                   0 == strcmp (category, gtk_source_mark_get_category (mark)))
-               {
-                       /* update the iter */
-                       gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer), &i, GTK_TEXT_MARK (mark));
-                       if (gtk_text_iter_compare (&i, iter) < 0)
-                       {
-                               *iter = i;
-                               return TRUE;
-                       }
-               }
-
-               --idx;
+               return FALSE;
        }
 
-       return FALSE;
+       return _gtk_source_marks_sequence_backward_iter (seq, iter);
 }
 
 /**
@@ -2181,34 +1982,19 @@ gtk_source_buffer_get_source_marks_at_iter (GtkSourceBuffer *buffer,
                                            GtkTextIter     *iter,
                                            const gchar     *category)
 {
-       GSList *marks, *l, *res;
+       GtkSourceMarksSequence *seq;
 
+       g_return_val_if_fail (GTK_SOURCE_IS_BUFFER (buffer), NULL);
        g_return_val_if_fail (iter != NULL, NULL);
 
-       if (buffer->priv->source_marks->len == 0)
-               return NULL;
-
-       res = NULL;
-       marks = gtk_text_iter_get_marks (iter);
+       seq = get_marks_sequence (buffer, category);
 
-       for (l = marks; l != NULL; l = l->next)
+       if (seq == NULL)
        {
-               GtkSourceMark *mark;
-
-               if (!GTK_SOURCE_IS_MARK (l->data))
-                       continue;
-
-               mark = GTK_SOURCE_MARK (l->data);
-               if (category == NULL ||
-                   0 == strcmp (category, gtk_source_mark_get_category (mark)))
-               {
-                       res = g_slist_prepend (res, l->data);
-               }
+               return NULL;
        }
 
-       g_slist_free (marks);
-
-       return g_slist_reverse (res);
+       return _gtk_source_marks_sequence_get_marks_at_iter (seq, iter);
 }
 
 /**
@@ -2230,42 +2016,31 @@ gtk_source_buffer_get_source_marks_at_line (GtkSourceBuffer *buffer,
                                            gint             line,
                                            const gchar     *category)
 {
-       GtkTextIter iter;
-       GSList *res;
+       GtkSourceMarksSequence *seq;
+       GtkTextIter start;
+       GtkTextIter end;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_BUFFER (buffer), NULL);
 
-       g_return_val_if_fail (GTK_SOURCE_IS_BUFFER (buffer), NULL);
+       seq = get_marks_sequence (buffer, category);
 
-       if (buffer->priv->source_marks->len == 0)
+       if (seq == NULL)
+       {
                return NULL;
+       }
 
        gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer),
-                                         &iter, line);
+                                         &start,
+                                         line);
 
-       res = gtk_source_buffer_get_source_marks_at_iter (buffer,
-                                                         &iter,
-                                                         category);
+       end = start;
 
-       while (gtk_source_buffer_forward_iter_to_source_mark (buffer,
-                                                             &iter,
-                                                             category))
+       if (!gtk_text_iter_ends_line (&end))
        {
-               if (gtk_text_iter_get_line (&iter) == line)
-               {
-                       GSList *l;
-
-                       l =  gtk_source_buffer_get_source_marks_at_iter (buffer,
-                                                                        &iter,
-                                                                        category);
-
-                       res = g_slist_concat (res, l);
-               }
-               else
-               {
-                       break;
-               }
+               gtk_text_iter_forward_to_line_end (&end);
        }
 
-       return res;
+       return _gtk_source_marks_sequence_get_marks_in_range (seq, &start, &end);
 }
 
 /**
@@ -2286,7 +2061,7 @@ gtk_source_buffer_remove_source_marks (GtkSourceBuffer   *buffer,
                                       const GtkTextIter *end,
                                       const gchar       *category)
 {
-       GtkTextIter iter;
+       GtkSourceMarksSequence *seq;
        GSList *list;
        GSList *l;
 
@@ -2294,40 +2069,23 @@ gtk_source_buffer_remove_source_marks (GtkSourceBuffer   *buffer,
        g_return_if_fail (start != NULL);
        g_return_if_fail (end != NULL);
 
-       iter = *start;
+       seq = get_marks_sequence (buffer, category);
 
-       list = gtk_source_buffer_get_source_marks_at_iter (buffer,
-                                                          &iter,
-                                                          category);
-
-       while (gtk_source_buffer_forward_iter_to_source_mark (buffer,
-                                                             &iter,
-                                                             category))
+       if (seq == NULL)
        {
-               if (gtk_text_iter_compare (&iter, end) <= 0)
-               {
-                       l =  gtk_source_buffer_get_source_marks_at_iter (buffer,
-                                                                        &iter,
-                                                                        category);
-
-                       list = g_slist_concat (list, l);
-               }
-               else
-               {
-                       break;
-               }
+               return;
        }
 
+       list = _gtk_source_marks_sequence_get_marks_in_range (seq, start, end);
+
        for (l = list; l != NULL; l = l->next)
        {
-               gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (buffer),
-                                            GTK_TEXT_MARK (l->data));
+               gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (buffer), l->data);
        }
 
        g_slist_free (list);
 }
 
-
 /**
  * gtk_source_buffer_iter_has_context_class:
  * @buffer: a #GtkSourceBuffer.
diff --git a/gtksourceview/gtksourcemark.c b/gtksourceview/gtksourcemark.c
index 85a7a2f..061f9eb 100644
--- a/gtksourceview/gtksourcemark.c
+++ b/gtksourceview/gtksourcemark.c
@@ -221,11 +221,15 @@ gtk_source_mark_next (GtkSourceMark *mark,
        g_return_val_if_fail (GTK_SOURCE_IS_MARK (mark), NULL);
 
        buffer = gtk_text_mark_get_buffer (GTK_TEXT_MARK (mark));
-       if (buffer != NULL)
-               return _gtk_source_buffer_source_mark_next (GTK_SOURCE_BUFFER (buffer),
-                                                           mark, category);
-       else
+
+       if (buffer == NULL)
+       {
                return NULL;
+       }
+
+       return _gtk_source_buffer_source_mark_next (GTK_SOURCE_BUFFER (buffer),
+                                                   mark,
+                                                   category);
 }
 
 /**
@@ -251,10 +255,14 @@ gtk_source_mark_prev (GtkSourceMark *mark,
        g_return_val_if_fail (GTK_SOURCE_IS_MARK (mark), NULL);
 
        buffer = gtk_text_mark_get_buffer (GTK_TEXT_MARK (mark));
-       if (buffer != NULL)
-               return _gtk_source_buffer_source_mark_prev (GTK_SOURCE_BUFFER (buffer),
-                                                           mark, category);
-       else
+
+       if (buffer == NULL)
+       {
                return NULL;
+       }
+
+       return _gtk_source_buffer_source_mark_prev (GTK_SOURCE_BUFFER (buffer),
+                                                   mark,
+                                                   category);
 }
 
diff --git a/gtksourceview/gtksourcemarkssequence.c b/gtksourceview/gtksourcemarkssequence.c
new file mode 100644
index 0000000..bfbd35f
--- /dev/null
+++ b/gtksourceview/gtksourcemarkssequence.c
@@ -0,0 +1,569 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */
+/* gtksourcemarkssequence.c
+ * This file is part of GtkSourceView
+ *
+ * Copyright (C) 2013 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * GtkSourceView 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 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView 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 Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "gtksourcemarkssequence.h"
+
+/* An object for storing GtkTextMarks. The text marks are sorted internally with
+ * a GSequence. Going to the previous or next text mark has a O(1) complexity.
+ * Finding a text mark in the neighborhood of a text iter has a O(log n)
+ * complexity, with 'n' the number of marks in the sequence.
+ *
+ * The GSequenceIter associated to a text mark is inserted into the text mark,
+ * with g_object_set_qdata(). So the text mark knows its position in the
+ * GSequence. This allows to use normal GtkTextMarks in the API, instead of
+ * using a subclass or a custom iter.
+ *
+ * The MarksSequence has a weak reference to the text buffer.
+ */
+
+enum
+{
+       PROP_0,
+       PROP_BUFFER,
+};
+
+struct _GtkSourceMarksSequencePrivate
+{
+       GtkTextBuffer *buffer;
+       GSequence *seq;
+
+       /* The quark used for storing the GSequenceIter into the text mark, with
+        * g_object_set_qdata().
+        */
+       GQuark quark;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GtkSourceMarksSequence, _gtk_source_marks_sequence, G_TYPE_OBJECT)
+
+static void
+remove_qdata (GtkTextMark            *mark,
+             GtkSourceMarksSequence *seq)
+{
+       g_object_set_qdata (G_OBJECT (mark),
+                           seq->priv->quark,
+                           NULL);
+}
+
+static void
+free_sequence (GtkSourceMarksSequence *seq)
+{
+       if (seq->priv->seq != NULL)
+       {
+               g_sequence_foreach (seq->priv->seq,
+                                   (GFunc)remove_qdata,
+                                   seq);
+
+               g_sequence_free (seq->priv->seq);
+               seq->priv->seq = NULL;
+       }
+}
+
+static gint
+compare_marks (GtkTextMark *mark1,
+              GtkTextMark *mark2)
+{
+       GtkTextBuffer *buffer;
+       GtkTextIter iter1;
+       GtkTextIter iter2;
+
+       g_assert (GTK_IS_TEXT_MARK (mark1));
+       g_assert (GTK_IS_TEXT_MARK (mark2));
+
+       buffer = gtk_text_mark_get_buffer (mark1);
+
+       g_assert (buffer == gtk_text_mark_get_buffer (mark2));
+
+       gtk_text_buffer_get_iter_at_mark (buffer, &iter1, mark1);
+       gtk_text_buffer_get_iter_at_mark (buffer, &iter2, mark2);
+
+       return gtk_text_iter_compare (&iter1, &iter2);
+}
+
+static void
+mark_set_cb (GtkTextBuffer          *buffer,
+            GtkTextIter            *location,
+            GtkTextMark            *mark,
+            GtkSourceMarksSequence *seq)
+{
+       GSequenceIter *seq_iter;
+
+       seq_iter = g_object_get_qdata (G_OBJECT (mark), seq->priv->quark);
+
+       if (seq_iter != NULL)
+       {
+               g_sequence_sort_changed (seq_iter,
+                                        (GCompareDataFunc)compare_marks,
+                                        NULL);
+       }
+}
+
+static void
+mark_deleted_cb (GtkTextBuffer          *buffer,
+                GtkTextMark            *mark,
+                GtkSourceMarksSequence *seq)
+{
+       _gtk_source_marks_sequence_remove (seq, mark);
+}
+
+static void
+set_buffer (GtkSourceMarksSequence *seq,
+           GtkTextBuffer          *buffer)
+{
+       g_assert (seq->priv->buffer == NULL);
+
+       seq->priv->buffer = buffer;
+
+       g_object_add_weak_pointer (G_OBJECT (buffer),
+                                  (gpointer *)&seq->priv->buffer);
+
+       g_signal_connect_object (buffer,
+                                "mark-set",
+                                G_CALLBACK (mark_set_cb),
+                                seq,
+                                0);
+
+       g_signal_connect_object (buffer,
+                                "mark-deleted",
+                                G_CALLBACK (mark_deleted_cb),
+                                seq,
+                                0);
+}
+
+static void
+_gtk_source_marks_sequence_dispose (GObject *object)
+{
+       GtkSourceMarksSequence *seq = GTK_SOURCE_MARKS_SEQUENCE (object);
+
+       if (seq->priv->buffer != NULL)
+       {
+               g_object_remove_weak_pointer (G_OBJECT (seq->priv->buffer),
+                                             (gpointer *)&seq->priv->buffer);
+
+               seq->priv->buffer = NULL;
+       }
+
+       free_sequence (seq);
+
+       G_OBJECT_CLASS (_gtk_source_marks_sequence_parent_class)->dispose (object);
+}
+
+static void
+_gtk_source_marks_sequence_get_property (GObject    *object,
+                                        guint       prop_id,
+                                        GValue     *value,
+                                        GParamSpec *pspec)
+{
+       GtkSourceMarksSequence *seq;
+
+       g_return_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (object));
+
+       seq = GTK_SOURCE_MARKS_SEQUENCE (object);
+
+       switch (prop_id)
+       {
+               case PROP_BUFFER:
+                       g_value_set_object (value, seq->priv->buffer);
+                       break;
+
+               default:
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                       break;
+       }
+}
+
+static void
+_gtk_source_marks_sequence_set_property (GObject      *object,
+                                        guint         prop_id,
+                                        const GValue *value,
+                                        GParamSpec   *pspec)
+{
+       GtkSourceMarksSequence *seq;
+
+       g_return_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (object));
+
+       seq = GTK_SOURCE_MARKS_SEQUENCE (object);
+
+       switch (prop_id)
+       {
+               case PROP_BUFFER:
+                       set_buffer (seq, g_value_get_object (value));
+                       break;
+
+               default:
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                       break;
+       }
+}
+
+static void
+_gtk_source_marks_sequence_class_init (GtkSourceMarksSequenceClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       object_class->dispose = _gtk_source_marks_sequence_dispose;
+       object_class->get_property = _gtk_source_marks_sequence_get_property;
+       object_class->set_property = _gtk_source_marks_sequence_set_property;
+
+       g_object_class_install_property (object_class,
+                                        PROP_BUFFER,
+                                        g_param_spec_object ("buffer",
+                                                             "Buffer",
+                                                             "The text buffer",
+                                                             GTK_TYPE_TEXT_BUFFER,
+                                                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+_gtk_source_marks_sequence_init (GtkSourceMarksSequence *seq)
+{
+       gchar *unique_str;
+
+       seq->priv = _gtk_source_marks_sequence_get_instance_private (seq);
+
+       seq->priv->seq = g_sequence_new ((GDestroyNotify)g_object_unref);
+
+       unique_str = g_strdup_printf ("gtk-source-marks-sequence-%p", seq);
+       seq->priv->quark = g_quark_from_string (unique_str);
+       g_free (unique_str);
+}
+
+GtkSourceMarksSequence *
+_gtk_source_marks_sequence_new (GtkTextBuffer *buffer)
+{
+       g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
+
+       return g_object_new (GTK_SOURCE_TYPE_MARKS_SEQUENCE,
+                            "buffer", buffer,
+                            NULL);
+}
+
+gboolean
+_gtk_source_marks_sequence_is_empty (GtkSourceMarksSequence *seq)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (seq), TRUE);
+
+       return g_sequence_get_length (seq->priv->seq) == 0;
+}
+
+void
+_gtk_source_marks_sequence_add (GtkSourceMarksSequence *seq,
+                               GtkTextMark            *mark)
+{
+       GSequenceIter *seq_iter;
+
+       g_return_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (seq));
+       g_return_if_fail (GTK_IS_TEXT_MARK (mark));
+       g_return_if_fail (gtk_text_mark_get_buffer (mark) == seq->priv->buffer);
+
+       seq_iter = g_object_get_qdata (G_OBJECT (mark), seq->priv->quark);
+
+       if (seq_iter != NULL)
+       {
+               /* The mark is already added. */
+               return;
+       }
+
+       seq_iter = g_sequence_insert_sorted (seq->priv->seq,
+                                            mark,
+                                            (GCompareDataFunc)compare_marks,
+                                            NULL);
+
+       g_object_ref (mark);
+       g_object_set_qdata (G_OBJECT (mark),
+                           seq->priv->quark,
+                           seq_iter);
+}
+
+void
+_gtk_source_marks_sequence_remove (GtkSourceMarksSequence *seq,
+                                  GtkTextMark            *mark)
+{
+       GSequenceIter *seq_iter;
+
+       g_return_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (seq));
+       g_return_if_fail (GTK_IS_TEXT_MARK (mark));
+
+       seq_iter = g_object_get_qdata (G_OBJECT (mark), seq->priv->quark);
+
+       if (seq_iter != NULL)
+       {
+               g_object_set_qdata (G_OBJECT (mark), seq->priv->quark, NULL);
+               g_sequence_remove (seq_iter);
+       }
+}
+
+GtkTextMark *
+_gtk_source_marks_sequence_next (GtkSourceMarksSequence *seq,
+                                GtkTextMark            *mark)
+{
+       GSequenceIter *seq_iter;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (seq), NULL);
+       g_return_val_if_fail (GTK_IS_TEXT_MARK (mark), NULL);
+       g_return_val_if_fail (gtk_text_mark_get_buffer (mark) == seq->priv->buffer, NULL);
+
+       seq_iter = g_object_get_qdata (G_OBJECT (mark), seq->priv->quark);
+
+       g_return_val_if_fail (seq_iter != NULL, NULL);
+
+       seq_iter = g_sequence_iter_next (seq_iter);
+
+       return g_sequence_iter_is_end (seq_iter) ? NULL : g_sequence_get (seq_iter);
+}
+
+GtkTextMark *
+_gtk_source_marks_sequence_prev (GtkSourceMarksSequence *seq,
+                                GtkTextMark            *mark)
+{
+       GSequenceIter *seq_iter;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (seq), NULL);
+       g_return_val_if_fail (GTK_IS_TEXT_MARK (mark), NULL);
+       g_return_val_if_fail (gtk_text_mark_get_buffer (mark) == seq->priv->buffer, NULL);
+
+       seq_iter = g_object_get_qdata (G_OBJECT (mark), seq->priv->quark);
+
+       g_return_val_if_fail (seq_iter != NULL, NULL);
+
+       if (g_sequence_iter_is_begin (seq_iter))
+       {
+               return NULL;
+       }
+
+       seq_iter = g_sequence_iter_prev (seq_iter);
+
+       return g_sequence_get (seq_iter);
+}
+
+/* Moves @iter forward to the next position where there is at least one mark.
+ * Returns %TRUE if @iter was moved.
+ */
+gboolean
+_gtk_source_marks_sequence_forward_iter (GtkSourceMarksSequence *seq,
+                                        GtkTextIter            *iter)
+{
+       GtkTextMark *mark;
+       GSequenceIter *seq_iter;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (seq), FALSE);
+       g_return_val_if_fail (iter != NULL, FALSE);
+       g_return_val_if_fail (gtk_text_iter_get_buffer (iter) == seq->priv->buffer, FALSE);
+
+       mark = gtk_text_buffer_create_mark (seq->priv->buffer,
+                                           NULL,
+                                           iter,
+                                           TRUE);
+
+       seq_iter = g_sequence_search (seq->priv->seq,
+                                     mark,
+                                     (GCompareDataFunc)compare_marks,
+                                     NULL);
+
+       gtk_text_buffer_delete_mark (seq->priv->buffer, mark);
+
+       while (!g_sequence_iter_is_end (seq_iter))
+       {
+               GtkTextMark *cur_mark = g_sequence_get (seq_iter);
+               GtkTextIter cur_iter;
+
+               gtk_text_buffer_get_iter_at_mark (seq->priv->buffer, &cur_iter, cur_mark);
+
+               if (gtk_text_iter_compare (iter, &cur_iter) < 0)
+               {
+                       *iter = cur_iter;
+                       return TRUE;
+               }
+
+               seq_iter = g_sequence_iter_next (seq_iter);
+       }
+
+       return FALSE;
+}
+
+/* Moves @iter backward to the previous position where there is at least one
+ * mark. Returns %TRUE if @iter was moved.
+ */
+gboolean
+_gtk_source_marks_sequence_backward_iter (GtkSourceMarksSequence *seq,
+                                         GtkTextIter            *iter)
+{
+       GtkTextMark *mark;
+       GSequenceIter *seq_iter;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (seq), FALSE);
+       g_return_val_if_fail (iter != NULL, FALSE);
+       g_return_val_if_fail (gtk_text_iter_get_buffer (iter) == seq->priv->buffer, FALSE);
+
+       mark = gtk_text_buffer_create_mark (seq->priv->buffer,
+                                           NULL,
+                                           iter,
+                                           TRUE);
+
+       seq_iter = g_sequence_search (seq->priv->seq,
+                                     mark,
+                                     (GCompareDataFunc)compare_marks,
+                                     NULL);
+
+       gtk_text_buffer_delete_mark (seq->priv->buffer, mark);
+
+       if (g_sequence_iter_is_end (seq_iter))
+       {
+               seq_iter = g_sequence_iter_prev (seq_iter);
+       }
+
+       if (g_sequence_iter_is_end (seq_iter))
+       {
+               /* The sequence is empty. */
+               return FALSE;
+       }
+
+       while (TRUE)
+       {
+               GtkTextMark *cur_mark;
+               GtkTextIter cur_iter;
+
+               cur_mark = g_sequence_get (seq_iter);
+
+               gtk_text_buffer_get_iter_at_mark (seq->priv->buffer, &cur_iter, cur_mark);
+
+               if (gtk_text_iter_compare (&cur_iter, iter) < 0)
+               {
+                       *iter = cur_iter;
+                       return TRUE;
+               }
+
+               if (g_sequence_iter_is_begin (seq_iter))
+               {
+                       break;
+               }
+
+               seq_iter = g_sequence_iter_prev (seq_iter);
+       }
+
+       return FALSE;
+}
+
+GSList *
+_gtk_source_marks_sequence_get_marks_in_range (GtkSourceMarksSequence *seq,
+                                              const GtkTextIter      *iter1,
+                                              const GtkTextIter      *iter2)
+{
+       GtkTextIter start;
+       GtkTextIter end;
+       GtkTextMark *mark_start;
+       GSequenceIter *seq_iter;
+       GSequenceIter *first_seq_iter = NULL;
+       GSList *ret = NULL;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_MARKS_SEQUENCE (seq), NULL);
+       g_return_val_if_fail (iter1 != NULL, NULL);
+       g_return_val_if_fail (iter2 != NULL, NULL);
+       g_return_val_if_fail (gtk_text_iter_get_buffer (iter1) == seq->priv->buffer, NULL);
+       g_return_val_if_fail (gtk_text_iter_get_buffer (iter2) == seq->priv->buffer, NULL);
+
+       start = *iter1;
+       end = *iter2;
+
+       gtk_text_iter_order (&start, &end);
+
+       mark_start = gtk_text_buffer_create_mark (seq->priv->buffer,
+                                                 NULL,
+                                                 &start,
+                                                 TRUE);
+
+       seq_iter = g_sequence_search (seq->priv->seq,
+                                     mark_start,
+                                     (GCompareDataFunc)compare_marks,
+                                     NULL);
+
+       gtk_text_buffer_delete_mark (seq->priv->buffer, mark_start);
+
+       if (g_sequence_iter_is_end (seq_iter))
+       {
+               seq_iter = g_sequence_iter_prev (seq_iter);
+       }
+
+       if (g_sequence_iter_is_end (seq_iter))
+       {
+               /* The sequence is empty. */
+               return NULL;
+       }
+
+       /* Find the first mark */
+
+       while (TRUE)
+       {
+               GtkTextMark *cur_mark;
+               GtkTextIter cur_iter;
+
+               cur_mark = g_sequence_get (seq_iter);
+               gtk_text_buffer_get_iter_at_mark (seq->priv->buffer, &cur_iter, cur_mark);
+
+               if (gtk_text_iter_compare (&cur_iter, &start) < 0)
+               {
+                       break;
+               }
+
+               first_seq_iter = seq_iter;
+
+               if (g_sequence_iter_is_begin (seq_iter))
+               {
+                       break;
+               }
+
+               seq_iter = g_sequence_iter_prev (seq_iter);
+       }
+
+       if (first_seq_iter == NULL)
+       {
+               /* The last mark in the sequence is before @start. */
+               return NULL;
+       }
+
+       /* Go forward until @end to fill the list of marks */
+
+       for (seq_iter = first_seq_iter;
+            !g_sequence_iter_is_end (seq_iter);
+            seq_iter = g_sequence_iter_next (seq_iter))
+       {
+               GtkTextMark *cur_mark;
+               GtkTextIter cur_iter;
+
+               cur_mark = g_sequence_get (seq_iter);
+               gtk_text_buffer_get_iter_at_mark (seq->priv->buffer, &cur_iter, cur_mark);
+
+               if (gtk_text_iter_compare (&end, &cur_iter) < 0)
+               {
+                       break;
+               }
+
+               ret = g_slist_prepend (ret, cur_mark);
+       }
+
+       return ret;
+}
+
+GSList *
+_gtk_source_marks_sequence_get_marks_at_iter (GtkSourceMarksSequence *seq,
+                                             const GtkTextIter      *iter)
+{
+       return _gtk_source_marks_sequence_get_marks_in_range (seq, iter, iter);
+}
diff --git a/gtksourceview/gtksourcemarkssequence.h b/gtksourceview/gtksourcemarkssequence.h
new file mode 100644
index 0000000..40cfb70
--- /dev/null
+++ b/gtksourceview/gtksourcemarkssequence.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */
+/* gtksourcemarkssequence.h
+ * This file is part of GtkSourceView
+ *
+ * Copyright (C) 2013 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * GtkSourceView 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 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView 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 Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GTK_SOURCE_MARKS_SEQUENCE_H__
+#define __GTK_SOURCE_MARKS_SEQUENCE_H__
+
+#include <gtk/gtk.h>
+#include "gtksourcetypes-private.h"
+
+G_BEGIN_DECLS
+
+#define GTK_SOURCE_TYPE_MARKS_SEQUENCE             (_gtk_source_marks_sequence_get_type ())
+#define GTK_SOURCE_MARKS_SEQUENCE(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GTK_SOURCE_TYPE_MARKS_SEQUENCE, GtkSourceMarksSequence))
+#define GTK_SOURCE_MARKS_SEQUENCE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), 
GTK_SOURCE_TYPE_MARKS_SEQUENCE, GtkSourceMarksSequenceClass))
+#define GTK_SOURCE_IS_MARKS_SEQUENCE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GTK_SOURCE_TYPE_MARKS_SEQUENCE))
+#define GTK_SOURCE_IS_MARKS_SEQUENCE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GTK_SOURCE_TYPE_MARKS_SEQUENCE))
+#define GTK_SOURCE_MARKS_SEQUENCE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GTK_SOURCE_TYPE_MARKS_SEQUENCE, GtkSourceMarksSequenceClass))
+
+typedef struct _GtkSourceMarksSequenceClass    GtkSourceMarksSequenceClass;
+typedef struct _GtkSourceMarksSequencePrivate  GtkSourceMarksSequencePrivate;
+
+struct _GtkSourceMarksSequence
+{
+       GObject parent;
+
+       GtkSourceMarksSequencePrivate *priv;
+};
+
+struct _GtkSourceMarksSequenceClass
+{
+       GObjectClass parent_class;
+};
+
+G_GNUC_INTERNAL
+GType                   _gtk_source_marks_sequence_get_type            (void) G_GNUC_CONST;
+
+G_GNUC_INTERNAL
+GtkSourceMarksSequence *_gtk_source_marks_sequence_new                 (GtkTextBuffer          *buffer);
+
+G_GNUC_INTERNAL
+gboolean                _gtk_source_marks_sequence_is_empty            (GtkSourceMarksSequence *seq);
+
+G_GNUC_INTERNAL
+void                    _gtk_source_marks_sequence_add                 (GtkSourceMarksSequence *seq,
+                                                                        GtkTextMark            *mark);
+
+G_GNUC_INTERNAL
+void                    _gtk_source_marks_sequence_remove              (GtkSourceMarksSequence *seq,
+                                                                        GtkTextMark            *mark);
+
+G_GNUC_INTERNAL
+GtkTextMark            *_gtk_source_marks_sequence_next                (GtkSourceMarksSequence *seq,
+                                                                        GtkTextMark            *mark);
+
+G_GNUC_INTERNAL
+GtkTextMark            *_gtk_source_marks_sequence_prev                (GtkSourceMarksSequence *seq,
+                                                                        GtkTextMark            *mark);
+
+G_GNUC_INTERNAL
+gboolean                _gtk_source_marks_sequence_forward_iter        (GtkSourceMarksSequence *seq,
+                                                                        GtkTextIter            *iter);
+
+G_GNUC_INTERNAL
+gboolean                _gtk_source_marks_sequence_backward_iter       (GtkSourceMarksSequence *seq,
+                                                                        GtkTextIter            *iter);
+
+G_GNUC_INTERNAL
+GSList                 *_gtk_source_marks_sequence_get_marks_at_iter   (GtkSourceMarksSequence *seq,
+                                                                        const GtkTextIter      *iter);
+
+G_GNUC_INTERNAL
+GSList                 *_gtk_source_marks_sequence_get_marks_in_range  (GtkSourceMarksSequence *seq,
+                                                                        const GtkTextIter      *iter1,
+                                                                        const GtkTextIter      *iter2);
+
+G_END_DECLS
+
+#endif /* __GTK_SOURCE_MARKS_SEQUENCE_H__ */
diff --git a/gtksourceview/gtksourcetypes-private.h b/gtksourceview/gtksourcetypes-private.h
index 6b88c2c..afb1c8a 100644
--- a/gtksourceview/gtksourcetypes-private.h
+++ b/gtksourceview/gtksourcetypes-private.h
@@ -32,6 +32,7 @@ typedef struct _GtkSourceContextEngine                GtkSourceContextEngine;
 typedef struct _GtkSourceEngine                        GtkSourceEngine;
 typedef struct _GtkSourceGutterRendererLines   GtkSourceGutterRendererLines;
 typedef struct _GtkSourceGutterRendererMarks   GtkSourceGutterRendererMarks;
+typedef struct _GtkSourceMarksSequence         GtkSourceMarksSequence;
 typedef struct _GtkSourcePixbufHelper          GtkSourcePixbufHelper;
 typedef struct _GtkSourceRegex                 GtkSourceRegex;
 typedef struct _GtkSourceUndoManagerDefault    GtkSourceUndoManagerDefault;
diff --git a/tests/test-mark.c b/tests/test-mark.c
index 6a30039..e37fd3c 100644
--- a/tests/test-mark.c
+++ b/tests/test-mark.c
@@ -16,33 +16,103 @@ test_create (void)
 }
 
 static void
-test_add_to_buffer (void)
+test_prev_next (void)
 {
-       GtkSourceBuffer *b;
-       GtkSourceMark *m1, *m2, *m3;
-       GtkTextIter i;
-
-       b = gtk_source_buffer_new (NULL);
-       m1 = gtk_source_mark_new ("Mark 1", "test");
-       m2 = gtk_source_mark_new ("Mark 2", "test2");
-       m3 = gtk_source_mark_new ("Mark 3", "test");
-
-       gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (b), &i);
-       gtk_text_buffer_add_mark (GTK_TEXT_BUFFER (b), GTK_TEXT_MARK (m1), &i);
-       gtk_text_buffer_add_mark (GTK_TEXT_BUFFER (b), GTK_TEXT_MARK (m2), &i);
-       gtk_text_buffer_add_mark (GTK_TEXT_BUFFER (b), GTK_TEXT_MARK (m3), &i);
-
-       g_assert (m2 == gtk_source_mark_next (m1, NULL));
-       g_assert (m3 == gtk_source_mark_next (m1, "test"));
-       g_assert (NULL == gtk_source_mark_next (m2, "test2"));
-       g_assert (NULL == gtk_source_mark_next (m3, NULL));
-
-       g_assert (m1 == gtk_source_mark_prev (m2, NULL));
-       g_assert (m1 == gtk_source_mark_prev (m3, "test"));
-       g_assert (NULL == gtk_source_mark_prev (m2, "test2"));
-       g_assert (NULL == gtk_source_mark_prev (m1, NULL));
-
-       g_object_unref (b);
+       GtkSourceBuffer *source_buffer = gtk_source_buffer_new (NULL);
+       GtkTextBuffer *text_buffer = GTK_TEXT_BUFFER (source_buffer);
+       GtkSourceMark *mark1, *mark2, *mark3;
+       GtkTextIter iter;
+
+       gtk_text_buffer_set_text (text_buffer, "text", -1);
+
+       gtk_text_buffer_get_start_iter (text_buffer, &iter);
+       mark1 = gtk_source_buffer_create_source_mark (source_buffer, NULL, "cat1", &iter);
+
+       gtk_text_iter_forward_char (&iter);
+       mark2 = gtk_source_buffer_create_source_mark (source_buffer, NULL, "cat2", &iter);
+
+       gtk_text_iter_forward_char (&iter);
+       mark3 = gtk_source_buffer_create_source_mark (source_buffer, NULL, "cat1", &iter);
+
+       g_assert (mark2 == gtk_source_mark_next (mark1, NULL));
+       g_assert (mark3 == gtk_source_mark_next (mark1, "cat1"));
+       g_assert (NULL == gtk_source_mark_next (mark2, "cat2"));
+       g_assert (NULL == gtk_source_mark_next (mark3, NULL));
+
+       g_assert (mark1 == gtk_source_mark_prev (mark2, NULL));
+       g_assert (mark1 == gtk_source_mark_prev (mark3, "cat1"));
+       g_assert (NULL == gtk_source_mark_prev (mark2, "cat2"));
+       g_assert (NULL == gtk_source_mark_prev (mark1, NULL));
+
+       g_object_unref (source_buffer);
+}
+
+static void
+test_forward_backward_iter (void)
+{
+       GtkSourceBuffer *source_buffer = gtk_source_buffer_new (NULL);
+       GtkTextBuffer *text_buffer = GTK_TEXT_BUFFER (source_buffer);
+       GtkTextIter iter;
+
+       gtk_text_buffer_set_text (text_buffer, "text", -1);
+
+       gtk_text_buffer_get_start_iter (text_buffer, &iter);
+       gtk_source_buffer_create_source_mark (source_buffer, NULL, "cat1", &iter);
+
+       gtk_text_iter_forward_char (&iter);
+       gtk_source_buffer_create_source_mark (source_buffer, NULL, "cat2", &iter);
+
+       gtk_text_iter_forward_char (&iter);
+       gtk_source_buffer_create_source_mark (source_buffer, NULL, "cat1", &iter);
+
+       gtk_text_buffer_get_start_iter (text_buffer, &iter);
+       gtk_source_buffer_forward_iter_to_source_mark (source_buffer, &iter, NULL);
+       g_assert_cmpint (1, ==, gtk_text_iter_get_offset (&iter));
+
+       gtk_text_buffer_get_start_iter (text_buffer, &iter);
+       gtk_source_buffer_forward_iter_to_source_mark (source_buffer, &iter, "cat1");
+       g_assert_cmpint (2, ==, gtk_text_iter_get_offset (&iter));
+
+       gtk_text_buffer_get_end_iter (text_buffer, &iter);
+       gtk_source_buffer_backward_iter_to_source_mark (source_buffer, &iter, NULL);
+       g_assert_cmpint (2, ==, gtk_text_iter_get_offset (&iter));
+
+       gtk_text_buffer_get_end_iter (text_buffer, &iter);
+       gtk_source_buffer_backward_iter_to_source_mark (source_buffer, &iter, "cat2");
+       g_assert_cmpint (1, ==, gtk_text_iter_get_offset (&iter));
+
+       g_object_unref (source_buffer);
+}
+
+static void
+test_get_source_marks_at_iter (void)
+{
+       GtkSourceBuffer *source_buffer = gtk_source_buffer_new (NULL);
+       GtkTextBuffer *text_buffer = GTK_TEXT_BUFFER (source_buffer);
+       GtkSourceMark *mark1, *mark2, *mark3;
+       GtkTextIter iter;
+       GSList *list;
+
+       gtk_text_buffer_set_text (text_buffer, "text", -1);
+
+       gtk_text_buffer_get_start_iter (text_buffer, &iter);
+       mark1 = gtk_source_buffer_create_source_mark (source_buffer, NULL, "cat1", &iter);
+       mark2 = gtk_source_buffer_create_source_mark (source_buffer, NULL, "cat2", &iter);
+       mark3 = gtk_source_buffer_create_source_mark (source_buffer, NULL, "cat1", &iter);
+
+       list = gtk_source_buffer_get_source_marks_at_iter (source_buffer, &iter, "cat1");
+       g_assert_cmpint (2, ==, g_slist_length (list));
+       g_assert (g_slist_find (list, mark1) != NULL);
+       g_assert (g_slist_find (list, mark3) != NULL);
+       g_slist_free (list);
+
+       list = gtk_source_buffer_get_source_marks_at_iter (source_buffer, &iter, NULL);
+       g_assert_cmpint (3, ==, g_slist_length (list));
+       g_assert (g_slist_find (list, mark1) != NULL);
+       g_assert (g_slist_find (list, mark2) != NULL);
+       g_assert (g_slist_find (list, mark3) != NULL);
+
+       g_object_unref (source_buffer);
 }
 
 int
@@ -51,7 +121,9 @@ main (int argc, char** argv)
        gtk_test_init (&argc, &argv);
 
        g_test_add_func ("/Mark/create", test_create);
-       g_test_add_func ("/Mark/add-to-buffer", test_add_to_buffer);
+       g_test_add_func ("/Mark/prev-next", test_prev_next);
+       g_test_add_func ("/Mark/forward-backward-iter", test_forward_backward_iter);
+       g_test_add_func ("/Mark/get-source-marks-at-iter", test_get_source_marks_at_iter);
 
        return g_test_run();
 }



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