[gnome-builder] libide/code: ignore (but track) encoding errors when loading file



commit 6aa41f61dd9c3ddccc89e46ed954260d310fac2c
Author: Christian Hergert <chergert redhat com>
Date:   Thu Oct 6 15:47:14 2022 -0700

    libide/code: ignore (but track) encoding errors when loading file

 src/libide/code/ide-buffer.c | 120 +++++++++++++++++++++++++++++++++++++++----
 src/libide/code/ide-buffer.h |   2 +
 2 files changed, 113 insertions(+), 9 deletions(-)
---
diff --git a/src/libide/code/ide-buffer.c b/src/libide/code/ide-buffer.c
index 7b337a795..17e965b0d 100644
--- a/src/libide/code/ide-buffer.c
+++ b/src/libide/code/ide-buffer.c
@@ -107,6 +107,7 @@ struct _IdeBuffer
   guint                   enable_addins : 1;
   guint                   changed_on_volume : 1;
   guint                   read_only : 1;
+  guint                   has_encoding_error : 1;
   guint                   highlight_diagnostics : 1;
   GtkSourceNewlineType    newline_type : 2;
 };
@@ -157,6 +158,7 @@ enum {
   PROP_FILE,
   PROP_FILE_SETTINGS,
   PROP_HAS_DIAGNOSTICS,
+  PROP_HAS_ENCODING_ERROR,
   PROP_HAS_SYMBOL_RESOLVERS,
   PROP_HIGHLIGHT_DIAGNOSTICS,
   PROP_IS_TEMPORARY,
@@ -249,6 +251,8 @@ static gboolean ide_buffer_can_do_newline_hack     (IdeBuffer              *self
                                                     guint                   len);
 static void     ide_buffer_guess_language          (IdeBuffer              *self);
 static void     ide_buffer_real_loaded             (IdeBuffer              *self);
+static void     _ide_buffer_set_has_encoding_error (IdeBuffer              *self,
+                                                    gboolean                has_encoding_error);
 static void     settle_async                       (IdeBuffer              *self,
                                                     GCancellable           *cancellable,
                                                     GAsyncReadyCallback     callback,
@@ -613,6 +617,10 @@ ide_buffer_get_property (GObject    *object,
       g_value_set_boolean (value, ide_buffer_has_diagnostics (self));
       break;
 
+    case PROP_HAS_ENCODING_ERROR:
+      g_value_set_boolean (value, ide_buffer_has_encoding_error (self));
+      break;
+
     case PROP_HAS_SYMBOL_RESOLVERS:
       g_value_set_boolean (value, ide_buffer_has_symbol_resolvers (self));
       break;
@@ -865,6 +873,11 @@ ide_buffer_class_init (IdeBufferClass *klass)
                           FALSE,
                           (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
+  properties [PROP_HAS_ENCODING_ERROR] =
+    g_param_spec_boolean ("has-encoding-error", NULL, NULL,
+                          FALSE,
+                          (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
   /**
    * IdeBuffer:has-symbol-resolvers:
    *
@@ -1402,6 +1415,27 @@ ide_buffer_progress_cb (goffset  current_num_bytes,
   ide_notification_set_progress (notif, progress);
 }
 
+static gboolean
+should_ignore_load_error (IdeBuffer    *self,
+                          const GError *error)
+{
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_BUFFER (self));
+  g_assert (error != NULL);
+
+  if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+    return TRUE;
+
+  if (g_error_matches (error, GTK_SOURCE_FILE_LOADER_ERROR, 
GTK_SOURCE_FILE_LOADER_ERROR_CONVERSION_FALLBACK) ||
+      g_error_matches (error, GTK_SOURCE_FILE_LOADER_ERROR, 
GTK_SOURCE_FILE_LOADER_ERROR_ENCODING_AUTO_DETECTION_FAILED))
+    {
+      _ide_buffer_set_has_encoding_error (self, TRUE);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
 static void
 ide_buffer_load_file_cb (GObject      *object,
                          GAsyncResult *result,
@@ -1431,8 +1465,9 @@ ide_buffer_load_file_cb (GObject      *object,
 
   if (!gtk_source_file_loader_load_finish (loader, result, &error))
     {
-      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+      if (!should_ignore_load_error (self, error))
         {
+          g_debug ("Failure loading file: %s", error->message);
           ide_buffer_set_state (self, IDE_BUFFER_STATE_FAILED);
           ide_notification_set_progress (state->notif, 0.0);
           ide_task_return_error (task, g_steal_pointer (&error));
@@ -1498,12 +1533,30 @@ _ide_buffer_load_file_async (IdeBuffer            *self,
   ide_task_set_task_data (task, state, load_state_free);
 
   ide_buffer_set_state (self, IDE_BUFFER_STATE_LOADING);
+  _ide_buffer_set_has_encoding_error (self, FALSE);
 
   /* Disable some features while we reload */
   gtk_source_buffer_set_highlight_syntax (GTK_SOURCE_BUFFER (self), FALSE);
   ide_highlight_engine_pause (self->highlight_engine);
 
+  /* Create our loader, inheriting some values like encoding from the
+   * source_file if previously set.
+   */
   loader = gtk_source_file_loader_new (GTK_SOURCE_BUFFER (self), self->source_file);
+
+  /* If the user specified an encoding, make sure that we apply
+   * that when reloading the file.
+   */
+  if (self->encoding != NULL)
+    {
+      g_autofree char *uri = g_file_get_uri (state->file);
+      GSList list = { (gpointer)self->encoding, NULL };
+
+      gtk_source_file_loader_set_candidate_encodings (loader, &list);
+      g_debug ("Using user-selected encoding of %s while loading '%s'",
+               gtk_source_encoding_get_charset (self->encoding), uri);
+    }
+
   gtk_source_file_loader_load_async (loader,
                                      G_PRIORITY_DEFAULT,
                                      cancellable,
@@ -1816,6 +1869,9 @@ ide_buffer_save_file_async (IdeBuffer            *self,
 
   ide_buffer_set_state (self, IDE_BUFFER_STATE_SAVING);
 
+  /* We're attempting to write our contents now, clear error */
+  _ide_buffer_set_has_encoding_error (self, FALSE);
+
   settle_async (self,
                 cancellable,
                 ide_buffer_save_file_settle_cb,
@@ -4248,6 +4304,7 @@ void
 ide_buffer_set_charset (IdeBuffer  *self,
                         const char *charset)
 {
+  const GtkSourceEncoding *encoding = NULL;
   GSList *all;
 
   g_return_if_fail (IDE_IS_BUFFER (self));
@@ -4259,19 +4316,28 @@ ide_buffer_set_charset (IdeBuffer  *self,
 
   for (const GSList *iter = all; iter; iter = iter->next)
     {
-      const GtkSourceEncoding *encoding = iter->data;
+      const GtkSourceEncoding *candidate_encoding = iter->data;
 
-      if (g_strcmp0 (charset, gtk_source_encoding_get_charset (encoding)) == 0)
+      if (g_strcmp0 (charset, gtk_source_encoding_get_charset (candidate_encoding)) == 0)
         {
-          if (self->encoding != encoding)
-            {
-              self->encoding = encoding;
-              g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CHARSET]);
-              break;
-            }
+          encoding = candidate_encoding;
+          break;
         }
     }
 
+  if (self->encoding != encoding)
+    {
+      self->encoding = encoding;
+      g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CHARSET]);
+
+      /* We changed encoding and the buffer is not modified. We should just
+       * reload the file using the new encoding so that the user can see the
+       * document as they expected it to.
+       */
+      if (!gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (self)))
+        _ide_buffer_load_file_async (self, NULL, NULL, NULL, NULL);
+    }
+
   g_slist_free (all);
 }
 
@@ -4298,3 +4364,39 @@ ide_buffer_set_newline_type (IdeBuffer            *self,
   self->newline_type = newline_type;
   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NEWLINE_TYPE]);
 }
+
+static void
+_ide_buffer_set_has_encoding_error (IdeBuffer *self,
+                                    gboolean   has_encoding_error)
+{
+  g_assert (IDE_IS_BUFFER (self));
+
+  has_encoding_error = !!has_encoding_error;
+
+  if (has_encoding_error != self->has_encoding_error)
+    {
+      self->has_encoding_error = has_encoding_error;
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_ENCODING_ERROR]);
+    }
+}
+
+/**
+ * ide_buffer_has_encoding_error:
+ * @self: a #IdeBuffer
+ *
+ * Gets the "has-encoding-error" property.
+ *
+ * This returns %TRUE if there was an error loading a file due to a failure
+ * to discover the encoding or if character conversions occurred.
+ *
+ * Returns: %FALSE if there were no encoding errors
+ *
+ * Since: 44
+ */
+gboolean
+ide_buffer_has_encoding_error (IdeBuffer *self)
+{
+  g_return_val_if_fail (IDE_IS_BUFFER (self), FALSE);
+
+  return self->has_encoding_error;
+}
diff --git a/src/libide/code/ide-buffer.h b/src/libide/code/ide-buffer.h
index b4a938a88..30fdc17c0 100644
--- a/src/libide/code/ide-buffer.h
+++ b/src/libide/code/ide-buffer.h
@@ -208,5 +208,7 @@ GtkSourceNewlineType    ide_buffer_get_newline_type              (IdeBuffer
 IDE_AVAILABLE_IN_ALL
 void                    ide_buffer_set_newline_type              (IdeBuffer               *self,
                                                                   GtkSourceNewlineType     newline_type);
+IDE_AVAILABLE_IN_44
+gboolean                ide_buffer_has_encoding_error            (IdeBuffer               *self);
 
 G_END_DECLS


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