This is one more attempt on unifying `TMTag`s and externally provided symbols for the symbol tree before giving up and making the plugins create the symbol tree by themselves (which leads to quite a significant code duplication). This time (unlike the previous attempts) I'm quite happy with the result though. I called the new structure `GeanySymbol` - could be renamed to something else if someone has better idea.
The idea is to make `GeanySymbol` operate in two modes: - either it is backed by a `TMTag` and created using `GeanySymbol *geany_symbol_new_from_tag(TMTag *tag)` - or it is created by some external source such as the LSP plugin using ```C GeanySymbol *geany_symbol_new(gchar *name, gchar *detail, gchar *scope, gchar *file, TMParserType lang, glong kind, gulong line, gulong pos, guint icon) ``` The corresponding structure looks this way: ```C typedef struct GeanySymbol { gchar *name; gchar *detail; gchar *scope; gchar *file; TMParserType lang; gulong line; gulong pos; glong kind; guint icon;
TMTag *tag;
gint refcount; /* the reference count of the symbol */ } GeanySymbol; ``` where either `tag != NULL` when using `geany_symbol_new_from_tag()`, or `tag == NULL` for `geany_symbol_new()`.
The important part is that the members of `GeanySymbol` cannot be accessed directly from other files but only using getters that either use the `tag` or the member of `GeanySymbol` such as ```C const gchar *geany_symbol_get_name(const GeanySymbol *sym) { if (sym->tag) return sym->tag->name; return sym->name; } ```
By this interface, all callers like the symbol tree implementation are completely shielded from the details of where the symbol comes from. The bulk of the diffs inside `symbols.c` is pretty much just using this interface instead of using `TMTag` directly and renaming `tag` to `symbol`. `get_symbol_name()` and `get_symbol_tooltip()` were moved to `GeanySymbol` implementation, and also (previously inlined) `geany_symbol_get_name_with_scope()` was moved there too.
The additional `PluginExtension` interface for using these symbols would be more or less identical to what I proposed in https://github.com/geany/geany/pull/3850, i.e. ```C gboolean (*doc_symbols_provided)(GeanyDocument *doc); GPtrArray *(*doc_symbols_get)(GeanyDocument *doc); ``` and - either additional `void (*doc_symbols_request)(GeanyDocument *doc, GCallback on_done);` in this interface making Geany request the extension for symbols (asynchronously) - or some `symbol_tree_reload()` function the plugin could call which would make Geany re-request symbols using `doc_symbols_get()` after the plugin gets them e.g. from the LSP server
@b4n How does something like this sound? The implementation here is incomplete (only tested with the `TMTag` "backend" of `GeanySymbol`) but I don't want to continue in case you have some reservations regarding this approach. You can view, comment on, or merge this pull request online at:
https://github.com/geany/geany/pull/3910
-- Commit Summary --
* Possible extension interface for symbol tree
-- File Changes --
M meson.build (3) M src/Makefile.am (1) M src/sidebar.c (7) M src/sidebar.h (2) A src/symbol.c (341) A src/symbol.h (67) M src/symbols.c (426) M src/symbols.h (2)
-- Patch Links --
https://github.com/geany/geany/pull/3910.patch https://github.com/geany/geany/pull/3910.diff
If it has a refcount, why not make it a `GObject` so that it's easy to create from non-C plugins (python via peasy for example).
If it has a refcount, why not make it a GObject so that it's easy to create from non-C plugins (python via peasy for example).
I just copied what was in `TMTag` to implement it quickly but good point, it could be a `GObject`. I'm not so familiar with what might be necessary for non-C plugins but how does https://github.com/geany/geany/pull/3849 look from this perspective? Could anything be improved there?
Would need to check in detail. Have you looked the gtkdoc that's generated from the new interfaces?
Have you looked the gtkdoc that's generated from the new interfaces?
What should I look for exactly? My main worry is ```C typedef struct { gboolean (*autocomplete_provided)(GeanyDocument *doc, gpointer data); void (*autocomplete_perform)(GeanyDocument *doc, gboolean force, gpointer data); gboolean (*calltips_provided)(GeanyDocument *doc, gpointer data); void (*calltips_show)(GeanyDocument *doc, gboolean force, gpointer data); gboolean (*goto_provided)(GeanyDocument *doc, gpointer data); gboolean (*goto_perform)(GeanyDocument *doc, gint pos, gboolean definition, gpointer data); gboolean (*symbol_highlight_provided)(GeanyDocument *doc, gpointer data); } PluginExtension; ```
which in C you would use this way:
```C static gboolean autocomplete_provided(GeanyDocument *doc, gpointer user_data) { }
static void autocomplete_perform(GeanyDocument *doc, gboolean force, gpointer user_data) { }
static PluginExtension extension = { .autocomplete_provided = autocomplete_provided, .autocomplete_perform = autocomplete_perform };
void plugin_init(G_GNUC_UNUSED GeanyData * data) { plugin_extension_register(&extension, 100, NULL); } ```
PluginExtension can also be allocated dynamically which would probably be necessary for other languages. Is such an interface OK for other languages?
Generally speaking, non-C is supported well only for GObject-based interfaces. A python plugin can easily define a GObject-derived type purely implemented in Python.
For anything else you need at least some supporting C code around. This is essentially what the "lib"-part of peasy does. It defines GObject-derived wrappers around existing Geany interfaces so that plugins make instances and inherit properly. Not all Geany interfaces need wrappers. We declare some of them as "GBoxed" such that the gobject-introspection magic makes them available to non-C languages automatically. We feed the gobject-introspection machinery mostly by converting the doxygen output to gtkdoc, and then have it generate the typelib from that.
So for `PluiginExtension` we would need to extend peasy for sure.
I asked specifically for `GeanySymbol`. At the very least it needs to be a GBoxed, but if it has a refcount and getters/setters for anything anyway it could also be a real GObject. Then peasy wouldn't need to wrap this type much.
(no time for this right now, but…) GObjects are nice for language inteop with GI indeed, but they aren't as cheap as e.g. a boxed type (just think about all the bells and whistles like properties, signals, private data, etc) and AFAIK a boxed type that has its own refcount works fine (just lies in `copy()` which actually just refs, and `free()` which just unrefs), and it's what we have for `TMTag` currently IIRC. I don't expect much custom code to support a proper GBoxed in Peasy, am I mistaken?
GBoxed works for non-C. You just have to add a non-C-friendly allocation/constructor because there's no generic `g_boxed_new()`.
GObjects are nice for language inteop with GI indeed, but they aren't as cheap
That's a good point, it should be cheap because for the symbol tree code, all the `TMTag`s have to be wrapped by `GeanySymbol` first and it would be best to have the overhead as small as possible. If we wanted, we could change `GeanySymbol` to something like ```C typedef struct GeanySymbol { ExternalSymbol *ext; TMTag *tag;
gint refcount; } GeanySymbol; ``` where `ExternalSymbol` would contain all the other members so `GeanySymbol` with the `TMTag` "backend" would be smaller and would not have to zero-fill so many members.
GBoxed works for non-C. You just have to add a non-C-friendly allocation/constructor because there's no generic g_boxed_new().
For GeanySymbol, this is hidden behind the two constructors: ```C GeanySymbol *geany_symbol_new_from_tag(TMTag *tag); GeanySymbol *geany_symbol_new(gchar *name, gchar *detail, gchar *scope, gchar *file, TMParserType lang, glong kind, gulong line, gulong pos, guint icon); ``` so I believe it should be satisfied already.
github-comments@lists.geany.org