[gnome-text-editor] spellcheck: plumb buffer information to spell adapter



commit ad3c4c3e4e55b3bff8a32dc5242b3aa85e46acff
Author: Christian Hergert <chergert redhat com>
Date:   Fri Jun 25 16:23:30 2021 -0700

    spellcheck: plumb buffer information to spell adapter
    
    The goal of the spell adapter is to handle the details of driving the
    spellcheck so that it can live outside of EditorDocument. That also makes
    it easier to migrate or use elsewhere in the future without having to
    extract it from EditorDocument (such as into GtkTextBuffer).

 src/editor-document.c                  |  59 ++++++---
 src/editor-text-buffer-spell-adapter.c | 233 +++++++++++++++++++++++++++++++++
 src/editor-text-buffer-spell-adapter.h |  47 +++++++
 src/meson.build                        |   1 +
 4 files changed, 325 insertions(+), 15 deletions(-)
---
diff --git a/src/editor-document.c b/src/editor-document.c
index 019fbdd..5c88059 100644
--- a/src/editor-document.c
+++ b/src/editor-document.c
@@ -29,6 +29,7 @@
 #include "editor-buffer-monitor-private.h"
 #include "editor-document-private.h"
 #include "editor-spell-checker.h"
+#include "editor-text-buffer-spell-adapter.h"
 #include "editor-session-private.h"
 #include "editor-window.h"
 
@@ -38,23 +39,25 @@
 
 struct _EditorDocument
 {
-  GtkSourceBuffer          parent_instance;
+  GtkSourceBuffer               parent_instance;
 
-  EditorBufferMonitor     *monitor;
-  GtkSourceFile           *file;
-  gchar                   *draft_id;
-  GtkTextTag              *line_spacing_tag;
-  const GtkSourceEncoding *encoding;
-  EditorSpellChecker      *spell_checker;
+  EditorBufferMonitor          *monitor;
+  GtkSourceFile                *file;
+  gchar                        *draft_id;
+  GtkTextTag                   *line_spacing_tag;
+  const GtkSourceEncoding      *encoding;
 
-  GtkSourceNewlineType     newline_type;
-  guint                    busy_count;
-  gdouble                  busy_progress;
+  EditorSpellChecker           *spell_checker;
+  EditorTextBufferSpellAdapter *spell_adapter;
 
-  guint                    readonly : 1;
-  guint                    needs_autosave : 1;
-  guint                    was_restored : 1;
-  guint                    externally_modified : 1;
+  GtkSourceNewlineType          newline_type;
+  guint                         busy_count;
+  gdouble                       busy_progress;
+
+  guint                         readonly : 1;
+  guint                         needs_autosave : 1;
+  guint                         was_restored : 1;
+  guint                         externally_modified : 1;
 };
 
 typedef struct
@@ -193,6 +196,7 @@ editor_document_insert_text (GtkTextBuffer *buffer,
 {
   EditorDocument *self = (EditorDocument *)buffer;
   guint offset;
+  guint length;
 
   g_assert (GTK_IS_TEXT_BUFFER (buffer));
   g_assert (pos != NULL);
@@ -202,6 +206,11 @@ editor_document_insert_text (GtkTextBuffer *buffer,
 
   GTK_TEXT_BUFFER_CLASS (editor_document_parent_class)->insert_text (buffer, pos, new_text, new_text_length);
 
+  length = gtk_text_iter_get_offset (pos) - offset;
+
+  if (length > 0)
+    editor_text_buffer_spell_adapter_insert_text (self->spell_adapter, offset, length);
+
   if (offset < TITLE_MAX_LEN && editor_document_get_file (self) == NULL)
     g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]);
 }
@@ -213,15 +222,21 @@ editor_document_delete_range (GtkTextBuffer *buffer,
 {
   EditorDocument *self = (EditorDocument *)buffer;
   guint offset;
+  guint length;
 
   g_assert (GTK_IS_TEXT_BUFFER (buffer));
   g_assert (start != NULL);
   g_assert (end != NULL);
+  g_assert (gtk_text_iter_compare (start, end) <= 0);
 
   offset = gtk_text_iter_get_offset (start);
+  length = gtk_text_iter_get_offset (end) - offset;
 
   GTK_TEXT_BUFFER_CLASS (editor_document_parent_class)->delete_range (buffer, start, end);
 
+  if (length > 0)
+    editor_text_buffer_spell_adapter_delete_range (self->spell_adapter, offset, length);
+
   if (offset < TITLE_MAX_LEN && editor_document_get_file (self) == NULL)
     g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]);
 }
@@ -294,6 +309,12 @@ editor_document_monitor_notify_changed_cb (EditorDocument      *self,
   _editor_document_set_externally_modified (self, changed);
 }
 
+static void
+on_cursor_moved_cb (EditorDocument *self)
+{
+  editor_text_buffer_spell_adapter_cursor_moved (self->spell_adapter);
+}
+
 static void
 editor_document_constructed (GObject *object)
 {
@@ -316,6 +337,7 @@ editor_document_finalize (GObject *object)
   g_clear_object (&self->monitor);
   g_clear_object (&self->file);
   g_clear_object (&self->spell_checker);
+  g_clear_object (&self->spell_adapter);
   g_clear_pointer (&self->draft_id, g_free);
 
   G_OBJECT_CLASS (editor_document_parent_class)->finalize (object);
@@ -450,6 +472,8 @@ editor_document_init (EditorDocument *self)
   self->file = gtk_source_file_new ();
   self->draft_id = g_uuid_string_random ();
   self->spell_checker = editor_spell_checker_new (NULL, NULL);
+  self->spell_adapter = editor_text_buffer_spell_adapter_new (GTK_TEXT_BUFFER (self),
+                                                              self->spell_checker);
 
   self->monitor = editor_buffer_monitor_new ();
   g_signal_connect_object (self->monitor,
@@ -460,6 +484,8 @@ editor_document_init (EditorDocument *self)
   g_object_bind_property (self->file, "location",
                           self->monitor, "file",
                           G_BINDING_SYNC_CREATE);
+
+  g_signal_connect (self, "cursor-moved", G_CALLBACK (on_cursor_moved_cb), NULL);
 }
 
 EditorDocument *
@@ -1704,5 +1730,8 @@ editor_document_set_spell_checker (EditorDocument     *self,
   g_return_if_fail (!spell_checker || EDITOR_IS_SPELL_CHECKER (spell_checker));
 
   if (g_set_object (&self->spell_checker, spell_checker))
-    g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SPELL_CHECKER]);
+    {
+      editor_text_buffer_spell_adapter_set_checker (self->spell_adapter, spell_checker);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SPELL_CHECKER]);
+    }
 }
diff --git a/src/editor-text-buffer-spell-adapter.c b/src/editor-text-buffer-spell-adapter.c
new file mode 100644
index 0000000..0549e96
--- /dev/null
+++ b/src/editor-text-buffer-spell-adapter.c
@@ -0,0 +1,233 @@
+/* editor-text-buffer-spell-adapter.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 "cjhtextregionprivate.h"
+
+#include "editor-spell-checker.h"
+#include "editor-text-buffer-spell-adapter.h"
+
+#define UNCHECKED GSIZE_TO_POINTER(0)
+#define CHECKED   GSIZE_TO_POINTER(1)
+
+struct _EditorTextBufferSpellAdapter
+{
+  GObject             parent_instance;
+  GtkTextBuffer      *buffer;
+  EditorSpellChecker *checker;
+  CjhTextRegion      *region;
+};
+
+G_DEFINE_TYPE (EditorTextBufferSpellAdapter, editor_text_buffer_spell_adapter, G_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_BUFFER,
+  PROP_CHECKER,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+EditorTextBufferSpellAdapter *
+editor_text_buffer_spell_adapter_new (GtkTextBuffer      *buffer,
+                                      EditorSpellChecker *checker)
+{
+  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
+  g_return_val_if_fail (!checker || EDITOR_IS_SPELL_CHECKER (checker), NULL);
+
+  return g_object_new (EDITOR_TYPE_TEXT_BUFFER_SPELL_ADAPTER,
+                       "buffer", buffer,
+                       "checker", checker,
+                       NULL);
+}
+
+static void
+editor_text_buffer_spell_adapter_set_buffer (EditorTextBufferSpellAdapter *self,
+                                             GtkTextBuffer                *buffer)
+{
+  g_assert (EDITOR_IS_TEXT_BUFFER_SPELL_ADAPTER (self));
+  g_assert (GTK_IS_TEXT_BUFFER (buffer));
+
+  if (g_set_weak_pointer (&self->buffer, buffer))
+    {
+      GtkTextIter begin, end;
+      guint offset;
+      guint length;
+
+      gtk_text_buffer_get_bounds (buffer, &begin, &end);
+
+      offset = gtk_text_iter_get_offset (&begin);
+      length = gtk_text_iter_get_offset (&end) - offset;
+
+      _cjh_text_region_insert (self->region, offset, length, UNCHECKED);
+    }
+}
+
+static void
+editor_text_buffer_spell_adapter_finalize (GObject *object)
+{
+  EditorTextBufferSpellAdapter *self = (EditorTextBufferSpellAdapter *)object;
+
+  g_clear_object (&self->checker);
+  g_clear_weak_pointer (&self->buffer);
+  g_clear_pointer (&self->region, _cjh_text_region_free);
+
+  G_OBJECT_CLASS (editor_text_buffer_spell_adapter_parent_class)->finalize (object);
+}
+
+static void
+editor_text_buffer_spell_adapter_get_property (GObject    *object,
+                                               guint       prop_id,
+                                               GValue     *value,
+                                               GParamSpec *pspec)
+{
+  EditorTextBufferSpellAdapter *self = EDITOR_TEXT_BUFFER_SPELL_ADAPTER (object);
+
+  switch (prop_id)
+    {
+    case PROP_BUFFER:
+      g_value_set_object (value, self->buffer);
+      break;
+
+    case PROP_CHECKER:
+      g_value_set_object (value, editor_text_buffer_spell_adapter_get_checker (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+editor_text_buffer_spell_adapter_set_property (GObject      *object,
+                                               guint         prop_id,
+                                               const GValue *value,
+                                               GParamSpec   *pspec)
+{
+  EditorTextBufferSpellAdapter *self = EDITOR_TEXT_BUFFER_SPELL_ADAPTER (object);
+
+  switch (prop_id)
+    {
+    case PROP_BUFFER:
+      editor_text_buffer_spell_adapter_set_buffer (self, g_value_get_object (value));
+      break;
+
+    case PROP_CHECKER:
+      editor_text_buffer_spell_adapter_set_checker (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+editor_text_buffer_spell_adapter_class_init (EditorTextBufferSpellAdapterClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = editor_text_buffer_spell_adapter_finalize;
+  object_class->get_property = editor_text_buffer_spell_adapter_get_property;
+  object_class->set_property = editor_text_buffer_spell_adapter_set_property;
+
+  properties [PROP_BUFFER] =
+    g_param_spec_object ("buffer",
+                         "Buffer",
+                         "Buffer",
+                         GTK_TYPE_TEXT_BUFFER,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_CHECKER] =
+    g_param_spec_object ("checker",
+                         "Checker",
+                         "Checker",
+                         EDITOR_TYPE_SPELL_CHECKER,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+editor_text_buffer_spell_adapter_init (EditorTextBufferSpellAdapter *self)
+{
+  self->region = _cjh_text_region_new (NULL, NULL);
+}
+
+EditorSpellChecker *
+editor_text_buffer_spell_adapter_get_checker (EditorTextBufferSpellAdapter *self)
+{
+  g_return_val_if_fail (EDITOR_IS_TEXT_BUFFER_SPELL_ADAPTER (self), NULL);
+
+  return self->checker;
+}
+
+void
+editor_text_buffer_spell_adapter_set_checker (EditorTextBufferSpellAdapter *self,
+                                              EditorSpellChecker           *checker)
+{
+  g_return_if_fail (EDITOR_IS_TEXT_BUFFER_SPELL_ADAPTER (self));
+  g_return_if_fail (!checker || EDITOR_IS_SPELL_CHECKER (checker));
+
+  if (g_set_object (&self->checker, checker))
+    {
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CHECKER]);
+    }
+}
+
+GtkTextBuffer *
+editor_text_buffer_spell_adapter_get_buffer (EditorTextBufferSpellAdapter *self)
+{
+  g_return_val_if_fail (EDITOR_IS_TEXT_BUFFER_SPELL_ADAPTER (self), NULL);
+
+  return self->buffer;
+}
+
+void
+editor_text_buffer_spell_adapter_insert_text (EditorTextBufferSpellAdapter *self,
+                                              guint                         offset,
+                                              guint                         length)
+{
+  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);
+}
+
+void
+editor_text_buffer_spell_adapter_delete_range (EditorTextBufferSpellAdapter *self,
+                                               guint                         offset,
+                                               guint                         length)
+{
+  g_return_if_fail (EDITOR_IS_TEXT_BUFFER_SPELL_ADAPTER (self));
+  g_return_if_fail (self->buffer != NULL);
+  g_return_if_fail (length > 0);
+
+  _cjh_text_region_remove (self->region, offset, length);
+}
+
+void
+editor_text_buffer_spell_adapter_cursor_moved (EditorTextBufferSpellAdapter *self)
+{
+  g_return_if_fail (EDITOR_IS_TEXT_BUFFER_SPELL_ADAPTER (self));
+  g_return_if_fail (self->buffer != NULL);
+
+}
diff --git a/src/editor-text-buffer-spell-adapter.h b/src/editor-text-buffer-spell-adapter.h
new file mode 100644
index 0000000..bd676ac
--- /dev/null
+++ b/src/editor-text-buffer-spell-adapter.h
@@ -0,0 +1,47 @@
+/* editor-text-buffer-spell-adapter.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>
+
+#include "editor-types.h"
+
+G_BEGIN_DECLS
+
+#define EDITOR_TYPE_TEXT_BUFFER_SPELL_ADAPTER (editor_text_buffer_spell_adapter_get_type())
+
+G_DECLARE_FINAL_TYPE (EditorTextBufferSpellAdapter, editor_text_buffer_spell_adapter, EDITOR, 
TEXT_BUFFER_SPELL_ADAPTER, GObject)
+
+EditorTextBufferSpellAdapter *editor_text_buffer_spell_adapter_new          (GtkTextBuffer                
*buffer,
+                                                                             EditorSpellChecker           
*checker);
+GtkTextBuffer                *editor_text_buffer_spell_adapter_get_buffer   (EditorTextBufferSpellAdapter 
*self);
+EditorSpellChecker           *editor_text_buffer_spell_adapter_get_checker  (EditorTextBufferSpellAdapter 
*self);
+void                          editor_text_buffer_spell_adapter_set_checker  (EditorTextBufferSpellAdapter 
*self,
+                                                                             EditorSpellChecker           
*checker);
+void                          editor_text_buffer_spell_adapter_insert_text  (EditorTextBufferSpellAdapter 
*self,
+                                                                             guint                         
offset,
+                                                                             guint                         
len);
+void                          editor_text_buffer_spell_adapter_delete_range (EditorTextBufferSpellAdapter 
*self,
+                                                                             guint                         
offset,
+                                                                             guint                         
len);
+void                          editor_text_buffer_spell_adapter_cursor_moved (EditorTextBufferSpellAdapter 
*self);
+
+G_END_DECLS
diff --git a/src/meson.build b/src/meson.build
index 3ba3182..96431d4 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -36,6 +36,7 @@ editor_sources = [
   'editor-spell-language.c',
   'editor-spell-menu.c',
   'editor-spell-provider.c',
+  'editor-text-buffer-spell-adapter.c',
   'editor-theme-selector.c',
   'editor-utils.c',
   'editor-window.c',


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