diff --git a/TODO b/TODO index 11403a04a3..43437ebe86 100644 --- a/TODO +++ b/TODO @@ -15,7 +15,6 @@ Note: features included in brackets have lower priority. programming languages (done for C-like filetypes using filetypes.common named styles) o asynchronous build commands on Windows - o (filetype-independent run command in build dialog & keybinding) o (better custom filetype support) o (custom template insertion - so user can add licenses, etc) o (selectable menu of arguments to use for Make, from Make Custom) diff --git a/doc/geany.txt b/doc/geany.txt index 8e0c52a758..04502dc323 100644 --- a/doc/geany.txt +++ b/doc/geany.txt @@ -2651,7 +2651,9 @@ number_ft_menu_items The maximum number of menu items in the 2 number_non_ft_menu_items The maximum number of menu items in the 3 on restart independent build section. number_exec_menu_items The maximum number of menu items in the 2 on restart - execute section of the Build menu. + filetype execute section. +number_exec_ind_menu_items The maximum number of menu items in the 2 on restart + independent execute section. ================================ =========================================== ========== =========== Statusbar Templates @@ -3027,24 +3029,26 @@ in to be configured. For example, if you change one of the default make commands to run say 'waf' you can also change the label to match. These settings are saved automatically when Geany is shut down. -The build menu is divided into four groups of items each with different +The build menu has four configurable groups of items each with different behaviors: -* Filetype build commands - are configurable and depend on the filetype of the - current document; they capture output in the compiler tab and parse it for +- *Filetype build commands* - these depend on the filetype of the + current document. They capture output in the compiler tab and parse it for errors. -* Independent build commands - are configurable and mostly don't depend on the - filetype of the current document; they also capture output in the - compiler tab and parse it for errors. -* Execute commands - are configurable and intended for executing your - program or other long running programs. The output is not parsed for - errors and is directed to the terminal command selected in `Tools - preferences`_. -* Fixed commands - these perform built-in actions: - - * Go to the next error. - * Go to the previous error. - * Show the build menu commands dialog. +- *Independent build commands* - these (mostly) don't depend on the + current filetype. They also capture and parse the output. +- *Filetype execute commands* - these depend on the current filetype + and they are intended for executing your program or other long running + programs. The output is not parsed for errors and is directed to the + terminal command selected in `Tools preferences`_. +- *Independent execute commands* - as above but not dependent on the + current filetype. + +The menu also has fixed commands - these perform built-in actions: + +* Go to the next error. +* Go to the previous error. +* Show the build menu commands dialog. The maximum numbers of items in each of the configurable groups can be configured in `Various preferences`_. Even though the maximum number of @@ -3085,15 +3089,18 @@ is shown in the following table: | | | | | Label: Make _Object | | | | | | Command: make %e.o | +--------------+---------------------+--------------------------+-------------------+-------------------------------+ -| Execute | Loads From: project | Loads From: | Loads From: | Label: _Execute | -| | file or else | geany.conf file in | filetypes.xxx in | Command: ./%e | -| | filetype defined in | ~/.config/geany or else | Geany install | | -| | project file | filetypes.xxx file in | | | -| | | ~/.config/geany/filedefs | Saves To: as user | | -| | Saves To: | | preferences left. | | -| | project file | Saves To: | | | -| | | filetypes.xxx file in | | | -| | | ~/.config/geany/filedefs | | | +| Filetype | Loads From: | Loads From: | Loads From: | Label: _Execute | +| Execute | filetype setting | filetypes.xxx file in | filetypes.xxx in | Command: ./%e | +| | stored in | ~/.config/geany/filedefs | Geany install | | +| | project file | | | | +| | | Saves To: Same | Saves To: as user | | +| | Saves To: Same | | preferences left. | | ++--------------+---------------------+--------------------------+-------------------+-------------------------------+ +| Independent | Loads From: project | Loads From: | None | None | +| Execute | file | geany.conf file in | | | +| | | ~/.config/geany | | | +| | Saves To: Same | | | | +| | | Saves To: Same | | | +--------------+---------------------+--------------------------+-------------------+-------------------------------+ The following notes on the table may reference cells by coordinate as *(group, source)*: @@ -3114,10 +3121,9 @@ The following notes on the table may reference cells by coordinate as *(group, s filetype-independent commands in a filetype file, this provides the ability to define filetype dependent default menu items. -* *(Execute, Project and Preferences)* - the project independent - execute and preferences independent execute commands can only be set by hand - editing the appropriate file, see `Preferences file format`_ and `Project file - format`_. +* Independent execute - the project and preferences independent execute + commands can only be set by hand editing the appropriate file, see + `Preferences file format`_ and `Project file format`_. Set Build Commands dialog ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -3190,7 +3196,8 @@ Keyboard shortcuts can be defined for: * the first two filetype build menu items * the first three independent build menu items -* the first execute menu item +* the first filetype execute menu item +* the first independent execute menu item * the fixed menu items (Next/Previous Error, Set Commands) In the keybindings configuration dialog (see `Keybinding preferences`_) diff --git a/src/build.c b/src/build.c index 058ec86510..b0453385c0 100644 --- a/src/build.c +++ b/src/build.c @@ -69,14 +69,6 @@ GeanyBuildInfo build_info = {GEANY_GBG_FT, 0, 0, NULL, GEANY_FILETYPES_NONE, NUL static gchar *current_dir_entered = NULL; -typedef struct RunInfo -{ - GPid pid; - gint file_type_id; -} RunInfo; - -static RunInfo *run_info; - #ifndef G_OS_WIN32 static const gchar RUN_SCRIPT_CMD[] = "geany_run_script_XXXXXX.sh"; #endif @@ -155,7 +147,7 @@ static struct } widgets; -static guint build_groups_count[GEANY_GBG_COUNT] = { 3, 4, 2 }; +static guint build_groups_count[GEANY_GBG_COUNT] = { 3, 4, 2, 2 }; static guint build_items_count = 9; static void build_exit_cb(GPid pid, gint status, gpointer user_data); @@ -349,84 +341,102 @@ static void printfcmds(void) } -/* macros to save typing and make the logic visible */ -#define return_cmd_if(src, cmds)\ - if (cmds != NULL && cmds[cmdindex].exists && below>src)\ - { \ - *fr=src; \ - if (printbuildcmds) \ - printf("cmd[%u,%u]=%u\n",cmdgrp,cmdindex,src); \ - return &(cmds[cmdindex]); \ - } - -#define return_ft_cmd_if(src, cmds)\ - if (ft != NULL && ft->priv->cmds != NULL \ - && ft->priv->cmds[cmdindex].exists && below>src)\ - { \ - *fr=src; \ - if (printbuildcmds) \ - printf("cmd[%u,%u]=%u\n",cmdgrp,cmdindex,src); \ - return &(ft->priv->cmds[cmdindex]); \ - } - - -/* get the next lowest command taking priority into account */ -static GeanyBuildCommand *get_next_build_cmd(GeanyDocument *doc, guint cmdgrp, guint cmdindex, - guint below, guint *from) +typedef struct CommandSet { - /* Note: parameter below used in macros above */ - GeanyFiletype *ft = NULL; - guint sink, *fr = &sink; + GeanyBuildSource src; + GeanyBuildCommand *cmds; +} CommandSet; +// Note: Could return CommandSet instead of using psrc +/* get the next lowest command taking priority into account + * below = only consider GeanyBuildSource values less than this parameter + * psrc = Address to write GeanyBuildSource */ +static GeanyBuildCommand *get_next_build_cmd(GeanyDocument *doc, + guint cmdgrp, guint cmdindex, guint below, guint *psrc) +{ g_return_val_if_fail(doc == NULL || doc->is_valid, NULL); if (printbuildcmds) printfcmds(); if (cmdgrp >= GEANY_GBG_COUNT) return NULL; - if (from != NULL) - fr = from; if (doc == NULL) doc = document_get_current(); - if (doc != NULL) - ft = doc->file_type; + + GeanyFiletypePrivate empty_ftp = {}, *ftp = + doc ? doc->file_type->priv : &empty_ftp; // avoid checking for null + CommandSet overloads[6] = {}; switch (cmdgrp) { - case GEANY_GBG_FT: /* order proj ft, home ft, ft, defft */ - if (ft != NULL) - { - return_ft_cmd_if(GEANY_BCS_PROJ, projfilecmds); - return_ft_cmd_if(GEANY_BCS_PREF, homefilecmds); - return_ft_cmd_if(GEANY_BCS_FT, filecmds); - } - return_cmd_if(GEANY_BCS_DEF, ft_def); + case GEANY_GBG_FT: /* order proj ft, home ft, ft, def ft */ + { + CommandSet cs[] = { + {GEANY_BCS_PROJ, ftp->projfilecmds}, + {GEANY_BCS_PREF, ftp->homefilecmds}, + {GEANY_BCS_FT, ftp->filecmds}, + {GEANY_BCS_DEF, ft_def}, + }; + memcpy(overloads, cs, sizeof(cs)); break; + } case GEANY_GBG_NON_FT: /* order proj, pref, def */ - return_cmd_if(GEANY_BCS_PROJ, non_ft_proj); - return_cmd_if(GEANY_BCS_PREF, non_ft_pref); - return_ft_cmd_if(GEANY_BCS_FT, ftdefcmds); - return_cmd_if(GEANY_BCS_DEF, non_ft_def); + { + CommandSet cs[] = { + {GEANY_BCS_PROJ, non_ft_proj}, + {GEANY_BCS_PREF, non_ft_pref}, + {GEANY_BCS_FT, ftp->ftdefcmds}, + {GEANY_BCS_DEF, non_ft_def}, + }; + memcpy(overloads, cs, sizeof(cs)); + break; + } + case GEANY_GBG_EXEC: /* order proj ft, home ft, ft, def */ + { + CommandSet cs[] = { + {GEANY_BCS_PROJ_FT, ftp->projexeccmds}, + {GEANY_BCS_FT, ftp->homeexeccmds}, + {GEANY_BCS_FT, ftp->execcmds}, + {GEANY_BCS_DEF, exec_def}, + }; + memcpy(overloads, cs, sizeof(cs)); break; - case GEANY_GBG_EXEC: /* order proj, proj ft, pref, home ft, ft, def */ - return_cmd_if(GEANY_BCS_PROJ, exec_proj); - return_ft_cmd_if(GEANY_BCS_PROJ_FT, projexeccmds); - return_cmd_if(GEANY_BCS_PREF, exec_pref); - return_ft_cmd_if(GEANY_BCS_FT, homeexeccmds); - return_ft_cmd_if(GEANY_BCS_FT, execcmds); - return_cmd_if(GEANY_BCS_DEF, exec_def); + } + case GEANY_GBG_EXEC_IND: /* order proj, pref */ + { + CommandSet cs[] = { + {GEANY_BCS_PROJ, exec_proj}, + {GEANY_BCS_PREF, exec_pref}, + }; + memcpy(overloads, cs, sizeof(cs)); break; + } default: - break; + return NULL; + } + for (guint i = 0; i < G_N_ELEMENTS(overloads); i++) + { + GeanyBuildCommand *cmds = overloads[i].cmds; + guint src = overloads[i].src; + + if (cmds != NULL && cmds[cmdindex].exists && src < below) + { + if (psrc) + *psrc = src; + if (printbuildcmds) + g_print("cmd[%u,%u]=%u below=%u; %s\n", cmdgrp, cmdindex, src, below, cmds[cmdindex].command); + return &(cmds[cmdindex]); + } } return NULL; } /* shortcut to start looking at the top */ -static GeanyBuildCommand *get_build_cmd(GeanyDocument *doc, guint grp, guint cmdindex, guint *from) +// Note: could remove psrc parameter, only passed once +static GeanyBuildCommand *get_build_cmd(GeanyDocument *doc, guint grp, guint cmdindex, guint *psrc) { - return get_next_build_cmd(doc, grp, cmdindex, GEANY_BCS_COUNT, from); + return get_next_build_cmd(doc, grp, cmdindex, GEANY_BCS_COUNT, psrc); } @@ -505,14 +515,21 @@ static GeanyBuildCommand **get_build_group_pointer(const GeanyBuildSource src, c case GEANY_BCS_FT: return ft ? &(ft->priv->execcmds): NULL; case GEANY_BCS_HOME_FT: return ft ? &(ft->priv->homeexeccmds): NULL; case GEANY_BCS_PROJ_FT: return ft ? &(ft->priv->projexeccmds): NULL; + default: return NULL; + } + break; + case GEANY_GBG_EXEC_IND: + switch (src) + { case GEANY_BCS_PREF: return &(exec_pref); case GEANY_BCS_PROJ: return &(exec_proj); default: return NULL; } break; default: - return NULL; + break; } + return NULL; } @@ -833,16 +850,13 @@ static void build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *d * the command; otherwise the command itself is returned. working_dir is a pointer * to the working directory from which the command is executed. Both strings are * in the locale encoding. */ -static gchar *prepare_run_cmd(GeanyDocument *doc, gchar **working_dir, guint cmdindex) +static gchar *prepare_run_cmd(GeanyDocument *doc, GeanyBuildCommand *cmd, gchar **working_dir) { - GeanyBuildCommand *cmd = NULL; const gchar *cmd_working_dir; gboolean autoclose = FALSE; gchar *cmd_string_utf8, *working_dir_utf8, *run_cmd, *cmd_string; GError *error = NULL; - cmd = get_build_cmd(doc, GEANY_GBG_EXEC, cmdindex, NULL); - cmd_string_utf8 = build_replace_placeholder(doc, cmd->command); cmd_working_dir = cmd->working_dir; if (EMPTY(cmd_working_dir)) @@ -900,20 +914,31 @@ static gchar *prepare_run_cmd(GeanyDocument *doc, gchar **working_dir, guint cmd } -static void build_run_cmd(GeanyDocument *doc, guint cmdindex) +// array of filetype run IDs followed by independent run IDs +static GPid *run_pids = NULL; + +static GPid *get_run_pid(GeanyBuildGroup grp, guint cmd) +{ + if (grp == GEANY_GBG_EXEC_IND) + cmd += build_groups_count[GEANY_GBG_EXEC]; + return &run_pids[cmd]; +} + +static void build_run_cmd(GeanyDocument *doc, guint group, guint cmdindex) { gchar *working_dir; gchar *run_cmd = NULL; if (! DOC_VALID(doc) || doc->file_name == NULL) return; - - run_cmd = prepare_run_cmd(doc, &working_dir, cmdindex); + GeanyBuildCommand *cmd = get_build_cmd(doc, group, cmdindex, NULL); + if (!cmd) + return; + + run_cmd = prepare_run_cmd(doc, cmd, &working_dir); if (run_cmd == NULL) return; - run_info[cmdindex].file_type_id = doc->file_type->id; - #ifdef HAVE_VTE if (vte_info.have_vte && vc->run_in_vte) { @@ -943,7 +968,7 @@ static void build_run_cmd(GeanyDocument *doc, guint cmdindex) gtk_widget_grab_focus(vc->vte); msgwin_show_hide(TRUE); - run_info[cmdindex].pid = 1; + *get_run_pid(group, cmdindex) = 1; g_free(vte_cmd); } else @@ -969,11 +994,11 @@ static void build_run_cmd(GeanyDocument *doc, guint cmdindex) utils_str_replace_all(&locale_term_cmd, "%c", run_cmd); - if (spawn_async(working_dir, locale_term_cmd, NULL, NULL, &(run_info[cmdindex].pid), + GPid *pid = get_run_pid(group, cmdindex); + if (spawn_async(working_dir, locale_term_cmd, NULL, NULL, pid, &error)) { - g_child_watch_add(run_info[cmdindex].pid, (GChildWatchFunc) run_exit_cb, - (gpointer) &(run_info[cmdindex])); + g_child_watch_add(*pid, run_exit_cb, pid); build_menu_update(doc); } else @@ -986,10 +1011,9 @@ static void build_run_cmd(GeanyDocument *doc, guint cmdindex) #ifndef G_OS_WIN32 g_unlink(run_cmd); #endif - run_info[cmdindex].pid = (GPid) 0; + *get_run_pid(group, cmdindex) = (GPid) 0; } } - g_free(working_dir); g_free(run_cmd); } @@ -1127,11 +1151,11 @@ static void build_exit_cb(GPid child_pid, gint status, gpointer user_data) static void run_exit_cb(GPid child_pid, gint status, gpointer user_data) { - RunInfo *run_info_data = user_data; + GPid *pid = user_data; g_spawn_close_pid(child_pid); - run_info_data->pid = 0; + *pid = 0; /* reset the stop button and menu item to the original meaning */ build_menu_update(NULL); } @@ -1274,11 +1298,12 @@ static void on_build_menu_item(GtkWidget *w, gpointer user_data) } return; } - else if (grp == GEANY_GBG_EXEC) + else if (grp == GEANY_GBG_EXEC || grp == GEANY_GBG_EXEC_IND) { - if (run_info[cmd].pid > (GPid) 1) + GPid *pid = get_run_pid(grp, cmd); + if (*pid > (GPid) 1) { - kill_process(&run_info[cmd].pid); + kill_process(pid); return; } bc = get_build_cmd(doc, grp, cmd, NULL); @@ -1294,7 +1319,7 @@ static void on_build_menu_item(GtkWidget *w, gpointer user_data) g_free(uri); } else - build_run_cmd(doc, cmd); + build_run_cmd(doc, grp, cmd); } else build_command(doc, grp, cmd, NULL); @@ -1308,6 +1333,7 @@ static void on_build_menu_item(GtkWidget *w, gpointer user_data) #define MENU_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_FT) #define MENU_NON_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_NON_FT) #define MENU_EXEC_REST (GEANY_GBG_COUNT + GEANY_GBG_EXEC) +#define MENU_EXEC_IND_REST (GEANY_GBG_COUNT + GEANY_GBG_EXEC_IND) /* the separator */ #define MENU_SEPARATOR (2*GEANY_GBG_COUNT) /* the fixed items */ @@ -1353,6 +1379,12 @@ static struct BuildMenuItemSpec { GBO_TO_CMD(GEANY_GBO_EXEC), NULL, on_build_menu_item}, {NULL, -1, MENU_EXEC_REST, GBO_TO_CMD(GEANY_GBO_EXEC) + 1, NULL, on_build_menu_item}, + {NULL, -1, MENU_SEPARATOR, + GBF_SEP_5, NULL, NULL}, + {GTK_STOCK_EXECUTE, GEANY_KEYS_BUILD_RUNINDEPENDENT, GEANY_GBG_EXEC_IND, + 0, NULL, on_build_menu_item}, + {NULL, -1, MENU_EXEC_IND_REST, + 1, NULL, on_build_menu_item}, {NULL, -1, MENU_SEPARATOR, GBF_SEP_4, NULL, NULL}, {GTK_STOCK_PREFERENCES, GEANY_KEYS_BUILD_OPTIONS, MENU_COMMANDS, @@ -1395,6 +1427,7 @@ static void create_build_menu(BuildMenuItems *build_menu_items) build_menu_items->menu_item[GEANY_GBG_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_FT]); build_menu_items->menu_item[GEANY_GBG_NON_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_NON_FT]); build_menu_items->menu_item[GEANY_GBG_EXEC] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_EXEC]); + build_menu_items->menu_item[GEANY_GBG_EXEC_IND] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_EXEC_IND]); build_menu_items->menu_item[GBG_FIXED] = g_new0(GtkWidget*, GBF_COUNT); for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i) @@ -1527,7 +1560,7 @@ void build_menu_update(GeanyDocument *doc) else { GtkWidget *image; - exec_running = run_info[cmd].pid > (GPid) 1; + exec_running = *get_run_pid(grp, cmd) > (GPid) 1; cmd_sensitivity = (bc != NULL) || exec_running; gtk_widget_set_sensitive(menu_item, cmd_sensitivity); if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC)) @@ -2200,20 +2233,16 @@ static gboolean build_read_commands(BuildDestination *dst, BuildTableData table_ void build_read_project(GeanyFiletype *ft, BuildTableData build_properties) { - BuildDestination menu_dst; + BuildDestination menu_dst = {0}; if (ft != NULL) { menu_dst.dst[GEANY_GBG_FT] = &(ft->priv->projfilecmds); + menu_dst.dst[GEANY_GBG_EXEC] = &ft->priv->projexeccmds; menu_dst.fileregexstr = &(ft->priv->projerror_regex_string); } - else - { - menu_dst.dst[GEANY_GBG_FT] = NULL; - menu_dst.fileregexstr = NULL; - } menu_dst.dst[GEANY_GBG_NON_FT] = &non_ft_proj; - menu_dst.dst[GEANY_GBG_EXEC] = &exec_proj; + menu_dst.dst[GEANY_GBG_EXEC_IND] = &exec_proj; menu_dst.nonfileregexstr = ®ex_proj; build_read_commands(&menu_dst, build_properties, GTK_RESPONSE_ACCEPT); @@ -2228,7 +2257,6 @@ static void show_build_commands_dialog(void) const gchar *title = _("Set Build Commands"); gint response; BuildTableData table_data; - BuildDestination prefdsts; if (doc != NULL) ft = doc->file_type; @@ -2243,6 +2271,7 @@ static void show_build_commands_dialog(void) /* run modally to prevent user changing idx filetype */ response = gtk_dialog_run(GTK_DIALOG(dialog)); + BuildDestination prefdsts = {0}; prefdsts.dst[GEANY_GBG_NON_FT] = &non_ft_pref; if (ft != NULL) { @@ -2250,12 +2279,6 @@ static void show_build_commands_dialog(void) prefdsts.fileregexstr = &(ft->priv->homeerror_regex_string); prefdsts.dst[GEANY_GBG_EXEC] = &(ft->priv->homeexeccmds); } - else - { - prefdsts.dst[GEANY_GBG_FT] = NULL; - prefdsts.fileregexstr = NULL; - prefdsts.dst[GEANY_GBG_EXEC] = NULL; - } prefdsts.nonfileregexstr = ®ex_pref; if (build_read_commands(&prefdsts, table_data, response) && ft != NULL) filetypes_save_commands(ft); @@ -2289,7 +2312,7 @@ static const gchar *build_grp_name = "build-menu"; * where gg = FT, NF, EX for the command group * nn = 2 digit command number * xx = LB for label, CM for command and WD for working dir */ -static const gchar *groups[GEANY_GBG_COUNT] = { "FT", "NF", "EX" }; +static const gchar *groups[GEANY_GBG_COUNT] = { "FT", "NF", "EX", "EX" }; static const gchar *fixedkey="xx_xx_xx"; #define set_key_grp(key, grp) (key[prefixlen + 0] = grp[0], key[prefixlen + 1] = grp[1]) @@ -2391,12 +2414,12 @@ void build_load_menu(GKeyFile *config, GeanyBuildSource src, gpointer p) break; case GEANY_BCS_PREF: build_load_menu_grp(config, &non_ft_pref, GEANY_GBG_NON_FT, NULL, FALSE); - build_load_menu_grp(config, &exec_pref, GEANY_GBG_EXEC, NULL, FALSE); + build_load_menu_grp(config, &exec_pref, GEANY_GBG_EXEC_IND, NULL, FALSE); SETPTR(regex_pref, g_key_file_get_string(config, build_grp_name, "error_regex", NULL)); break; case GEANY_BCS_PROJ: build_load_menu_grp(config, &non_ft_proj, GEANY_GBG_NON_FT, NULL, FALSE); - build_load_menu_grp(config, &exec_proj, GEANY_GBG_EXEC, NULL, FALSE); + build_load_menu_grp(config, &exec_proj, GEANY_GBG_EXEC_IND, NULL, FALSE); SETPTR(regex_proj, g_key_file_get_string(config, build_grp_name, "error_regex", NULL)); pj = (GeanyProject*)p; if (p == NULL) @@ -2481,7 +2504,7 @@ void build_load_menu(GKeyFile *config, GeanyBuildSource src, gpointer p) if (!EMPTY(value)) { if (exec_proj == NULL) - exec_proj = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]); + exec_proj = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC_IND]); if (! exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists) { exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists = TRUE; @@ -2598,7 +2621,7 @@ void build_save_menu(GKeyFile *config, gpointer ptr, GeanyBuildSource src) break; case GEANY_BCS_PREF: build_save_menu_grp(config, non_ft_pref, GEANY_GBG_NON_FT, NULL); - build_save_menu_grp(config, exec_pref, GEANY_GBG_EXEC, NULL); + build_save_menu_grp(config, exec_pref, GEANY_GBG_EXEC_IND, NULL); if (!EMPTY(regex_pref)) g_key_file_set_string(config, build_grp_name, "error_regex", regex_pref); else @@ -2607,7 +2630,7 @@ void build_save_menu(GKeyFile *config, gpointer ptr, GeanyBuildSource src) case GEANY_BCS_PROJ: pj = (GeanyProject*)ptr; build_save_menu_grp(config, non_ft_proj, GEANY_GBG_NON_FT, NULL); - build_save_menu_grp(config, exec_proj, GEANY_GBG_EXEC, NULL); + build_save_menu_grp(config, exec_proj, GEANY_GBG_EXEC_IND, NULL); if (!EMPTY(regex_proj)) g_key_file_set_string(config, build_grp_name, "error_regex", regex_proj); else @@ -2703,7 +2726,8 @@ void build_init(void) ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]); non_ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]); exec_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]); - run_info = g_new0(RunInfo, build_groups_count[GEANY_GBG_EXEC]); + run_pids = g_new0(GPid, build_groups_count[GEANY_GBG_EXEC] + + build_groups_count[GEANY_GBG_EXEC_IND]); for (cmdindex = 0; default_cmds[cmdindex].command != NULL; ++cmdindex) { @@ -2815,6 +2839,9 @@ gboolean build_keybinding(guint key_id) case GEANY_KEYS_BUILD_RUN: item = menu_items->menu_item[GEANY_GBG_EXEC][GBO_TO_CMD(GEANY_GBO_EXEC)]; break; + case GEANY_KEYS_BUILD_RUNINDEPENDENT: + item = menu_items->menu_item[GEANY_GBG_EXEC_IND][0]; + break; case GEANY_KEYS_BUILD_OPTIONS: item = menu_items->menu_item[GBG_FIXED][GBF_COMMANDS]; break; diff --git a/src/build.h b/src/build.h index f9d720d06a..a34372d5ab 100644 --- a/src/build.h +++ b/src/build.h @@ -36,6 +36,7 @@ typedef enum GEANY_GBG_FT, /**< filetype items */ GEANY_GBG_NON_FT, /**< non filetype items.*/ GEANY_GBG_EXEC, /**< execute items */ + GEANY_GBG_EXEC_IND, /**< independent execute items */ GEANY_GBG_COUNT /**< count of groups. */ } GeanyBuildGroup; @@ -87,6 +88,7 @@ enum GeanyBuildFixedMenuItems GBF_SEP_2, GBF_SEP_3, GBF_SEP_4, + GBF_SEP_5, GBF_COUNT }; diff --git a/src/keybindings.c b/src/keybindings.c index 2696ba064b..05cdddcc04 100644 --- a/src/keybindings.c +++ b/src/keybindings.c @@ -704,6 +704,8 @@ static void init_default_kb(void) 0, 0, "build_previouserror", _("Previous error"), NULL); add_kb(group, GEANY_KEYS_BUILD_RUN, NULL, GDK_F5, 0, "build_run", _("Run"), NULL); + add_kb(group, GEANY_KEYS_BUILD_RUNINDEPENDENT, NULL, + GDK_F5, GDK_SHIFT_MASK, "build_run_ind", _("_Independent Run"), NULL); add_kb(group, GEANY_KEYS_BUILD_OPTIONS, NULL, 0, 0, "build_options", _("Build options"), NULL); diff --git a/src/keybindings.h b/src/keybindings.h index 221c8864fc..27cc5c5b87 100644 --- a/src/keybindings.h +++ b/src/keybindings.h @@ -273,8 +273,9 @@ enum GeanyKeyBindingID GEANY_KEYS_FORMAT_SENDTOCMD8, /**< Keybinding. */ GEANY_KEYS_FORMAT_SENDTOCMD9, /**< Keybinding. */ GEANY_KEYS_EDITOR_DELETELINETOBEGINNING, /**< Keybinding. */ - GEANY_KEYS_DOCUMENT_STRIPTRAILINGSPACES, /**< Keybinding. - * @since 1.34 (API 238) */ + GEANY_KEYS_DOCUMENT_STRIPTRAILINGSPACES, /**< Keybinding. */ + GEANY_KEYS_BUILD_RUNINDEPENDENT, /**< Keybinding. + * @since 1.34 (API 239) */ GEANY_KEYS_COUNT /* must not be used by plugins */ }; diff --git a/src/keyfile.c b/src/keyfile.c index 2d60f3ba88..55949a7d79 100644 --- a/src/keyfile.c +++ b/src/keyfile.c @@ -117,6 +117,7 @@ static struct gint number_ft_menu_items; gint number_non_ft_menu_items; gint number_exec_menu_items; + gint number_exec_ind_menu_items; } build_menu_prefs; @@ -284,6 +285,8 @@ static void init_pref_groups(void) "number_non_ft_menu_items", 0); stash_group_add_integer(group, &build_menu_prefs.number_exec_menu_items, "number_exec_menu_items", 0); + stash_group_add_integer(group, &build_menu_prefs.number_exec_ind_menu_items, + "number_exec_ind_menu_items", 0); } @@ -1000,6 +1003,7 @@ static void load_dialog_prefs(GKeyFile *config) build_set_group_count(GEANY_GBG_FT, build_menu_prefs.number_ft_menu_items); build_set_group_count(GEANY_GBG_NON_FT, build_menu_prefs.number_non_ft_menu_items); build_set_group_count(GEANY_GBG_EXEC, build_menu_prefs.number_exec_menu_items); + build_set_group_count(GEANY_GBG_EXEC_IND, build_menu_prefs.number_exec_ind_menu_items); build_load_menu(config, GEANY_BCS_PREF, NULL); } diff --git a/src/project.c b/src/project.c index 53cab8130a..a8440e1526 100644 --- a/src/project.c +++ b/src/project.c @@ -443,6 +443,7 @@ static void destroy_project(gboolean open_default) /* remove project non filetype build menu items */ build_remove_menu_item(GEANY_BCS_PROJ, GEANY_GBG_NON_FT, -1); build_remove_menu_item(GEANY_BCS_PROJ, GEANY_GBG_EXEC, -1); + build_remove_menu_item(GEANY_BCS_PROJ, GEANY_GBG_EXEC_IND, -1); g_free(app->project->name); g_free(app->project->description); @@ -810,8 +811,6 @@ static gboolean update_config(const PropertyDialogElements *e, gboolean new_proj GtkTextIter start, end; GtkTextBuffer *buffer; GeanyDocument *doc = document_get_current(); - GeanyBuildCommand *oldvalue; - GeanyFiletype *ft = doc ? doc->file_type : NULL; GtkWidget *widget; gchar *tmp; GString *str; @@ -827,16 +826,25 @@ static gboolean update_config(const PropertyDialogElements *e, gboolean new_proj stash_group_update(node->data, e->dialog); /* read the project build menu */ - oldvalue = ft ? ft->priv->projfilecmds : NULL; - build_read_project(ft, e->build_properties); - - if (ft != NULL && ft->priv->projfilecmds != oldvalue && ft->priv->project_list_entry < 0) + GeanyFiletype *ft = doc ? doc->file_type : NULL; + if (ft && ft->priv->project_list_entry < 0) { - if (p->priv->build_filetypes_list == NULL) - p->priv->build_filetypes_list = g_ptr_array_new(); - ft->priv->project_list_entry = p->priv->build_filetypes_list->len; - g_ptr_array_add(p->priv->build_filetypes_list, ft); + GeanyBuildCommand *oldbuild = ft->priv->projfilecmds; + GeanyBuildCommand *oldexec = ft->priv->projexeccmds; + build_read_project(ft, e->build_properties); + + if (ft->priv->projfilecmds != oldbuild || + ft->priv->projexeccmds != oldexec) + { + if (p->priv->build_filetypes_list == NULL) + p->priv->build_filetypes_list = g_ptr_array_new(); + ft->priv->project_list_entry = p->priv->build_filetypes_list->len; + g_ptr_array_add(p->priv->build_filetypes_list, ft); + } } + else + build_read_project(NULL, e->build_properties); + build_menu_update(doc); widget = ui_lookup_widget(e->dialog, "radio_long_line_disabled_project");