[gnome-text-editor] spellcheck: extract iteration into cursor helper



commit a7ca6fc666d36fbf19dc011967b1a9e0f5ae25c3
Author: Christian Hergert <chergert redhat com>
Date:   Mon Jun 28 14:05:52 2021 -0700

    spellcheck: extract iteration into cursor helper

 src/editor-spell-cursor.c              | 135 +++++++++++++++++++++++++++++++++
 src/editor-spell-cursor.h              |  47 ++++++++++++
 src/editor-text-buffer-spell-adapter.c |  63 +++------------
 src/meson.build                        |   1 +
 4 files changed, 192 insertions(+), 54 deletions(-)
---
diff --git a/src/editor-spell-cursor.c b/src/editor-spell-cursor.c
new file mode 100644
index 0000000..e536cfc
--- /dev/null
+++ b/src/editor-spell-cursor.c
@@ -0,0 +1,135 @@
+/* editor-spell-cursor.c
+ *
+ * Copyright 2021 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "config.h"
+
+#include "editor-spell-cursor.h"
+
+static char *
+editor_spell_cursor_word (EditorSpellCursor *cursor)
+{
+  g_assert (cursor != NULL);
+  g_assert (!cursor->exhausted);
+
+  return gtk_text_iter_get_slice (&cursor->word_begin, &cursor->word_end);
+}
+
+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;
+  gtk_text_iter_order (&cursor->begin, &cursor->end);
+  cursor->word_begin = cursor->begin;
+  cursor->word_end = cursor->begin;
+  cursor->exhausted = FALSE;
+}
+
+char *
+editor_spell_cursor_next_word (EditorSpellCursor *cursor)
+{
+  g_return_val_if_fail (cursor != NULL, NULL);
+
+  if (cursor->exhausted)
+    return NULL;
+
+  /* If this is the initial movement, then we need to handle the
+   * case where the first word overlaps the boundary.
+   */
+  if (gtk_text_iter_equal (&cursor->word_begin, &cursor->word_end))
+    {
+      if (!gtk_text_iter_starts_word (&cursor->word_begin))
+        {
+          if (gtk_text_iter_backward_word_start (&cursor->word_begin))
+            {
+              cursor->word_end = cursor->word_begin;
+              gtk_text_iter_forward_word_end (&cursor->word_end);
+            }
+           else
+            {
+              if (!gtk_text_iter_forward_word_end (&cursor->word_end))
+                goto exhausted;
+
+              cursor->word_begin = cursor->word_end;
+              gtk_text_iter_backward_word_start (&cursor->word_begin);
+            }
+        }
+
+      /* If the word position overlaps the region, then we can return it.
+       * Otherwise we need to try to move forward (the regular flow).
+       */
+      if (gtk_text_iter_compare (&cursor->word_end, &cursor->begin) > 0)
+        return editor_spell_cursor_word (cursor);
+    }
+
+  if (!gtk_text_iter_forward_word_end (&cursor->word_end))
+    goto exhausted;
+
+  cursor->word_begin = cursor->word_end;
+  gtk_text_iter_backward_word_start (&cursor->word_begin);
+
+  if (gtk_text_iter_compare (&cursor->word_begin, &cursor->end) <= 0)
+    return editor_spell_cursor_word (cursor);
+
+exhausted:
+  cursor->exhausted = TRUE;
+
+  return NULL;
+}
+
+void
+editor_spell_cursor_tag (EditorSpellCursor *cursor)
+{
+  g_return_if_fail (cursor != NULL);
+
+  gtk_text_buffer_apply_tag (cursor->buffer,
+                             cursor->misspelled_tag,
+                             &cursor->word_begin,
+                             &cursor->word_end);
+}
+
+gboolean
+editor_spell_cursor_contains_tag (EditorSpellCursor *cursor,
+                                  GtkTextTag        *tag)
+{
+  GtkTextIter toggle_iter;
+
+  if (tag == NULL || cursor->exhausted)
+    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;
+
+  return gtk_text_iter_compare (&cursor->word_end, &toggle_iter) > 0;
+}
diff --git a/src/editor-spell-cursor.h b/src/editor-spell-cursor.h
new file mode 100644
index 0000000..230fc13
--- /dev/null
+++ b/src/editor-spell-cursor.h
@@ -0,0 +1,47 @@
+/* editor-spell-cursor.h
+ *
+ * Copyright 2021 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+  GtkTextBuffer *buffer;
+  GtkTextTag    *misspelled_tag;
+  GtkTextIter    begin;
+  GtkTextIter    end;
+  GtkTextIter    word_begin;
+  GtkTextIter    word_end;
+  guint          exhausted : 1;
+} EditorSpellCursor;
+
+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);
+
+G_END_DECLS
diff --git a/src/editor-text-buffer-spell-adapter.c b/src/editor-text-buffer-spell-adapter.c
index 77b9b87..05b5d54 100644
--- a/src/editor-text-buffer-spell-adapter.c
+++ b/src/editor-text-buffer-spell-adapter.c
@@ -23,6 +23,7 @@
 #include "cjhtextregionprivate.h"
 
 #include "editor-spell-checker.h"
+#include "editor-spell-cursor.h"
 #include "editor-text-buffer-spell-adapter.h"
 
 #define UNCHECKED          GSIZE_TO_POINTER(0)
@@ -111,34 +112,16 @@ scan_for_next_unchecked (CjhTextRegion *region,
   return state.found;
 }
 
-static inline gboolean
-word_contains_no_spell_context (EditorTextBufferSpellAdapter *self,
-                                const GtkTextIter            *begin,
-                                const GtkTextIter            *end)
-{
-  GtkTextIter toggle_iter;
-
-  if (self->no_spell_check_tag == NULL)
-    return FALSE;
-
-  if (gtk_text_iter_has_tag (begin, self->no_spell_check_tag))
-    return TRUE;
-
-  toggle_iter = *begin;
-  if (!gtk_text_iter_forward_to_tag_toggle (&toggle_iter, self->no_spell_check_tag))
-    return FALSE;
-
-  return gtk_text_iter_compare (end, &toggle_iter) > 0;
-}
-
 static gboolean
 editor_text_buffer_spell_adapter_update_range (EditorTextBufferSpellAdapter *self,
                                                gsize                         begin_offset,
                                                gsize                         end_offset,
                                                gint64                        deadline)
 {
-  GtkTextIter iter, begin, end;
+  EditorSpellCursor cursor;
+  GtkTextIter begin, end;
   gsize position;
+  char *word;
 
   g_assert (EDITOR_IS_TEXT_BUFFER_SPELL_ADAPTER (self));
 
@@ -150,46 +133,18 @@ editor_text_buffer_spell_adapter_update_range (EditorTextBufferSpellAdapter *sel
 
   gtk_text_buffer_get_iter_at_offset (self->buffer, &begin, position);
   gtk_text_buffer_get_iter_at_offset (self->buffer, &end, end_offset);
-  iter = begin;
-
   gtk_text_buffer_remove_tag (self->buffer, self->tag, &begin, &end);
 
-  if (!gtk_text_iter_starts_word (&iter))
+  editor_spell_cursor_init (&cursor, &begin, &end, self->tag);
+  while ((word = editor_spell_cursor_next_word (&cursor)))
     {
-      if (gtk_text_iter_is_start (&iter))
-        gtk_text_iter_forward_word_end (&iter);
-      gtk_text_iter_backward_word_start (&iter);
-    }
-
-  while (gtk_text_iter_compare (&iter, &end) < 0)
-    {
-      GtkTextIter word_end = iter;
-      char *word;
-
-      if (!gtk_text_iter_forward_word_end (&word_end))
-        break;
-
-      /* Skip until we are out of the no-spell-check region if necessary */
-      if (word_contains_no_spell_context (self, &iter, &word_end))
+      if (!editor_spell_cursor_contains_tag (&cursor, self->no_spell_check_tag))
         {
-          if (!gtk_text_iter_ends_tag (&word_end, self->no_spell_check_tag))
-            gtk_text_iter_forward_to_tag_toggle (&word_end, self->no_spell_check_tag);
-          goto move_next_word;
+          if (!editor_spell_checker_check_word (self->checker, word, -1))
+            editor_spell_cursor_tag (&cursor);
         }
 
-      word = gtk_text_iter_get_slice (&iter, &word_end);
-      if (!editor_spell_checker_check_word (self->checker, word, -1))
-        gtk_text_buffer_apply_tag (self->buffer, self->tag, &iter, &word_end);
       g_free (word);
-
-    move_next_word:
-      if (!gtk_text_iter_forward_word_end (&word_end))
-        break;
-
-      iter = word_end;
-
-      if (!gtk_text_iter_backward_word_start (&iter))
-        break;
     }
 
   _cjh_text_region_replace (self->region, begin_offset, end_offset - begin_offset, CHECKED);
diff --git a/src/meson.build b/src/meson.build
index 96431d4..cc438dc 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -33,6 +33,7 @@ editor_sources = [
   'editor-sidebar-row.c',
   'editor-signal-group.c',
   'editor-spell-checker.c',
+  'editor-spell-cursor.c',
   'editor-spell-language.c',
   'editor-spell-menu.c',
   'editor-spell-provider.c',


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