[gnome-builder/wip/gtk4-port] libide/foundry: add IdeRunCommands
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/gtk4-port] libide/foundry: add IdeRunCommands
- Date: Thu, 30 Jun 2022 18:28:46 +0000 (UTC)
commit 8605f0f724b19034346bd009d79895d033759ccb
Author: Christian Hergert <chergert redhat com>
Date: Thu Jun 30 11:28:37 2022 -0700
libide/foundry: add IdeRunCommands
This is the start of an abstraction to simplify getting access to run
commands and to make them persistent/shared across all the subsystems.
It's still incomplete, but shows what the API will look like. I want to
hook some things up so that we wait until the pipeline is ready before we
populate providers.
src/libide/foundry/ide-foundry-compat.c | 17 ++
src/libide/foundry/ide-run-command-provider.c | 32 +++
src/libide/foundry/ide-run-command-provider.h | 3 +
src/libide/foundry/ide-run-commands.c | 317 ++++++++++++++++++++++++++
src/libide/foundry/ide-run-commands.h | 47 ++++
src/libide/foundry/ide-run-manager.h | 3 +-
src/libide/foundry/libide-foundry.h | 1 +
src/libide/foundry/meson.build | 2 +
8 files changed, 421 insertions(+), 1 deletion(-)
---
diff --git a/src/libide/foundry/ide-foundry-compat.c b/src/libide/foundry/ide-foundry-compat.c
index b6a3746c7..b72fd2d9b 100644
--- a/src/libide/foundry/ide-foundry-compat.c
+++ b/src/libide/foundry/ide-foundry-compat.c
@@ -27,6 +27,7 @@
#include "ide-device-manager.h"
#include "ide-config-manager.h"
#include "ide-foundry-compat.h"
+#include "ide-run-commands.h"
#include "ide-run-manager.h"
#include "ide-runtime-manager.h"
#include "ide-test-manager.h"
@@ -216,3 +217,19 @@ ide_test_manager_from_context (IdeContext *context)
return ensure_child_typed_borrowed (context, IDE_TYPE_TEST_MANAGER);
}
+
+/**
+ * ide_run_commands_from_context:
+ * @context: an #IdeContext
+ *
+ * Gets the default #IdeRunCommands instance for @context.
+ *
+ * Returns: (transfer none): an #IdeRunCommands
+ */
+IdeRunCommands *
+ide_run_commands_from_context (IdeContext *context)
+{
+ g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
+
+ return ensure_child_typed_borrowed (context, IDE_TYPE_RUN_COMMANDS);
+}
diff --git a/src/libide/foundry/ide-run-command-provider.c b/src/libide/foundry/ide-run-command-provider.c
index cefee71ca..c685b1635 100644
--- a/src/libide/foundry/ide-run-command-provider.c
+++ b/src/libide/foundry/ide-run-command-provider.c
@@ -26,9 +26,24 @@
G_DEFINE_INTERFACE (IdeRunCommandProvider, ide_run_command_provider, IDE_TYPE_OBJECT)
+enum {
+ INVALIDATED,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS];
+
static void
ide_run_command_provider_default_init (IdeRunCommandProviderInterface *iface)
{
+ signals[INVALIDATED] =
+ g_signal_new ("invalidated",
+ G_TYPE_FROM_INTERFACE (iface),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (IdeRunCommandProviderInterface, invalidated),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0);
}
void
@@ -62,3 +77,20 @@ ide_run_command_provider_list_commands_finish (IdeRunCommandProvider *self,
return IDE_RUN_COMMAND_PROVIDER_GET_IFACE (self)->list_commands_finish (self, result, error);
}
+
+/**
+ * ide_run_command_provider_invalidate:
+ * @self: a #IdeRunCommandProvider
+ *
+ * Emits the #IdeRunCommandProvider::invalidated signal.
+ *
+ * This often results in #IdeRunCommands requesting a new set of results for
+ * the run command provider via ide_run_command_provider_list_commands_async().
+ */
+void
+ide_run_command_provider_invalidate (IdeRunCommandProvider *self)
+{
+ g_return_if_fail (IDE_IS_RUN_COMMAND_PROVIDER (self));
+
+ g_signal_emit (self, signals[INVALIDATED], 0);
+}
diff --git a/src/libide/foundry/ide-run-command-provider.h b/src/libide/foundry/ide-run-command-provider.h
index ff1345774..79f491704 100644
--- a/src/libide/foundry/ide-run-command-provider.h
+++ b/src/libide/foundry/ide-run-command-provider.h
@@ -37,6 +37,7 @@ struct _IdeRunCommandProviderInterface
{
GTypeInterface parent_iface;
+ void (*invalidated) (IdeRunCommandProvider *self);
void (*list_commands_async) (IdeRunCommandProvider *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
@@ -46,6 +47,8 @@ struct _IdeRunCommandProviderInterface
GError **error);
};
+IDE_AVAILABLE_IN_ALL
+void ide_run_command_provider_invalidate (IdeRunCommandProvider *self);
IDE_AVAILABLE_IN_ALL
void ide_run_command_provider_list_commands_async (IdeRunCommandProvider *self,
GCancellable *cancellable,
diff --git a/src/libide/foundry/ide-run-commands.c b/src/libide/foundry/ide-run-commands.c
new file mode 100644
index 000000000..743fcc9de
--- /dev/null
+++ b/src/libide/foundry/ide-run-commands.c
@@ -0,0 +1,317 @@
+/* ide-run-commands.c
+ *
+ * Copyright 2022 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 "ide-run-commands"
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include <libide-plugins.h>
+#include <libide-threading.h>
+
+#include "ide-run-command.h"
+#include "ide-run-command-provider.h"
+#include "ide-run-commands.h"
+
+struct _IdeRunCommands
+{
+ IdeObject parent_instance;
+ IdeExtensionSetAdapter *addins;
+ GListStore *models;
+ GtkFlattenListModel *flatten_model;
+ GHashTable *provider_to_model;
+};
+
+static GType
+ide_run_commands_get_item_type (GListModel *model)
+{
+ return IDE_TYPE_RUN_COMMAND;
+}
+
+static guint
+ide_run_commands_get_n_items (GListModel *model)
+{
+ IdeRunCommands *self = IDE_RUN_COMMANDS (model);
+ return g_list_model_get_n_items (G_LIST_MODEL (self->flatten_model));
+}
+
+static gpointer
+ide_run_commands_get_item (GListModel *model,
+ guint position)
+{
+ IdeRunCommands *self = IDE_RUN_COMMANDS (model);
+ return g_list_model_get_item (G_LIST_MODEL (self->flatten_model), position);
+}
+
+static void
+list_model_iface_init (GListModelInterface *iface)
+{
+ iface->get_item_type = ide_run_commands_get_item_type;
+ iface->get_n_items = ide_run_commands_get_n_items;
+ iface->get_item = ide_run_commands_get_item;
+}
+
+G_DEFINE_FINAL_TYPE_WITH_CODE (IdeRunCommands, ide_run_commands, IDE_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
+
+static void
+ide_run_commands_items_changed_cb (IdeRunCommands *self,
+ guint position,
+ guint removed,
+ guint added,
+ GListModel *model)
+{
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_RUN_COMMANDS (self));
+ g_assert (G_IS_LIST_MODEL (model));
+
+ g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
+
+ IDE_EXIT;
+}
+
+static void
+ide_run_commands_provider_invalidated_cb (IdeRunCommands *self,
+ IdeRunCommandProvider *provider)
+{
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_RUN_COMMANDS (self));
+ g_assert (IDE_IS_RUN_COMMAND_PROVIDER (provider));
+
+ /* TODO: queue update for changes */
+
+ IDE_EXIT;
+}
+
+static void
+ide_run_commands_provider_added_cb (IdeExtensionSetAdapter *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ IdeRunCommandProvider *provider = (IdeRunCommandProvider *)exten;
+ IdeRunCommands *self = user_data;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_RUN_COMMAND_PROVIDER (provider));
+ g_assert (IDE_IS_RUN_COMMANDS (self));
+
+ g_signal_connect_object (provider,
+ "invalidated",
+ G_CALLBACK (ide_run_commands_provider_invalidated_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ ide_run_commands_provider_invalidated_cb (self, provider);
+
+ IDE_EXIT;
+}
+
+static void
+ide_run_commands_provider_removed_cb (IdeExtensionSetAdapter *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ IdeRunCommandProvider *provider = (IdeRunCommandProvider *)exten;
+ g_autoptr(IdeRunCommandProvider) stolen_key = NULL;
+ g_autoptr(GListModel) stolen_value = NULL;
+ IdeRunCommands *self = user_data;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_RUN_COMMAND_PROVIDER (provider));
+ g_assert (IDE_IS_RUN_COMMANDS (self));
+
+ if (g_hash_table_steal_extended (self->provider_to_model,
+ provider,
+ (gpointer *)&stolen_key,
+ (gpointer *)&stolen_value))
+ {
+ if (stolen_value != NULL)
+ {
+ guint position;
+
+ if (g_list_store_find (self->models, stolen_value, &position))
+ g_list_store_remove (self->models, position);
+ }
+ }
+
+ IDE_EXIT;
+}
+
+static void
+ide_run_commands_parent_set (IdeObject *object,
+ IdeObject *parent)
+{
+ IdeRunCommands *self = (IdeRunCommands *)object;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_RUN_COMMANDS (self));
+ g_assert (!parent || IDE_IS_OBJECT (parent));
+
+ if (parent == NULL)
+ IDE_EXIT;
+
+ self->addins = ide_extension_set_adapter_new (IDE_OBJECT (self),
+ peas_engine_get_default (),
+ IDE_TYPE_RUN_COMMAND_PROVIDER,
+ NULL, NULL);
+ g_signal_connect (self->addins,
+ "extension-added",
+ G_CALLBACK (ide_run_commands_provider_added_cb),
+ self);
+ g_signal_connect (self->addins,
+ "extension-removed",
+ G_CALLBACK (ide_run_commands_provider_removed_cb),
+ self);
+ ide_extension_set_adapter_foreach (self->addins,
+ ide_run_commands_provider_added_cb,
+ self);
+
+ IDE_EXIT;
+}
+
+static void
+ide_run_commands_dispose (GObject *object)
+{
+ IdeRunCommands *self = (IdeRunCommands *)object;
+
+ ide_clear_and_destroy_object (&self->addins);
+ g_clear_object (&self->models);
+ g_clear_pointer (&self->provider_to_model, g_hash_table_unref);
+
+ G_OBJECT_CLASS (ide_run_commands_parent_class)->dispose (object);
+}
+
+static void
+ide_run_commands_class_init (IdeRunCommandsClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ IdeObjectClass *ide_object_class = IDE_OBJECT_CLASS (klass);
+
+ object_class->dispose = ide_run_commands_dispose;
+
+ ide_object_class->parent_set = ide_run_commands_parent_set;
+}
+
+static void
+ide_run_commands_init (IdeRunCommands *self)
+{
+ self->provider_to_model = g_hash_table_new_full (NULL, NULL, g_object_unref, g_object_unref);
+ self->models = g_list_store_new (G_TYPE_LIST_STORE);
+ self->flatten_model = gtk_flatten_list_model_new (g_object_ref (G_LIST_MODEL (self->models)));
+
+ g_signal_connect_object (self->flatten_model,
+ "items-changed",
+ G_CALLBACK (ide_run_commands_items_changed_cb),
+ self,
+ G_CONNECT_SWAPPED);
+}
+
+/**
+ * ide_run_commands_dup_by_id:
+ * @self: a #IdeRunCommands
+ * @id: (nullable): the id of the run command
+ *
+ * Finds an #IdeRunCommand by it's id.
+ *
+ * %NULL is allowed for @id out of convenience, but will return %NULL.
+ *
+ * Returns: (transfer full) (nullable): an #IdeRunCommand or %NULL
+ */
+IdeRunCommand *
+ide_run_commands_dup_by_id (IdeRunCommands *self,
+ const char *id)
+{
+ guint n_items;
+
+ IDE_ENTRY;
+
+ g_return_val_if_fail (IDE_IS_RUN_COMMANDS (self), NULL);
+
+ if (id == NULL)
+ IDE_RETURN (NULL);
+
+ n_items = g_list_model_get_n_items (G_LIST_MODEL (self));
+
+ IDE_TRACE_MSG ("Locating command by id %s in list of %u commands", id, n_items);
+
+ for (guint i = 0; i < n_items; i++)
+ {
+ g_autoptr(IdeRunCommand) run_command = g_list_model_get_item (G_LIST_MODEL (self), i);
+ const char *run_command_id = ide_run_command_get_id (run_command);
+
+ if (ide_str_equal0 (run_command_id, id))
+ IDE_RETURN (g_steal_pointer (&run_command));
+ }
+
+ IDE_RETURN (NULL);
+}
+
+static gboolean
+filter_run_command_by_kind (gpointer item,
+ gpointer user_data)
+{
+ return ide_run_command_get_kind (item) == GPOINTER_TO_INT (user_data);
+}
+
+/**
+ * ide_run_commands_list_by_kind:
+ * @self: an #IdeRunCommands
+ * @kind: an #IdeRunCommandKind
+ *
+ * Creates a new #GListModel of #IdeRunCommand filtered by @kind
+ *
+ * The model will update as new commands are added or removed from @self.
+ *
+ * Returns: (transfer full): a #GListModel
+ */
+GListModel *
+ide_run_commands_list_by_kind (IdeRunCommands *self,
+ IdeRunCommandKind kind)
+{
+ g_autoptr(GtkCustomFilter) filter = NULL;
+ g_autoptr(GtkFilterListModel) model = NULL;
+
+ g_return_val_if_fail (IDE_IS_RUN_COMMANDS (self), NULL);
+
+ filter = gtk_custom_filter_new (filter_run_command_by_kind,
+ GINT_TO_POINTER (kind),
+ NULL);
+ model = gtk_filter_list_model_new (g_object_ref (G_LIST_MODEL (self)),
+ GTK_FILTER (g_steal_pointer (&filter)));
+
+ return G_LIST_MODEL (g_steal_pointer (&model));
+}
diff --git a/src/libide/foundry/ide-run-commands.h b/src/libide/foundry/ide-run-commands.h
new file mode 100644
index 000000000..77ad0533b
--- /dev/null
+++ b/src/libide/foundry/ide-run-commands.h
@@ -0,0 +1,47 @@
+/* ide-run-commands.h
+ *
+ * Copyright 2022 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
+
+#if !defined (IDE_FOUNDRY_INSIDE) && !defined (IDE_FOUNDRY_COMPILATION)
+# error "Only <libide-foundry.h> can be included directly."
+#endif
+
+#include <libide-core.h>
+
+#include "ide-run-command.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_RUN_COMMANDS (ide_run_commands_get_type())
+
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (IdeRunCommands, ide_run_commands, IDE, RUN_COMMANDS, IdeObject)
+
+IDE_AVAILABLE_IN_ALL
+IdeRunCommands *ide_run_commands_from_context (IdeContext *context);
+IDE_AVAILABLE_IN_ALL
+GListModel *ide_run_commands_list_by_kind (IdeRunCommands *self,
+ IdeRunCommandKind kind);
+IDE_AVAILABLE_IN_ALL
+IdeRunCommand *ide_run_commands_dup_by_id (IdeRunCommands *self,
+ const char *id);
+
+G_END_DECLS
diff --git a/src/libide/foundry/ide-run-manager.h b/src/libide/foundry/ide-run-manager.h
index 23f08cf72..d10ab5501 100644
--- a/src/libide/foundry/ide-run-manager.h
+++ b/src/libide/foundry/ide-run-manager.h
@@ -24,8 +24,9 @@
# error "Only <libide-foundry.h> can be included directly."
#endif
+#include <libpeas/peas.h>
+
#include <libide-core.h>
-#include <libide-plugins.h>
#include "ide-foundry-types.h"
diff --git a/src/libide/foundry/libide-foundry.h b/src/libide/foundry/libide-foundry.h
index af98a548e..362c7eeab 100644
--- a/src/libide/foundry/libide-foundry.h
+++ b/src/libide/foundry/libide-foundry.h
@@ -58,6 +58,7 @@ G_BEGIN_DECLS
#include "ide-pipeline.h"
#include "ide-run-command.h"
#include "ide-run-command-provider.h"
+#include "ide-run-commands.h"
#include "ide-run-context.h"
#include "ide-run-manager.h"
#include "ide-run-tool.h"
diff --git a/src/libide/foundry/meson.build b/src/libide/foundry/meson.build
index 8c3430f1f..e8155ec93 100644
--- a/src/libide/foundry/meson.build
+++ b/src/libide/foundry/meson.build
@@ -43,6 +43,7 @@ libide_foundry_public_headers = [
'ide-pipeline.h',
'ide-run-command.h',
'ide-run-command-provider.h',
+ 'ide-run-commands.h',
'ide-run-context.h',
'ide-run-manager.h',
'ide-run-tool.h',
@@ -123,6 +124,7 @@ libide_foundry_public_sources = [
'ide-pipeline.c',
'ide-run-command.c',
'ide-run-command-provider.c',
+ 'ide-run-commands.c',
'ide-run-context.c',
'ide-run-manager.c',
'ide-run-tool.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]