As a diff is sometimes clearer than words, here's an additional one addressing some discussions above (it's on top of my previous patch, but I don't mean to suggest they should be applied as-is). It includes _provided() that can tell the caller whether it was the winner, user_data for the vfuncs, and a fairly thorough multi-extension support.

Another not-actually-so-big diff
diff --git a/src/document.c b/src/document.c
index adf5b8cc6..9bcbc7996 100644
--- a/src/document.c
+++ b/src/document.c
@@ -2720,7 +2720,7 @@ void document_highlight_tags(GeanyDocument *doc)
 	GString *keywords_str;
 	gint keyword_idx;
 
-	if (! plugin_extension_active(plugin_extension_geany))
+	if (! plugin_extension_symbol_highlight_provided(doc, plugin_extension_geany))
 		return;
 
 	/* some filetypes support type keywords (such as struct names), but not
diff --git a/src/editor.c b/src/editor.c
index 7a1d2fdc1..0d0d5ff23 100644
--- a/src/editor.c
+++ b/src/editor.c
@@ -838,11 +838,8 @@ static void on_char_added(GeanyEditor *editor, SCNotification *nt)
 		}
 	}
 
-	if (plugin_extension_calltips_provided(editor->document))
-		plugin_extension_calltips_show(editor->document, FALSE);
-
-	if (plugin_extension_autocomplete_provided(editor->document))
-		plugin_extension_autocomplete_perform(editor->document, FALSE);
+	plugin_extension_calltips_show(editor->document, FALSE);
+	plugin_extension_autocomplete_perform(editor->document, FALSE);
 
 	check_line_breaking(editor, pos);
 }
@@ -1137,7 +1134,7 @@ static gboolean on_editor_notify(G_GNUC_UNUSED GObject *object, GeanyEditor *edi
 			/* now that autocomplete is finishing or was cancelled, reshow calltips
 			 * if they were showing */
 			autocomplete_scope_shown = FALSE;
-			if (plugin_extension_active(plugin_extension_geany))
+			if (plugin_extension_calltips_provided(doc, plugin_extension_geany))
 				request_reshowing_calltip(nt);
 			break;
 		case SCN_NEEDSHOWN:
@@ -1152,7 +1149,7 @@ static gboolean on_editor_notify(G_GNUC_UNUSED GObject *object, GeanyEditor *edi
 			break;
 
 		case SCN_CALLTIPCLICK:
-			if (plugin_extension_active(plugin_extension_geany) && nt->position > 0)
+			if (plugin_extension_calltips_provided(doc, plugin_extension_geany) && nt->position > 0)
 			{
 				switch (nt->position)
 				{
@@ -5295,7 +5292,7 @@ void editor_indent(GeanyEditor *editor, gboolean increase)
 }
 
 
-void editor_extension_autocomplete_perform(GeanyDocument *doc, gboolean force)
+void editor_extension_autocomplete_perform(GeanyDocument *doc, gboolean force, gpointer data G_GNUC_UNUSED)
 {
 	gint pos = sci_get_current_position(doc->editor->sci);
 
@@ -5335,7 +5332,7 @@ void editor_extension_autocomplete_perform(GeanyDocument *doc, gboolean force)
 }
 
 
-void editor_extension_calltips_show(GeanyDocument *doc, gboolean force)
+void editor_extension_calltips_show(GeanyDocument *doc, gboolean force, gpointer data G_GNUC_UNUSED)
 {
 	gint pos = sci_get_current_position(doc->editor->sci);
 
diff --git a/src/editor.h b/src/editor.h
index 95c36a3e3..cee71df5e 100644
--- a/src/editor.h
+++ b/src/editor.h
@@ -279,8 +279,8 @@ void editor_snippets_free(void);
 
 const GeanyEditorPrefs *editor_get_prefs(GeanyEditor *editor);
 
-void editor_extension_autocomplete_perform(struct GeanyDocument *doc, gboolean force);
-void editor_extension_calltips_show(struct GeanyDocument *doc, gboolean force);
+void editor_extension_autocomplete_perform(struct GeanyDocument *doc, gboolean force, gpointer data);
+void editor_extension_calltips_show(struct GeanyDocument *doc, gboolean force, gpointer data);
 
 
 /* General editing functions */
diff --git a/src/keybindings.c b/src/keybindings.c
index 9eba0ef30..b5e08172f 100644
--- a/src/keybindings.c
+++ b/src/keybindings.c
@@ -2152,12 +2152,10 @@ static gboolean cb_func_editor_action(guint key_id)
 			sci_send_command(doc->editor->sci, SCI_LINETRANSPOSE);
 			break;
 		case GEANY_KEYS_EDITOR_AUTOCOMPLETE:
-			if (plugin_extension_autocomplete_provided(doc))
-				plugin_extension_autocomplete_perform(doc, TRUE);
+			plugin_extension_autocomplete_perform(doc, TRUE);
 			break;
 		case GEANY_KEYS_EDITOR_CALLTIP:
-			if (plugin_extension_calltips_provided(doc))
-				plugin_extension_calltips_show(doc, TRUE);
+			plugin_extension_calltips_show(doc, TRUE);
 			break;
 		case GEANY_KEYS_EDITOR_CONTEXTACTION:
 			if (check_current_word(doc, FALSE))
diff --git a/src/libmain.c b/src/libmain.c
index d849bfa45..a54831d5a 100644
--- a/src/libmain.c
+++ b/src/libmain.c
@@ -46,6 +46,7 @@
 #include "navqueue.h"
 #include "notebook.h"
 #include "plugins.h"
+#include "pluginextension.h"
 #include "projectprivate.h"
 #include "prefs.h"
 #include "printing.h"
@@ -1033,6 +1034,8 @@ void main_init_headless(void)
 	memset(&template_prefs, 0, sizeof(GeanyTemplatePrefs));
 	memset(&ui_prefs, 0, sizeof(UIPrefs));
 	memset(&ui_widgets, 0, sizeof(UIWidgets));
+
+	plugin_extension_register(plugin_extension_geany, 0, NULL);
 }
 
 
diff --git a/src/pluginextension.c b/src/pluginextension.c
index 0460a7c5a..5d0c8d546 100644
--- a/src/pluginextension.c
+++ b/src/pluginextension.c
@@ -24,12 +24,12 @@
 #include "symbols.h"
 
 
-static gboolean func_return_true(GeanyDocument *doc)
+static gboolean func_return_true(GeanyDocument *doc, gpointer data)
 {
 	return TRUE;
 }
 
-static GPtrArray *func_return_ptrarr(GeanyDocument *doc)
+static GPtrArray *func_return_ptrarr(GeanyDocument *doc, gpointer data)
 {
 	return NULL;
 }
@@ -50,86 +50,161 @@ static PluginExtension plugin_extension_geany_intenral = {
 	.symbol_highlight_provided = func_return_true
 };
 
-static PluginExtension *current_extension = &plugin_extension_geany_intenral;
+typedef struct
+{
+	PluginExtension *extension;
+	gpointer data;
+	gint priority;
+} PluginExtensionEntry;
+
+static GList *all_extensions = NULL;
+
 PluginExtension *plugin_extension_geany = &plugin_extension_geany_intenral;
 
 
-GEANY_API_SYMBOL
-void plugin_extension_register(PluginExtension *extension)
+/* sort higher priorities first */
+static gint sort_extension_entries(gconstpointer a, gconstpointer b)
 {
-	/* possibly, in the future if there's a need for multiple extensions,
-	 * have a list of extensions and add/remove to/from the list */
-	current_extension = extension;
+	const PluginExtensionEntry *entry_a = a;
+	const PluginExtensionEntry *entry_b = b;
+
+	return entry_b->priority - entry_a->priority;
 }
 
 
 GEANY_API_SYMBOL
-void plugin_extension_unregister(PluginExtension *extension)
+void plugin_extension_register(PluginExtension *extension, gint priority, gpointer data)
 {
-	current_extension = &plugin_extension_geany_intenral;
+	PluginExtensionEntry *entry = g_malloc(sizeof *entry);
+
+	entry->extension = extension;
+	entry->data = data;
+	entry->priority = priority;
+
+	all_extensions = g_list_insert_sorted(all_extensions, entry, sort_extension_entries);
 }
 
 
 GEANY_API_SYMBOL
-gboolean plugin_extension_active(PluginExtension *extension)
+void plugin_extension_unregister(PluginExtension *extension)
 {
-	return current_extension == extension;
+	for (GList *node = all_extensions; node; node = node->next)
+	{
+		PluginExtensionEntry *entry = node->data;
+
+		if (entry->extension == extension)
+		{
+			g_free(entry);
+			all_extensions = g_list_delete_link(all_extensions, node);
+			break;
+		}
+	}
 }
 
 
-/* allow plugins not to implement all the functions and fall back to the dummy
- * implementation */
-#define CALL_IF_EXISTS(f) (current_extension->f ? current_extension->f : plugin_extension_geany_intenral.f)
+/*
+ * @brief Checks whether a feature is provided
+ * @param f The virtual function name
+ * @param doc The document to check the feature on
+ * @param ext A @c PluginExtension, or @c NULL
+ * @returns @c TRUE if the feature is provided, @c FALSE otherwise.  If @p ext
+ *          is @c NULL, it check whether any extension provides the feature;
+ *          if it is an extension, it check whether it's this extension that
+ *          provides the feature (taking into account possible overrides).
+ */
+#define CALL_PROVIDED(f, doc, ext)												\
+	G_STMT_START {																\
+		for (GList *node = all_extensions; node; node = node->next)				\
+		{																		\
+			PluginExtensionEntry *entry = node->data;							\
+																				\
+			if (entry->extension->f && entry->extension->f(doc, entry->data))	\
+				return (ext) ? entry->extension == (ext) : TRUE;				\
+		}																		\
+		return FALSE;															\
+	} G_STMT_END
+
+/*
+ * @brief Calls the extension implementation for f_provided/f_perform
+ * @param f_provided The name of the virtual function checking if the feature is provided
+ * @param doc The document to check the feature on
+ * @param f_perform The name of the virtual function implementing the feature
+ * @param args Arguments for @p f_perform. This should include @c entry->data as the last argument
+ * @param defret Return value if the feature is not implemented
+ * @returns The return value of @p f_perform or @p defret
+ */
+#define CALL_PERFORM(f_provided, doc, f_perform, args, defret)					\
+	G_STMT_START {																\
+		for (GList *node = all_extensions; node; node = node->next)				\
+		{																		\
+			PluginExtensionEntry *entry = node->data;							\
+																				\
+			if (entry->extension->f_provided &&									\
+				entry->extension->f_provided(doc, entry->data))					\
+			{																	\
+				if (entry->extension->f_perform)								\
+					return entry->extension->f_perform args;					\
+				break;															\
+			}																	\
+		}																		\
+		return defret;															\
+	} G_STMT_END
+
 
-gboolean plugin_extension_autocomplete_provided(GeanyDocument *doc)
+GEANY_API_SYMBOL
+gboolean plugin_extension_autocomplete_provided(GeanyDocument *doc, PluginExtension *ext)
 {
-	return CALL_IF_EXISTS(autocomplete_provided)(doc);
+	CALL_PROVIDED(autocomplete_provided, doc, ext);
 }
 
 
 void plugin_extension_autocomplete_perform(GeanyDocument *doc, gboolean force)
 {
-	CALL_IF_EXISTS(autocomplete_perform)(doc, force);
+	CALL_PERFORM(autocomplete_provided, doc, autocomplete_perform, (doc, force, entry->data), /* void */);
 }
 
 
-gboolean plugin_extension_calltips_provided(GeanyDocument *doc)
+GEANY_API_SYMBOL
+gboolean plugin_extension_calltips_provided(GeanyDocument *doc, PluginExtension *ext)
 {
-	return CALL_IF_EXISTS(calltips_provided)(doc);
+	CALL_PROVIDED(calltips_provided, doc, ext);
 }
 
 
 void plugin_extension_calltips_show(GeanyDocument *doc, gboolean force)
 {
-	CALL_IF_EXISTS(calltips_show)(doc, force);
+	CALL_PERFORM(calltips_provided, doc, calltips_show, (doc, force, entry->data), /* void */);
 }
 
 
-gboolean plugin_extension_goto_provided(GeanyDocument *doc)
+GEANY_API_SYMBOL
+gboolean plugin_extension_goto_provided(GeanyDocument *doc, PluginExtension *ext)
 {
-	return CALL_IF_EXISTS(goto_provided)(doc);
+	CALL_PROVIDED(goto_provided, doc, ext);
 }
 
 
 void plugin_extension_goto_perform(GeanyDocument *doc, gint pos, gboolean definition)
 {
-	CALL_IF_EXISTS(goto_perform)(doc, pos, definition);
+	CALL_PERFORM(goto_provided, doc, goto_perform, (doc, pos, definition, entry->data), /* void */);
 }
 
 
-gboolean plugin_extension_doc_symbols_provided(GeanyDocument *doc)
+GEANY_API_SYMBOL
+gboolean plugin_extension_doc_symbols_provided(GeanyDocument *doc, PluginExtension *ext)
 {
-	return CALL_IF_EXISTS(doc_symbols_provided)(doc);
+	CALL_PROVIDED(doc_symbols_provided, doc, ext);
 }
 
 
 GPtrArray *plugin_extension_doc_symbols_get(GeanyDocument *doc)
 {
-	return CALL_IF_EXISTS(doc_symbols_get)(doc);
+	CALL_PERFORM(doc_symbols_provided, doc, doc_symbols_get, (doc, entry->data), NULL);
 }
 
 
-gboolean plugin_extension_symbol_highlight_provided(GeanyDocument *doc)
+GEANY_API_SYMBOL
+gboolean plugin_extension_symbol_highlight_provided(GeanyDocument *doc, PluginExtension *ext)
 {
-	return CALL_IF_EXISTS(symbol_highlight_provided)(doc);
+	CALL_PROVIDED(symbol_highlight_provided, doc, ext);
 }
diff --git a/src/pluginextension.h b/src/pluginextension.h
index 48866a4a6..3f018efd8 100644
--- a/src/pluginextension.h
+++ b/src/pluginextension.h
@@ -31,47 +31,42 @@ G_BEGIN_DECLS
 
 
 typedef struct {
-	gboolean (*autocomplete_provided)(GeanyDocument *doc);
-	void (*autocomplete_perform)(GeanyDocument *doc, gboolean force);
+	gboolean (*autocomplete_provided)(GeanyDocument *doc, gpointer data);
+	void (*autocomplete_perform)(GeanyDocument *doc, gboolean force, gpointer data);
 
-	gboolean (*calltips_provided)(GeanyDocument *doc);
-	void (*calltips_show)(GeanyDocument *doc, gboolean force);
+	gboolean (*calltips_provided)(GeanyDocument *doc, gpointer data);
+	void (*calltips_show)(GeanyDocument *doc, gboolean force, gpointer data);
 
-	gboolean (*goto_provided)(GeanyDocument *doc);
-	void (*goto_perform)(GeanyDocument *doc, gint pos, gboolean definition);
+	gboolean (*goto_provided)(GeanyDocument *doc, gpointer data);
+	void (*goto_perform)(GeanyDocument *doc, gint pos, gboolean definition, gpointer data);
 
-	gboolean (*doc_symbols_provided)(GeanyDocument *doc);
-	GPtrArray *(*doc_symbols_get)(GeanyDocument *doc);
+	gboolean (*doc_symbols_provided)(GeanyDocument *doc, gpointer data);
+	GPtrArray *(*doc_symbols_get)(GeanyDocument *doc, gpointer data);
 
-	gboolean (*symbol_highlight_provided)(GeanyDocument *doc);
+	gboolean (*symbol_highlight_provided)(GeanyDocument *doc, gpointer data);
 
 	gchar _dummy[1024];
 } PluginExtension;
 
 
-void plugin_extension_register(PluginExtension *extension);
+void plugin_extension_register(PluginExtension *extension, gint priority, gpointer data);
 void plugin_extension_unregister(PluginExtension *extension);
-gboolean plugin_extension_active(PluginExtension *extension);
 
+gboolean plugin_extension_autocomplete_provided(GeanyDocument *doc, PluginExtension *ext);
+gboolean plugin_extension_calltips_provided(GeanyDocument *doc, PluginExtension *ext);
+gboolean plugin_extension_goto_provided(GeanyDocument *doc, PluginExtension *ext);
+gboolean plugin_extension_doc_symbols_provided(GeanyDocument *doc, PluginExtension *ext);
+gboolean plugin_extension_symbol_highlight_provided(GeanyDocument *doc, PluginExtension *ext);
 
 #ifdef GEANY_PRIVATE
 
 extern PluginExtension *plugin_extension_geany;
 
-gboolean plugin_extension_autocomplete_provided(GeanyDocument *doc);
 void plugin_extension_autocomplete_perform(GeanyDocument *doc, gboolean force);
-
-gboolean plugin_extension_calltips_provided(GeanyDocument *doc);
 void plugin_extension_calltips_show(GeanyDocument *doc, gboolean force);
-
-gboolean plugin_extension_goto_provided(GeanyDocument *doc);
 void plugin_extension_goto_perform(GeanyDocument *doc, gint pos, gboolean definition);
-
-gboolean plugin_extension_doc_symbols_provided(GeanyDocument *doc);
 GPtrArray *plugin_extension_doc_symbols_get(GeanyDocument *doc);
 
-gboolean plugin_extension_symbol_highlight_provided(GeanyDocument *doc);
-
 #endif /* GEANY_PRIVATE */
 
 G_END_DECLS
diff --git a/src/symbols.c b/src/symbols.c
index e1585ece1..366db783a 100644
--- a/src/symbols.c
+++ b/src/symbols.c
@@ -1708,7 +1708,7 @@ gboolean symbols_goto_tag(const gchar *name, gint pos, gboolean definition)
 {
 	GeanyDocument *doc = document_get_current();
 
-	if (plugin_extension_goto_provided(doc))
+	if (plugin_extension_goto_provided(doc, NULL))
 	{
 		/* FIXME: this should return TRUE on success so click handling in
 		 * editor.c can let the even pass through if there was nothing to do here
@@ -1722,7 +1722,7 @@ gboolean symbols_goto_tag(const gchar *name, gint pos, gboolean definition)
 }
 
 
-void symbols_goto_perform(GeanyDocument *doc, gint pos, gboolean definition)
+void symbols_goto_perform(GeanyDocument *doc, gint pos, gboolean definition, gpointer data G_GNUC_UNUSED)
 {
 	const gchar *name;
 
diff --git a/src/symbols.h b/src/symbols.h
index 00face7f8..070f4ed42 100644
--- a/src/symbols.h
+++ b/src/symbols.h
@@ -60,7 +60,7 @@ void symbols_show_load_tags_dialog(void);
 
 gboolean symbols_goto_tag(const gchar *name, gint pos, gboolean definition);
 
-void symbols_goto_perform(GeanyDocument *doc, gint pos, gboolean definition);
+void symbols_goto_perform(GeanyDocument *doc, gint pos, gboolean definition, gpointer data);
 
 gint symbols_get_current_function(GeanyDocument *doc, const gchar **tagname);
 


Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.Message ID: <geany/geany/pull/3849/c2150913604@github.com>