Hi All,

After experimenting with a few different ideas and approaches, I think I have come up with a way to implement the core of "filetype plugins"[0] without too much effort or code.

----

Terminology:

"Filetype plugin" - or "ft-plugin"; at this point is just normal plugin that would call special API functions and implement any number of features for any number of filetypes.

"Feature" - one of the features an ft-plugin would want to override, for example syntax highlighting or auto-completion.

"Provider" - an ft-plugin that provides one or more features for one or more filetypes. For example if it can provide calltips, it could be referred to as a "Calltip Provider". Each ft-plugin can have a number of "providers" for the various features and filetypes.

"Registration" - the act (ie. function call) of an ft-plugin declaring its interest in providing a feature for a filetype. An ft-plugin can register to provide one or more features for one or more filetypes.

----

In this design, a new module (c/h file) would be added to manage the filetype plugins and which features they provide for which filetypes. A mapping (ex. GHashTable) could be used to map from Filetype to a list (ex. GQueue) of data describing the needed information for a registered provider. The list would be ordered in the same order as registration and could also be re-ordered by the user using a GUI (more on this below).

The order of the list of plugins registered to provide a feature for a given feature/filetype pair would determine the priority given when Geany asks the provider to perform its function. The callback functions could return a boolean telling Geany whether the provider performed its function or whether it should try the next provider in the list, similar to many GTK+ callbacks. If no provider performs its function, or there are no providers registered for a given feature/filetype, then Geany would take its existing code path to provide the feature itself.

When a plugin is unloaded, the mapping and lists of providers would be updated to remove any providers registered by that plugin so Geany doesn't call into an unloaded plugin. The next provider registered (if any) would have the first chance to now provide the feature.

When a plugin registers its intent to provide a feature (or perhaps after it has registered all the features it wishes to provide), Geany could check whether there is already another plugin providing this feature. Geany could ask the user if they would like to resolve the conflict, and if they would, then it could show a management dialog (see attachment for mockup), allowing the user to control the priority of the plugin's provider for a given feature/filetype by moving them up or down in a list, and possibly being able to completely disable a provider entirely (via a checkbox in the list or something).

To give an idea, the registration function called by plugins might look something like this:

    gboolean ftplugin_register_provider(GeanyPlugin*,
        GeanyFiletypeID, GeanyFiletypeFeature, GCallback, gpointer);

Or perhaps it could use varargs similar to many GTK+/GObject functions, terminated by a sentinel, to register many filetype feature providers in one call, making it easier to implement a less annoying conflict handling scheme - not nagging after each registration call.

The callback function would have an actual signature suitable for implementing the specific feature. For example, the callback for a syntax highlighting provider might be something like this:

    gboolean (*) (GeanyPlugin*, GeanyDocument*,
        guint start_pos, guint end_pos, gpointer user_data);

The document would be the document that needs highlighting, and allow the plugin to access Scintilla and its buffer. The start/end positions would indicate where to highlight. This is in-line with Scintilla's 'style-needed' notification used to implement container lexers. I don't want to get bogged down on the actual specific signatures at this point, I just wanted to give an example.

To enable Geany to use the providers, in the existing code just before it's about to provide the feature itself as it does now, we could insert a check/call to try the ft-plugin providers. If nobody performed the feature, then it would continue to the existing code path. This should limit the number of changes needed to Geany. Some features would necessarily require more changes, for example syntax highlighting would require Geany to switch from the Scintilla lexer to the container lexer and back as plugins start/stop providing the feature. It will require care for features that are activated often to ensure minimal performance degradation when looking up and calling into the provider, as this would happen in the main code paths (unless someone has a better way).

Hopefully I have described enough details of my proposed design to allow everyone to understand what I mean. If there's any questions or suggestions, please let me know.

Thanks,
Matthew Brush

[0]: I'm still waiting for someone to propose a better name :)


























_______________________________________________
Devel mailing list
Devel@lists.geany.org
https://lists.geany.org/cgi-bin/mailman/listinfo/devel

Reply via email to