[gnome-builder] session: add plugin interface for session tracking
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] session: add plugin interface for session tracking
- Date: Tue, 24 Jul 2018 07:25:39 +0000 (UTC)
commit 4a0f1c6f97e84a057287ac1cf67ac054406d5b87
Author: Christian Hergert <chergert redhat com>
Date: Tue Jul 24 00:13:51 2018 -0700
session: add plugin interface for session tracking
This can be used by plugins to save/restore session state when a project
is closed/re-opened.
doc/help/plugins/index.rst | 1 +
doc/help/plugins/session.rst | 44 +++
src/libide/ide-context.c | 95 +++++++
src/libide/ide.h | 2 +
src/libide/meson.build | 1 +
src/libide/session/ide-session-addin.c | 164 +++++++++++
src/libide/session/ide-session-addin.h | 74 +++++
src/libide/session/ide-session.c | 485 +++++++++++++++++++++++++++++++++
src/libide/session/ide-session.h | 53 ++++
src/libide/session/meson.build | 14 +
10 files changed, 933 insertions(+)
---
diff --git a/doc/help/plugins/index.rst b/doc/help/plugins/index.rst
index aa37f43ab..c84f3d14b 100644
--- a/doc/help/plugins/index.rst
+++ b/doc/help/plugins/index.rst
@@ -26,6 +26,7 @@ You may also chooser to implement extensions in C or Vala.
keybindings
langserv
search
+ session
menus
preferences
transfers
diff --git a/doc/help/plugins/session.rst b/doc/help/plugins/session.rst
new file mode 100644
index 000000000..6eb078bfd
--- /dev/null
+++ b/doc/help/plugins/session.rst
@@ -0,0 +1,44 @@
+################
+Session Tracking
+################
+
+Some plugins may want to save state when the user closes Builder.
+The `Ide.SessionAddin` allows for saving and restoring state when a project is closed or re-opened.
+
+.. code-block:: python3
+
+ # my_plugin.py
+
+ import gi
+
+ from gi.repository import GObject
+ from gi.repository import Gio
+ from gi.repository import Ide
+
+ class MySessionAddin(Ide.Object, Ide.SessionAddin):
+
+ def do_save_async(self, cancellable, callback, data):
+ # Create our async task
+ task = Ide.Task.new(sel, cancellable, callback)
+
+ # State is saved as a variant
+ task.result = GLib.Variant.new_int(123)
+
+ # Now complete task
+ task.return_boolean(True)
+
+ def do_save_finish(self, task):
+ if task.propagate_boolean():
+ return task.result
+
+ def do_restore_async(self, state, cancellable, callback, data):
+ # Create our async task
+ task = Ide.Task.new(sel, cancellable, callback)
+
+ # state is a GLib.Variant matching what we saved
+
+ # Now complete task
+ task.return_boolean(True)
+
+ def do_restore_finish(self, task):
+ return task.propagate_boolean()
diff --git a/src/libide/ide-context.c b/src/libide/ide-context.c
index 365879183..3d5cac419 100644
--- a/src/libide/ide-context.c
+++ b/src/libide/ide-context.c
@@ -52,6 +52,7 @@
#include "runtimes/ide-runtime-manager.h"
#include "search/ide-search-engine.h"
#include "search/ide-search-provider.h"
+#include "session/ide-session.h"
#include "snippets/ide-snippet-storage.h"
#include "testing/ide-test-manager.h"
#include "toolchain/ide-toolchain-manager.h"
@@ -122,6 +123,7 @@ struct _IdeContext
IdeRuntimeManager *runtime_manager;
IdeToolchainManager *toolchain_manager;
IdeSearchEngine *search_engine;
+ IdeSession *session;
IdeSnippetStorage *snippets;
IdeTestManager *test_manager;
IdeProject *project;
@@ -594,6 +596,7 @@ ide_context_finalize (GObject *object)
g_clear_object (&self->project_file);
g_clear_object (&self->recent_manager);
g_clear_object (&self->runtime_manager);
+ g_clear_object (&self->session);
g_clear_object (&self->toolchain_manager);
g_clear_object (&self->test_manager);
g_clear_object (&self->unsaved_files);
@@ -909,6 +912,10 @@ ide_context_init (IdeContext *self)
self->snippets = ide_snippet_storage_new ();
+ self->session = g_object_new (IDE_TYPE_SESSION,
+ "context", self,
+ NULL);
+
IDE_EXIT;
}
@@ -1297,6 +1304,46 @@ ide_context_init_tests (gpointer source_object,
ide_task_return_boolean (task, TRUE);
}
+static void
+ide_context_init_session_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeSession *session = (IdeSession *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (IDE_IS_SESSION (session));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ if (!ide_session_restore_finish (session, result, &error))
+ g_warning ("Failed to restore session: %s", error->message);
+
+ ide_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_context_init_session (gpointer source_object,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ IdeContext *self = source_object;
+ g_autoptr(IdeTask) task = NULL;
+
+ g_assert (IDE_IS_CONTEXT (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = ide_task_new (self, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, ide_context_init_session);
+
+ ide_session_restore_async (self->session,
+ cancellable,
+ ide_context_init_session_cb,
+ g_steal_pointer (&task));
+}
+
static void
ide_context_service_added (PeasExtensionSet *set,
PeasPluginInfo *info,
@@ -1821,6 +1868,7 @@ ide_context_init_async (GAsyncInitable *initable,
ide_context_init_run_manager,
ide_context_init_diagnostics_manager,
ide_context_init_tests,
+ ide_context_init_session,
ide_context_init_loaded,
NULL);
}
@@ -1986,6 +2034,52 @@ ide_context_unload_configuration_manager (gpointer source_object,
IDE_EXIT;
}
+static void
+ide_context_unload_session_save_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeSession *session = (IdeSession *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (IDE_IS_SESSION (session));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ /* unfortunate if this happens, but not much we can do */
+ if (!ide_session_save_finish (session, result, &error))
+ g_warning ("Failed to save session: %s", error->message);
+
+ ide_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_context_unload_session (gpointer source_object,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ IdeContext *self = source_object;
+ g_autoptr(IdeTask) task = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_CONTEXT (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+ g_assert (IDE_IS_SESSION (self->session));
+
+ task = ide_task_new (self, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, ide_context_unload_session);
+
+ ide_session_save_async (self->session,
+ cancellable,
+ ide_context_unload_session_save_cb,
+ g_steal_pointer (&task));
+
+ IDE_EXIT;
+}
+
static void
ide_context_unload__unsaved_files_save_cb (GObject *object,
GAsyncResult *result,
@@ -2086,6 +2180,7 @@ ide_context_do_unload_locked (IdeContext *self)
ide_task_get_cancellable (task),
ide_context_unload_cb,
g_object_ref (task),
+ ide_context_unload_session,
ide_context_unload_configuration_manager,
ide_context_unload_buffer_manager,
ide_context_unload_unsaved_files,
diff --git a/src/libide/ide.h b/src/libide/ide.h
index 0f142554c..31f0f677c 100644
--- a/src/libide/ide.h
+++ b/src/libide/ide.h
@@ -157,6 +157,8 @@ G_BEGIN_DECLS
#include "search/ide-search-reducer.h"
#include "search/ide-search-result.h"
#include "search/ide-tagged-entry.h"
+#include "session/ide-session.h"
+#include "session/ide-session-addin.h"
#include "snippets/ide-snippet.h"
#include "snippets/ide-snippet-chunk.h"
#include "snippets/ide-snippet-context.h"
diff --git a/src/libide/meson.build b/src/libide/meson.build
index 4e8bc731b..e0643eda4 100644
--- a/src/libide/meson.build
+++ b/src/libide/meson.build
@@ -82,6 +82,7 @@ subdir('rename')
subdir('runner')
subdir('runtimes')
subdir('search')
+subdir('session')
subdir('snippets')
subdir('sourceview')
subdir('storage')
diff --git a/src/libide/session/ide-session-addin.c b/src/libide/session/ide-session-addin.c
new file mode 100644
index 000000000..7f86c4d19
--- /dev/null
+++ b/src/libide/session/ide-session-addin.c
@@ -0,0 +1,164 @@
+/* ide-session-addin.c
+ *
+ * Copyright 2018 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"
+
+#define G_LOG_DOMAIN "ide-session-addin"
+
+#include "ide-session-addin.h"
+
+G_DEFINE_INTERFACE (IdeSessionAddin, ide_session_addin, IDE_TYPE_OBJECT)
+
+static void
+ide_session_addin_real_save_async (IdeSessionAddin *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_task_report_new_error (self, callback, user_data,
+ ide_session_addin_real_save_async,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "Save not supported");
+}
+
+static GVariant *
+ide_session_addin_real_save_finish (IdeSessionAddin *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+ide_session_addin_real_restore_async (IdeSessionAddin *self,
+ GVariant *state,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_task_report_new_error (self, callback, user_data,
+ ide_session_addin_real_restore_async,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "Restore not supported");
+}
+
+static gboolean
+ide_session_addin_real_restore_finish (IdeSessionAddin *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+ide_session_addin_default_init (IdeSessionAddinInterface *iface)
+{
+ iface->save_async = ide_session_addin_real_save_async;
+ iface->save_finish = ide_session_addin_real_save_finish;
+ iface->restore_async = ide_session_addin_real_restore_async;
+ iface->restore_finish = ide_session_addin_real_restore_finish;
+}
+
+/**
+ * ide_session_addin_save_async:
+ * @self: a #IdeSessionAddin
+ * @cancellable: (nullable): A #GCancellable or %NULL
+ * @callback: callback to execute upon completion
+ * @user_data: closure data for @callback
+ *
+ * Asynchronous request to save state about the session.
+ *
+ * The resulting state will be provided when restoring the addin
+ * at a future time.
+ *
+ * Since: 3.30
+ */
+void
+ide_session_addin_save_async (IdeSessionAddin *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (IDE_IS_SESSION_ADDIN (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ IDE_SESSION_ADDIN_GET_IFACE (self)->save_async (self, cancellable, callback, user_data);
+}
+
+/**
+ * ide_session_addin_save_finish:
+ * @self: a #IdeSessionAddin
+ *
+ * Completes an asynchronous request to save session state.
+ *
+ * The resulting #GVariant will be used to restore state at a future time.
+ *
+ * Returns: (transfer full) (nullable): a #GVariant or %NULL.
+ *
+ * Since: 3.30
+ */
+GVariant *
+ide_session_addin_save_finish (IdeSessionAddin *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_SESSION_ADDIN (self), FALSE);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+ return IDE_SESSION_ADDIN_GET_IFACE (self)->save_finish (self, result, error);
+}
+
+/**
+ * ide_session_addin_restore_async:
+ * @self: a #IdeSessionAddin
+ * @state: a #GVariant of previous state
+ * @cancellable: (nullable): A #GCancellable or %NULL
+ * @callback: callback to execute upon completion
+ * @user_data: closure data for @callback
+ *
+ * Asynchronous request to restore session state by the addin.
+ *
+ * Since: 3.30
+ */
+void
+ide_session_addin_restore_async (IdeSessionAddin *self,
+ GVariant *state,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (IDE_IS_SESSION_ADDIN (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ IDE_SESSION_ADDIN_GET_IFACE (self)->restore_async (self, state, cancellable, callback, user_data);
+}
+
+gboolean
+ide_session_addin_restore_finish (IdeSessionAddin *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_SESSION_ADDIN (self), FALSE);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+ return IDE_SESSION_ADDIN_GET_IFACE (self)->restore_finish (self, result, error);
+}
diff --git a/src/libide/session/ide-session-addin.h b/src/libide/session/ide-session-addin.h
new file mode 100644
index 000000000..eb8723412
--- /dev/null
+++ b/src/libide/session/ide-session-addin.h
@@ -0,0 +1,74 @@
+/* ide-session-addin.h
+ *
+ * Copyright 2018 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 "ide-object.h"
+#include "ide-version-macros.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SESSION_ADDIN (ide_session_addin_get_type ())
+
+IDE_AVAILABLE_IN_3_30
+G_DECLARE_INTERFACE (IdeSessionAddin, ide_session_addin, IDE, SESSION_ADDIN, IdeObject)
+
+struct _IdeSessionAddinInterface
+{
+ GTypeInterface parent;
+
+ void (*save_async) (IdeSessionAddin *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ GVariant *(*save_finish) (IdeSessionAddin *self,
+ GAsyncResult *result,
+ GError **error);
+ void (*restore_async) (IdeSessionAddin *self,
+ GVariant *state,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*restore_finish) (IdeSessionAddin *self,
+ GAsyncResult *result,
+ GError **error);
+};
+
+IDE_AVAILABLE_IN_3_30
+void ide_session_addin_save_async (IdeSessionAddin *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_30
+GVariant *ide_session_addin_save_finish (IdeSessionAddin *self,
+ GAsyncResult *result,
+ GError **error);
+IDE_AVAILABLE_IN_3_30
+void ide_session_addin_restore_async (IdeSessionAddin *self,
+ GVariant *state,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_30
+gboolean ide_session_addin_restore_finish (IdeSessionAddin *self,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
diff --git a/src/libide/session/ide-session.c b/src/libide/session/ide-session.c
new file mode 100644
index 000000000..a6b52cc08
--- /dev/null
+++ b/src/libide/session/ide-session.c
@@ -0,0 +1,485 @@
+/* ide-session.c
+ *
+ * Copyright 2018 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"
+
+#define G_LOG_DOMAIN "ide-session"
+
+#include <libpeas/peas.h>
+
+#include "ide-context.h"
+#include "ide-debug.h"
+#include "ide-global.h"
+#include "ide-session.h"
+
+#include "session/ide-session-addin.h"
+#include "threading/ide-task.h"
+
+struct _IdeSession
+{
+ IdeObject parent_instance;
+ PeasExtensionSet *addins;
+};
+
+typedef struct
+{
+ GPtrArray *addins;
+ GVariantDict dict;
+ gint active;
+} Save;
+
+typedef struct
+{
+ GPtrArray *addins;
+ GVariant *state;
+ gint active;
+} Restore;
+
+G_DEFINE_TYPE (IdeSession, ide_session, IDE_TYPE_OBJECT)
+
+static void
+restore_free (Restore *r)
+{
+ g_assert (r != NULL);
+
+ g_clear_pointer (&r->addins, g_ptr_array_unref);
+ g_clear_pointer (&r->state, g_variant_unref);
+ g_slice_free (Restore, r);
+}
+
+static void
+save_free (Save *s)
+{
+ g_assert (s != NULL);
+ g_assert (s->active == 0);
+
+ g_clear_pointer (&s->addins, g_ptr_array_unref);
+ g_slice_free (Save, s);
+}
+
+static void
+collect_addins_cb (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ GPtrArray *ar = user_data;
+
+ g_assert (PEAS_IS_EXTENSION_SET (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_SESSION_ADDIN (exten));
+ g_assert (ar != NULL);
+
+ g_ptr_array_add (ar, g_object_ref (exten));
+}
+
+static void
+ide_session_dispose (GObject *object)
+{
+ IdeSession *self = (IdeSession *)object;
+
+ IDE_ENTRY;
+
+ g_clear_object (&self->addins);
+
+ G_OBJECT_CLASS (ide_session_parent_class)->dispose (object);
+
+ IDE_EXIT;
+}
+
+static void
+ide_session_constructed (GObject *object)
+{
+ IdeSession *self = (IdeSession *)object;
+ IdeContext *context;
+
+ G_OBJECT_CLASS (ide_session_parent_class)->constructed (object);
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+
+ self->addins = peas_extension_set_new (peas_engine_get_default (),
+ IDE_TYPE_SESSION_ADDIN,
+ "context", context,
+ NULL);
+}
+
+static void
+ide_session_class_init (IdeSessionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = ide_session_constructed;
+ object_class->dispose = ide_session_dispose;
+}
+
+static void
+ide_session_init (IdeSession *self)
+{
+}
+
+static void
+ide_session_restore_addin_restore_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeSessionAddin *addin = (IdeSessionAddin *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+ Restore *r;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SESSION_ADDIN (addin));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ r = ide_task_get_task_data (task);
+
+ g_assert (r != NULL);
+ g_assert (r->addins != NULL);
+ g_assert (r->active > 0);
+ g_assert (r->state != NULL);
+
+ if (!ide_session_addin_restore_finish (addin, result, &error))
+ g_warning ("%s: %s", G_OBJECT_TYPE_NAME (addin), error->message);
+
+ r->active--;
+
+ if (r->active == 0)
+ ide_task_return_boolean (task, TRUE);
+
+ IDE_EXIT;
+}
+
+static void
+ide_session_restore_load_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GFile *file = (GFile *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GBytes) bytes = NULL;
+ GCancellable *cancellable;
+ Restore *r;
+
+ IDE_ENTRY;
+
+ g_assert (G_IS_FILE (file));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ r = ide_task_get_task_data (task);
+ cancellable = ide_task_get_cancellable (task);
+
+ g_assert (r != NULL);
+ g_assert (r->addins != NULL);
+ g_assert (r->active > 0);
+ g_assert (r->state == NULL);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ if (!(bytes = g_file_load_bytes_finish (file, result, NULL, &error)))
+ {
+ ide_task_return_error (task, g_steal_pointer (&error));
+ IDE_EXIT;
+ }
+
+ if (g_bytes_get_size (bytes) == 0)
+ {
+ ide_task_return_boolean (task, TRUE);
+ IDE_EXIT;
+ }
+
+ r->state = g_variant_new_from_bytes (G_VARIANT_TYPE_VARDICT, bytes, FALSE);
+
+ if (r->state == NULL)
+ {
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_DATA,
+ "Failed to decode session state");
+ IDE_EXIT;
+ }
+
+ g_assert (r->addins != NULL);
+ g_assert (r->addins->len > 0);
+
+ for (guint i = 0; i < r->addins->len; i++)
+ {
+ IdeSessionAddin *addin = g_ptr_array_index (r->addins, i);
+ g_autoptr(GVariant) state = NULL;
+
+ g_assert (IDE_IS_SESSION_ADDIN (addin));
+
+ state = g_variant_lookup_value (r->state,
+ G_OBJECT_TYPE_NAME (addin),
+ NULL);
+
+ ide_session_addin_restore_async (addin,
+ state,
+ cancellable,
+ ide_session_restore_addin_restore_cb,
+ g_object_ref (task));
+ }
+
+ IDE_EXIT;
+}
+
+/**
+ * ide_session_restore_async:
+ * @self: an #IdeSession
+ * @cancellable: (nullable): a #GCancellbale or %NULL
+ * @callback: the callback to execute upon completion
+ * @user_data: user data for callback
+ *
+ * This function will asynchronously restore the state of the project to
+ * the point it was last saved (typically upon shutdown). This includes
+ * open documents and editor splits to the degree possible.
+ *
+ * Since: 3.30
+ */
+void
+ide_session_restore_async (IdeSession *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(IdeTask) task = NULL;
+ g_autoptr(GFile) file = NULL;
+ IdeContext *context;
+ Restore *r;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_SESSION (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = ide_task_new (self, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, ide_session_restore_async);
+
+ r = g_slice_new0 (Restore);
+ r->addins = g_ptr_array_new_with_free_func (g_object_unref);
+ peas_extension_set_foreach (self->addins, collect_addins_cb, r->addins);
+ r->active = r->addins->len;
+ ide_task_set_task_data (task, r, restore_free);
+
+ if (r->active == 0)
+ {
+ ide_task_return_boolean (task, TRUE);
+ IDE_EXIT;
+ }
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+ file = ide_context_cache_file (context, "session.gvariant", NULL);
+
+ g_file_load_bytes_async (file,
+ cancellable,
+ ide_session_restore_load_cb,
+ g_steal_pointer (&task));
+
+ IDE_EXIT;
+}
+
+gboolean
+ide_session_restore_finish (IdeSession *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ gboolean ret;
+
+ IDE_ENTRY;
+
+ g_return_val_if_fail (IDE_IS_SESSION (self), FALSE);
+ g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
+
+ ret = ide_task_propagate_boolean (IDE_TASK (result), error);
+
+ IDE_RETURN (ret);
+}
+
+static void
+ide_session_save_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GFile *file = (GFile *)object;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(IdeTask) task = user_data;
+
+ IDE_ENTRY;
+
+ g_assert (G_IS_FILE (file));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ if (!g_file_replace_contents_finish (file, result, NULL, &error))
+ ide_task_return_error (task, g_steal_pointer (&error));
+ else
+ ide_task_return_boolean (task, TRUE);
+
+ IDE_EXIT;
+}
+
+static void
+ide_session_save_addin_save_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeSessionAddin *addin = (IdeSessionAddin *)object;
+ g_autoptr(GVariant) variant = NULL;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+ IdeSession *self;
+ Save *s;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_SESSION_ADDIN (addin));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ self = ide_task_get_source_object (task);
+ s = ide_task_get_task_data (task);
+
+ g_assert (IDE_IS_SESSION (self));
+ g_assert (s != NULL);
+ g_assert (s->addins != NULL);
+ g_assert (s->active > 0);
+
+ variant = ide_session_addin_save_finish (addin, result, &error);
+
+ if (error != NULL)
+ g_warning ("%s: %s", G_OBJECT_TYPE_NAME (addin), error->message);
+
+ if (variant != NULL)
+ {
+ g_assert (!g_variant_is_floating (variant));
+ g_variant_dict_insert_value (&s->dict, G_OBJECT_TYPE_NAME (addin), variant);
+ }
+
+ s->active--;
+
+ if (s->active == 0)
+ {
+ g_autoptr(GVariant) state = NULL;
+ g_autoptr(GBytes) bytes = NULL;
+ g_autoptr(GFile) file = NULL;
+ GCancellable *cancellable;
+ IdeContext *context;
+
+ state = g_variant_take_ref (g_variant_dict_end (&s->dict));
+ bytes = g_variant_get_data_as_bytes (state);
+
+ cancellable = ide_task_get_cancellable (task);
+ context = ide_object_get_context (IDE_OBJECT (self));
+ file = ide_context_cache_file (context, "session.gvariant", NULL);
+
+ if (ide_task_return_error_if_cancelled (task))
+ IDE_EXIT;
+
+ g_file_replace_contents_bytes_async (file,
+ bytes,
+ NULL,
+ FALSE,
+ G_FILE_CREATE_NONE,
+ cancellable,
+ ide_session_save_cb,
+ g_steal_pointer (&task));
+ }
+
+ IDE_EXIT;
+}
+
+/**
+ * ide_session_save_async:
+ * @self: an #IdeSession
+ * @cancellable: (nullable): a #GCancellable, or %NULL
+ * @callback: a callback to execute upon completion
+ * @user_data: user data for @callback
+ *
+ * This function will request that various components save their active state
+ * so that the project may be restored to the current layout when the project
+ * is re-opened at a later time.
+ *
+ * Since: 3.30
+ */
+void
+ide_session_save_async (IdeSession *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(IdeTask) task = NULL;
+ Save *s;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_SESSION (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = ide_task_new (self, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, ide_session_save_async);
+
+ s = g_slice_new0 (Save);
+ s->addins = g_ptr_array_new_with_free_func (g_object_unref);
+ g_variant_dict_init (&s->dict, NULL);
+ peas_extension_set_foreach (self->addins, collect_addins_cb, s->addins);
+ s->active = s->addins->len;
+ ide_task_set_task_data (task, s, save_free);
+
+ if (s->active == 0)
+ {
+ ide_task_return_boolean (task, TRUE);
+ IDE_EXIT;
+ }
+
+ for (guint i = 0; i < s->addins->len; i++)
+ {
+ IdeSessionAddin *addin = g_ptr_array_index (s->addins, i);
+
+ ide_session_addin_save_async (addin,
+ cancellable,
+ ide_session_save_addin_save_cb,
+ g_object_ref (task));
+ }
+
+ g_assert (s != NULL);
+ g_assert (s->active > 0);
+ g_assert (s->addins->len == s->active);
+
+ IDE_EXIT;
+}
+
+gboolean
+ide_session_save_finish (IdeSession *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ gboolean ret;
+
+ IDE_ENTRY;
+
+ g_return_val_if_fail (IDE_IS_SESSION (self), FALSE);
+ g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
+
+ ret = ide_task_propagate_boolean (IDE_TASK (result), error);
+
+ IDE_RETURN (ret);
+}
diff --git a/src/libide/session/ide-session.h b/src/libide/session/ide-session.h
new file mode 100644
index 000000000..0db083913
--- /dev/null
+++ b/src/libide/session/ide-session.h
@@ -0,0 +1,53 @@
+/* ide-session.h
+ *
+ * Copyright 2018 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 "ide-object.h"
+
+#include "ide-version-macros.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SESSION (ide_session_get_type())
+
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (IdeSession, ide_session, IDE, SESSION, IdeObject)
+
+IDE_AVAILABLE_IN_ALL
+void ide_session_restore_async (IdeSession *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_session_restore_finish (IdeSession *self,
+ GAsyncResult *result,
+ GError **error);
+IDE_AVAILABLE_IN_ALL
+void ide_session_save_async (IdeSession *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_session_save_finish (IdeSession *self,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
diff --git a/src/libide/session/meson.build b/src/libide/session/meson.build
new file mode 100644
index 000000000..f14360332
--- /dev/null
+++ b/src/libide/session/meson.build
@@ -0,0 +1,14 @@
+session_headers = [
+ 'ide-session.h',
+ 'ide-session-addin.h',
+]
+
+session_sources = [
+ 'ide-session.c',
+ 'ide-session-addin.c',
+]
+
+libide_public_headers += files(session_headers)
+libide_public_sources += files(session_sources)
+
+install_headers(session_headers, subdir: join_paths(libide_header_subdir, 'session'))
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]