[gnome-text-editor/wip/chergert/spelling-cursor] wip spell cursor




commit b366c7f2e45e04be6d834ac423a4748f482e94cc
Author: Christian Hergert <chergert redhat com>
Date:   Sat Jul 10 15:53:18 2021 -0700

    wip spell cursor

 src/editor-spell-cursor.c              | 280 +++++++++++++++++++++++++--------
 src/editor-spell-cursor.h              |  31 ++--
 src/editor-text-buffer-spell-adapter.c | 135 +++++++++-------
 src/meson.build                        |   5 +
 src/test-spell-cursor.c                |  56 +++++++
 5 files changed, 360 insertions(+), 147 deletions(-)
---
diff --git a/src/editor-spell-cursor.c b/src/editor-spell-cursor.c
index 0800c87..7d8ef0c 100644
--- a/src/editor-spell-cursor.c
+++ b/src/editor-spell-cursor.c
@@ -20,46 +20,135 @@
 
 #include "config.h"
 
+#include "cjhtextregionprivate.h"
 #include "editor-spell-cursor.h"
 
-static char *
-editor_spell_cursor_word (EditorSpellCursor *cursor)
+#define RUN_UNCHECKED NULL
+
+typedef struct
+{
+  CjhTextRegion *region;
+  GtkTextBuffer *buffer;
+  gssize pos;
+} RegionIter;
+
+typedef struct
 {
-  g_assert (cursor != NULL);
-  g_assert (!cursor->exhausted);
+  GtkTextBuffer *buffer;
+  GtkTextTag *tag;
+  GtkTextIter pos;
+} TagIter;
 
-  return gtk_text_iter_get_slice (&cursor->word_begin, &cursor->word_end);
+typedef struct
+{
+  GtkTextBuffer *buffer;
+  GtkTextIter word_begin;
+  GtkTextIter word_end;
+} WordIter;
+
+struct _EditorSpellCursor
+{
+  RegionIter region;
+  TagIter tag;
+  WordIter word;
+};
+
+static void
+region_iter_init (RegionIter    *self,
+                  GtkTextBuffer *buffer,
+                  CjhTextRegion *region)
+{
+  self->region = region;
+  self->buffer = buffer;
+  self->pos = -1;
 }
 
-void
-editor_spell_cursor_init (EditorSpellCursor *cursor,
-                          const GtkTextIter *begin,
-                          const GtkTextIter *end,
-                          GtkTextTag        *misspelled_tag)
-{
-  g_return_if_fail (cursor != NULL);
-  g_return_if_fail (begin != NULL);
-  g_return_if_fail (end != NULL);
-  g_return_if_fail (gtk_text_iter_get_buffer (begin) == gtk_text_iter_get_buffer (end));
-
-  cursor->buffer = gtk_text_iter_get_buffer (begin);
-  cursor->misspelled_tag = misspelled_tag;
-  cursor->begin = *begin;
-  cursor->end = *end;
-  cursor->exhausted = FALSE;
-
-  gtk_text_iter_order (&cursor->begin, &cursor->end);
-  gtk_text_iter_backward_word_start (&cursor->begin);
-  gtk_text_iter_forward_word_end (&cursor->end);
-
-  cursor->word_begin = cursor->begin;
-  cursor->word_end = cursor->begin;
-
-  /* Clear the tag for the (possibly extended) region */
-  gtk_text_buffer_remove_tag (cursor->buffer,
-                              cursor->misspelled_tag,
-                              &cursor->begin,
-                              &cursor->end);
+static gboolean
+region_iter_next_cb (gsize                   position,
+                     const CjhTextRegionRun *run,
+                     gpointer                user_data)
+{
+  if (run->data == RUN_UNCHECKED)
+    {
+      gsize *pos = user_data;
+      *pos = position;
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+region_iter_next (RegionIter  *self,
+                  GtkTextIter *iter)
+{
+  gsize pos;
+
+  if (self->pos >= (gssize)_cjh_text_region_get_length (self->region))
+    {
+      gtk_text_buffer_get_end_iter (self->buffer, iter);
+      return FALSE;
+    }
+
+  if (self->pos < 0)
+    pos = 0;
+  else
+    pos = self->pos;
+
+  _cjh_text_region_foreach_in_range (self->region,
+                                     pos,
+                                     _cjh_text_region_get_length (self->region),
+                                     region_iter_next_cb,
+                                     &pos);
+
+  gtk_text_buffer_get_iter_at_offset (self->buffer, iter, pos);
+
+  if (pos != self->pos)
+    {
+      self->pos = pos;
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+region_iter_seek (RegionIter        *self,
+                  const GtkTextIter *iter)
+{
+  self->pos = gtk_text_iter_get_offset (iter) + 1;
+}
+
+static void
+tag_iter_init (TagIter       *self,
+               GtkTextBuffer *buffer,
+               GtkTextTag    *tag)
+{
+  self->buffer = buffer;
+  self->tag = tag;
+  gtk_text_buffer_get_start_iter (buffer, &self->pos);
+}
+
+static gboolean
+tag_iter_next (TagIter     *self,
+               GtkTextIter *pos)
+{
+  if (self->tag && gtk_text_iter_has_tag (&self->pos, self->tag))
+    {
+      /* Should always succeed because we are within the tag */
+      gtk_text_iter_forward_to_tag_toggle (&self->pos, self->tag);
+    }
+
+  *pos = self->pos;
+
+  return TRUE;
+}
+
+static void
+tag_iter_seek (TagIter           *self,
+               const GtkTextIter *iter)
+{
+  self->pos = *iter;
 }
 
 static gboolean
@@ -94,56 +183,111 @@ backward_word_start (GtkTextIter *iter)
   return FALSE;
 }
 
-char *
-editor_spell_cursor_next_word (EditorSpellCursor *cursor)
+static void
+word_iter_init (WordIter      *self,
+                GtkTextBuffer *buffer)
 {
-  g_return_val_if_fail (cursor != NULL, NULL);
-
-  if (cursor->exhausted)
-    return NULL;
+  self->buffer = buffer;
+  gtk_text_buffer_get_start_iter (buffer, &self->word_begin);
+  self->word_end = self->word_begin;
+}
 
-  if (!forward_word_end (&cursor->word_end))
-    goto exhausted;
+static gboolean
+word_iter_next (WordIter    *self,
+                GtkTextIter *word_begin,
+                GtkTextIter *word_end)
+{
+  if (!forward_word_end (&self->word_end))
+    {
+      *word_begin = self->word_end;
+      *word_end = self->word_end;
+      return FALSE;
+    }
+
+  self->word_begin = self->word_end;
+
+  if (!backward_word_start (&self->word_begin))
+    {
+      *word_begin = self->word_end;
+      *word_end = self->word_end;
+      return FALSE;
+    }
+
+  *word_begin = self->word_begin;
+  *word_end = self->word_end;
+
+  return TRUE;
+}
 
-  cursor->word_begin = cursor->word_end;
+static void
+word_iter_seek (WordIter          *self,
+                const GtkTextIter *iter)
+{
+  self->word_begin = *iter;
+  self->word_end = *iter;
+}
 
-  if (!backward_word_start (&cursor->word_begin))
-    goto exhausted;
+EditorSpellCursor *
+editor_spell_cursor_new (GtkTextBuffer *buffer,
+                         CjhTextRegion *region,
+                         GtkTextTag    *no_spell_check_tag)
+{
+  EditorSpellCursor *self;
 
-  return editor_spell_cursor_word (cursor);
+  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
+  g_return_val_if_fail (region != NULL, NULL);
+  g_return_val_if_fail (!no_spell_check_tag || GTK_IS_TEXT_TAG (no_spell_check_tag), NULL);
 
-exhausted:
-  cursor->exhausted = TRUE;
+  self = g_rc_box_new0 (EditorSpellCursor);
+  region_iter_init (&self->region, buffer, region);
+  tag_iter_init (&self->tag, buffer, no_spell_check_tag);
+  word_iter_init (&self->word, buffer);
 
-  return NULL;
+  return self;
 }
 
 void
-editor_spell_cursor_tag (EditorSpellCursor *cursor)
+editor_spell_cursor_free (EditorSpellCursor *self)
 {
-  g_return_if_fail (cursor != NULL);
-
-  gtk_text_buffer_apply_tag (cursor->buffer,
-                             cursor->misspelled_tag,
-                             &cursor->word_begin,
-                             &cursor->word_end);
+  g_rc_box_release (self);
 }
 
 gboolean
-editor_spell_cursor_contains_tag (EditorSpellCursor *cursor,
-                                  GtkTextTag        *tag)
+editor_spell_cursor_next (EditorSpellCursor *self,
+                          GtkTextIter       *word_begin,
+                          GtkTextIter       *word_end)
 {
-  GtkTextIter toggle_iter;
-
-  if (tag == NULL || cursor->exhausted)
+  /* Try to advance skipping any checked region in the buffer */
+  if (!region_iter_next (&self->region, word_end))
+    {
+      *word_begin = *word_end;
+      return FALSE;
+    }
+
+  /* Pass that position to the next iter, so it can skip
+   * past anything that is already checked. Then try to move
+   * forward so that we can skip past regions in the text
+   * buffer that are to be ignored by spellcheck.
+   */
+  tag_iter_seek (&self->tag, word_end);
+  if (!tag_iter_next (&self->tag, word_end))
+    {
+      *word_begin = *word_end;
+      return FALSE;
+    }
+
+  /* Now pass that information to the word iter, so that it can
+   * jump forward to the next word starting from our tag/region
+   * positions.
+   */
+  word_iter_seek (&self->word, word_end);
+  if (!word_iter_next (&self->word, word_begin, word_end))
     return FALSE;
 
-  if (gtk_text_iter_has_tag (&cursor->word_begin, tag))
-    return TRUE;
-
-  toggle_iter = cursor->word_begin;
-  if (!gtk_text_iter_forward_to_tag_toggle (&toggle_iter, tag))
-    return FALSE;
+  /* Now pass our new position to the region so that it will
+   * skip past the word when advancing.
+   */
+  region_iter_seek (&self->region, word_end);
 
-  return gtk_text_iter_compare (&cursor->word_end, &toggle_iter) > 0;
+  return TRUE;
 }
diff --git a/src/editor-spell-cursor.h b/src/editor-spell-cursor.h
index 230fc13..f813199 100644
--- a/src/editor-spell-cursor.h
+++ b/src/editor-spell-cursor.h
@@ -1,4 +1,4 @@
-/* editor-spell-cursor.h
+/* editor-spell-cursor.c
  *
  * Copyright 2021 Christian Hergert <chergert redhat com>
  *
@@ -24,24 +24,17 @@
 
 G_BEGIN_DECLS
 
-typedef struct
-{
-  GtkTextBuffer *buffer;
-  GtkTextTag    *misspelled_tag;
-  GtkTextIter    begin;
-  GtkTextIter    end;
-  GtkTextIter    word_begin;
-  GtkTextIter    word_end;
-  guint          exhausted : 1;
-} EditorSpellCursor;
+typedef struct _EditorSpellCursor EditorSpellCursor;
+typedef struct _CjhTextRegion     CjhTextRegion;
 
-void      editor_spell_cursor_init         (EditorSpellCursor *cursor,
-                                            const GtkTextIter *begin,
-                                            const GtkTextIter *end,
-                                            GtkTextTag        *misspelled_tag);
-char     *editor_spell_cursor_next_word    (EditorSpellCursor *cursor);
-void      editor_spell_cursor_tag          (EditorSpellCursor *cursor);
-gboolean  editor_spell_cursor_contains_tag (EditorSpellCursor *cursor,
-                                            GtkTextTag        *tag);
+EditorSpellCursor *editor_spell_cursor_new  (GtkTextBuffer     *buffer,
+                                             CjhTextRegion     *region,
+                                             GtkTextTag        *no_spell_check_tag);
+void               editor_spell_cursor_free (EditorSpellCursor *cursor);
+gboolean           editor_spell_cursor_next (EditorSpellCursor *cursor,
+                                             GtkTextIter       *word_begin,
+                                             GtkTextIter       *word_end);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (EditorSpellCursor, editor_spell_cursor_free)
 
 G_END_DECLS
diff --git a/src/editor-text-buffer-spell-adapter.c b/src/editor-text-buffer-spell-adapter.c
index 94e05b3..e7bf886 100644
--- a/src/editor-text-buffer-spell-adapter.c
+++ b/src/editor-text-buffer-spell-adapter.c
@@ -28,8 +28,8 @@
 #include "editor-spell-language.h"
 #include "editor-text-buffer-spell-adapter.h"
 
-#define UNCHECKED          GSIZE_TO_POINTER(0)
-#define CHECKED            GSIZE_TO_POINTER(1)
+#define RUN_UNCHECKED      GSIZE_TO_POINTER(0)
+#define RUN_CHECKED        GSIZE_TO_POINTER(1)
 #define UPDATE_DELAY_MSECS 100
 #define UPDATE_QUANTA_USEC (G_USEC_PER_SEC/1000L*2) /* 2 msec */
 
@@ -88,17 +88,25 @@ editor_text_buffer_spell_adapter_new (GtkTextBuffer      *buffer,
                        NULL);
 }
 
+static inline gboolean
+contains_iter (const GtkTextIter *begin,
+               const GtkTextIter *end,
+               const GtkTextIter *iter)
+{
+  return gtk_text_iter_compare (begin, iter) <= 0 &&
+         gtk_text_iter_compare (iter, end) <= 0;
+}
+
 static gboolean
-scan_for_next_unchecked_cb (gsize                   offset,
-                            const CjhTextRegionRun *run,
-                            gpointer                user_data)
+get_unchecked_start_cb (gsize                   offset,
+                        const CjhTextRegionRun *run,
+                        gpointer                user_data)
 {
-  ScanForUnchecked *state = user_data;
+  gsize *pos = user_data;
 
-  if (run->data == UNCHECKED)
+  if (run->data == RUN_UNCHECKED)
     {
-      state->offset = offset;
-      state->found = TRUE;
+      *pos = offset;
       return TRUE;
     }
 
@@ -106,78 +114,83 @@ scan_for_next_unchecked_cb (gsize                   offset,
 }
 
 static gboolean
-scan_for_next_unchecked (CjhTextRegion *region,
-                         gsize          begin,
-                         gsize          end,
-                         gsize         *position)
-{
-  ScanForUnchecked state = {0};
-  _cjh_text_region_foreach_in_range (region, begin, end, scan_for_next_unchecked_cb, &state);
-  *position = state.offset;
-  return state.found;
-}
-
-static inline gboolean
-contains_iter (const GtkTextIter *begin,
-               const GtkTextIter *end,
-               const GtkTextIter *iter)
+get_unchecked_start (CjhTextRegion *region,
+                     GtkTextBuffer *buffer,
+                     GtkTextIter   *iter)
 {
-  return gtk_text_iter_compare (begin, iter) <= 0 &&
-         gtk_text_iter_compare (iter, end) <= 0;
+  gsize pos = G_MAXSIZE;
+  _cjh_text_region_foreach (region, get_unchecked_start_cb, &pos);
+  if (pos == G_MAXSIZE)
+    return FALSE;
+  gtk_text_buffer_get_iter_at_offset (buffer, iter, pos);
+  return TRUE;
 }
 
 static gboolean
 editor_text_buffer_spell_adapter_update_range (EditorTextBufferSpellAdapter *self,
-                                               gsize                         begin_offset,
-                                               gsize                         end_offset,
                                                gint64                        deadline)
 {
-  EditorSpellCursor cursor;
-  GtkTextIter begin, end, insert;
-  gsize position;
+  g_autoptr(EditorSpellCursor) cursor = NULL;
+  GtkTextMark *mark;
+  GtkTextIter insert, word_begin, word_end, last_match, begin;
   gboolean ret = FALSE;
   guint checked = 0;
-  char *word;
 
   g_assert (EDITOR_IS_TEXT_BUFFER_SPELL_ADAPTER (self));
 
-  if (!scan_for_next_unchecked (self->region, begin_offset, end_offset, &position))
-    return FALSE;
-
   /* Ignore while we are loading or saving */
   if (editor_document_get_busy (EDITOR_DOCUMENT (self->buffer)))
     return TRUE;
 
-  gtk_text_buffer_get_iter_at_mark (self->buffer, &insert,
-                                    gtk_text_buffer_get_insert (self->buffer));
-  gtk_text_buffer_get_iter_at_offset (self->buffer, &begin, position);
-  gtk_text_buffer_get_iter_at_offset (self->buffer, &end, end_offset);
-  gtk_text_buffer_remove_tag (self->buffer, self->tag, &begin, &end);
+  cursor = editor_spell_cursor_new (self->buffer, self->region, self->no_spell_check_tag);
+  mark = gtk_text_buffer_get_insert (self->buffer);
+  gtk_text_buffer_get_iter_at_mark (self->buffer, &insert, mark);
 
-  editor_spell_cursor_init (&cursor, &begin, &end, self->tag);
-  while ((word = editor_spell_cursor_next_word (&cursor)))
+  /* Get the first unchecked position so that we can remove the tag
+   * from it up to the first word match.
+   */
+  if (!get_unchecked_start (self->region, self->buffer, &begin))
     {
+      _cjh_text_region_replace (self->region,
+                                0,
+                                _cjh_text_region_get_length (self->region),
+                                RUN_CHECKED);
+      return FALSE;
+    }
+
+  last_match = begin;
+  while (editor_spell_cursor_next (cursor, &word_begin, &word_end))
+    {
+      g_autofree char *word = gtk_text_iter_get_slice (&word_begin, &word_end);
+
+      g_print ("Word: %s\n", word);
+
       checked++;
 
-      if (!editor_spell_cursor_contains_tag (&cursor, self->no_spell_check_tag) &&
-          !contains_iter (&cursor.word_begin, &cursor.word_end, &insert))
+      if (!contains_iter (&word_begin, &word_end, &insert) &&
+          !editor_spell_checker_check_word (self->checker, word, -1))
         {
-          if (!editor_spell_checker_check_word (self->checker, word, -1))
-            editor_spell_cursor_tag (&cursor);
+          gtk_text_buffer_remove_tag (self->buffer, self->tag, &last_match, &word_end);
+          gtk_text_buffer_apply_tag (self->buffer, self->tag, &word_begin, &word_end);
+          last_match = word_end;
         }
 
-      g_free (word);
-
       /* Check deadline every five words */
       if (checked % 5 == 0 && deadline < g_get_monotonic_time ())
         {
-          end_offset = MAX (begin_offset, gtk_text_iter_get_offset (&cursor.word_end));
           ret = TRUE;
           break;
         }
     }
 
-  _cjh_text_region_replace (self->region, begin_offset, end_offset - begin_offset, CHECKED);
+  /* Now remove from the last match to the end position */
+  if (!gtk_text_iter_equal (&word_end, &last_match))
+    gtk_text_buffer_remove_tag (self->buffer, self->tag, &last_match, &word_end);
+
+  _cjh_text_region_replace (self->region,
+                            gtk_text_iter_get_offset (&begin),
+                            gtk_text_iter_get_offset (&word_end) - gtk_text_iter_get_offset (&begin),
+                            RUN_CHECKED);
 
   return ret;
 }
@@ -187,13 +200,11 @@ editor_text_buffer_spell_adapter_update (EditorTextBufferSpellAdapter *self)
 {
   gint64 deadline;
   gboolean has_more;
-  gsize length;
 
   g_assert (EDITOR_IS_TEXT_BUFFER_SPELL_ADAPTER (self));
 
   deadline = g_get_monotonic_time () + UPDATE_QUANTA_USEC;
-  length = _cjh_text_region_get_length (self->region);
-  has_more = editor_text_buffer_spell_adapter_update_range (self, 0, length, deadline);
+  has_more = editor_text_buffer_spell_adapter_update_range (self, deadline);
 
   if (has_more)
     return G_SOURCE_CONTINUE;
@@ -214,6 +225,10 @@ editor_text_buffer_spell_adapter_queue_update (EditorTextBufferSpellAdapter *sel
       return;
     }
 
+  /* TODO: We want an *initial* delay of UPDATE_DELAY_MSECS, but then after
+   *       that we probably want something close to the widgets update
+   *       interval so we make progress each frame.
+   */
   if (self->update_source == 0)
     self->update_source = g_timeout_add_full (G_PRIORITY_LOW,
                                               UPDATE_DELAY_MSECS,
@@ -233,7 +248,7 @@ editor_text_buffer_spell_adapter_invalidate_all (EditorTextBufferSpellAdapter *s
 
   if (length > 0)
     {
-      _cjh_text_region_replace (self->region, 0, length - 1, UNCHECKED);
+      _cjh_text_region_replace (self->region, 0, length - 1, RUN_UNCHECKED);
       editor_text_buffer_spell_adapter_queue_update (self);
     }
 }
@@ -289,7 +304,7 @@ invalidate_tag_region_cb (EditorTextBufferSpellAdapter *self,
       gsize begin_offset = gtk_text_iter_get_offset (begin);
       gsize end_offset = gtk_text_iter_get_offset (end);
 
-      _cjh_text_region_replace (self->region, begin_offset, end_offset - begin_offset, UNCHECKED);
+      _cjh_text_region_replace (self->region, begin_offset, end_offset - begin_offset, RUN_UNCHECKED);
       editor_text_buffer_spell_adapter_queue_update (self);
     }
 }
@@ -313,7 +328,7 @@ editor_text_buffer_spell_adapter_set_buffer (EditorTextBufferSpellAdapter *self,
       offset = gtk_text_iter_get_offset (&begin);
       length = gtk_text_iter_get_offset (&end) - offset;
 
-      _cjh_text_region_insert (self->region, offset, length, UNCHECKED);
+      _cjh_text_region_insert (self->region, offset, length, RUN_UNCHECKED);
 
       self->tag = gtk_text_buffer_create_tag (buffer, NULL,
                                               "underline", PANGO_UNDERLINE_ERROR,
@@ -531,7 +546,7 @@ editor_text_buffer_spell_adapter_set_checker (EditorTextBufferSpellAdapter *self
       if (length > 0)
         {
           _cjh_text_region_remove (self->region, 0, length - 1);
-          _cjh_text_region_insert (self->region, 0, length, UNCHECKED);
+          _cjh_text_region_insert (self->region, 0, length, RUN_UNCHECKED);
           g_assert_cmpint (length, ==, _cjh_text_region_get_length (self->region));
         }
 
@@ -558,7 +573,7 @@ editor_text_buffer_spell_adapter_insert_text (EditorTextBufferSpellAdapter *self
   g_return_if_fail (EDITOR_IS_TEXT_BUFFER_SPELL_ADAPTER (self));
   g_return_if_fail (length > 0);
 
-  _cjh_text_region_insert (self->region, offset, length, UNCHECKED);
+  _cjh_text_region_insert (self->region, offset, length, RUN_UNCHECKED);
 
   editor_text_buffer_spell_adapter_queue_update (self);
 }
@@ -570,10 +585,10 @@ invalidate_surrounding (EditorTextBufferSpellAdapter *self,
   g_assert (EDITOR_IS_TEXT_BUFFER_SPELL_ADAPTER (self));
 
   if (offset)
-    _cjh_text_region_replace (self->region, offset - 1, 1, UNCHECKED);
+    _cjh_text_region_replace (self->region, offset - 1, 1, RUN_UNCHECKED);
 
   if (offset + 1 < _cjh_text_region_get_length (self->region))
-    _cjh_text_region_replace (self->region, offset, 1, UNCHECKED);
+    _cjh_text_region_replace (self->region, offset, 1, RUN_UNCHECKED);
 
   editor_text_buffer_spell_adapter_queue_update (self);
 }
diff --git a/src/meson.build b/src/meson.build
index 8d2e640..0694730 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -91,3 +91,8 @@ editor = executable('gnome-text-editor', editor_sources + editor_enums + [build_
          dependencies: editor_deps,
               install: true,
 )
+
+executable('test-spell-cursor', 'test-spell-cursor.c',
+  dependencies: libgtk_dep,
+  include_directories: [include_directories('..')],
+)
diff --git a/src/test-spell-cursor.c b/src/test-spell-cursor.c
new file mode 100644
index 0000000..be33a24
--- /dev/null
+++ b/src/test-spell-cursor.c
@@ -0,0 +1,56 @@
+#include "editor-spell-cursor.c"
+#include "cjhtextregion.c"
+
+static const char *test_text = "this is a series of words";
+
+static char *
+next_word (EditorSpellCursor *cursor)
+{
+  GtkTextIter begin, end;
+
+  if (editor_spell_cursor_next (cursor, &begin, &end))
+    return gtk_text_iter_get_slice (&begin, &end);
+
+  return NULL;
+}
+
+static void
+test_cursor (void)
+{
+  g_autoptr(GtkTextBuffer) buffer = gtk_text_buffer_new (NULL);
+  CjhTextRegion *region = _cjh_text_region_new (NULL, NULL);
+  g_autoptr(EditorSpellCursor) cursor = editor_spell_cursor_new (buffer, region, NULL);
+  char *word;
+
+  gtk_text_buffer_set_text (buffer, test_text, -1);
+  _cjh_text_region_insert (region, 0, strlen (test_text), NULL);
+
+  word = next_word (cursor);
+  g_assert_cmpstr (word, ==, "this");
+
+  word = next_word (cursor);
+  g_assert_cmpstr (word, ==, "is");
+
+  word = next_word (cursor);
+  g_assert_cmpstr (word, ==, "a");
+
+  word = next_word (cursor);
+  g_assert_cmpstr (word, ==, "series");
+
+  word = next_word (cursor);
+  g_assert_cmpstr (word, ==, "of");
+
+  word = next_word (cursor);
+  g_assert_cmpstr (word, ==, "words");
+
+  _cjh_text_region_free (region);
+}
+
+int
+main (int argc,
+      char *argv[])
+{
+  g_test_init (&argc, &argv, NULL);
+  g_test_add_func ("/Spelling/Cursor/basic", test_cursor);
+  return g_test_run ();
+}


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