geany / geany-plugins

The combined Geany Plugins collection
http://plugins.geany.org/
590 stars 266 forks source link

Workbench: project-related and global build commands #733

Open pztrn opened 6 years ago

pztrn commented 6 years ago

I'm using Geany while writing Go code and it's vital to have some commands (like golint and gofmt) to be launched periodically. As Geany doesn't allow me to execute command for every CTRL+S press (or I don't know how to do that), it's good to have an ability to write custom commands and bind them to a hotkey. But this is configurable only per-project, and what if I have 10 projects in workbench? I will configure these commands repeatedly for all 10 projects.

What about adding a possibility to configure these commands from Workbench's tab globally (in workbench settings) and per-project (by right-clicking on project header, for example, and selecting "Set build commands for project...")?

lpaulsen93 commented 6 years ago

As I am also using Geany for C development myself I have the same goal to be able to set some project settings using the workbench plugin. But this will be a long term goal and not happen in the near future I think.

Also, I still have to make up my mind how it is done best. In C the build commands are often the same but just different parameters (filepath, include directories...). So I thought about maybe extending the standard build dialog so that plugins could define own variables/placeholders (like %e, %d, %f).

elextr commented 6 years ago

What the build system really needs is another source for plugins to use, but which would not be saved by Geany. Then plugins can inject any command having performed any transforms on them. At the moment a plugin has to remember to put the Geany command back if it sets its own, or that command will be saved when Geany exits.

elextr commented 6 years ago

PS see this for how the system works and what a "source" is.

lpaulsen93 commented 6 years ago

@elextr: thanks for the pointer. So we would need to extend the list like below

  1. default settings coded in Geany
  2. settings in the system, that is the installed filetype file for the type of file selected in the editor
  3. settings from the user preferences file
  4. settings from a user configured filetype file for the type of file selected in the editor
  5. settings independent of filetype from the open project file
  6. settings for a specific filetype for the type of file selected in the editor which come from the open project file
  7. settings provided by a plugin (with a behaviour specific to the plugin)

If there is more than one plugin, what do we do? Implement a kind of priority value given by the plugins? I assume there is no internal infrastructure for something like this yet, or is it?

elextr commented 6 years ago

@LarsGit223 yes thats exactly what I mean.

If there is more than one plugin, what do we do?

Probably the same as when two plugins share any resources now, fail in unpredictable ways. This already occurs when you run two plugins that both use markers (bookmarks is one, not sure of the other).

Whilst I have argued in the past that Geany needs to somehow mediate between plugins, I have become convinced that in reality Geany just can't know what to do, and either the plugin authors have to write them to play nice together, or the user has to not use both at the same time. Since Geany doesn't know what plugins do, how is it going to decide who wins? Should the "project" plugin that says its priority zero beat the "C language features" plugin that also calls itself priority zero and also wants to set commands? And what about when the current file is a Python file? Geany just doesn't know.

Since any mechanism is going to be specific to what the plugins want to do, and shared between plugins, maybe if there is a mechanism you can implement for a specific case it should go in your shared plugin library, eg

plugin_set shared commands_decided_by_arm_wrestling(credentials, command_number, command)
codebrainz commented 6 years ago

From previous discussions, I think the suggestion (consensus?) was that for resources like markers, indicators and such, it would be best for Geany to manage and allocate these for the plugins so that each plugin doesn't try and use the same indices.

From the filetype plugin discussions, I think the idea was that plugins would "register" themselves as handling a particular filetype. This could be further extended for such things as project plugins by the plugins registering as providing a particular "role", and Geany would advise the user if two plugins tried to claim the same role when the second one was activated. This would also be useful in general since it doesn't make much sense to have two project plugins, debugger plugins, auto-bracket plugins, etc.

elextr commented 6 years ago

From previous discussions, I think the suggestion (consensus?) was that for resources like markers, indicators and such, it would be best for Geany to manage and allocate these for the plugins so that each plugin doesn't try and use the same indices.

Yes, and thats what I argued in the past too, but my problem is that Geany can only handle "first come first served" which might work for markers, but isn't so wonderful for build commands.

From the filetype plugin discussions, I think the idea was that plugins would "register" themselves as handling a particular filetype

Yeah thats certainly the ideal approach, but we are a long way from that ATM.

lpaulsen93 commented 6 years ago

I had a look at the code in build.c and tried to understand what it's doing. This is how I understood it:

If someone (a plugin) wants to re-write the build menu it would have to call build_menu_update and then the menu items are re-written according to the magic/selection process described above.

If we want to add plugins to be a build command source we would need to define a new GeanyBuildSource, e.g. GEANY_BCS_PLUGIN.

I'm not sure where the commands for the various sources are saved in memory. I assume the pointers to the commands are these:

/* the various groups of commands not in the filetype struct */
static GeanyBuildCommand *ft_def = NULL;
static GeanyBuildCommand *non_ft_proj = NULL;
static GeanyBuildCommand *non_ft_pref = NULL;
static GeanyBuildCommand *non_ft_def = NULL;
static GeanyBuildCommand *exec_proj = NULL;
static GeanyBuildCommand *exec_pref = NULL;
static GeanyBuildCommand *exec_def = NULL;

So e.g. exec_proj holds the commands for the prjoject and does not point to one command but to an array of 9 commands (maximum?).

For the case that there is more than one active plugin wanting to be a GeanyBuildSource I wonder if it wouldn't be better to call a function which is doing the decision for the plugins and returns the list of commands that won.

Also if I understood it correctly the build dialog can be re-used as it saves the set commands into dst which in this case would need to point to the memory area for the workbench plugin (using function GtkWidget *build_commands_table(GeanyDocument *doc, GeanyBuildSource dst, BuildTableData *table_data, GeanyFiletype *ft).

So what about exposing only one pointer, e,g, GeanyBuildCommand *exec_plugins = NULL; to geany-plugins and call some function which is doing the work of return_cmd_if for the plugins? And as @elextr suggested put some registration/priority handling/arm wrestling code to the shared utils lib in geany-plugins.

Just an idea/brainstorming. Maybe I understood some things wrong, let's see.

elextr commented 6 years ago

If we want to add plugins to be a build command source we would need to define a new GeanyBuildSource, e.g. GEANY_BCS_PLUGIN.

Correct

So e.g. exec_proj holds the commands for the prjoject and does not point to one command but to an array of 9 commands (maximum?).

It points to the project commands for the execute section of the menu correct. Not sure its limited to 9, but whatever the number is, its set at startup and can't be changed without restarting Geany, and the defaults are only 3/4/2 for filetype, non-filetype and execute sections. Remember the bigger you make the numbers the bigger the Set Build Commands dialog gets, but the menu only shows commands that are not empty.

For the case that there is more than one active plugin wanting to be a GeanyBuildSource I wonder if it wouldn't be better to call a function which is doing the decision for the plugins and returns the list of commands that won.

Note that there are already build access functions in the plugin API, plugins shouldn't be accessing the Geany internal structures. But the existing functions make no decisions what the plugin can do, and I repeat my question from previous posts, how does Geany decide who won?

As @codebrainz pointed out, for markers Geany can allocate them since there is more than one marker, so until exhaustion Geany just provides the first free. But for build commands, each command slot only holds one command, so one of the plugins will naturally "win" the slot and no other plugin can use it.

Also if I understood it correctly the build dialog can be re-used as it saves the set commands into dst which in this case would need to point to the memory area for the workbench plugin (using function GtkWidget build_commands_table(GeanyDocument doc, GeanyBuildSource dst, BuildTableData table_data, GeanyFiletype ft).

You mean the Set Build Commands dialog I guess. My thought was that plugin commands NOT be set there, just shown as insensitive since they are the highest priority.

For example lets say you added %w as the Wonder Workbench substitution. You need to do that before the command is used by Geany since Geany doesn't know how to substitute it, so if the user is entering the %w in the Geany dialog, then when a command was about to be run you would have to re-write the command with the substitution done, then wait for Geany to run the command, then replace the original for the user to edit (or something similar). Easier for the user to edit it in a Workbench dialog and you just write the version you want run in the Geany slot and update it when whatever controls the Wonder Workbench substitution changes. And thats why I suggested that Geany would not save plugin commands, the plugin would be saving the original unsubstituted version.

Also for @codebrainz temporarily languishing Clanglib plugin, the commands might be created in some complex algorithm to handle all the include paths and options and stuff, not just be simple substitutions.

By having the user edit it in the plugins dialog you can reinforce that they can (or must) use the plugin specific features.

So what about exposing only one pointer, e,g, GeanyBuildCommand *exec_plugins = NULL; to geany-plugins and call some function which is doing the work of return_cmd_if for the plugins?

These pointers are not and should not be exposed, they are Geany implementation details. Thats another reason not to have the user edit plugin commands in the Geany dialog.

And as @elextr suggested put some registration/priority handling/arm wrestling code to the shared utils lib in geany-plugins.

Yes, then that can do whatever algorithm plugins agree to use, eg Workbench always wins :grin:

lpaulsen93 commented 6 years ago

You mean the Set Build Commands dialog I guess. My thought was that plugin commands NOT be set there, just shown as insensitive since they are the highest priority.

These pointers are not and should not be exposed, they are Geany implementation details. Thats another reason not to have the user edit plugin commands in the Geany dialog.

By re-using it, I meant using only the GUI. I thought it would write the settings made by the user to dst so that it would not change any other data that belongs to geany.

Note that there are already build access functions in the plugin API, plugins shouldn't be accessing the Geany internal structures. But the existing functions make no decisions what the plugin can do, and I repeat my question from previous posts, how does Geany decide who won?

I still need to look at those. Right now I am not sure which tasks can be done where and how we can limit code duplication. I think these are the three main tasks:

  1. Load and save build command data for a plugin (of course a job for the plugin)
  2. Having a GUI dialog to edit these settings (as written above I thought the existing geany dialog could be re-used WITHOUT changing any of the geany data - I want to pass over the pointer for the workbench data as dst.
  3. A menu for the user to start the build commands (I thought extension of the six sources with a seventh for plugins meant to re-use this because it would decide which command to run)
  4. Having code that actually runs a build command (also done through the above I thought)

If a plugin or plugins has it's own complete build menu and set build commands dialog, then there wouldn't be any interaction with geany? So are you talking about to duplicate that code for the plugins?

Anyway, still have to look at the existing API functions. Thanks for the detailed answer :thumbsup:

elextr commented 6 years ago

By re-using it, I meant using only the GUI. I thought it would write the settings made by the user to dst so that it would not change any other data that belongs to geany.

I don't recommend re-using the Geany GUI implementation, that was created a long time ago. It handles the fact that there is a variable number of commands by creating a dialog with code, and so the number of commands can only be changed at startup, and it was not at all written to be re-used. These days you should probably use a treeview as a list defined by UI Builder XML (as shown in an example in that link), much simpler, and easy to add the extra features the plugin needs.

I expect a plugin wants to set commands so it can do something different to Geany, so there is no reason for the plugin to duplicate the whole machinery of the cascading priority levels, the plugin is only interested in its own commands which are guaranteed to be the highest priority and so be the ones to be run.

As noted above, all that should need changing in Geany is to add a new BCS_PLUGIN source level and check it isn't editable or saved and restored by Geany. Since most stuff works off that enum, hopefully most stuff will "just work".

Load and save build command data for a plugin (of course a job for the plugin)

Indeed, and pass it to Geany as the plugin (extended BCS_PLUGIN source) command using the existing API.

Having a GUI dialog to edit these settings (as written above I thought the existing geany dialog could be re-used WITHOUT changing any of the geany data - I want to pass over the pointer for the workbench data as dst.

Addressed above.

A menu for the user to start the build commands (I thought extension of the six sources with a seventh for plugins meant to re-use this because it would decide which command to run)

My understanding is that the existing menu is to be used, just the decision on what command has highest priority is always in favour of a plugin command if its set. IIRC that decision is made in one place in Geany, and with the extra BCS_PLUGIN level as the highest priority it will automagically happen.

Having code that actually runs a build command (also done through the above I thought)

Yes, done by above using the existing mechanisms and menu and shortcuts.

lpaulsen93 commented 6 years ago

@elextr: I am now starting work on this feature and I wondered if I write my own GUI and stuff, why should the workbench plugin use the "build-system" of geany. Is there a way to let geany "simply" execute some command without going the way over an extra build source like GEANY_BCS_PLUGIN? (or is that just the reason why)

lpaulsen93 commented 5 years ago

@pztrn: you wrote

I'm using Geany while writing Go code and it's vital to have some commands (like golint and gofmt) to be launched periodically.

Apart from having some workbench global settings can't your problem be solved by using filetype specific settings? It should be possible to set golint and gofmt for filetype go.

elextr commented 5 years ago

@LarsGit223 I somehow missed your 15 Jun post.

why should the workbench plugin use the "build-system" of geany.

Only to have the commands in the build command menu, if you have your own menu thats entirely fine.

Is there a way to let geany "simply" execute some command without going the way over an extra build source like GEANY_BCS_PLUGIN? (or is that just the reason why)

Obviously spawning is available to plugins.

But remember Geany also parses the returns of build commands for error messages and writes it to the compiler window so you can click and go to the line. You would have to duplicate that (or expose something thats actually really messy and spread from breakfast to dinner inside Geany, well from build.c to msgwin.c to filetypes.c anyway, not good for an API).

lpaulsen93 commented 5 years ago

@elextr: thanks for the response, better late than never :grin:

Actually I stopped working on this for two reasons:

IMHO the not GUI related part in Geany could need a complete re-write and it would be cool to have some of these features/properties:

Just some ideas :smiley: I guess this would break compatibility to other plugins so I did not even start working on it (also I did not find much references to the build data in Geany-Plugins but maybe there are some in external plugins repos).

elextr commented 5 years ago

Well, given that some Geany contributors were saying it is already waaay too complicated and were pushing back any increase in features, good luck with the total re-write :grin:

Perhaps it would be better for you to just reimplement it in workbench for your purposes and maybe at some point in the future you can make a simpler PR to disable Geanys features so only yours are available, if you think having both will confuse users.

lpaulsen93 commented 5 years ago

Perhaps it would be better for you to just reimplement it in workbench for your purposes and maybe at some point in the future you can make a simpler PR to disable Geanys features so only yours are available, if you think having both will confuse users.

Hmmm...I think not because I do not really like to duplicate it all. Also a more dynamic solution would be useful for others too. Maybe I start a re-write some time but I got the feeling it won't have good chances to be accepted.