[gnome-builder] app: add handler to open files from command line



commit bec629a39757c0b8f1841683dc6467e9b350b1ae
Author: Christian Hergert <chergert redhat com>
Date:   Thu Jun 30 13:30:48 2016 -0700

    app: add handler to open files from command line
    
    This fixes support for `gnome-builder path/to/some/file.c`. It will try to
    locate the project (which might be path/configure.ac) and load it first,
    the the file within that workbench.
    
    However, Builder currently does a lot of setup work during startup (which
    should be improved in followup commits). So this will have a bit of a delay
    while starting up and is not as fast as say, running `gedit foo.c`.
    
    In particular, we should be able to avoid scanning for recent files in this
    situation.

 libide/Makefile.am                           |    1 +
 libide/application/ide-application-open.c    |  251 ++++++++++++++++++++++++++
 libide/application/ide-application-private.h |   10 +
 libide/application/ide-application.c         |    7 +
 4 files changed, 269 insertions(+), 0 deletions(-)
---
diff --git a/libide/Makefile.am b/libide/Makefile.am
index 639477b..91459fb 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -164,6 +164,7 @@ libide_1_0_la_public_sources =                            \
        application/ide-application-addin.c               \
        application/ide-application-tool.c                \
        application/ide-application.c                     \
+       application/ide-application-open.c                \
        buffers/ide-buffer-change-monitor.c               \
        buffers/ide-buffer-manager.c                      \
        buffers/ide-buffer.c                              \
diff --git a/libide/application/ide-application-open.c b/libide/application/ide-application-open.c
new file mode 100644
index 0000000..1f81420
--- /dev/null
+++ b/libide/application/ide-application-open.c
@@ -0,0 +1,251 @@
+/* ide-application-open.c
+ *
+ * Copyright (C) 2016 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/>.
+ */
+
+#define G_LOG_DOMAIN "ide-application-open"
+
+#include "application/ide-application.h"
+#include "application/ide-application-private.h"
+#include "workbench/ide-workbench.h"
+#include "vcs/ide-vcs.h"
+
+typedef struct
+{
+  GPtrArray *files;
+  gchar     *hint;
+} IdeApplicationOpen;
+
+static void ide_application_open_tick (GTask *task);
+
+static void
+ide_application_open_free (gpointer data)
+{
+  IdeApplicationOpen *state = data;
+
+  g_free (state->hint);
+  g_ptr_array_unref (state->files);
+  g_slice_free (IdeApplicationOpen, state);
+}
+
+static gboolean
+workbench_manages_file (IdeWorkbench *workbench,
+                        GFile        *file)
+{
+  IdeContext *context;
+  IdeVcs *vcs;
+  GFile *workdir;
+
+  g_assert (IDE_IS_WORKBENCH (workbench));
+  g_assert (G_IS_FILE (file));
+
+  if (NULL == (context = ide_workbench_get_context (workbench)))
+    return FALSE;
+
+  vcs = ide_context_get_vcs (context);
+  workdir = ide_vcs_get_working_directory (vcs);
+
+  return g_file_has_prefix (file, workdir);
+}
+
+static gboolean
+maybe_open_with_existing_workspace (IdeApplication *self,
+                                    GFile          *file,
+                                    const gchar    *hint,
+                                    GCancellable   *cancellable)
+{
+  GList *windows;
+  GList *iter;
+
+  g_assert (IDE_IS_APPLICATION (self));
+  g_assert (G_IS_FILE (file));
+
+  windows = gtk_application_get_windows (GTK_APPLICATION (self));
+
+  for (iter = windows; iter != NULL; iter = iter->next)
+    {
+      GtkWindow *window = iter->data;
+
+      if (IDE_IS_WORKBENCH (window) &&
+          workbench_manages_file (IDE_WORKBENCH (window), file))
+        {
+          ide_workbench_open_files_async (IDE_WORKBENCH (window),
+                                          &file,
+                                          1,
+                                          hint,
+                                          0,
+                                          cancellable,
+                                          NULL,
+                                          NULL);
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+static void
+ide_application_open_project_cb (GObject      *object,
+                                 GAsyncResult *result,
+                                 gpointer      user_data)
+{
+  IdeWorkbench *workbench = (IdeWorkbench *)object;
+  IdeApplicationOpen *state;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GFile) file = NULL;
+
+  g_assert (IDE_IS_WORKBENCH (workbench));
+  g_assert (G_IS_TASK (task));
+
+  state = g_task_get_task_data (task);
+
+  file = g_object_ref (g_ptr_array_index (state->files, state->files->len - 1));
+  g_ptr_array_remove_index (state->files, state->files->len - 1);
+
+  if (!ide_workbench_open_project_finish (workbench, result, &error))
+    g_warning ("%s", error->message);
+  else
+    ide_workbench_open_files_async (workbench, &file, 1, state->hint, 0, NULL, NULL, NULL);
+
+  ide_application_open_tick (task);
+}
+
+static void
+ide_application_open_tick (GTask *task)
+{
+  IdeApplication *self;
+  IdeApplicationOpen *state;
+  IdeWorkbench *workbench;
+  GCancellable *cancellable;
+  GFile *next;
+  guint i;
+
+  g_assert (G_IS_TASK (task));
+
+  self = g_task_get_source_object (task);
+  state = g_task_get_task_data (task);
+  cancellable = g_task_get_cancellable (task);
+
+  g_assert (IDE_IS_APPLICATION (self));
+  g_assert (state != NULL);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  /*
+   * Try to open each of our available files with an existing workspace
+   * since we could have gotten a new workspace since the last file
+   * we opened.
+   */
+
+  for (i = state->files->len; i > 0; i--)
+    {
+      GFile *file = g_ptr_array_index (state->files, i - 1);
+
+      /*
+       * We walk backwards via the array so we can safely remove
+       * items as we go. We could do remove_index_fast(), but it
+       * seems worthwhile to preserve the stack ordering as much
+       * as possible. This way, the files are shown in the editor
+       * with a similar stacking to the request.
+       */
+      if (maybe_open_with_existing_workspace (self, file, state->hint, cancellable))
+        g_ptr_array_remove_index (state->files, i - 1);
+    }
+
+  /*
+   * If we have no files left, we can complete the task now.
+   */
+  if (state->files->len == 0)
+    {
+      g_task_return_boolean (task, TRUE);
+      return;
+    }
+
+  /*
+   * Try to open the next file in the list, which will result in a
+   * new workbench being loaded (and therefore might allow us to
+   * open further files in that workbench).
+   */
+
+  next = g_ptr_array_index (state->files, state->files->len - 1);
+
+  workbench = g_object_new (IDE_TYPE_WORKBENCH,
+                            "application", self,
+                            NULL);
+
+  ide_workbench_open_project_async (workbench,
+                                    next,
+                                    cancellable,
+                                    ide_application_open_project_cb,
+                                    g_object_ref (task));
+
+  gtk_window_present (GTK_WINDOW (workbench));
+}
+
+void
+ide_application_open_async (IdeApplication       *self,
+                            GFile               **files,
+                            gint                  n_files,
+                            const gchar          *hint,
+                            GCancellable         *cancellable,
+                            GAsyncReadyCallback   callback,
+                            gpointer              user_data)
+{
+  g_autoptr(GTask) task = NULL;
+  g_autoptr(GPtrArray) ar = NULL;
+  IdeApplicationOpen *state;
+  guint i;
+
+  g_return_if_fail (IDE_IS_APPLICATION (self));
+  g_return_if_fail (!n_files || files != NULL);
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_application_open_async);
+  g_task_set_check_cancellable (task, FALSE);
+
+  /*
+   * We have to open each file one at a time so that we don't race to
+   * open the same containing project multiple times.
+   */
+
+  ar = g_ptr_array_new_with_free_func (g_object_unref);
+
+  for (i = 0; i < n_files; i++)
+    {
+      GFile *file = files [i];
+
+      if (!maybe_open_with_existing_workspace (self, file, hint, cancellable))
+        g_ptr_array_add (ar, g_object_ref (file));
+    }
+
+  state = g_slice_new0 (IdeApplicationOpen);
+  state->hint = g_strdup (hint);
+  state->files = g_steal_pointer (&ar);
+
+  g_task_set_task_data (task, state, ide_application_open_free);
+  ide_application_open_tick (task);
+}
+
+gboolean
+ide_application_open_finish (IdeApplication  *self,
+                             GAsyncResult    *result,
+                             GError         **error)
+{
+  g_return_val_if_fail (IDE_IS_APPLICATION (self), FALSE);
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
diff --git a/libide/application/ide-application-private.h b/libide/application/ide-application-private.h
index 5e299ff..3988ae6 100644
--- a/libide/application/ide-application-private.h
+++ b/libide/application/ide-application-private.h
@@ -75,6 +75,16 @@ gboolean ide_application_local_command_line         (GApplication          *appl
                                                      gint                  *exit_status) G_GNUC_INTERNAL;
 void     ide_application_run_tests                  (IdeApplication        *self);
 gboolean ide_application_get_disable_theme_tracking (IdeApplication        *self) G_GNUC_INTERNAL;
+void     ide_application_open_async                 (IdeApplication        *self,
+                                                     GFile                **files,
+                                                     gint                   n_files,
+                                                     const gchar           *hint,
+                                                     GCancellable          *cancellable,
+                                                     GAsyncReadyCallback    callback,
+                                                     gpointer               user_data);
+gboolean ide_application_open_finish                (IdeApplication        *self,
+                                                     GAsyncResult          *reuslt,
+                                                     GError               **error);
 
 G_END_DECLS
 
diff --git a/libide/application/ide-application.c b/libide/application/ide-application.c
index 89df18b..7514eb1 100644
--- a/libide/application/ide-application.c
+++ b/libide/application/ide-application.c
@@ -396,6 +396,13 @@ ide_application_open (GApplication  *application,
 {
   g_assert (IDE_IS_APPLICATION (application));
 
+  ide_application_open_async (IDE_APPLICATION (application),
+                              files,
+                              n_files,
+                              hint,
+                              NULL,
+                              NULL,
+                              NULL);
 }
 
 static void


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