[gnome-builder] clang-format: simplify clang-format buffer save addin



commit 050b2448123a2c69aec17199a4bf506b9d94658c
Author: Christian Hergert <chergert redhat com>
Date:   Mon Nov 8 13:17:25 2021 -0800

    clang-format: simplify clang-format buffer save addin

 .../clang-format/gb-clang-format-buffer-addin.c    | 370 ++++++---------------
 1 file changed, 107 insertions(+), 263 deletions(-)
---
diff --git a/src/plugins/clang-format/gb-clang-format-buffer-addin.c 
b/src/plugins/clang-format/gb-clang-format-buffer-addin.c
index b49f4b0e3..71a500f9a 100644
--- a/src/plugins/clang-format/gb-clang-format-buffer-addin.c
+++ b/src/plugins/clang-format/gb-clang-format-buffer-addin.c
@@ -32,313 +32,156 @@
 struct _GbClangFormatBufferAddin
 {
   GObject parent_instance;
-  gchar *working_directory;
-  gssize cursor_position;
-  IdePage *page;
-  gssize header_len;
 };
 
-typedef struct {
-  IdeBuffer *buffer;
-  IdePage *page;
-} Lookup;
+static GSettings *settings;
 
 static void
-foreach_page_cb (GtkWidget *page,
-                 gpointer   user_data)
+scroll_page_to_insert (GtkWidget *widget,
+                       gpointer   data)
 {
-  Lookup *l = user_data;
+  IdePage *page = (IdePage *)widget;
+  IdeBuffer *buffer = data;
 
-  if (l->page == NULL &&
-      IDE_IS_EDITOR_PAGE (page) &&
-      ide_editor_page_get_buffer (IDE_EDITOR_PAGE (page)) == l->buffer)
-    l->page = IDE_PAGE (page);
-}
-
-static IdePage *
-get_page (IdeBuffer *buffer)
-{
-  Lookup lookup = {buffer, NULL};
-  IdeContext *context = ide_buffer_ref_context (buffer);
-  IdeWorkbench *workbench = ide_workbench_from_context (context);
-  ide_workbench_foreach_page (workbench, foreach_page_cb, &lookup);
-
-  return lookup.page;
-}
-
-gboolean
-format_on_save_enabled (IdeBuffer *buffer)
-{
-  g_autoptr (GSettings) settings = g_settings_new ("org.gnome.builder");
-
-  return g_settings_get_boolean (settings, "format-on-save");
-}
-
-gboolean
-is_formattable_language (IdeBuffer *buffer)
-{
-  const gchar *lang_id;
-
-  lang_id = ide_buffer_get_language_id (buffer);
-  if (lang_id == NULL)
-    {
-      g_debug ("Language ID was NULL");
-      return FALSE;
-    }
-
-  if (strcmp (lang_id, "c") == 0 || strcmp (lang_id, "chdr") == 0 ||
-      strcmp (lang_id, "cpp") == 0 || strcmp (lang_id, "cpphdr") == 0 ||
-      strcmp (lang_id, "objc") == 0)
-    return TRUE;
-
-  return FALSE;
-}
-
-char *
-project_root_directory (IdeBuffer *buffer)
-{
-  IdeContext *context;
-  GFile *workdir;
-
-  context = ide_buffer_ref_context (buffer);
-  if (context == NULL)
-    {
-      g_warning ("Failed to get IdeContext");
-      return NULL;
-    }
-
-  workdir = ide_context_ref_workdir (context);
-  if (workdir == NULL)
-    {
-      g_warning ("Failed to get working directory");
-      return NULL;
-    }
-
-  return g_file_get_path (workdir);
-}
-
-gboolean
-clang_format_config_exists (GbClangFormatBufferAddin *self,
-                            IdeBuffer *buffer)
-{
-  GFile *config;
-
-  config = g_file_new_build_filename (self->working_directory, ".clang-format", NULL);
-
-  return g_file_query_exists (config, NULL);
-}
+  g_assert (IDE_IS_PAGE (page));
+  g_assert (IDE_IS_BUFFER (buffer));
 
-gint
-get_cursor_position (IdeBuffer *buffer)
-{
-  GtkTextBuffer *textbuffer;
-  gint cursor_position;
+  if (!IDE_IS_EDITOR_PAGE (page))
+    return;
 
-  textbuffer = GTK_TEXT_BUFFER (buffer);
-  g_object_get (G_OBJECT (textbuffer), "cursor-position", &cursor_position, NULL);
+  if (buffer != ide_editor_page_get_buffer (IDE_EDITOR_PAGE (page)))
+    return;
 
-  return cursor_position;
+  gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (ide_editor_page_get_view (IDE_EDITOR_PAGE (page))),
+                                      gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer)));
 }
 
-gssize
-get_header_length (gchar *data)
+static gboolean
+clang_supports_language (GtkSourceLanguage *language)
 {
-  gssize len = g_utf8_strlen (data, G_MAXSSIZE);
-
-  for (gssize i = 0; i < len; ++i)
-      if (data[i] == '\n')
-          return (i + 1 <= len ? (i + 1) : i);
-
-  return 0;
+  static const char *supported[] = { "c", "chdr", "cpp", "cpphdr", "objc", NULL };
+  g_assert (!language || GTK_SOURCE_IS_LANGUAGE (language));
+  return language != NULL &&
+         g_strv_contains (supported, gtk_source_language_get_id (language));
 }
 
-gboolean
-parse_header (GbClangFormatBufferAddin *self,
-              gchar                    *data)
+static int
+get_cursor_position (const char *str,
+                     guint       length)
 {
-  JsonParser *parser;
-  gboolean ret;
-  GError *error;
+  g_autoptr(JsonParser) parser = NULL;
+  JsonObject *object;
   JsonNode *root;
-  JsonObject *cursor;
 
-  self->header_len = get_header_length (data);
-  if (self->header_len <= 0)
-    {
-      g_warning ("Empty header");
-      return FALSE;
-    }
+  g_assert (str != NULL);
 
-  error = NULL;
   parser = json_parser_new ();
-  ret = json_parser_load_from_data (parser, data, self->header_len, &error);
-  if (ret == FALSE)
-    {
-      g_warning ("Unable to parse JSON: %s", error->message);
-      g_error_free (error);
-      g_object_unref (parser);
-      return FALSE;
-    }
+  if (!json_parser_load_from_data (parser, str, length, NULL))
+    return -1;
 
-  root = json_parser_get_root (parser);
-  cursor = json_node_get_object (root);
-  if (cursor == NULL)
-    {
-      g_warning ("clang-format didn't return cursor position");
-      g_object_unref (parser);
-      return FALSE;
-    }
-  self->cursor_position = json_object_get_int_member (cursor, "Cursor");
-
-  g_object_unref (parser);
-  return TRUE;
-}
+  if (!(root = json_parser_get_root (parser)) ||
+      !JSON_NODE_HOLDS_OBJECT (root) ||
+      !(object = json_node_get_object (root)) ||
+      !json_object_has_member (object, "Cursor"))
+    return -1;
 
-gchar *
-format_cursor_arg (gssize position)
-{
-  return g_strdup_printf ("--cursor=%zu", position);
+  return json_object_get_int_member (object, "Cursor");
 }
 
-IdeSubprocess *
-create_process (GbClangFormatBufferAddin *self)
-{
-  g_autoptr (IdeSubprocessLauncher) launcher = NULL;
-  IdeSubprocess *subprocess = NULL;
-  GPtrArray *args;
-  GError *error = NULL;
-  gchar *cursor_arg;
-
-  cursor_arg = format_cursor_arg (self->cursor_position);
-
-  args = g_ptr_array_new ();
-  g_ptr_array_add (args, (gchar *)"clang-format");
-  g_ptr_array_add (args, cursor_arg);
-  g_ptr_array_add (args, NULL);
-
-  launcher = ide_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDIN_PIPE
-                                          | G_SUBPROCESS_FLAGS_STDOUT_PIPE
-                                          | G_SUBPROCESS_FLAGS_STDERR_PIPE);
-  ide_subprocess_launcher_set_cwd (launcher, self->working_directory);
-  ide_subprocess_launcher_set_argv (launcher,
-                                    (const gchar *const *)args->pdata);
-  subprocess = ide_subprocess_launcher_spawn (launcher, NULL, &error);
-
-  g_ptr_array_free (args, TRUE);
-  g_free (cursor_arg);
-  return subprocess;
-}
-
-gboolean
-process_communicate (IdeSubprocess  *process,
-                     IdeBuffer      *buffer,
-                     gchar         **stdout_buf)
+static void
+gb_clang_format_buffer_addin_save_file (IdeBufferAddin *addin,
+                                        IdeBuffer      *buffer,
+                                        GFile          *file)
 {
-  const gchar *stdin_buf;
-  gchar *stderr_buf = NULL;
-  GError *error = NULL;
-  gboolean ret;
-
+  GbClangFormatBufferAddin *self = (GbClangFormatBufferAddin *)addin;
+  g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+  g_autoptr(IdeSubprocess) subprocess = NULL;
+  g_autoptr(IdeContext) context = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GFile) workdir = NULL;
+  g_autoptr(GFile) dot_clang_format = NULL;
+  g_autofree char *cursor_arg = NULL;
+  g_autofree char *stdout_buf = NULL;
+  GtkSourceLanguage *language;
+  IdeWorkbench *workbench;
+  const char *stdin_buf;
+  const char *formatted;
+  GtkTextIter iter;
+  int cursor_position;
+
+  IDE_ENTRY;
+
+  g_assert (GB_IS_CLANG_FORMAT_BUFFER_ADDIN (self));
+  g_assert (IDE_IS_BUFFER (buffer));
+  g_assert (G_IS_FILE (file));
+
+  if (!g_settings_get_boolean (settings, "format-on-save"))
+    IDE_EXIT;
+
+  if (!(context = ide_buffer_ref_context (buffer)))
+    IDE_EXIT;
+
+  if (!(workdir = ide_context_ref_workdir (context)) || !g_file_is_native (workdir))
+    IDE_EXIT;
+
+  if (!(language = gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (buffer))) ||
+      !clang_supports_language (language))
+    IDE_EXIT;
+
+  dot_clang_format = g_file_get_child (workdir, ".clang-format");
+  if (!g_file_query_exists (dot_clang_format, NULL))
+    IDE_EXIT;
+
+  gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
+                                    &iter,
+                                    gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer)));
+  cursor_arg = g_strdup_printf ("--cursor=%u", gtk_text_iter_get_offset (&iter));
   stdin_buf = g_bytes_get_data (ide_buffer_dup_content (buffer), NULL);
 
-  ret = ide_subprocess_communicate_utf8 (process, stdin_buf, NULL, stdout_buf,
-                                         &stderr_buf, &error);
-  if (ret == FALSE)
+  launcher = ide_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDIN_PIPE |
+                                          G_SUBPROCESS_FLAGS_STDOUT_PIPE |
+                                          G_SUBPROCESS_FLAGS_STDERR_SILENCE);
+  ide_subprocess_launcher_set_cwd (launcher, g_file_peek_path (workdir));
+  ide_subprocess_launcher_push_argv (launcher, "clang-format");
+  ide_subprocess_launcher_push_argv (launcher, cursor_arg);
+
+  if (!(subprocess = ide_subprocess_launcher_spawn (launcher, NULL, &error)))
     {
-      g_warning ("clang-format failed: %s", error->message);
-      g_free (error);
-      return FALSE;
+      g_debug ("Failed to spawn clang-format: %s", error->message);
+      IDE_EXIT;
     }
 
-  return TRUE;
-}
-
-void
-run_clang_format (GbClangFormatBufferAddin *self,
-                  IdeBuffer                *buffer)
-{
-  IdeSubprocess *process;
-  gchar *stdout_buf = NULL;
-  gchar *data_start;
-  gsize data_len;
-  GtkTextIter cursor;
-
-  process = create_process (self);
-  if (process_communicate (process, buffer, &stdout_buf) == FALSE)
-    return;
-
-  if (parse_header (self, stdout_buf) == FALSE)
-    return;
-
-  data_start = stdout_buf + self->header_len;
-  data_len = strlen (data_start);
-  if (data_len <= 0)
+  if (!ide_subprocess_communicate_utf8 (subprocess, stdin_buf, NULL, &stdout_buf, NULL, &error))
     {
-      g_warning ("No output");
-      return;
+      g_debug ("Failed to communicate with subprocess: %s", error->message);
+      IDE_EXIT;
     }
 
-  if (data_start[data_len - 1] == '\n')
-      data_len--;
-
-  gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
-
-  gtk_text_buffer_set_text (GTK_TEXT_BUFFER (buffer), data_start, data_len);
-
-  gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &cursor);
-  gtk_text_iter_set_offset (&cursor, self->cursor_position);
-  gtk_text_buffer_place_cursor (GTK_TEXT_BUFFER (buffer), &cursor);
-
-  gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
-
-  if (self->page != NULL)
+  if (!(formatted = strchr (stdout_buf, '\n')))
     {
-      IdeSourceView *source_view = ide_editor_page_get_view (IDE_EDITOR_PAGE (self->page));
-      gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (source_view), &cursor, 0.25, FALSE, 0.5, 0.5);
+      g_debug ("Missing or corrupted data from clang-format");
+      IDE_EXIT;
     }
-  else
-      g_warning ("Failed to get page");
-}
-
-static void
-gb_clang_format_buffer_addin_save_file (IdeBufferAddin *addin,
-                                        IdeBuffer      *buffer,
-                                        GFile          *file)
-{
-  GbClangFormatBufferAddin *self;
 
-  if (format_on_save_enabled (buffer) == FALSE)
-      return;
+  if ((cursor_position = get_cursor_position (stdout_buf, formatted - stdout_buf)) < 0)
+    IDE_EXIT;
 
-  if (is_formattable_language (buffer) == FALSE)
-      return;
-
-  self = (GbClangFormatBufferAddin *)addin;
-  self->working_directory = project_root_directory (buffer);
-  if (self->working_directory == NULL)
-    {
-      g_warning ("Failed to get working directory");
-      return;
-    }
+  gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
+  gtk_text_buffer_set_text (GTK_TEXT_BUFFER (buffer), formatted + 1, -1);
+  gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (buffer), &iter, cursor_position);
+  gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer), &iter, &iter);
+  gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
 
-  if (clang_format_config_exists (self, buffer) == FALSE)
-    {
-      g_debug ("No .clang-format");
-      return;
-    }
-  self->cursor_position = get_cursor_position (buffer);
-  self->page = get_page (buffer);
+  workbench = ide_workbench_from_context (context);
+  ide_workbench_foreach_page (workbench, scroll_page_to_insert, buffer);
 
-  run_clang_format (self, buffer);
+  IDE_EXIT;
 }
 
 static void
 buffer_addin_iface_init (IdeBufferAddinInterface *iface)
 {
-  iface->load = NULL;
-  iface->unload = NULL;
   iface->save_file = gb_clang_format_buffer_addin_save_file;
-  iface->file_loaded = NULL;
 }
 
 G_DEFINE_FINAL_TYPE_WITH_CODE (GbClangFormatBufferAddin, gb_clang_format_buffer_addin, G_TYPE_OBJECT,
@@ -347,6 +190,7 @@ G_DEFINE_FINAL_TYPE_WITH_CODE (GbClangFormatBufferAddin, gb_clang_format_buffer_
 static void
 gb_clang_format_buffer_addin_class_init (GbClangFormatBufferAddinClass *klass)
 {
+  settings = g_settings_new ("org.gnome.builder");
 }
 
 static void


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