Branch: refs/heads/master Author: Jiří Techet techet@gmail.com Committer: Jiří Techet techet@gmail.com Date: Sun, 30 Jun 2024 09:52:40 UTC Commit: 358d585c297d75f62bfe051ff1f250c4a38ca95b https://github.com/geany/geany/commit/358d585c297d75f62bfe051ff1f250c4a38ca9...
Log Message: ----------- Add PluginExtension documentation
Co-authored-by: Colomban Wendling ban@herbesfolles.org Co-authored-by: Lex Trotman elextr@gmail.com
Modified Paths: -------------- doc/plugins.dox src/pluginextension.c src/pluginextension.h
Modified: doc/plugins.dox 143 lines changed, 143 insertions(+), 0 deletions(-) =================================================================== @@ -40,6 +40,7 @@ GeanyFuncs::cleanup functions). @section pluginsupport Plugin Support - @link howto Plugin HowTo @endlink - get started - @ref proxy +- @ref plugin_extension - @ref legacy - @link plugindata.h Plugin Datatypes and Macros @endlink - @link pluginsignals.c Plugin Signals @endlink @@ -54,6 +55,7 @@ GeanyFuncs::cleanup functions). - @link filetypes.h @endlink - @link keybindings.h @endlink - @link msgwindow.h @endlink +- @link pluginextension.h @endlink - @link project.h @endlink - @link sciwrappers.h Scintilla Wrapper Functions @endlink - @link spawn.h Spawning programs @endlink @@ -1116,4 +1118,145 @@ static void proxy_cleanup(GeanyPlugin *plugin, gpointer pdata) @endcode
+@page plugin_extension Plugin Extension HowTo + +@section plugin_extension_intro Introduction + +Originally the Geany plugin API only allowed plugins to add to Geany +functionality, plugins could not modify Geany built-in functionality, but since +Geany 2.1 the PluginExtension API allows plugins to take over some of +the core Geany functionality: autocopletion, calltip display, symbol goto, and +typename highlighting inside document. + +@section plugin_extension_init Initialization and cleanup + +Plugins using the @c PluginExtension API are just normal plugins and behave as +described in @link howto Plugin HowTo@endlink. + +First, any plugin interested in using this interface has to register its +@c PluginExtension structure pointer using @c plugin_extension_register(). This +typically happens in the @c init() function of the plugin. Registered +@c PluginExtension pointers have to be unregistered before the plugin is +unloaded using @c plugin_extension_unregister(), typically inside the +@c cleanup() function of the plugin. + +@section plugin_extension_impl Implementing extensions + +Inside the @c PluginExtension struct, the plugin fills-in the pointers of the +functions it wishes to implement. Typically, these functions +come in pairs: + - functions assigned to members ending with @c _provided are used by Geany to + query the plugin whether it implements the particular feature for the passed + document + - functions assigned to members ending with @c _perform are used by Geany to + pass control to the plugin to perform the feature instead of performing the + Geany built-in functionality. + +When the plugin returns @c TRUE from the function assigned to the @c _provided +member of @c PluginExtension, it indicates +it wants to take control of the particular feature and disable Geany's +implementation. However, returning @c TRUE does not automatically guarantee that +the plugin's implementation is executed - if there are multiple plugins competing +for implementing a feature, the extension with the highest priority +passed into the @c plugin_extension_register() function gets executed. + +A plugin can perform a check if it gets executed for the +particular feature; e.g. for autocompletion the plugin can use +@c plugin_extension_autocomplete_provided() which returns @c TRUE if the +passed extension is executed, taking into account all registered extension +priorities and the return values of all functions assigned to +@c autocomplete_provided members of the registered extensions. +This can be used if the plugin needs to perform auxiliary actions outside the +function assigned to @c autocomplete_perform to verify it is actually active +for this feature. + +@section plugin_extension_ex Example + +Below you will find an example of a plugin implementing autocompletion for +Python. The full version of this code can be found under +plugins/demopluginext.c inside the Geany repository. + + +@code +/* License blob */ + +#include <geanyplugin.h> + +static gboolean autocomplete_provided(GeanyDocument *doc, gpointer data) +{ + /* Check whether the plugin provides the feature for the passed document */ + return doc->file_type->id == GEANY_FILETYPES_PYTHON; +} + + +static void autocomplete_perform(GeanyDocument *doc, gboolean force, gpointer data) +{ + /* The autocompletion logic comes here, including the autocompletion UI + * display (either using some custom widget or using Scintilla's + * SCI_AUTOCSHOW) */ +} + + +/* The PluginExtension struct - we only implement autocompletion here. */ +static PluginExtension extension = { + .autocomplete_provided = autocomplete_provided, + .autocomplete_perform = autocomplete_perform +}; + + +static gboolean on_editor_notify(G_GNUC_UNUSED GObject *obj, GeanyEditor *editor, SCNotification *nt, + G_GNUC_UNUSED gpointer user_data) +{ + if (nt->nmhdr.code == SCN_AUTOCSELECTION) + { + if (plugin_extension_autocomplete_provided(editor->document, &extension)) + { + /* This is an example of using plugin_extension_autocomplete_provided() + * to detect whether this plugin extension was used to perform + * autocompletion. */ + msgwin_status_add("PluginExtensionDemo autocompleted '%s'", nt->text); + } + } + + return FALSE; +} + + +static PluginCallback plugin_callbacks[] = { + {"editor-notify", (GCallback) &on_editor_notify, FALSE, NULL}, + {NULL, NULL, FALSE, NULL} +}; + + +static gboolean init_func(GeanyPlugin *plugin, gpointer pdata) +{ + /* Extension registration */ + plugin_extension_register(&extension, "Python keyword autocompletion", 450, NULL); + return TRUE; +} + + +static void cleanup_func(GeanyPlugin *plugin, gpointer pdata) +{ + /* Extension unregistration */ + plugin_extension_unregister(&extension); +} + + +G_MODULE_EXPORT +void geany_load_module(GeanyPlugin *plugin) +{ + plugin->info->name = "PluginExtensionDemo"; + plugin->info->description = "Demo performing simple Python keyword autocompletion"; + plugin->info->version = "1.0"; + plugin->info->author = "John Doe john.doe@example.org"; + + plugin->funcs->init = init_func; + plugin->funcs->cleanup = cleanup_func; + plugin->funcs->callbacks = plugin_callbacks; + + GEANY_PLUGIN_REGISTER(plugin, 248); +} +@endcode + */
Modified: src/pluginextension.c 102 lines changed, 102 insertions(+), 0 deletions(-) =================================================================== @@ -43,6 +43,49 @@ static gint sort_extension_entries(gconstpointer a, gconstpointer b) }
+/** + * Registers the provided extension in Geany. There can be multiple extensions + * registered in Geany - these are sorted by the priority + * parameter. When executing functions assigned to the @c PluginExtension + * members ending with @c _perform, Geany goes + * through the registered extensions and executes the @c _perform() function of + * the first extension for which the function assigned to the corresponding + * @c _provided member returns @c TRUE. + * + * This function is typically called in the plugin's @c init() function. + * + * Plugins wishing to re-register themselves, e.g. with a different priority, + * should first unregister themselves using @c plugin_extension_unregister() + * and call @c plugin_extension_register() afterwards. + * + * @param extension A pointer to the @c PluginExtension structure to register. + * This pointer and the @c PluginExtension it points to have to remain valid + * until the extension is unregistered. All fields of the @c PluginExtension + * structure have to be fully initialized either to the @c NULL pointer or to a + * proper implementation. Usually, this structure is statically allocated which + * automatically zero-initializes uninitialized members as appropriate. + * @param ext_name Human-readable name of the extension that can appear in + * the user interface. The string should be reasonably unique so extensions can + * be distinguished from each other. + * @param priority Extension priority. The recommended values are: + * - if the extension is the only thing the plugin provides, and if it targets + * a single filetype, use 400 <= priority < 500. + * - if the extension is the only thing the plugin provides, but it targets + * several filetypes, use 300 <= priority < 400. + * - if the plugin provides other features than the extension, but it only + * targets a single filetype, use 200 <= priority < 300. + * - if the plugin provides other features than the extension, and targets + * several filetypes, use 100 <= priority < 200. + * - a priority of 0 or less is reserved, and using it has unspecified behavior. + * @param data User data passed to the functions from the @c PluginExtension + * struct. + * @warning Plugins are responsible for calling @c plugin_extension_unregister() + * when they no longer provide the extension and when the plugin is unloaded. + * + * @see @c plugin_extension_unregister(). + * + * @since 2.1 + **/ GEANY_API_SYMBOL void plugin_extension_register(PluginExtension *extension, const gchar *ext_name, gint priority, gpointer data) @@ -60,6 +103,17 @@ void plugin_extension_register(PluginExtension *extension, const gchar *ext_name }
+/** + * Plugins are responsible for calling this function when they no longer + * provide the extension, at the latest in the plugin's @c cleanup() function. + * + * @param extension The @c PluginExtension structure pointer to unregister, as + * previously registered with @c plugin_extension_register(). + * + * @see @c plugin_extension_register(). + * + * @since 2.1 + **/ GEANY_API_SYMBOL void plugin_extension_unregister(PluginExtension *extension) { @@ -129,6 +183,31 @@ void plugin_extension_unregister(PluginExtension *extension) } G_STMT_END
+/* + * Geany itself can also pass NULL as the extension parameter to determine + * whether there is any extension implementing autocompletion. Plugins should + * not use this and rely on Geany's extension list and the priorities so + * this feature is not documented in the plugin API. + */ +/** + * Plugins can call this function to check whether, based on the extensions + * registered and the provided extension priorities, the extension passed in + * the @c ext parameter is used for autocompletion. + * + * Plugins will typically call this function with their own @c PluginExtension + * to check, if they get (or got) executed for autocompletion of the + * provided document. This is useful for various auxiliary functions such as + * cleanups after the function assigned to @c autocomplete_perform is completed + * so plugins know they executed this function and do not have to store this + * information by some other means. + * + * @param doc Document for which the check is performed. + * @param ext The extension for which the check is performed. + * @return Returns @c TRUE if autocompletion provided by the passed extension + * is used, @c FALSE otherwise. + * + * @since 2.1 + **/ GEANY_API_SYMBOL gboolean plugin_extension_autocomplete_provided(GeanyDocument *doc, PluginExtension *ext) { @@ -142,6 +221,13 @@ void plugin_extension_autocomplete_perform(GeanyDocument *doc, gboolean force) }
+/** + * Checks whether the provided extension is used for showing calltips. + * + * @see @c plugin_extension_autocomplete_provided() + * + * @since 2.1 + **/ GEANY_API_SYMBOL gboolean plugin_extension_calltips_provided(GeanyDocument *doc, PluginExtension *ext) { @@ -155,6 +241,14 @@ void plugin_extension_calltips_show(GeanyDocument *doc, gboolean force) }
+/** + * Checks whether the provided extension is used for going to symbol + * definition/declaration. + * + * @see @c plugin_extension_autocomplete_provided() + * + * @since 2.1 + **/ GEANY_API_SYMBOL gboolean plugin_extension_goto_provided(GeanyDocument *doc, PluginExtension *ext) { @@ -168,6 +262,14 @@ gboolean plugin_extension_goto_perform(GeanyDocument *doc, gint pos, gboolean de }
+/** + * Checks whether the provided extension is used for highlighting symbols in + * the document. + * + * @see @c plugin_extension_autocomplete_provided() + * + * @since 2.1 + **/ GEANY_API_SYMBOL gboolean plugin_extension_symbol_highlight_provided(GeanyDocument *doc, PluginExtension *ext) {
Modified: src/pluginextension.h 129 lines changed, 128 insertions(+), 1 deletions(-) =================================================================== @@ -18,6 +18,13 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+/** + * @file pluginextension.h + * This file defines an interface allowing plugins to take over some of the + * core functionality provided by Geany: autocompletion, calltips, + * symbol goto, and types highlighting inside document. + **/ + #ifndef GEANY_PLUGIN_EXTENSION_H #define GEANY_PLUGIN_EXTENSION_H 1
@@ -26,18 +33,138 @@
G_BEGIN_DECLS
-typedef struct { +/** + * Structure serving as an interface between plugins and Geany allowing + * plugins to inform Geany about what features they provide and allowing + * Geany to delegate its functionality to the plugins. + * + * Depending on the functionality they provide, plugins should assign pointers + * to the functions implementing this interface to the appropriate members of + * the structure. Not all of the functions have to be implemented by the + * plugin - member pointers of unimplemented functions can be left to contain + * the @c NULL pointer. + * + * Typically, functions from this interface come in pairs. Functions assigned to + * the members ending with @c _provided inform Geany whether the plugin + * implements the given feature for the passed document. Functions assigned + * to the members ending with @c _perform are called by Geany at appropriate + * moments to inform the plugin when to perform the given feature. + * + * The extension is defined by the pointers in the PluginExtension structure and + * is registered in Geany using the @c plugin_extension_register() function. + * + * @warning The API provided by this file is subject to change and should not be + * considered stable at this point. That said, it is highly probable that if + * a change of this API is required in the future, it will not be of a major + * nature and should not require major modifications of the affected plugins + * (e.g. added/modified parameter of a function and similar changes). + **/ +typedef struct +{ + /** + * Pointer to function called by Geany to check whether the plugin + * implements autocompletion for the provided document. + * + * @param doc The document for which Geany is querying whether the plugin + * provides autocompletion. This allows plugins to restrict their + * autocompletion implementation to documents of specific filetypes or other + * characteristics. + * @param data User data passed during the @c plugin_extension_register() + * call. + * + * @return Plugins should return @c TRUE if they impelment autocompletion + * for the provided document, @c FALSE otherwise. + * + * @since 2.1 + **/ gboolean (*autocomplete_provided)(GeanyDocument *doc, gpointer data); + /** + * Pointer to function called by Geany to inform the plugin to perform + * autocompletion, e.g by showing an autocompletion popup window with + * suggestions. + * + * @param doc The document for which autocompletion is being performed. + * @param force @c TRUE when autocompletion was requested explicitly by the + * user, e.g. by pressing a keybinding requesting the autocompletion popup + * to appear. @c FALSE means autocompletion is being auto-performed as the + * user types, possibly allowing the plugin not to do anything if there are + * no meaningful suggestions. + * @param data User data passed during the @c plugin_extension_register() + * call. + * + * @since 2.1 + **/ void (*autocomplete_perform)(GeanyDocument *doc, gboolean force, gpointer data);
+ /** + * Pointer to function called by Geany to check whether the plugin + * implements calltips containing function signatures for the provided + * document. + * + * @see @c autocomplete_provided() for more details. + * + * @since 2.1 + **/ gboolean (*calltips_provided)(GeanyDocument *doc, gpointer data); + /** + * Pointer to function called by Geany to inform the plugin to show calltips. + * + * @see @c autocomplete_perform() for more details. + * + * @since 2.1 + **/ void (*calltips_show)(GeanyDocument *doc, gboolean force, gpointer data);
+ /** + * Pointer to function called by Geany to check whether the plugin implements + * symbol definition/declaration goto functionality. + * + * @see @c autocomplete_provided() for more details. + * + * @since 2.1 + **/ gboolean (*goto_provided)(GeanyDocument *doc, gpointer data); + /** + * Pointer to function called by Geany to inform the plugin to perform + * symbol goto. + * + * @param doc The document for which symbol goto is being performed. + * @param pos Scintilla position in the document at which the goto request + * was invoked (typically corresponds to the caret position or the position + * where the corresponding mouse event happened). + * @param definition If @c TRUE, this is the go to definition request, if + * @c FALSE, this is the goto declaration request. + * @param data User data passed during the @c plugin_extension_register() + * call. + * + * @return Plugins should return @c TRUE if the goto was performed, @c FALSE + * otherwise. Plugins that work asynchronously and do not know the result + * at the moment this function is called should return @c TRUE. However, such + * plugins can perform some synchronous pre-check such as whether there is + * an identifier at the caret position and if the necessary conditions are + * not satisfied and the plugins know that goto cannot be performed, they + * should return @c FALSE. + * + * @since 2.1 + **/ gboolean (*goto_perform)(GeanyDocument *doc, gint pos, gboolean definition, gpointer data);
+ /** + * Pointer to function called by Geany to check whether the plugin implements + * additional symbol (e.g. type) highlighting in Scintilla. + * + * @see @c autocomplete_provided() for more details. + * @note There is no function in the @c PluginExtension structure informing + * plugins to perform symbol highlighting. Plugins + * implementing symbol highlighting should perform it at the appropriate + * moments based on Scintilla and Geany events such as when the document + * becomes visible or when the document is modified. + * + * @since 2.1 + **/ gboolean (*symbol_highlight_provided)(GeanyDocument *doc, gpointer data);
+ /* Padding serving for possible future extensions of this API. */ void (*_dummy[100])(void); } PluginExtension;
-------------- This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).