[gnome-builder] beautifier plugin: add support for generic commands



commit 26db3f4613230b8f90d4751ab546364748997d6a
Author: Sebastien Lafargue <slafargue gnome org>
Date:   Sun Dec 4 00:31:43 2016 +0100

    beautifier plugin: add support for generic commands
    
    in config.ini files, you can now use
    command-pattern key and commandline like pattern:
    
    the command name is used as the first argument
    
    @c@ is replaced by the config file path
    @s@ is replaced by the selected text file
    
    for example:
    
    command-pattern = uncrustify -c @c@ -f @s@

 .beautifier/c/config.ini                   |    5 +
 plugins/beautifier/gb-beautifier-config.c  |   85 +++++++++++++------
 plugins/beautifier/gb-beautifier-config.h  |    5 +-
 plugins/beautifier/gb-beautifier-process.c |  126 +++++++++++++++++++++++-----
 4 files changed, 171 insertions(+), 50 deletions(-)
---
diff --git a/.beautifier/c/config.ini b/.beautifier/c/config.ini
index 22f9bf3..3b79ccf 100644
--- a/.beautifier/c/config.ini
+++ b/.beautifier/c/config.ini
@@ -14,3 +14,8 @@ name = GNOME Builder project
 
 command = clang-format
 name = GNOME Builder project clang-format
+
+[fake.cfg]
+
+command-pattern = uncrustify -c @c@ -f @s@
+name = GNOME Builder project pattern
diff --git a/plugins/beautifier/gb-beautifier-config.c b/plugins/beautifier/gb-beautifier-config.c
index d2c5d48..73f0aac 100644
--- a/plugins/beautifier/gb-beautifier-config.c
+++ b/plugins/beautifier/gb-beautifier-config.c
@@ -16,6 +16,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <string.h>
 #include <ide.h>
 
 #include "gb-beautifier-private.h"
@@ -31,6 +32,9 @@ config_entry_clear_func (gpointer data)
   g_object_unref (entry->file);
   g_free (entry->name);
   g_free (entry->lang_id);
+
+  if (entry->command_args != NULL)
+    g_ptr_array_unref (entry->command_args);
 }
 
 static void
@@ -132,7 +136,14 @@ add_entries_from_config_ini_file (GbBeautifierWorkbenchAddin *self,
         {
           g_autofree gchar *display_name = NULL;
           g_autofree gchar *command = NULL;
+          g_autofree gchar *command_pattern = NULL;
+          g_autoptr(GFile) file = NULL;
+          g_autofree gchar *config_path = NULL;
+          g_auto(GStrv) strv = NULL;
+          gint argc;
           gchar *profile;
+          gboolean has_command;
+          gboolean has_command_pattern;
 
           profile = profiles [i];
           if (0 == g_strcmp0 (profile, "global"))
@@ -149,41 +160,65 @@ add_entries_from_config_ini_file (GbBeautifierWorkbenchAddin *self,
           if (gb_beautifier_config_check_duplicates (self, entries, lang_id, display_name))
             continue;
 
-          if ( NULL != (command = g_key_file_get_string (key_file, profile, "command", &error)))
+          has_command = g_key_file_has_key (key_file, profile, "command", &error);
+          has_command_pattern = g_key_file_has_key (key_file, profile, "command-pattern", &error);
+          if (!has_command && !has_command_pattern)
             {
-              g_autoptr(GFile) file = NULL;
-              g_autofree gchar *file_path = NULL;
+              g_warning ("beautifier plugin: neither command nor command-pattern keys found");
+              g_warning ("entry \"%s\" disabled", display_name);
+              continue;
+            }
 
-              if (0 == g_strcmp0 (command, "uncrustify"))
-                entry.command = GB_BEAUTIFIER_CONFIG_COMMAND_UNCRUSTIFY;
-              else if (0 == g_strcmp0 (command, "clang-format"))
+          memset (&entry, 0, sizeof(GbBeautifierConfigEntry));
+          if (has_command)
+            {
+              command = g_key_file_get_string (key_file, profile, "command", &error);
+              if (0 == g_strcmp0 (command, "clang-format"))
                 entry.command = GB_BEAUTIFIER_CONFIG_COMMAND_CLANG_FORMAT;
               else
-                goto fail;
+                {
+                  g_warning ("beautifier plugin: command key out of possible values");
+                  g_warning ("entry \"%s\" disabled", display_name);
+                  continue;
+                }
+            }
+          else
+            {
+              command_pattern = g_key_file_get_string (key_file, profile, "command-pattern", &error);
+              if (!g_shell_parse_argv (command_pattern, &argc, &strv, &error))
+                {
+                  g_warning ("beautifier plugin: \"%s\"", error->message);
+                  return FALSE;
+                }
+
+              entry.command = GB_BEAUTIFIER_CONFIG_COMMAND_NONE;
+              entry.command_args = g_ptr_array_new_with_free_func (g_free);
+              for (gint j = 0; strv [j] != NULL; ++j)
+                g_ptr_array_add (entry.command_args, g_strdup (strv [j]));
+
+              g_ptr_array_add (entry.command_args, NULL);
+            }
+
+          config_path = g_build_filename (base_path, real_lang_id, profile, NULL);
+          file = g_file_new_for_path (config_path);
+          if (g_file_query_exists (file, NULL))
+            {
+              entry.name = g_steal_pointer (&display_name);
+              entry.file = g_steal_pointer (&file);
+              entry.lang_id = g_strdup (lang_id);
 
-              file_path = g_build_filename (base_path, real_lang_id, profile, NULL);
-              file = g_file_new_for_path (file_path);
-              if (g_file_query_exists (file, NULL))
+              if (0 == g_strcmp0 (default_profile, profile))
                 {
-                  entry.name = g_steal_pointer (&display_name);
-                  entry.file = g_steal_pointer (&file);
-                  entry.lang_id = g_strdup (lang_id);
-
-                  if (0 == g_strcmp0 (default_profile, profile))
-                    {
-                      entry.is_default = TRUE;
-                      g_clear_pointer (&default_profile, g_free);
-                    }
-                  else
-                    entry.is_default = FALSE;
-
-                  g_array_append_val (entries, entry);
+                  entry.is_default = TRUE;
+                  g_clear_pointer (&default_profile, g_free);
                 }
               else
-                g_warning ("Can't find \"%s\"", file_path);
+                entry.is_default = FALSE;
+
+              g_array_append_val (entries, entry);
             }
           else
-             goto fail;
+            g_warning ("Can't find \"%s\"", config_path);
         }
     }
 
diff --git a/plugins/beautifier/gb-beautifier-config.h b/plugins/beautifier/gb-beautifier-config.h
index 94d8cfe..53b9642 100644
--- a/plugins/beautifier/gb-beautifier-config.h
+++ b/plugins/beautifier/gb-beautifier-config.h
@@ -27,8 +27,8 @@ G_BEGIN_DECLS
 
 typedef enum
 {
-  GB_BEAUTIFIER_CONFIG_COMMAND_CLANG_FORMAT,
-  GB_BEAUTIFIER_CONFIG_COMMAND_UNCRUSTIFY,
+  GB_BEAUTIFIER_CONFIG_COMMAND_NONE,
+  GB_BEAUTIFIER_CONFIG_COMMAND_CLANG_FORMAT
 } GbBeautifierConfigCommand;
 
 typedef struct
@@ -37,6 +37,7 @@ typedef struct
   GFile                     *file;
   gchar                     *name;
   GbBeautifierConfigCommand  command;
+  GPtrArray                 *command_args;
   guint                      is_default : 1;
 } GbBeautifierConfigEntry;
 
diff --git a/plugins/beautifier/gb-beautifier-process.c b/plugins/beautifier/gb-beautifier-process.c
index 2c7dace..0b9e137 100644
--- a/plugins/beautifier/gb-beautifier-process.c
+++ b/plugins/beautifier/gb-beautifier-process.c
@@ -32,6 +32,7 @@ typedef struct
   GtkTextMark                *begin_mark;
   GtkTextMark                *end_mark;
   GbBeautifierConfigCommand   command;
+  GPtrArray                  *command_args;
   GFile                      *src_file;
   GFile                      *config_file;
   GFile                      *tmp_workdir_file;
@@ -68,44 +69,101 @@ process_state_free (gpointer data)
   g_free (state->lang_id);
   g_free (state->text);
 
+  if (state->command_args != NULL)
+    g_ptr_array_unref (state->command_args);
+
   g_slice_free (ProcessState, state);
 }
 
+static gchar *
+match_and_replace (const gchar *str,
+                   const gchar *pattern,
+                   const gchar *replacement)
+{
+  g_autofree gchar *head = NULL;
+  g_autofree gchar *tail = NULL;
+  gchar *needle;
+  gsize head_len;
+
+  g_assert (!ide_str_empty0 (str));
+  g_assert (!ide_str_empty0 (pattern));
+
+  if (NULL != (needle = g_strstr_len (str, -1, pattern)))
+    {
+      head_len = needle - str;
+      if (head_len > 0)
+        head = g_strndup (str, head_len);
+      else
+        head = g_strdup ("");
+
+      tail = needle + strlen (pattern);
+      if (*tail != '\0')
+        tail = g_strdup (tail);
+      else
+        tail = g_strdup ("");
+
+      return g_strconcat (head, replacement, tail, NULL);
+    }
+  else
+    return NULL;
+}
+
+static void
+command_args_expand (GbBeautifierWorkbenchAddin *self,
+                     GPtrArray                  *args,
+                     ProcessState               *state)
+{
+  g_autofree gchar *src_path = NULL;
+  g_autofree gchar *config_path = NULL;
+  gchar **arg_adr;
+  gchar *new_arg;
+  gboolean has_config = TRUE;
+
+  src_path = g_file_get_path (state->src_file);
+  if (G_IS_FILE (state->config_file))
+    config_path = g_file_get_path (state->config_file);
+  else
+    has_config = FALSE;
+
+  for (gint i = 0; g_ptr_array_index (args, i) != NULL; ++i)
+    {
+      arg_adr = (gchar **)&g_ptr_array_index (args, i);
+      if (NULL != (new_arg = match_and_replace (*arg_adr, "@s@", src_path)))
+        {
+          g_free (*arg_adr);
+          *arg_adr = new_arg;
+        }
+      else if (has_config &&
+               NULL != (new_arg = match_and_replace (*arg_adr, "@c@", config_path)))
+        {
+          g_free (*arg_adr);
+          *arg_adr = new_arg;
+        }
+    }
+}
+
 static GSubprocess *
-gb_beautifier_process_create_for_uncrustify (GbBeautifierWorkbenchAddin *self,
-                                             ProcessState               *state,
-                                             GError                     *error)
+gb_beautifier_process_create_generic (GbBeautifierWorkbenchAddin *self,
+                                      ProcessState               *state,
+                                      GError                     *error)
 {
   GSubprocess *subprocess = NULL;
-  GPtrArray *args;
-  gchar *config_path;
   gchar *src_path;
 
   g_assert (GB_IS_BEAUTIFIER_WORKBENCH_ADDIN (self));
   g_assert (state != NULL);
 
-  config_path = g_file_get_path (state->config_file);
   src_path = g_file_get_path (state->src_file);
 
-  g_assert (!ide_str_empty0 (config_path));
   g_assert (!ide_str_empty0 (src_path));
   g_assert (!ide_str_empty0 (state->lang_id));
 
-  args = g_ptr_array_new ();
-  g_ptr_array_add (args, "uncrustify");
-
-  g_ptr_array_add (args, "-c");
-  g_ptr_array_add (args, config_path);
-  g_ptr_array_add (args, "-f");
-  g_ptr_array_add (args, src_path);
-  g_ptr_array_add (args, NULL);
-
-  subprocess = g_subprocess_newv ((const gchar * const *)args->pdata,
+  command_args_expand (self, state->command_args, state);
+  subprocess = g_subprocess_newv ((const gchar * const *)state->command_args->pdata,
                                   G_SUBPROCESS_FLAGS_STDOUT_PIPE |
                                   G_SUBPROCESS_FLAGS_STDERR_PIPE,
                                   &error);
 
-  g_ptr_array_free (args, TRUE);
   return subprocess;
 }
 
@@ -184,6 +242,7 @@ process_communicate_utf8_cb (GObject      *object,
   g_autoptr (GSubprocess) process = (GSubprocess *)object;
   g_autoptr (GTask) task = (GTask *)user_data;
   g_autofree gchar *stdout_str = NULL;
+  g_autofree gchar *stderr_str = NULL;
   g_autoptr(GError) error = NULL;
   GtkSourceCompletion *completion;
   GtkTextBuffer *buffer;
@@ -195,7 +254,7 @@ process_communicate_utf8_cb (GObject      *object,
   g_assert (G_IS_ASYNC_RESULT (result));
   g_assert (G_IS_TASK (task));
 
-  if (!g_subprocess_communicate_utf8_finish (process, result, &stdout_str, NULL, &error))
+  if (!g_subprocess_communicate_utf8_finish (process, result, &stdout_str, &stderr_str, &error))
     {
       g_task_return_error (task, g_steal_pointer (&error));
       return;
@@ -229,6 +288,11 @@ process_communicate_utf8_cb (GObject      *object,
 
       g_task_return_boolean (task, TRUE);
     }
+  else
+    g_warning ("beautify plugin: output empty\n");
+
+  g_warning ("beautify plugin stderr:\n%s\n", stderr_str);
+
 }
 
 static void
@@ -251,12 +315,10 @@ create_tmp_file_cb (GObject      *object,
   if (NULL == (state->src_file = gb_beautifier_helper_create_tmp_file_finish (self, result, &error)))
     goto fail;
 
-  if (state->command == GB_BEAUTIFIER_CONFIG_COMMAND_UNCRUSTIFY)
-    process = gb_beautifier_process_create_for_uncrustify (self, state, error);
-  else if (state->command == GB_BEAUTIFIER_CONFIG_COMMAND_CLANG_FORMAT)
+  if (state->command == GB_BEAUTIFIER_CONFIG_COMMAND_CLANG_FORMAT)
     process = gb_beautifier_process_create_for_clang_format (self, state, error);
   else
-    g_assert_not_reached ();
+    process = gb_beautifier_process_create_generic (self, state, error);
 
   if (process != NULL)
     {
@@ -280,6 +342,22 @@ fail:
   return;
 }
 
+static GPtrArray *
+command_args_copy (GPtrArray *args)
+{
+  GPtrArray *args_copy;
+
+  g_assert (args != NULL);
+
+  args_copy = g_ptr_array_new_with_free_func (g_free);
+  for (gint i = 0; g_ptr_array_index (args, i) != NULL; ++i)
+    g_ptr_array_add (args_copy, g_strdup (g_ptr_array_index (args, i)));
+
+  g_ptr_array_add (args_copy, NULL);
+
+  return args_copy;
+}
+
 void
 gb_beautifier_process_launch_async (GbBeautifierWorkbenchAddin  *self,
                                     IdeSourceView               *source_view,
@@ -325,6 +403,8 @@ gb_beautifier_process_launch_async (GbBeautifierWorkbenchAddin  *self,
   state->config_file = g_file_dup (entry->file);
   state->lang_id = g_strdup (lang_id);
 
+  if (entry->command_args != NULL)
+    state->command_args = command_args_copy (entry->command_args);
   task = g_task_new (self, cancellable, callback, user_data);
   g_task_set_source_tag (task, gb_beautifier_process_launch_async);
   g_task_set_task_data (task, state, process_state_free);


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