/* ide-buffer-addin.c * * Copyright 2017-2019 Christian Hergert * * 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 . * * SPDX-License-Identifier: GPL-3.0-or-later */ #define G_LOG_DOMAIN "ide-buffer-addin" #include "config.h" #include #include #include "ide-buffer.h" #include "ide-buffer-addin.h" #include "ide-buffer-addin-private.h" #include "ide-buffer-private.h" /** * SECTION:ide-buffer-addin * @title: IdeBufferAddin * @short_description: addins for #IdeBuffer * * The #IdeBufferAddin allows a plugin to register an object that will be * created with every #IdeBuffer. It can register extra features with the * buffer or extend it as necessary. * * Once use of #IdeBufferAddin is to add a spellchecker to the buffer that * may be used by views to show the misspelled words. This is preferrable * to adding a spellchecker in each view because it allows for multiple * views to share one spellcheker on the underlying buffer. * * Since: 3.32 */ G_DEFINE_INTERFACE (IdeBufferAddin, ide_buffer_addin, G_TYPE_OBJECT) static void ide_buffer_addin_real_settle_async (IdeBufferAddin *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(IdeTask) task = NULL; task = ide_task_new (self, cancellable, callback, user_data); ide_task_set_source_tag (task, ide_buffer_addin_real_settle_async); ide_task_set_priority (task, G_PRIORITY_HIGH); ide_task_return_boolean (task, TRUE); } static gboolean ide_buffer_addin_real_settle_finish (IdeBufferAddin *self, GAsyncResult *result, GError **error) { return ide_task_propagate_boolean (IDE_TASK (result), error); } static void ide_buffer_addin_default_init (IdeBufferAddinInterface *iface) { iface->settle_async = ide_buffer_addin_real_settle_async; iface->settle_finish = ide_buffer_addin_real_settle_finish; } /** * ide_buffer_addin_load: * @self: an #IdeBufferAddin * @buffer: an #IdeBuffer * * This calls the load virtual function of #IdeBufferAddin to request * that the addin load itself. * * Since: 3.32 */ void ide_buffer_addin_load (IdeBufferAddin *self, IdeBuffer *buffer) { g_return_if_fail (IDE_IS_MAIN_THREAD ()); g_return_if_fail (IDE_IS_BUFFER_ADDIN (self)); g_return_if_fail (IDE_IS_BUFFER (buffer)); if (IDE_BUFFER_ADDIN_GET_IFACE (self)->load) IDE_BUFFER_ADDIN_GET_IFACE (self)->load (self, buffer); } /** * ide_buffer_addin_unload: * @self: an #IdeBufferAddin * @buffer: an #IdeBuffer * * This calls the unload virtual function of #IdeBufferAddin to request * that the addin unload itself. * * The addin should cancel any in-flight operations and attempt to drop * references to the buffer or any other machinery as soon as possible. * * Since: 3.32 */ void ide_buffer_addin_unload (IdeBufferAddin *self, IdeBuffer *buffer) { g_return_if_fail (IDE_IS_MAIN_THREAD ()); g_return_if_fail (IDE_IS_BUFFER_ADDIN (self)); g_return_if_fail (IDE_IS_BUFFER (buffer)); if (IDE_BUFFER_ADDIN_GET_IFACE (self)->unload) IDE_BUFFER_ADDIN_GET_IFACE (self)->unload (self, buffer); } /** * ide_buffer_addin_file_loaded: * @self: a #IdeBufferAddin * @buffer: an #IdeBuffer * @file: a #GFile * * This function is called for an addin after a file has been loaded from disk. * * It is not guaranteed that this function will be called for addins that were * loaded after the buffer already loaded a file. * * Since: 3.32 */ void ide_buffer_addin_file_loaded (IdeBufferAddin *self, IdeBuffer *buffer, GFile *file) { g_return_if_fail (IDE_IS_MAIN_THREAD ()); g_return_if_fail (IDE_IS_BUFFER_ADDIN (self)); g_return_if_fail (IDE_IS_BUFFER (buffer)); g_return_if_fail (G_IS_FILE (file)); if (IDE_BUFFER_ADDIN_GET_IFACE (self)->file_loaded) IDE_BUFFER_ADDIN_GET_IFACE (self)->file_loaded (self, buffer, file); } /** * ide_buffer_addin_save_file: * @self: a #IdeBufferAddin * @buffer: an #IdeBuffer * @file: a #GFile * * This function gives a chance for plugins to modify the buffer right before * writing to disk. * * Since: 3.32 */ void ide_buffer_addin_save_file (IdeBufferAddin *self, IdeBuffer *buffer, GFile *file) { g_return_if_fail (IDE_IS_MAIN_THREAD ()); g_return_if_fail (IDE_IS_BUFFER_ADDIN (self)); g_return_if_fail (IDE_IS_BUFFER (buffer)); g_return_if_fail (G_IS_FILE (file)); if (IDE_BUFFER_ADDIN_GET_IFACE (self)->save_file) IDE_BUFFER_ADDIN_GET_IFACE (self)->save_file (self, buffer, file); } /** * ide_buffer_addin_file_saved: * @self: a #IdeBufferAddin * @buffer: an #IdeBuffer * @file: a #GFile * * This function is called for an addin after a file has been saved to disk. * * Since: 3.32 */ void ide_buffer_addin_file_saved (IdeBufferAddin *self, IdeBuffer *buffer, GFile *file) { g_return_if_fail (IDE_IS_MAIN_THREAD ()); g_return_if_fail (IDE_IS_BUFFER_ADDIN (self)); g_return_if_fail (IDE_IS_BUFFER (buffer)); g_return_if_fail (G_IS_FILE (file)); if (IDE_BUFFER_ADDIN_GET_IFACE (self)->file_saved) IDE_BUFFER_ADDIN_GET_IFACE (self)->file_saved (self, buffer, file); } /** * ide_buffer_addin_language_set: * @self: an #IdeBufferAddin * @buffer: an #IdeBuffer * @language_id: the GtkSourceView language identifier * * This vfunc is called when the source language in the buffer changes. This * will only be delivered to addins that support multiple languages. * * Since: 3.32 */ void ide_buffer_addin_language_set (IdeBufferAddin *self, IdeBuffer *buffer, const gchar *language_id) { g_return_if_fail (IDE_IS_BUFFER_ADDIN (self)); g_return_if_fail (IDE_IS_BUFFER (buffer)); if (IDE_BUFFER_ADDIN_GET_IFACE (self)->language_set) IDE_BUFFER_ADDIN_GET_IFACE (self)->language_set (self, buffer, language_id); } /** * ide_buffer_addin_change_settled: * @self: an #IdeBufferAddin * @buffer: an #ideBuffer * * This function is called when the buffer has settled after a number of * changes provided by the user. It is a convenient way to know when you * should perform more background work without having to coalesce work * yourself. * * Since: 3.32 */ void ide_buffer_addin_change_settled (IdeBufferAddin *self, IdeBuffer *buffer) { g_return_if_fail (IDE_IS_BUFFER_ADDIN (self)); g_return_if_fail (IDE_IS_BUFFER (buffer)); if (IDE_BUFFER_ADDIN_GET_IFACE (self)->change_settled) IDE_BUFFER_ADDIN_GET_IFACE (self)->change_settled (self, buffer); } /** * ide_buffer_addin_style_scheme_changed: * @self: an #IdeBufferAddin * @buffer: an #IdeBuffer * * This function is called when the #GtkSourceStyleScheme of the #IdeBuffer * has changed. * * Since: 3.32 */ void ide_buffer_addin_style_scheme_changed (IdeBufferAddin *self, IdeBuffer *buffer) { g_return_if_fail (IDE_IS_BUFFER_ADDIN (self)); g_return_if_fail (IDE_IS_BUFFER (buffer)); if (IDE_BUFFER_ADDIN_GET_IFACE (self)->style_scheme_changed) IDE_BUFFER_ADDIN_GET_IFACE (self)->style_scheme_changed (self, buffer); } /** * ide_buffer_addin_find_by_module_name: * @buffer: an #IdeBuffer * @module_name: the module name of the addin * * Locates an addin attached to the #IdeBuffer by the name of the module * that provides the addin. * * Returns: (transfer none) (nullable): An #IdeBufferAddin or %NULL * * Since: 3.32 */ IdeBufferAddin * ide_buffer_addin_find_by_module_name (IdeBuffer *buffer, const gchar *module_name) { PeasPluginInfo *plugin_info; IdeExtensionSetAdapter *set; PeasExtension *ret = NULL; g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL); g_return_val_if_fail (IDE_IS_BUFFER (buffer), NULL); g_return_val_if_fail (module_name != NULL, NULL); set = _ide_buffer_get_addins (buffer); /* Addins might not be loaded */ if (set == NULL) return NULL; plugin_info = peas_engine_get_plugin_info (peas_engine_get_default (), module_name); if (plugin_info != NULL) ret = ide_extension_set_adapter_get_extension (set, plugin_info); else g_warning ("Failed to locate addin named %s", module_name); return ret ? IDE_BUFFER_ADDIN (ret) : NULL; } void _ide_buffer_addin_load_cb (IdeExtensionSetAdapter *set, PeasPluginInfo *plugin_info, PeasExtension *exten, gpointer user_data) { IdeBuffer *buffer = user_data; g_return_if_fail (IDE_IS_EXTENSION_SET_ADAPTER (set)); g_return_if_fail (plugin_info != NULL); g_return_if_fail (IDE_IS_BUFFER_ADDIN (exten)); g_return_if_fail (IDE_IS_BUFFER (user_data)); ide_buffer_addin_load (IDE_BUFFER_ADDIN (exten), buffer); if (ide_buffer_get_state (buffer) == IDE_BUFFER_STATE_READY && !ide_buffer_get_is_temporary (buffer)) { IdeBufferFileLoad closure = { .buffer = buffer, .file = ide_buffer_get_file (buffer), }; _ide_buffer_addin_file_loaded_cb (set, plugin_info, exten, &closure); } } void _ide_buffer_addin_unload_cb (IdeExtensionSetAdapter *set, PeasPluginInfo *plugin_info, PeasExtension *exten, gpointer user_data) { g_return_if_fail (IDE_IS_EXTENSION_SET_ADAPTER (set)); g_return_if_fail (plugin_info != NULL); g_return_if_fail (IDE_IS_BUFFER_ADDIN (exten)); g_return_if_fail (IDE_IS_BUFFER (user_data)); ide_buffer_addin_unload (IDE_BUFFER_ADDIN (exten), IDE_BUFFER (user_data)); } void _ide_buffer_addin_file_loaded_cb (IdeExtensionSetAdapter *set, PeasPluginInfo *plugin_info, PeasExtension *exten, gpointer user_data) { IdeBufferFileLoad *load = user_data; g_return_if_fail (IDE_IS_EXTENSION_SET_ADAPTER (set)); g_return_if_fail (plugin_info != NULL); g_return_if_fail (IDE_IS_BUFFER_ADDIN (exten)); g_return_if_fail (load != NULL); g_return_if_fail (IDE_IS_BUFFER (load->buffer)); g_return_if_fail (G_IS_FILE (load->file)); ide_buffer_addin_file_loaded (IDE_BUFFER_ADDIN (exten), load->buffer, load->file); } void _ide_buffer_addin_save_file_cb (IdeExtensionSetAdapter *set, PeasPluginInfo *plugin_info, PeasExtension *exten, gpointer user_data) { IdeBufferFileSave *save = user_data; g_return_if_fail (IDE_IS_EXTENSION_SET_ADAPTER (set)); g_return_if_fail (plugin_info != NULL); g_return_if_fail (IDE_IS_BUFFER_ADDIN (exten)); g_return_if_fail (save != NULL); g_return_if_fail (IDE_IS_BUFFER (save->buffer)); g_return_if_fail (G_IS_FILE (save->file)); ide_buffer_addin_save_file (IDE_BUFFER_ADDIN (exten), save->buffer, save->file); } void _ide_buffer_addin_file_saved_cb (IdeExtensionSetAdapter *set, PeasPluginInfo *plugin_info, PeasExtension *exten, gpointer user_data) { IdeBufferFileSave *save = user_data; g_return_if_fail (IDE_IS_EXTENSION_SET_ADAPTER (set)); g_return_if_fail (plugin_info != NULL); g_return_if_fail (IDE_IS_BUFFER_ADDIN (exten)); g_return_if_fail (save != NULL); g_return_if_fail (IDE_IS_BUFFER (save->buffer)); g_return_if_fail (G_IS_FILE (save->file)); ide_buffer_addin_file_saved (IDE_BUFFER_ADDIN (exten), save->buffer, save->file); } void _ide_buffer_addin_language_set_cb (IdeExtensionSetAdapter *set, PeasPluginInfo *plugin_info, PeasExtension *exten, gpointer user_data) { IdeBufferLanguageSet *lang = user_data; g_return_if_fail (IDE_IS_EXTENSION_SET_ADAPTER (set)); g_return_if_fail (plugin_info != NULL); g_return_if_fail (IDE_IS_BUFFER_ADDIN (exten)); g_return_if_fail (lang != NULL); g_return_if_fail (IDE_IS_BUFFER (lang->buffer)); ide_buffer_addin_language_set (IDE_BUFFER_ADDIN (exten), lang->buffer, lang->language_id); } void _ide_buffer_addin_change_settled_cb (IdeExtensionSetAdapter *set, PeasPluginInfo *plugin_info, PeasExtension *exten, gpointer user_data) { g_return_if_fail (IDE_IS_EXTENSION_SET_ADAPTER (set)); g_return_if_fail (plugin_info != NULL); g_return_if_fail (IDE_IS_BUFFER_ADDIN (exten)); g_return_if_fail (IDE_IS_BUFFER (user_data)); ide_buffer_addin_change_settled (IDE_BUFFER_ADDIN (exten), IDE_BUFFER (user_data)); } void _ide_buffer_addin_style_scheme_changed_cb (IdeExtensionSetAdapter *set, PeasPluginInfo *plugin_info, PeasExtension *exten, gpointer user_data) { g_return_if_fail (IDE_IS_EXTENSION_SET_ADAPTER (set)); g_return_if_fail (plugin_info != NULL); g_return_if_fail (IDE_IS_BUFFER_ADDIN (exten)); g_return_if_fail (IDE_IS_BUFFER (user_data)); ide_buffer_addin_style_scheme_changed (IDE_BUFFER_ADDIN (exten), IDE_BUFFER (user_data)); } void ide_buffer_addin_settle_async (IdeBufferAddin *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (IDE_IS_MAIN_THREAD ()); g_return_if_fail (IDE_IS_BUFFER_ADDIN (self)); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); IDE_BUFFER_ADDIN_GET_IFACE (self)->settle_async (self, cancellable, callback, user_data); } gboolean ide_buffer_addin_settle_finish (IdeBufferAddin *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (IDE_IS_MAIN_THREAD (), FALSE); g_return_val_if_fail (IDE_IS_BUFFER_ADDIN (self), FALSE); g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); return IDE_BUFFER_ADDIN_GET_IFACE (self)->settle_finish (self, result, error); }