[gnome-builder/wip/chergert/shellcmd: 7/7] shellcmd: wip on a shellcmd command provider
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/chergert/shellcmd: 7/7] shellcmd: wip on a shellcmd command provider
- Date: Tue, 6 Aug 2019 23:02:38 +0000 (UTC)
commit 2d79735355e540b1acc711e5f8b808971edd1d56
Author: Christian Hergert <chergert redhat com>
Date: Sun Aug 4 23:41:00 2019 -0700
shellcmd: wip on a shellcmd command provider
meson_options.txt | 1 +
src/plugins/meson.build | 1 +
.../shellcmd/gbp-shellcmd-command-provider.c | 134 ++++
.../shellcmd/gbp-shellcmd-command-provider.h | 31 +
src/plugins/shellcmd/gbp-shellcmd-command.c | 780 +++++++++++++++++++++
src/plugins/shellcmd/gbp-shellcmd-command.h | 58 ++
src/plugins/shellcmd/meson.build | 29 +
src/plugins/shellcmd/shellcmd-plugin.c | 34 +
src/plugins/shellcmd/shellcmd.gresource.xml | 6 +
src/plugins/shellcmd/shellcmd.plugin | 10 +
10 files changed, 1084 insertions(+)
---
diff --git a/meson_options.txt b/meson_options.txt
index 36fcfc969..8984e9f69 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -65,6 +65,7 @@ option('plugin_quick_highlight', type: 'boolean')
option('plugin_retab', type: 'boolean')
option('plugin_rls', type: 'boolean')
option('plugin_rustup', type: 'boolean')
+option('plugin_shellcmd', type: 'boolean')
option('plugin_spellcheck', type: 'boolean')
option('plugin_sysprof', type: 'boolean')
option('plugin_sysroot', type: 'boolean')
diff --git a/src/plugins/meson.build b/src/plugins/meson.build
index 17583bf9e..3731824aa 100644
--- a/src/plugins/meson.build
+++ b/src/plugins/meson.build
@@ -105,6 +105,7 @@ subdir('restore-cursor')
subdir('retab')
subdir('rls')
subdir('rustup')
+subdir('shellcmd')
subdir('snippets')
subdir('spellcheck')
subdir('sublime')
diff --git a/src/plugins/shellcmd/gbp-shellcmd-command-provider.c
b/src/plugins/shellcmd/gbp-shellcmd-command-provider.c
new file mode 100644
index 000000000..0f6e9593d
--- /dev/null
+++ b/src/plugins/shellcmd/gbp-shellcmd-command-provider.c
@@ -0,0 +1,134 @@
+/* gbp-shellcmd-command-provider.c
+ *
+ * Copyright 2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-shellcmd-command-provider"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <libide-threading.h>
+
+#include "gbp-shellcmd-command.h"
+#include "gbp-shellcmd-command-provider.h"
+
+struct _GbpShellcmdCommandProvider
+{
+ GObject parent_instance;
+};
+
+static void
+gbp_shellcmd_command_provider_query_async (IdeCommandProvider *provider,
+ IdeWorkspace *workspace,
+ const gchar *typed_text,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GbpShellcmdCommandProvider *self = (GbpShellcmdCommandProvider *)provider;
+ g_autoptr(IdeTask) task = NULL;
+ g_autoptr(GPtrArray) ret = NULL;
+ g_autofree gchar *bash_c = NULL;
+ g_autofree gchar *quoted = NULL;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND_PROVIDER (provider));
+ g_assert (IDE_IS_WORKSPACE (workspace));
+ g_assert (typed_text != NULL);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = ide_task_new (self, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, gbp_shellcmd_command_provider_query_async);
+
+ quoted = g_shell_quote (typed_text);
+ bash_c = g_strdup_printf ("/bin/sh -c %s", quoted);
+
+ ret = g_ptr_array_new_with_free_func ((GDestroyNotify)ide_object_unref_and_destroy);
+
+ g_ptr_array_add (ret,
+ g_object_new (GBP_TYPE_SHELLCMD_COMMAND,
+ "title", _("Run in host environment"),
+ "subtitle", typed_text,
+ "command", bash_c,
+ "locality", GBP_SHELLCMD_COMMAND_LOCALITY_HOST,
+ NULL));
+
+ g_ptr_array_add (ret,
+ g_object_new (GBP_TYPE_SHELLCMD_COMMAND,
+ "title", _("Run in build environment"),
+ "subtitle", typed_text,
+ "command", bash_c,
+ "locality", GBP_SHELLCMD_COMMAND_LOCALITY_BUILD,
+ NULL));
+
+ g_ptr_array_add (ret,
+ g_object_new (GBP_TYPE_SHELLCMD_COMMAND,
+ "title", _("Run in runtime environment"),
+ "subtitle", typed_text,
+ "command", bash_c,
+ "locality", GBP_SHELLCMD_COMMAND_LOCALITY_RUN,
+ NULL));
+
+ ide_task_return_pointer (task,
+ g_steal_pointer (&ret),
+ g_ptr_array_unref);
+}
+
+static GPtrArray *
+gbp_shellcmd_command_provider_query_finish (IdeCommandProvider *provider,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_autoptr(GPtrArray) ret = NULL;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND_PROVIDER (provider));
+ g_assert (IDE_IS_TASK (result));
+
+ ret = ide_task_propagate_pointer (IDE_TASK (result), error);
+ return IDE_PTR_ARRAY_STEAL_FULL (&ret);
+}
+
+static void
+command_provider_iface_init (IdeCommandProviderInterface *iface)
+{
+ iface->query_async = gbp_shellcmd_command_provider_query_async;
+ iface->query_finish = gbp_shellcmd_command_provider_query_finish;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GbpShellcmdCommandProvider, gbp_shellcmd_command_provider, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (IDE_TYPE_COMMAND_PROVIDER,
+ command_provider_iface_init))
+
+static void
+gbp_shellcmd_command_provider_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (gbp_shellcmd_command_provider_parent_class)->finalize (object);
+}
+
+static void
+gbp_shellcmd_command_provider_class_init (GbpShellcmdCommandProviderClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gbp_shellcmd_command_provider_finalize;
+}
+
+static void
+gbp_shellcmd_command_provider_init (GbpShellcmdCommandProvider *self)
+{
+}
diff --git a/src/plugins/shellcmd/gbp-shellcmd-command-provider.h
b/src/plugins/shellcmd/gbp-shellcmd-command-provider.h
new file mode 100644
index 000000000..b744937a1
--- /dev/null
+++ b/src/plugins/shellcmd/gbp-shellcmd-command-provider.h
@@ -0,0 +1,31 @@
+/* gbp-shellcmd-command-provider.h
+ *
+ * Copyright 2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-gui.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_SHELLCMD_COMMAND_PROVIDER (gbp_shellcmd_command_provider_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpShellcmdCommandProvider, gbp_shellcmd_command_provider, GBP,
SHELLCMD_COMMAND_PROVIDER, GObject)
+
+G_END_DECLS
diff --git a/src/plugins/shellcmd/gbp-shellcmd-command.c b/src/plugins/shellcmd/gbp-shellcmd-command.c
new file mode 100644
index 000000000..7d2e71857
--- /dev/null
+++ b/src/plugins/shellcmd/gbp-shellcmd-command.c
@@ -0,0 +1,780 @@
+/* gbp-shellcmd-command.c
+ *
+ * Copyright 2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-shellcmd-command"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <libide-gui.h>
+#include <libide-editor.h>
+#include <libide-terminal.h>
+#include <libide-threading.h>
+
+#include "ide-gui-private.h"
+
+#include "gbp-shellcmd-command.h"
+#include "gbp-shellcmd-enums.h"
+
+struct _GbpShellcmdCommand
+{
+ IdeObject parent_instance;
+ GbpShellcmdCommandLocality locality;
+ gchar *shortcut;
+ gchar *subtitle;
+ gchar *title;
+ gchar *command;
+ gchar *cwd;
+ IdeEnvironment *environment;
+};
+
+enum {
+ PROP_0,
+ PROP_COMMAND,
+ PROP_CWD,
+ PROP_ENVIRONMENT,
+ PROP_LOCALITY,
+ PROP_SHORTCUT,
+ PROP_SUBTITLE,
+ PROP_TITLE,
+ N_PROPS
+};
+
+static void command_iface_init (IdeCommandInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GbpShellcmdCommand, gbp_shellcmd_command, IDE_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (IDE_TYPE_COMMAND, command_iface_init))
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+gbp_shellcmd_command_finalize (GObject *object)
+{
+ GbpShellcmdCommand *self = (GbpShellcmdCommand *)object;
+
+ g_clear_pointer (&self->shortcut, g_free);
+ g_clear_pointer (&self->title, g_free);
+ g_clear_pointer (&self->command, g_free);
+ g_clear_pointer (&self->cwd, g_free);
+ g_clear_object (&self->environment);
+
+ G_OBJECT_CLASS (gbp_shellcmd_command_parent_class)->finalize (object);
+}
+
+static void
+gbp_shellcmd_command_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GbpShellcmdCommand *self = GBP_SHELLCMD_COMMAND (object);
+
+ switch (prop_id)
+ {
+ case PROP_COMMAND:
+ g_value_set_string (value, gbp_shellcmd_command_get_command (self));
+ break;
+
+ case PROP_ENVIRONMENT:
+ g_value_set_object (value, gbp_shellcmd_command_get_environment (self));
+ break;
+
+ case PROP_CWD:
+ g_value_set_string (value, gbp_shellcmd_command_get_cwd (self));
+ break;
+
+ case PROP_LOCALITY:
+ g_value_set_enum (value, gbp_shellcmd_command_get_locality (self));
+ break;
+
+ case PROP_SHORTCUT:
+ g_value_set_string (value, gbp_shellcmd_command_get_shortcut (self));
+ break;
+
+ case PROP_SUBTITLE:
+ g_value_take_string (value, ide_command_get_subtitle (IDE_COMMAND (self)));
+ break;
+
+ case PROP_TITLE:
+ g_value_take_string (value, ide_command_get_title (IDE_COMMAND (self)));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gbp_shellcmd_command_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GbpShellcmdCommand *self = GBP_SHELLCMD_COMMAND (object);
+
+ switch (prop_id)
+ {
+ case PROP_COMMAND:
+ gbp_shellcmd_command_set_command (self, g_value_get_string (value));
+ break;
+
+ case PROP_CWD:
+ gbp_shellcmd_command_set_cwd (self, g_value_get_string (value));
+ break;
+
+ case PROP_LOCALITY:
+ gbp_shellcmd_command_set_locality (self, g_value_get_enum (value));
+ break;
+
+ case PROP_SHORTCUT:
+ gbp_shellcmd_command_set_shortcut (self, g_value_get_string (value));
+ break;
+
+ case PROP_SUBTITLE:
+ gbp_shellcmd_command_set_subtitle (self, g_value_get_string (value));
+ break;
+
+ case PROP_TITLE:
+ gbp_shellcmd_command_set_title (self, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gbp_shellcmd_command_class_init (GbpShellcmdCommandClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gbp_shellcmd_command_finalize;
+ object_class->get_property = gbp_shellcmd_command_get_property;
+ object_class->set_property = gbp_shellcmd_command_set_property;
+
+ properties [PROP_COMMAND] =
+ g_param_spec_string ("command",
+ "Command",
+ "The shell command to execute",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_CWD] =
+ g_param_spec_string ("cwd",
+ "CWD",
+ "The working directory for the command",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_ENVIRONMENT] =
+ g_param_spec_object ("environment",
+ "Environment",
+ "The environment variables for the command",
+ IDE_TYPE_ENVIRONMENT,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_LOCALITY] =
+ g_param_spec_enum ("locality",
+ "Locality",
+ "Where the command should be executed",
+ GBP_TYPE_SHELLCMD_COMMAND_LOCALITY,
+ GBP_SHELLCMD_COMMAND_LOCALITY_HOST,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_SHORTCUT] =
+ g_param_spec_string ("shortcut",
+ "Shortcut",
+ "The shortcut to use to activate the command",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_SUBTITLE] =
+ g_param_spec_string ("subtitle",
+ "Subtitle",
+ "The subtitle of the command for display purposes",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_TITLE] =
+ g_param_spec_string ("title",
+ "Title",
+ "The title of the command for display purposes",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+gbp_shellcmd_command_init (GbpShellcmdCommand *self)
+{
+}
+
+const gchar *
+gbp_shellcmd_command_get_cwd (GbpShellcmdCommand *self)
+{
+ g_return_val_if_fail (GBP_IS_SHELLCMD_COMMAND (self), NULL);
+
+ return self->cwd;
+}
+
+void
+gbp_shellcmd_command_set_cwd (GbpShellcmdCommand *self,
+ const gchar *cwd)
+{
+ g_return_if_fail (GBP_IS_SHELLCMD_COMMAND (self));
+
+ if (!ide_str_equal0 (cwd, self->cwd))
+ {
+ g_free (self->cwd);
+ self->cwd = g_strdup (cwd);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CWD]);
+ }
+}
+
+static gchar *
+gbp_shellcmd_command_get_title (IdeCommand *command)
+{
+ GbpShellcmdCommand *self = (GbpShellcmdCommand *)command;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND (self));
+
+ if (self->title == NULL)
+ return g_strdup (_("Shell command"));
+ else
+ return g_strdup (self->title);
+}
+
+static gchar *
+gbp_shellcmd_command_get_subtitle (IdeCommand *command)
+{
+ GbpShellcmdCommand *self = (GbpShellcmdCommand *)command;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND (self));
+
+ if (self->subtitle)
+ return g_strdup (self->subtitle);
+ else
+ return g_strdup (self->command);
+}
+
+static void
+gbp_shellcmd_command_wait_check_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeSubprocess *subprocess = (IdeSubprocess *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (IDE_IS_SUBPROCESS (subprocess));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ if (!ide_subprocess_wait_check_finish (subprocess, result, &error))
+ ide_task_return_error (task, g_steal_pointer (&error));
+ else
+ ide_task_return_boolean (task, TRUE);
+}
+
+static void
+gbp_shellcmd_command_apply (GbpShellcmdCommand *self,
+ IdeSubprocessLauncher *launcher,
+ GFile *relative_to)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GFile) cwd = NULL;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND (self));
+ g_assert (IDE_IS_SUBPROCESS_LAUNCHER (launcher));
+ g_assert (G_IS_FILE (relative_to));
+
+ if (self->cwd != NULL)
+ {
+ if (g_path_is_absolute (self->cwd))
+ cwd = g_file_new_for_path (self->cwd);
+ else
+ cwd = g_file_get_child (relative_to, self->cwd);
+ }
+ else
+ {
+ cwd = g_object_ref (relative_to);
+ }
+
+ ide_subprocess_launcher_set_cwd (launcher, g_file_peek_path (cwd));
+
+ if (self->environment != NULL)
+ {
+ g_auto(GStrv) env = ide_environment_get_environ (self->environment);
+
+ if (env != NULL)
+ {
+ for (guint i = 0; env[i]; i++)
+ {
+ g_autofree gchar *key = NULL;
+ g_autofree gchar *val = NULL;
+
+ if (ide_environ_parse (env[i], &key, &val))
+ ide_subprocess_launcher_setenv (launcher, key, val, TRUE);
+ }
+ }
+ }
+}
+
+static void
+gbp_shellcmd_command_run_host (GbpShellcmdCommand *self,
+ gchar **argv,
+ IdeTask *task)
+{
+ g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+ g_autoptr(IdeTerminalLauncher) tlauncher = NULL;
+ g_autoptr(IdeSubprocess) subprocess = NULL;
+ g_autoptr(IdeContext) context = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GFile) workdir = NULL;
+ IdeWorkspace *workspace;
+ IdeWorkbench *workbench;
+ IdeSurface *surface;
+ IdePage *page;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND (self));
+ g_assert (argv != NULL);
+ g_assert (IDE_IS_TASK (task));
+
+ context = ide_object_ref_context (IDE_OBJECT (self));
+ workdir = ide_context_ref_workdir (context);
+
+ if (!(workbench = _ide_workbench_from_context (context)) ||
+ (!(workspace = ide_workbench_get_workspace_by_type (workbench, IDE_TYPE_PRIMARY_WORKSPACE)) &&
+ !(workspace = ide_workbench_get_workspace_by_type (workbench, IDE_TYPE_EDITOR_WORKSPACE)) &&
+ !(workspace = ide_workbench_get_workspace_by_type (workbench, IDE_TYPE_TERMINAL_WORKSPACE))) ||
+ (!(surface = ide_workspace_get_surface_by_name (workspace, "editor")) &&
+ !(surface = ide_workspace_get_surface_by_name (workspace, "terminal"))))
+ {
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_FOUND,
+ "Failed to locate a workspace for the terminal page");
+ return;
+ }
+
+ launcher = ide_subprocess_launcher_new (0);
+
+ ide_subprocess_launcher_push_args (launcher, (const gchar * const *)argv);
+ ide_subprocess_launcher_set_run_on_host (launcher, TRUE);
+ ide_subprocess_launcher_set_clear_env (launcher, FALSE);
+
+ gbp_shellcmd_command_apply (self, launcher, workdir);
+
+ tlauncher = ide_terminal_launcher_new_for_launcher (launcher);
+ page = g_object_new (IDE_TYPE_TERMINAL_PAGE,
+ "close-on-exit", FALSE,
+ "launcher", tlauncher,
+ "manage-spawn", TRUE,
+ "respawn-on-exit", FALSE,
+ "visible", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (surface), GTK_WIDGET (page));
+
+ ide_task_return_boolean (task, TRUE);
+}
+
+static void
+gbp_shellcmd_command_run_app (GbpShellcmdCommand *self,
+ gchar **argv,
+ IdeTask *task)
+{
+ g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+ g_autoptr(IdeTerminalLauncher) tlauncher = NULL;
+ g_autoptr(IdeContext) context = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GFile) workdir = NULL;
+ IdeWorkspace *workspace;
+ IdeWorkbench *workbench;
+ IdeSurface *surface;
+ IdePage *page;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND (self));
+ g_assert (argv != NULL);
+ g_assert (IDE_IS_TASK (task));
+
+ context = ide_object_ref_context (IDE_OBJECT (self));
+ workdir = ide_context_ref_workdir (context);
+
+ if (!(workbench = _ide_workbench_from_context (context)) ||
+ (!(workspace = ide_workbench_get_workspace_by_type (workbench, IDE_TYPE_PRIMARY_WORKSPACE)) &&
+ !(workspace = ide_workbench_get_workspace_by_type (workbench, IDE_TYPE_EDITOR_WORKSPACE)) &&
+ !(workspace = ide_workbench_get_workspace_by_type (workbench, IDE_TYPE_TERMINAL_WORKSPACE))) ||
+ (!(surface = ide_workspace_get_surface_by_name (workspace, "editor")) &&
+ !(surface = ide_workspace_get_surface_by_name (workspace, "terminal"))))
+ {
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_FOUND,
+ "Failed to locate a workspace for the terminal page");
+ return;
+ }
+
+ launcher = ide_subprocess_launcher_new (0);
+
+ ide_subprocess_launcher_push_args (launcher, (const gchar * const *)argv);
+ ide_subprocess_launcher_set_run_on_host (launcher, FALSE);
+ ide_subprocess_launcher_set_clear_env (launcher, FALSE);
+
+ gbp_shellcmd_command_apply (self, launcher, workdir);
+
+ tlauncher = ide_terminal_launcher_new_for_launcher (launcher);
+ page = g_object_new (IDE_TYPE_TERMINAL_PAGE,
+ "close-on-exit", FALSE,
+ "launcher", tlauncher,
+ "manage-spawn", TRUE,
+ "respawn-on-exit", FALSE,
+ "visible", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (surface), GTK_WIDGET (page));
+
+ ide_task_return_boolean (task, TRUE);
+}
+
+static void
+gbp_shellcmd_command_run_runner (GbpShellcmdCommand *self,
+ gchar **argv,
+ IdeTask *task)
+{
+ g_autoptr(IdeTerminalLauncher) launcher = NULL;
+ g_autoptr(IdeContext) context = NULL;
+ g_autofree gchar *cwd = NULL;
+ IdeBuildManager *build_manager;
+ IdeWorkspace *workspace;
+ IdeWorkbench *workbench;
+ IdePipeline *pipeline;
+ IdeRuntime *runtime;
+ IdeSurface *surface;
+ IdePage *page;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND (self));
+ g_assert (argv != NULL);
+ g_assert (IDE_IS_TASK (task));
+
+ if (!(context = ide_object_ref_context (IDE_OBJECT (self))) ||
+ !(build_manager = ide_build_manager_from_context (context)) ||
+ !(pipeline = ide_build_manager_get_pipeline (build_manager)) ||
+ !(runtime = ide_pipeline_get_runtime (pipeline)))
+ {
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_INITIALIZED,
+ _("Cannot spawn terminal in runtime environment because build pipeline is
not initialized"));
+ return;
+ }
+
+ if (!(workbench = _ide_workbench_from_context (context)) ||
+ (!(workspace = ide_workbench_get_workspace_by_type (workbench, IDE_TYPE_PRIMARY_WORKSPACE)) &&
+ !(workspace = ide_workbench_get_workspace_by_type (workbench, IDE_TYPE_EDITOR_WORKSPACE)) &&
+ !(workspace = ide_workbench_get_workspace_by_type (workbench, IDE_TYPE_TERMINAL_WORKSPACE))) ||
+ (!(surface = ide_workspace_get_surface_by_name (workspace, "editor")) &&
+ !(surface = ide_workspace_get_surface_by_name (workspace, "terminal"))))
+ {
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_FOUND,
+ "Failed to locate a workspace for the terminal page");
+ return;
+ }
+
+ launcher = ide_terminal_launcher_new_for_runner (runtime);
+ ide_terminal_launcher_set_shell (launcher, argv[0]);
+ ide_terminal_launcher_set_args (launcher, (const gchar * const *)&argv[1]);
+
+ if (self->cwd != NULL)
+ {
+ if (g_path_is_absolute (self->cwd))
+ cwd = g_strdup (self->cwd);
+ else
+ ide_pipeline_build_builddir_path (pipeline, self->cwd, NULL);
+ }
+
+ if (cwd != NULL)
+ ide_terminal_launcher_set_cwd (launcher, cwd);
+ else
+ ide_terminal_launcher_set_cwd (launcher,
+ ide_pipeline_get_builddir (pipeline));
+
+ page = g_object_new (IDE_TYPE_TERMINAL_PAGE,
+ "close-on-exit", FALSE,
+ "launcher", launcher,
+ "manage-spawn", TRUE,
+ "respawn-on-exit", FALSE,
+ "visible", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (surface), GTK_WIDGET (page));
+
+ ide_task_return_boolean (task, TRUE);
+}
+
+static void
+gbp_shellcmd_command_run_build (GbpShellcmdCommand *self,
+ gchar **argv,
+ IdeTask *task)
+{
+ g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+ g_autoptr(IdeSubprocess) subprocess = NULL;
+ g_autoptr(IdeContext) context = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GFile) builddir = NULL;
+ IdeBuildManager *build_manager;
+ GCancellable *cancellable;
+ IdeWorkbench *workbench;
+ IdeWorkspace *workspace;
+ IdePipeline *pipeline;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND (self));
+ g_assert (argv != NULL);
+ g_assert (IDE_IS_TASK (task));
+
+ context = ide_object_ref_context (IDE_OBJECT (self));
+ workbench = _ide_workbench_from_context (context);
+ workspace = ide_workbench_get_current_workspace (workbench);
+ build_manager = ide_build_manager_from_context (context);
+ pipeline = ide_build_manager_get_pipeline (build_manager);
+
+ if (pipeline == NULL)
+ {
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_INITIALIZED,
+ _("Cannot spawn process because build pipeline is not yet available"));
+ return;
+ }
+
+ builddir = g_file_new_for_path (ide_pipeline_get_builddir (pipeline));
+ launcher = ide_pipeline_create_launcher (pipeline, &error);
+
+ if (launcher == NULL)
+ {
+ ide_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ ide_pipeline_attach_pty (pipeline, launcher);
+ ide_subprocess_launcher_push_args (launcher, (const gchar * const *)argv);
+
+ if (G_IS_ACTION_GROUP (workspace) &&
+ g_action_group_has_action (G_ACTION_GROUP (workspace), "view-output"))
+ dzl_gtk_widget_action (GTK_WIDGET (workspace), "win", "view-output", NULL);
+
+ gbp_shellcmd_command_apply (self, launcher, builddir);
+
+ cancellable = ide_task_get_cancellable (task);
+ subprocess = ide_subprocess_launcher_spawn (launcher, cancellable, &error);
+
+ if (subprocess == NULL)
+ ide_task_return_error (task, g_steal_pointer (&error));
+ else
+ ide_subprocess_wait_check_async (subprocess,
+ cancellable,
+ gbp_shellcmd_command_wait_check_cb,
+ g_object_ref (task));
+}
+
+static void
+gbp_shellcmd_command_run_async (IdeCommand *command,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GbpShellcmdCommand *self = (GbpShellcmdCommand *)command;
+ g_autoptr(IdeTask) task = NULL;
+ g_autoptr(GError) error = NULL;
+ g_auto(GStrv) argv = NULL;
+ gint argc = 0;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = ide_task_new (self, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, gbp_shellcmd_command_run_async);
+
+ if (self->command == NULL)
+ {
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "No command to execute");
+ return;
+ }
+
+ if (!g_shell_parse_argv (self->command, &argc, &argv, &error))
+ {
+ ide_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ switch (self->locality)
+ {
+ case GBP_SHELLCMD_COMMAND_LOCALITY_HOST:
+ gbp_shellcmd_command_run_host (self, argv, task);
+ break;
+
+ case GBP_SHELLCMD_COMMAND_LOCALITY_APP:
+ gbp_shellcmd_command_run_app (self, argv, task);
+ break;
+
+ case GBP_SHELLCMD_COMMAND_LOCALITY_BUILD:
+ gbp_shellcmd_command_run_build (self, argv, task);
+ break;
+
+ case GBP_SHELLCMD_COMMAND_LOCALITY_RUN:
+ gbp_shellcmd_command_run_runner (self, argv, task);
+ break;
+
+ default:
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "Unknown command locality");
+ return;
+ }
+}
+
+static gboolean
+gbp_shellcmd_command_run_finish (IdeCommand *command,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_assert (GBP_IS_SHELLCMD_COMMAND (command));
+ g_assert (IDE_IS_TASK (result));
+
+ return ide_task_propagate_boolean (IDE_TASK (result), error);
+}
+
+static void
+command_iface_init (IdeCommandInterface *iface)
+{
+ iface->get_title = gbp_shellcmd_command_get_title;
+ iface->get_subtitle = gbp_shellcmd_command_get_subtitle;
+ iface->run_async = gbp_shellcmd_command_run_async;
+ iface->run_finish = gbp_shellcmd_command_run_finish;
+}
+
+GbpShellcmdCommandLocality
+gbp_shellcmd_command_get_locality (GbpShellcmdCommand *self)
+{
+ g_return_val_if_fail (GBP_IS_SHELLCMD_COMMAND (self), 0);
+
+ return self->locality;
+}
+
+void
+gbp_shellcmd_command_set_locality (GbpShellcmdCommand *self,
+ GbpShellcmdCommandLocality locality)
+{
+ g_return_if_fail (GBP_IS_SHELLCMD_COMMAND (self));
+
+ if (self->locality != locality)
+ {
+ self->locality = locality;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_LOCALITY]);
+ }
+
+}
+
+const gchar *
+gbp_shellcmd_command_get_command (GbpShellcmdCommand *self)
+{
+ g_return_val_if_fail (GBP_IS_SHELLCMD_COMMAND (self), NULL);
+
+ return self->command;
+}
+
+void
+gbp_shellcmd_command_set_command (GbpShellcmdCommand *self,
+ const gchar *command)
+{
+ g_return_if_fail (GBP_IS_SHELLCMD_COMMAND (self));
+
+ if (!ide_str_equal0 (command, self->command))
+ {
+ g_free (self->command);
+ self->command = g_strdup (command);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_COMMAND]);
+ }
+}
+
+IdeEnvironment *
+gbp_shellcmd_command_get_environment (GbpShellcmdCommand *self)
+{
+ g_return_val_if_fail (GBP_IS_SHELLCMD_COMMAND (self), NULL);
+
+ if (self->environment == NULL)
+ self->environment = ide_environment_new ();
+
+ return self->environment;
+}
+
+const gchar *
+gbp_shellcmd_command_get_shortcut (GbpShellcmdCommand *self)
+{
+ g_return_val_if_fail (GBP_IS_SHELLCMD_COMMAND (self), NULL);
+
+ return self->shortcut;
+}
+
+void
+gbp_shellcmd_command_set_shortcut (GbpShellcmdCommand *self,
+ const gchar *shortcut)
+{
+ g_return_if_fail (GBP_IS_SHELLCMD_COMMAND (self));
+
+ if (!ide_str_equal0 (shortcut, self->shortcut))
+ {
+ g_free (self->shortcut);
+ self->shortcut = g_strdup (shortcut);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SHORTCUT]);
+ }
+}
+
+void
+gbp_shellcmd_command_set_title (GbpShellcmdCommand *self,
+ const gchar *title)
+{
+ g_return_if_fail (GBP_IS_SHELLCMD_COMMAND (self));
+
+ if (!ide_str_equal0 (title, self->title))
+ {
+ g_free (self->title);
+ self->title = g_strdup (title);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]);
+ }
+}
+
+void
+gbp_shellcmd_command_set_subtitle (GbpShellcmdCommand *self,
+ const gchar *subtitle)
+{
+ g_return_if_fail (GBP_IS_SHELLCMD_COMMAND (self));
+
+ if (!ide_str_equal0 (subtitle, self->subtitle))
+ {
+ g_free (self->subtitle);
+ self->subtitle = g_strdup (subtitle);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SUBTITLE]);
+ }
+}
diff --git a/src/plugins/shellcmd/gbp-shellcmd-command.h b/src/plugins/shellcmd/gbp-shellcmd-command.h
new file mode 100644
index 000000000..90c600aca
--- /dev/null
+++ b/src/plugins/shellcmd/gbp-shellcmd-command.h
@@ -0,0 +1,58 @@
+/* gbp-shellcmd-command.h
+ *
+ * Copyright 2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-gui.h>
+#include <libide-threading.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_SHELLCMD_COMMAND (gbp_shellcmd_command_get_type())
+
+typedef enum
+{
+ GBP_SHELLCMD_COMMAND_LOCALITY_HOST,
+ GBP_SHELLCMD_COMMAND_LOCALITY_APP,
+ GBP_SHELLCMD_COMMAND_LOCALITY_BUILD,
+ GBP_SHELLCMD_COMMAND_LOCALITY_RUN,
+} GbpShellcmdCommandLocality;
+
+G_DECLARE_FINAL_TYPE (GbpShellcmdCommand, gbp_shellcmd_command, GBP, SHELLCMD_COMMAND, IdeObject)
+
+GbpShellcmdCommandLocality gbp_shellcmd_command_get_locality (GbpShellcmdCommand *self);
+void gbp_shellcmd_command_set_locality (GbpShellcmdCommand *self,
+ GbpShellcmdCommandLocality locality);
+const gchar *gbp_shellcmd_command_get_command (GbpShellcmdCommand *self);
+void gbp_shellcmd_command_set_command (GbpShellcmdCommand *self,
+ const gchar *command);
+const gchar *gbp_shellcmd_command_get_cwd (GbpShellcmdCommand *self);
+void gbp_shellcmd_command_set_cwd (GbpShellcmdCommand *self,
+ const gchar *cwd);
+IdeEnvironment *gbp_shellcmd_command_get_environment (GbpShellcmdCommand *self);
+const gchar *gbp_shellcmd_command_get_shortcut (GbpShellcmdCommand *self);
+void gbp_shellcmd_command_set_shortcut (GbpShellcmdCommand *self,
+ const gchar *shortcut);
+void gbp_shellcmd_command_set_subtitle (GbpShellcmdCommand *self,
+ const gchar *subtitle);
+void gbp_shellcmd_command_set_title (GbpShellcmdCommand *self,
+ const gchar *title);
+
+G_END_DECLS
diff --git a/src/plugins/shellcmd/meson.build b/src/plugins/shellcmd/meson.build
new file mode 100644
index 000000000..b8d1cf63d
--- /dev/null
+++ b/src/plugins/shellcmd/meson.build
@@ -0,0 +1,29 @@
+if get_option('plugin_shellcmd')
+
+plugins_sources += files([
+ 'shellcmd-plugin.c',
+ 'gbp-shellcmd-command.c',
+ 'gbp-shellcmd-command-provider.c',
+])
+
+plugin_shellcmd_enum_headers = [
+ 'gbp-shellcmd-command.h',
+]
+
+plugin_shellcmd_enums = gnome.mkenums_simple('gbp-shellcmd-enums',
+ body_prefix: '#include "config.h"',
+ header_prefix: '#include <libide-gui.h>',
+ sources: plugin_shellcmd_enum_headers,
+)
+
+plugin_shellcmd_resources = gnome.compile_resources(
+ 'shellcmd-resources',
+ 'shellcmd.gresource.xml',
+ c_name: 'gbp_shellcmd',
+)
+
+plugins_sources += plugin_shellcmd_enums
+plugins_sources += plugin_shellcmd_resources
+plugins_include_directories += [include_directories('.')]
+
+endif
diff --git a/src/plugins/shellcmd/shellcmd-plugin.c b/src/plugins/shellcmd/shellcmd-plugin.c
new file mode 100644
index 000000000..fe4f15a28
--- /dev/null
+++ b/src/plugins/shellcmd/shellcmd-plugin.c
@@ -0,0 +1,34 @@
+/* shellcmd-plugin.c
+ *
+ * Copyright 2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "config.h"
+
+#include <libide-gui.h>
+#include <libpeas/peas.h>
+
+#include "gbp-shellcmd-command-provider.h"
+
+_IDE_EXTERN void
+_gbp_shellcmd_register_types (PeasObjectModule *module)
+{
+ peas_object_module_register_extension_type (module,
+ IDE_TYPE_COMMAND_PROVIDER,
+ GBP_TYPE_SHELLCMD_COMMAND_PROVIDER);
+}
diff --git a/src/plugins/shellcmd/shellcmd.gresource.xml b/src/plugins/shellcmd/shellcmd.gresource.xml
new file mode 100644
index 000000000..4af547c31
--- /dev/null
+++ b/src/plugins/shellcmd/shellcmd.gresource.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/plugins/shellcmd">
+ <file>shellcmd.plugin</file>
+ </gresource>
+</gresources>
diff --git a/src/plugins/shellcmd/shellcmd.plugin b/src/plugins/shellcmd/shellcmd.plugin
new file mode 100644
index 000000000..bfd130ff7
--- /dev/null
+++ b/src/plugins/shellcmd/shellcmd.plugin
@@ -0,0 +1,10 @@
+[Plugin]
+Authors=Christian Hergert <christian hergert me>
+Builtin=true
+Copyright=Copyright © 2019 Christian Hergert
+Depends=editor;
+Description=Run shell commands from your project
+Embedded=_gbp_shellcmd_register_types
+Hidden=true
+Module=shellcmd
+Name=Shell Commands
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]