New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proxy plugins #629
Proxy plugins #629
Conversation
I don't know where to PR'ify but here's a converted GeanyPy acting as a real-word showcase: https://github.com/kugel-/geany-plugins/tree/geanypy-pluxy |
@b4n @elextr @codebrainz sorry to annoy you but I really really want this for 1.26 so give it a look pretty please |
@kugel- this weekend is a long-weekend here, if you ping me on Saturday (I'm about half a day behind your timezone), I will try to sit down with this and at least give it some testing/reviewing. It looks to have a build error for this newish function on all platforms, according to Travis. |
I'm busy this WE, but I should be on it starting next Monday. Feel free to ping me continuously if I don't ;) |
* demoproxy.c - this file is part of Geany, a fast and lightweight IDE | ||
* | ||
* Copyright 2007-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de> | ||
* Copyright 2007-2012 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you wrote this new file, it would be more appropriate to put your copyright in there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree this should be copyright @kugel-
@kugel- all travis builds show an implicitly defined function. Did you miss an include file? |
Here's what GCC tells me when I build this:
|
I find the |
The separate API allows to do the registration in the plugin's init(), and have the file extensions deppend on plugin configuration. Also a API-wise separattion to geany_plugin_register() is needed to allow for nested proxies. A proxy can be a proxy for many types. the only limitation is that only one set of ProxyFuncs is supported per proxy. But this limitation is not a problem, since the proxies can do their own dispatching. For geany it simplifies storing the pointers (avoids introducing a list of vtables, etc) |
if (tmp == NULL) | ||
return FALSE; | ||
/* ensure the dot is really part of the filename */ | ||
else if (strchr(tmp, G_DIR_SEPARATOR) != NULL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On Windows we probably also need to check /
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh look. I used strchr(). I'm inconsistent with myself :/
Regarding the /, do we have to do this everywhere? I didn't notice an #ifdef hell about that yet
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't deconstruct paths manually very often, and would generally use g_path_get_basename()
. But we do have utils_tidy_path()
that does it. I'm not certain we need it here, but I could easily imagine it could happen.
@b4n @codebrainz @elextr I hopefully addressed all remarks, please review again (I'll take care of the travis issue later on) |
I forced pushed to some fixup commits since those haven't been commented on yet. |
* | ||
* Note: This is not installed by default, but (on *nix) you can install it as follows: | ||
* | ||
* Compile geany.Then copy or symlink the plugins/demoproxy.so and plugins/demoproxytest.px files |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing space between sentences
static gpointer plugin_load_gmodule(GeanyPlugin *proxy, GeanyPlugin *subplugin, const gchar *fname, gpointer pdata) | ||
{ | ||
GModule *module; | ||
GeanyData **p_geany_data; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unused
Note to myself: don't forget to bump API when merging this! |
BTW if anyone wants to actually test this, there is the demoproxy (obviously) but also an adapted geanypy on my repo |
(I quickly tried to start squashing commits, and it'll be a little hard because many fixes alter next commit/fixes a bit too much for automatic merges to work. Doing it will take some time) @kugel- thanks for the link, it'll be interesting to see a real usage :) |
You're welcome. I posted it already in the very early days of the PR :-) The Squashing turns our non-trivial, I'll take care |
Here is the merged branch: https://github.com/kugel-/geany/tree/pluxy-merge I added commits, which should be self-explainatory. Other than that, I fixed a @SInCE tag and a last "pluxies" occurence. Tell me if I should push that -merge branch to this PR. |
I also added the above mengtioned as commits here |
@@ -712,6 +805,40 @@ static void remove_sources(Plugin *plugin) | |||
} | |||
|
|||
|
|||
/* returns true if the plugin is native, i.e. not loaded through a proxy. | |||
* This determines whether it's backed by a GModule */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this comment looks wrong
Currently they encapsulate loading and unloading of standard plugins. In the future plugins can provide such functions to load their types of plugins. Such a dummy proxy plugin is implemented now to load standard plugins so that these aren't going to be specially handled.
Being a GModule is actually a detail of standard plugins. Future proxy plugins might need different handles. Therefore replace the module field with a more generic pointer and encapsulate the GModule detail further. This pointer shall be returned from GeanyProxyFuncs::load and is passed back to GeanyProxyFuncs::unload, and isn't interpreted by Geany.
…re added During the loading of the active plugins they are also initialized (done at startup). As a result, these plugins could be pluxys and make more plugins available, some of which may be active as well. Because of this the loop has to be restarted if pluxies become available to also load active plugins that depend on the pluxy. The loop is only restarted at the end so only nested pluxys could possibly cause the loop to be run more than twice.
When a file extension alone is ambigious as to whether a potential plugin is really handled then the proxy should use the probe hook to find out. This can be especially helpful when two pluxies work on the same file extension. The proxy's probe() should return PROXY_IGNORED or PROXY_MATCHED accordingly. A special flag value, PROXY_NOLOAD, can be or'ed into PROXY_MATCHED to say that the file belongs to the proxy, but isn't directly loaded and should not be handled by any other proxy or geany itself. Example for PROXY_IGNORED: geanypy only supports python2 at the moment. So, scripts written for python3 aren't handled by it and should be skipped for the PM dialog. Or perhaps they are handled by another proxy that supports python3. Example for PROXY_NOLOAD: A pluxy registers for the metadata file extension (.plugin) where author etc is in. The actual implmentation is in a python script (.py). The .py file is tied to the .plugin and should not be processed by other pluxies. Thus, the pluxy also registers for the .py extension but returns PROXY_MATCHED|PROXY_NOLOAD for it (if it would return only PROXY_MATCHED the sub-plugin would show up twice in the PM dialog).
This function finally allows plugins to register themselves as a proxy for one or more file extensions. Lots of documentation is added to doc/plugins.dox, please refer to that for more details.
When enabling/disabling pluxys in the PM dialog the list of available plugins might change. If plugins before the pluxy go/come then the wrong plugin becomes selected (the selected row number stays the same). Re-apply the selection to the current one in the toggle callback to overcome this issue.
@b4n pushed fixes. I updated https://github.com/kugel-/geany/tree/pluxy-merge accordingly. |
LGTM, and merge branch is in sync. @codebrainz could you give it a quick test please? |
This demo proxy does not actually do anything useful. It simply loads pseudo-plugins from an ini-style file. The point is that there will be a plugin in the PM dialog for each ini. Each ini-plugin also causes a menu item to be generated.
Geany now remembers how many plugins depend on a pluxy. It uses this information to disable the "Active" checkbox in the PM dialog. Additionally, the PM dialog displays plugins in a hierarchical manner, so that sub-plugins are shown next to pluxy. This is espcially handy since it makes the sub-plugin <-> pluxy relationship really obvious, and it's easier to spot which plugins need to be disabled before the pluxy can be disabled. This allows to remove code to re-select the plugin because the row (respective to the hierarchy level) does not change anymore.
g_ptr_array_insert() is too recent (2.40), but prepending is required. GQueue is a fine replacement with better old-glib support, at the expense of working with a doubly-linked list instead of plain array.
In the future we might want to enable calling it again to set new supported plugin types/extensions. This is not implemented yet, but in order to allow this in the future we have to prevent it now, otherwise we'd need to break the API.
Pre-rebase commits were:
|
Add support for plugins acting as proxies for foreign plugins, promoting foreign plugins to first-class citizen.
This is the patch set that implements proxy plugins as per mailing list thread a few months ago.
Please see commit a006f71 ("plugins: add geany_plugin_register_proxy() to the plugin API") which contains a detailed description via API documentation.
Short summary: Plugins can register as proxy plugins. They register a list of file extensions and a few callbacks. Then, when Geany scans for normal plugins it will also look for these new file types, and call the proxy plugin's callbacks instead of the built-in shared library loader. The proxy will ultimately call geany_plugin_register() on behalf of the sub-plugin.
To the user, these sub-plugins are indistinguishable except the Plugin Manager indicates the relationship to the proxy via a TreeView. Within Geany, they are treated as first-class citizens.
Please review (and ideally merge).