[gnome-builder] app: add handler to open files from command line
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] app: add handler to open files from command line
- Date: Thu, 30 Jun 2016 20:32:07 +0000 (UTC)
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]