[gnome-text-editor] spellcheck: add plumbing for spellcheckers



commit 07ba65135fb87482e6bef58b0d1b01d49e469d36
Author: Christian Hergert <chergert redhat com>
Date:   Fri Jun 25 13:42:43 2021 -0700

    spellcheck: add plumbing for spellcheckers

 meson.build                                 |   3 +
 src/editor-spell-checker.c                  | 231 ++++++++++++++++++++++++++++
 src/editor-spell-checker.h                  |  38 +++++
 src/editor-spell-language.c                 | 141 +++++++++++++++++
 src/editor-spell-language.h                 |  54 +++++++
 src/editor-spell-provider.c                 | 201 ++++++++++++++++++++++++
 src/editor-spell-provider.h                 |  53 +++++++
 src/editor-types.h                          |   3 +
 src/enchant/editor-enchant-spell-language.c | 179 +++++++++++++++++++++
 src/enchant/editor-enchant-spell-language.h |  35 +++++
 src/enchant/editor-enchant-spell-provider.c | 136 ++++++++++++++++
 src/enchant/editor-enchant-spell-provider.h |  33 ++++
 src/enchant/meson.build                     |   5 +
 src/meson.build                             |   4 +
 14 files changed, 1116 insertions(+)
---
diff --git a/meson.build b/meson.build
index 153525d..7532983 100644
--- a/meson.build
+++ b/meson.build
@@ -21,15 +21,18 @@ endif
 glib_req_version = '2.69'
 gtk_req_version = '4.3'
 gtksourceview_req_version = '5.0'
+enchant_req_version = '2.2.0'
 
 glib_req = '>= @0@'.format(glib_req_version)
 gtk_req = '>= @0@'.format(gtk_req_version)
 gtksourceview_req = '>= @0@'.format(gtksourceview_req_version)
+enchant_req = '>= @0@'.format(enchant_req_version)
 
 libglib_dep = dependency('gio-unix-2.0', version: glib_req)
 libgtk_dep = dependency('gtk4', version: gtk_req)
 libgtksourceview_dep = dependency('gtksourceview-5', version: gtksourceview_req)
 libadwaita_dep = dependency('libadwaita-1')
+libenchant_dep = dependency('enchant-2', version: enchant_req)
 
 # Specify minimum library versions
 glib_major = glib_req_version.split('.')[0].to_int()
diff --git a/src/editor-spell-checker.c b/src/editor-spell-checker.c
new file mode 100644
index 0000000..ee5e491
--- /dev/null
+++ b/src/editor-spell-checker.c
@@ -0,0 +1,231 @@
+/* editor-spell-checker.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-checker.h"
+#include "editor-spell-provider.h"
+
+struct _EditorSpellChecker
+{
+  GObject              parent_instance;
+  EditorSpellProvider *provider;
+  const char          *language;
+};
+
+G_DEFINE_TYPE (EditorSpellChecker, editor_spell_checker, G_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_LANGUAGE,
+  PROP_PROVIDER,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+/**
+ * editor_spell_checker_new:
+ *
+ * Create a new #EditorSpellChecker.
+ *
+ * Returns: (transfer full): a newly created #EditorSpellChecker
+ */
+EditorSpellChecker *
+editor_spell_checker_new (EditorSpellProvider *provider,
+                          const char          *language)
+{
+  g_return_val_if_fail (EDITOR_IS_SPELL_PROVIDER (provider) || !provider, NULL);
+
+  return g_object_new (EDITOR_TYPE_SPELL_CHECKER,
+                       "provider", provider,
+                       "language", language,
+                       NULL);
+}
+
+static void
+editor_spell_checker_constructed (GObject *object)
+{
+  EditorSpellChecker *self = (EditorSpellChecker *)object;
+
+  g_assert (EDITOR_IS_SPELL_CHECKER (self));
+
+  G_OBJECT_CLASS (editor_spell_checker_parent_class)->constructed (object);
+
+  if (self->provider == NULL)
+    self->provider = editor_spell_provider_get_default ();
+}
+
+static void
+editor_spell_checker_finalize (GObject *object)
+{
+  EditorSpellChecker *self = (EditorSpellChecker *)object;
+
+  g_clear_object (&self->provider);
+
+  G_OBJECT_CLASS (editor_spell_checker_parent_class)->finalize (object);
+}
+
+static void
+editor_spell_checker_get_property (GObject    *object,
+                                   guint       prop_id,
+                                   GValue     *value,
+                                   GParamSpec *pspec)
+{
+  EditorSpellChecker *self = EDITOR_SPELL_CHECKER (object);
+
+  switch (prop_id)
+    {
+    case PROP_PROVIDER:
+      g_value_set_object (value, editor_spell_checker_get_provider (self));
+      break;
+
+    case PROP_LANGUAGE:
+      g_value_set_string (value, editor_spell_checker_get_language (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+editor_spell_checker_set_property (GObject      *object,
+                                   guint         prop_id,
+                                   const GValue *value,
+                                   GParamSpec   *pspec)
+{
+  EditorSpellChecker *self = EDITOR_SPELL_CHECKER (object);
+
+  switch (prop_id)
+    {
+    case PROP_PROVIDER:
+      self->provider = g_value_dup_object (value);
+      break;
+
+    case PROP_LANGUAGE:
+      editor_spell_checker_set_language (self, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+editor_spell_checker_class_init (EditorSpellCheckerClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->constructed = editor_spell_checker_constructed;
+  object_class->finalize = editor_spell_checker_finalize;
+  object_class->get_property = editor_spell_checker_get_property;
+  object_class->set_property = editor_spell_checker_set_property;
+
+  /**
+   * EditorSpellChecker:language:
+   *
+   * The "language" to use when checking words with the configured
+   * #EditorSpellProvider. For example, `en_US`.
+   */
+  properties [PROP_LANGUAGE] =
+    g_param_spec_string ("language",
+                         "Language",
+                         "The language code",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * EditorSpellChecker:provider:
+   *
+   * The "provider" property contains the provider that is providing
+   * information to the spell checker.
+   *
+   * Currently, only Enchant is supported, and requires using the
+   * #EditorEnchantSpellProvider. Setting this to %NULL will get
+   * the default provider.
+   */
+  properties [PROP_PROVIDER] =
+    g_param_spec_object ("provider",
+                         "Provider",
+                         "The spell check provider",
+                         EDITOR_TYPE_SPELL_PROVIDER,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+editor_spell_checker_init (EditorSpellChecker *self)
+{
+}
+
+/**
+ * editor_spell_checker_get_language:
+ *
+ * Gets the language being used by the spell checker.
+ *
+ * Returns: (nullable): a string describing the current language.
+ */
+const char *
+editor_spell_checker_get_language (EditorSpellChecker *self)
+{
+  g_return_val_if_fail (EDITOR_IS_SPELL_CHECKER (self), NULL);
+
+  return self->language;
+}
+
+/**
+ * editor_spell_checker_set_language:
+ * @self: an #EditorSpellChecker
+ * @language: the language to use
+ *
+ * Sets the language code to use when communicating with the provider,
+ * such as `en_US`.
+ */
+void
+editor_spell_checker_set_language (EditorSpellChecker *self,
+                                   const char         *language)
+{
+  g_return_if_fail (EDITOR_IS_SPELL_CHECKER (self));
+
+  if (g_strcmp0 (language, self->language) != 0)
+    {
+      self->language = g_intern_string (language);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_LANGUAGE]);
+    }
+}
+
+/**
+ * editor_spell_checker_get_provider:
+ *
+ * Gets the spell provider used by the spell checker.
+ *
+ * Currently, only Enchant-2 is supported.
+ *
+ * Returns: (transfer none) (not nullable): an #EditorSpellProvider
+ */
+EditorSpellProvider *
+editor_spell_checker_get_provider (EditorSpellChecker *self)
+{
+  g_return_val_if_fail (EDITOR_IS_SPELL_CHECKER (self), NULL);
+
+  return self->provider;
+}
diff --git a/src/editor-spell-checker.h b/src/editor-spell-checker.h
new file mode 100644
index 0000000..f29b5fe
--- /dev/null
+++ b/src/editor-spell-checker.h
@@ -0,0 +1,38 @@
+/* editor-spell-checker.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 "editor-types.h"
+
+G_BEGIN_DECLS
+
+#define EDITOR_TYPE_SPELL_CHECKER (editor_spell_checker_get_type())
+
+G_DECLARE_FINAL_TYPE (EditorSpellChecker, editor_spell_checker, EDITOR, SPELL_CHECKER, GObject)
+
+EditorSpellChecker  *editor_spell_checker_new          (EditorSpellProvider *provider,
+                                                        const char          *language);
+EditorSpellProvider *editor_spell_checker_get_provider (EditorSpellChecker  *self);
+const char          *editor_spell_checker_get_language (EditorSpellChecker  *self);
+void                 editor_spell_checker_set_language (EditorSpellChecker  *self,
+                                                        const char          *language);
+
+G_END_DECLS
diff --git a/src/editor-spell-language.c b/src/editor-spell-language.c
new file mode 100644
index 0000000..23c5b02
--- /dev/null
+++ b/src/editor-spell-language.c
@@ -0,0 +1,141 @@
+/* editor-spell-language.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 <string.h>
+
+#include "editor-spell-language.h"
+
+typedef struct
+{
+  const char *code;
+} EditorSpellLanguagePrivate;
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (EditorSpellLanguage, editor_spell_language, G_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_CODE,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+editor_spell_language_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+  EditorSpellLanguage *self = EDITOR_SPELL_LANGUAGE (object);
+
+  switch (prop_id)
+    {
+    case PROP_CODE:
+      g_value_set_string (value, editor_spell_language_get_code (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+editor_spell_language_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  EditorSpellLanguage *self = EDITOR_SPELL_LANGUAGE (object);
+  EditorSpellLanguagePrivate *priv = editor_spell_language_get_instance_private (self);
+
+  switch (prop_id)
+    {
+    case PROP_CODE:
+      priv->code = g_intern_string (g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+editor_spell_language_class_init (EditorSpellLanguageClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->get_property = editor_spell_language_get_property;
+  object_class->set_property = editor_spell_language_set_property;
+
+  properties [PROP_CODE] =
+    g_param_spec_string ("code",
+                         "Code",
+                         "The language code",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+editor_spell_language_init (EditorSpellLanguage *self)
+{
+}
+
+const char *
+editor_spell_language_get_code (EditorSpellLanguage *self)
+{
+  EditorSpellLanguagePrivate *priv = editor_spell_language_get_instance_private (self);
+
+  g_return_val_if_fail (EDITOR_IS_SPELL_LANGUAGE (self), NULL);
+
+  return priv->code;
+}
+
+gboolean
+editor_spell_language_contains_word (EditorSpellLanguage *self,
+                                     const char          *word,
+                                     gssize               word_len)
+{
+  g_return_val_if_fail (EDITOR_IS_SPELL_LANGUAGE (self), FALSE);
+  g_return_val_if_fail (word != NULL, FALSE);
+
+  return EDITOR_SPELL_LANGUAGE_GET_CLASS (self)->contains_word (self, word, word_len);
+}
+
+char **
+editor_spell_language_list_corrections (EditorSpellLanguage *self,
+                                        const char          *word,
+                                        gssize               word_len)
+{
+  g_return_val_if_fail (EDITOR_IS_SPELL_LANGUAGE (self), NULL);
+  g_return_val_if_fail (word != NULL, NULL);
+  g_return_val_if_fail (word != NULL || word_len == 0, NULL);
+
+  if (word_len < 0)
+    word_len = strlen (word);
+
+  if (word_len == 0)
+    return NULL;
+
+  return EDITOR_SPELL_LANGUAGE_GET_CLASS (self)->list_corrections (self, word, word_len);
+}
diff --git a/src/editor-spell-language.h b/src/editor-spell-language.h
new file mode 100644
index 0000000..adfe37e
--- /dev/null
+++ b/src/editor-spell-language.h
@@ -0,0 +1,54 @@
+/* editor-spell-language.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 "editor-types.h"
+
+G_BEGIN_DECLS
+
+#define EDITOR_TYPE_SPELL_LANGUAGE (editor_spell_language_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (EditorSpellLanguage, editor_spell_language, EDITOR, SPELL_LANGUAGE, GObject)
+
+struct _EditorSpellLanguageClass
+{
+  GObjectClass parent_class;
+
+  gboolean    (*contains_word)    (EditorSpellLanguage *self,
+                                   const char          *word,
+                                   gssize               word_len);
+  char      **(*list_corrections) (EditorSpellLanguage *self,
+                                   const char          *word,
+                                   gssize               word_len);
+
+  /*< private >*/
+  gpointer _reserved[8];
+};
+
+const char  *editor_spell_language_get_code         (EditorSpellLanguage *self);
+gboolean     editor_spell_language_contains_word    (EditorSpellLanguage *self,
+                                                     const char          *word,
+                                                     gssize               word_len);
+char       **editor_spell_language_list_corrections (EditorSpellLanguage *self,
+                                                     const char          *word,
+                                                     gssize               word_len);
+
+G_END_DECLS
diff --git a/src/editor-spell-provider.c b/src/editor-spell-provider.c
new file mode 100644
index 0000000..053d568
--- /dev/null
+++ b/src/editor-spell-provider.c
@@ -0,0 +1,201 @@
+/* editor-spell-provider.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-provider.h"
+
+#include "enchant/editor-enchant-spell-provider.h"
+
+typedef struct
+{
+  char *display_name;
+} EditorSpellProviderPrivate;
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (EditorSpellProvider, editor_spell_provider, G_TYPE_OBJECT,
+                                  G_ADD_PRIVATE (EditorSpellProvider))
+
+enum {
+  PROP_0,
+  PROP_DISPLAY_NAME,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+editor_spell_provider_finalize (GObject *object)
+{
+  EditorSpellProvider *self = (EditorSpellProvider *)object;
+  EditorSpellProviderPrivate *priv = editor_spell_provider_get_instance_private (self);
+
+  g_clear_pointer (&priv->display_name, g_free);
+
+  G_OBJECT_CLASS (editor_spell_provider_parent_class)->finalize (object);
+}
+
+static void
+editor_spell_provider_get_property (GObject    *object,
+                                   guint       prop_id,
+                                   GValue     *value,
+                                   GParamSpec *pspec)
+{
+  EditorSpellProvider *self = EDITOR_SPELL_PROVIDER (object);
+
+  switch (prop_id)
+    {
+    case PROP_DISPLAY_NAME:
+      g_value_set_string (value, editor_spell_provider_get_display_name (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+editor_spell_provider_set_property (GObject      *object,
+                                   guint         prop_id,
+                                   const GValue *value,
+                                   GParamSpec   *pspec)
+{
+  EditorSpellProvider *self = EDITOR_SPELL_PROVIDER (object);
+  EditorSpellProviderPrivate *priv = editor_spell_provider_get_instance_private (self);
+
+  switch (prop_id)
+    {
+    case PROP_DISPLAY_NAME:
+      priv->display_name = g_value_dup_string (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+editor_spell_provider_class_init (EditorSpellProviderClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = editor_spell_provider_finalize;
+  object_class->get_property = editor_spell_provider_get_property;
+  object_class->set_property = editor_spell_provider_set_property;
+
+  properties [PROP_DISPLAY_NAME] =
+    g_param_spec_string ("display-name",
+                         "Display Name",
+                         "Display Name",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+editor_spell_provider_init (EditorSpellProvider *self)
+{
+}
+
+const char *
+editor_spell_provider_get_display_name (EditorSpellProvider *self)
+{
+  EditorSpellProviderPrivate *priv = editor_spell_provider_get_instance_private (self);
+
+  g_return_val_if_fail (EDITOR_IS_SPELL_PROVIDER (self), NULL);
+
+  return priv->display_name;
+}
+
+/**
+ * editor_spell_provider_get_default:
+ *
+ * Gets the default spell provider.
+ *
+ * Returns: (transfer none): an #EditorSpellProvider
+ */
+EditorSpellProvider *
+editor_spell_provider_get_default (void)
+{
+  static EditorSpellProvider *instance;
+
+  if (instance == NULL)
+    {
+      instance = editor_enchant_spell_provider_new ();
+      g_set_weak_pointer (&instance, instance);
+    }
+
+  return instance;
+}
+
+/**
+ * editor_spell_provider_supports_language:
+ * @self: an #EditorSpellProvider
+ * @language: the language such as `en_US`.
+ *
+ * Checks of @language is supported by the provider.
+ *
+ * Returns: %TRUE if @language is supported, otherwise %FALSE
+ */
+gboolean
+editor_spell_provider_supports_language (EditorSpellProvider *self,
+                                         const char          *language)
+{
+  g_return_val_if_fail (EDITOR_IS_SPELL_PROVIDER (self), FALSE);
+  g_return_val_if_fail (language != NULL, FALSE);
+
+  return EDITOR_SPELL_PROVIDER_GET_CLASS (self)->supports_language (self, language);
+}
+
+/**
+ * editor_spell_provider_list_languages:
+ * @self: an #EditorSpellProvider
+ *
+ * Gets a list of the languages supported by the provider.
+ *
+ * Returns: (transfer full) (array zero-terminated=1): an array of language codes
+ */
+char **
+editor_spell_provider_list_languages (EditorSpellProvider *self)
+{
+  g_return_val_if_fail (EDITOR_IS_SPELL_PROVIDER (self), NULL);
+
+  return EDITOR_SPELL_PROVIDER_GET_CLASS (self)->list_languages (self);
+}
+
+/**
+ * editor_spell_provider_get_language:
+ * @self: an #EditorSpellProvider
+ * @language: the language to load such as `en_US`.
+ *
+ * Gets an #EditorSpellLanguage for the requested language, or %NULL
+ * if the language is not supported.
+ *
+ * Returns: (transfer full) (nullable): an #EditorSpellProvider or %NULL
+ */
+EditorSpellLanguage *
+editor_spell_provider_get_language (EditorSpellProvider *self,
+                                    const char          *language)
+{
+  g_return_val_if_fail (EDITOR_IS_SPELL_PROVIDER (self), NULL);
+  g_return_val_if_fail (language != NULL, NULL);
+
+  return EDITOR_SPELL_PROVIDER_GET_CLASS (self)->get_language (self, language);
+}
diff --git a/src/editor-spell-provider.h b/src/editor-spell-provider.h
new file mode 100644
index 0000000..293e4b1
--- /dev/null
+++ b/src/editor-spell-provider.h
@@ -0,0 +1,53 @@
+/* editor-spell-provider.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 "editor-types.h"
+
+G_BEGIN_DECLS
+
+#define EDITOR_TYPE_SPELL_PROVIDER (editor_spell_provider_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (EditorSpellProvider, editor_spell_provider, EDITOR, SPELL_PROVIDER, GObject)
+
+struct _EditorSpellProviderClass
+{
+  GObjectClass parent_class;
+
+  char                **(*list_languages)    (EditorSpellProvider *self);
+  gboolean              (*supports_language) (EditorSpellProvider *self,
+                                              const char          *language);
+  EditorSpellLanguage  *(*get_language)      (EditorSpellProvider *self,
+                                              const char          *language);
+
+  /*< private >*/
+  gpointer _reserved[8];
+};
+
+EditorSpellProvider  *editor_spell_provider_get_default       (void);
+const char           *editor_spell_provider_get_display_name  (EditorSpellProvider *self);
+gboolean              editor_spell_provider_supports_language (EditorSpellProvider *self,
+                                                               const char          *language);
+char                **editor_spell_provider_list_languages    (EditorSpellProvider *self);
+EditorSpellLanguage  *editor_spell_provider_get_language      (EditorSpellProvider *self,
+                                                               const char          *language);
+
+G_END_DECLS
diff --git a/src/editor-types.h b/src/editor-types.h
index 8f4dfdf..2941653 100644
--- a/src/editor-types.h
+++ b/src/editor-types.h
@@ -41,6 +41,9 @@ typedef struct _EditorPreferencesWindow    EditorPreferencesWindow;
 typedef struct _EditorSession              EditorSession;
 typedef struct _EditorSidebar              EditorSidebar;
 typedef struct _EditorSignalGroup          EditorSignalGroup;
+typedef struct _EditorSpellChecker         EditorSpellChecker;
+typedef struct _EditorSpellLanguage        EditorSpellLanguage;
+typedef struct _EditorSpellProvider        EditorSpellProvider;
 typedef struct _EditorTab                  EditorTab;
 typedef struct _EditorWindow               EditorWindow;
 
diff --git a/src/enchant/editor-enchant-spell-language.c b/src/enchant/editor-enchant-spell-language.c
new file mode 100644
index 0000000..4b8736a
--- /dev/null
+++ b/src/enchant/editor-enchant-spell-language.c
@@ -0,0 +1,179 @@
+/* editor-enchant-spell-language.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 <enchant.h>
+
+#include "editor-enchant-spell-language.h"
+
+struct _EditorEnchantSpellLanguage
+{
+  EditorSpellLanguage parent_instance;
+  EnchantDict *native;
+};
+
+G_DEFINE_TYPE (EditorEnchantSpellLanguage, editor_enchant_spell_language, EDITOR_TYPE_SPELL_LANGUAGE)
+
+enum {
+  PROP_0,
+  PROP_NATIVE,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+/**
+ * editor_enchant_spell_language_new:
+ *
+ * Create a new #EditorEnchantSpellLanguage.
+ *
+ * Returns: (transfer full): a newly created #EditorEnchantSpellLanguage
+ */
+EditorSpellLanguage *
+editor_enchant_spell_language_new (const char *code,
+                                   gpointer    native)
+{
+  return g_object_new (EDITOR_TYPE_ENCHANT_SPELL_LANGUAGE,
+                       "code", code,
+                       "native", native,
+                       NULL);
+}
+
+static gboolean
+editor_enchant_spell_language_contains_word (EditorSpellLanguage *language,
+                                             const char          *word,
+                                             gssize               word_len)
+{
+  EditorEnchantSpellLanguage *self = (EditorEnchantSpellLanguage *)language;
+
+  g_assert (EDITOR_IS_ENCHANT_SPELL_LANGUAGE (self));
+  g_assert (word != NULL);
+  g_assert (word_len > 0);
+
+  return enchant_dict_check (self->native, word, word_len) == 0;
+}
+
+static char **
+editor_enchant_spell_language_list_corrections (EditorSpellLanguage *language,
+                                                const char          *word,
+                                                gssize               word_len)
+{
+  EditorEnchantSpellLanguage *self = (EditorEnchantSpellLanguage *)language;
+  size_t count = 0;
+  char **tmp;
+  char **ret = NULL;
+
+  g_assert (EDITOR_IS_ENCHANT_SPELL_LANGUAGE (self));
+  g_assert (word != NULL);
+  g_assert (word_len > 0);
+
+  if ((tmp = enchant_dict_suggest (self->native, word, word_len, &count)) && count > 0)
+    {
+      ret = g_strdupv (tmp);
+      enchant_dict_free_string_list (self->native, tmp);
+    }
+
+  return g_steal_pointer (&ret);
+}
+
+static void
+editor_enchant_spell_language_finalize (GObject *object)
+{
+  EditorEnchantSpellLanguage *self = (EditorEnchantSpellLanguage *)object;
+
+  /* Owned by provider */
+  self->native = NULL;
+
+  G_OBJECT_CLASS (editor_enchant_spell_language_parent_class)->finalize (object);
+}
+
+static void
+editor_enchant_spell_language_get_property (GObject    *object,
+                                            guint       prop_id,
+                                            GValue     *value,
+                                            GParamSpec *pspec)
+{
+  EditorEnchantSpellLanguage *self = EDITOR_ENCHANT_SPELL_LANGUAGE (object);
+
+  switch (prop_id)
+    {
+    case PROP_NATIVE:
+      g_value_set_pointer (value, self->native);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+editor_enchant_spell_language_set_property (GObject      *object,
+                                            guint         prop_id,
+                                            const GValue *value,
+                                            GParamSpec   *pspec)
+{
+  EditorEnchantSpellLanguage *self = EDITOR_ENCHANT_SPELL_LANGUAGE (object);
+
+  switch (prop_id)
+    {
+    case PROP_NATIVE:
+      self->native = g_value_get_pointer (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+editor_enchant_spell_language_class_init (EditorEnchantSpellLanguageClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  EditorSpellLanguageClass *spell_language_class = EDITOR_SPELL_LANGUAGE_CLASS (klass);
+
+  object_class->finalize = editor_enchant_spell_language_finalize;
+  object_class->get_property = editor_enchant_spell_language_get_property;
+  object_class->set_property = editor_enchant_spell_language_set_property;
+
+  spell_language_class->contains_word = editor_enchant_spell_language_contains_word;
+  spell_language_class->list_corrections = editor_enchant_spell_language_list_corrections;
+
+  properties [PROP_NATIVE] =
+    g_param_spec_pointer ("native",
+                          "Native",
+                          "The native enchant dictionary",
+                          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+editor_enchant_spell_language_init (EditorEnchantSpellLanguage *self)
+{
+}
+
+gpointer
+editor_enchant_spell_language_get_native (EditorEnchantSpellLanguage *self)
+{
+  g_return_val_if_fail (EDITOR_IS_ENCHANT_SPELL_LANGUAGE (self), NULL);
+
+  return self->native;
+}
diff --git a/src/enchant/editor-enchant-spell-language.h b/src/enchant/editor-enchant-spell-language.h
new file mode 100644
index 0000000..4b0d0f6
--- /dev/null
+++ b/src/enchant/editor-enchant-spell-language.h
@@ -0,0 +1,35 @@
+/* editor-enchant-spell-language.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 "editor-spell-language.h"
+
+G_BEGIN_DECLS
+
+#define EDITOR_TYPE_ENCHANT_SPELL_LANGUAGE (editor_enchant_spell_language_get_type())
+
+G_DECLARE_FINAL_TYPE (EditorEnchantSpellLanguage, editor_enchant_spell_language, EDITOR, 
ENCHANT_SPELL_LANGUAGE, EditorSpellLanguage)
+
+EditorSpellLanguage *editor_enchant_spell_language_new        (const char                 *code,
+                                                               gpointer                    native);
+gpointer             editor_enchant_spell_language_get_native (EditorEnchantSpellLanguage *self);
+
+G_END_DECLS
diff --git a/src/enchant/editor-enchant-spell-provider.c b/src/enchant/editor-enchant-spell-provider.c
new file mode 100644
index 0000000..93b1e1c
--- /dev/null
+++ b/src/enchant/editor-enchant-spell-provider.c
@@ -0,0 +1,136 @@
+/* editor-enchant-spell-provider.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 <glib/gi18n.h>
+#include <enchant.h>
+
+#include "editor-enchant-spell-language.h"
+#include "editor-enchant-spell-provider.h"
+
+struct _EditorEnchantSpellProvider
+{
+  EditorSpellProvider parent_instance;
+};
+
+G_DEFINE_TYPE (EditorEnchantSpellProvider, editor_enchant_spell_provider, EDITOR_TYPE_SPELL_PROVIDER)
+
+static GHashTable *languages;
+
+static EnchantBroker *
+get_broker (void)
+{
+  static EnchantBroker *broker;
+
+  if (broker == NULL)
+    broker = enchant_broker_init ();
+
+  return broker;
+}
+
+/**
+ * editor_enchant_spell_provider_new:
+ *
+ * Create a new #EditorEnchantSpellProvider.
+ *
+ * Returns: (transfer full): a newly created #EditorEnchantSpellProvider
+ */
+EditorSpellProvider *
+editor_enchant_spell_provider_new (void)
+{
+  return g_object_new (EDITOR_TYPE_ENCHANT_SPELL_PROVIDER,
+                       "display-name", _("Enchant 2"),
+                       NULL);
+}
+
+static gboolean
+editor_enchant_spell_provider_supports_language (EditorSpellProvider *provider,
+                                                 const char          *language)
+{
+  EditorEnchantSpellProvider *self = (EditorEnchantSpellProvider *)provider;
+
+  g_assert (EDITOR_IS_ENCHANT_SPELL_PROVIDER (self));
+  g_assert (language != NULL);
+
+  return enchant_broker_dict_exists (get_broker (), language);
+}
+
+static void
+list_languages_cb (const char * const  lang_tag,
+                   const char * const  provider_name,
+                   const char * const  provider_desc,
+                   const char * const  provider_file,
+                   void               *user_data)
+{
+  GArray *ar = user_data;
+  char *code = g_strdup (lang_tag);
+  g_array_append_val (ar, code);
+}
+
+static char **
+editor_enchant_spell_provider_list_languages (EditorSpellProvider *provider)
+{
+  EnchantBroker *broker = get_broker ();
+  GArray *ar = g_array_new (TRUE, FALSE, sizeof (char *));
+  enchant_broker_list_dicts (broker, list_languages_cb, ar);
+  return (char **)(gpointer)g_array_free (ar, FALSE);
+}
+
+static EditorSpellLanguage *
+editor_enchant_spell_provider_get_language (EditorSpellProvider *provider,
+                                            const char          *language)
+{
+  EditorSpellLanguage *ret;
+
+  g_assert (EDITOR_IS_ENCHANT_SPELL_PROVIDER (provider));
+  g_assert (language != NULL);
+
+  if (languages == NULL)
+    languages = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
+
+  if (!(ret = g_hash_table_lookup (languages, language)))
+    {
+      EnchantDict *dict = enchant_broker_request_dict (get_broker (), language);
+
+      if (dict == NULL)
+        return NULL;
+
+      ret = editor_enchant_spell_language_new (language, dict);
+      g_hash_table_insert (languages, (char *)g_intern_string (language), ret);
+    }
+
+  return ret ? g_object_ref (ret) : NULL;
+}
+
+static void
+editor_enchant_spell_provider_class_init (EditorEnchantSpellProviderClass *klass)
+{
+  EditorSpellProviderClass *spell_provider_class = EDITOR_SPELL_PROVIDER_CLASS (klass);
+
+  spell_provider_class->supports_language = editor_enchant_spell_provider_supports_language;
+  spell_provider_class->list_languages = editor_enchant_spell_provider_list_languages;
+  spell_provider_class->get_language = editor_enchant_spell_provider_get_language;
+}
+
+static void
+editor_enchant_spell_provider_init (EditorEnchantSpellProvider *self)
+{
+}
diff --git a/src/enchant/editor-enchant-spell-provider.h b/src/enchant/editor-enchant-spell-provider.h
new file mode 100644
index 0000000..8ae6c63
--- /dev/null
+++ b/src/enchant/editor-enchant-spell-provider.h
@@ -0,0 +1,33 @@
+/* editor-enchant-spell-provider.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 "editor-spell-provider.h"
+
+G_BEGIN_DECLS
+
+#define EDITOR_TYPE_ENCHANT_SPELL_PROVIDER (editor_enchant_spell_provider_get_type())
+
+G_DECLARE_FINAL_TYPE (EditorEnchantSpellProvider, editor_enchant_spell_provider, EDITOR, 
ENCHANT_SPELL_PROVIDER, EditorSpellProvider)
+
+EditorSpellProvider *editor_enchant_spell_provider_new (void);
+
+G_END_DECLS
diff --git a/src/enchant/meson.build b/src/enchant/meson.build
new file mode 100644
index 0000000..3d88a29
--- /dev/null
+++ b/src/enchant/meson.build
@@ -0,0 +1,5 @@
+editor_deps += [libenchant_dep]
+editor_sources += files([
+  'editor-enchant-spell-language.c',
+  'editor-enchant-spell-provider.c',
+])
diff --git a/src/meson.build b/src/meson.build
index 732f3e0..c4e462b 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -30,6 +30,9 @@ editor_sources = [
   'editor-sidebar-model.c',
   'editor-sidebar-row.c',
   'editor-signal-group.c',
+  'editor-spell-checker.c',
+  'editor-spell-language.c',
+  'editor-spell-provider.c',
   'editor-theme-selector.c',
   'editor-utils.c',
   'editor-window.c',
@@ -68,6 +71,7 @@ build_ident_h = vcs_tag(
        output: 'build-ident.h',
 )
 
+subdir('enchant')
 subdir('defaults')
 subdir('modelines')
 subdir('editorconfig')


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