Please provide a way to easily show the compiler error(s) for the current line. _(if it's not already existing and I missed it)_
If I navigate to a red `underscored` line, I'd like to easily see which error(s) is/are being reported for that line. I don't like to manually scroll trough the compiler tab to find the error matching my line. And I also don't like to walk trough the errors on order via the _Next Error_ command.
For example the compiler tab could scroll to the first error for this line. I guess this would be pretty easy to implement and it would already be a big help.
Background:
I'm using [Rubocop](https://github.com/rubocop/rubocop) according to [this](https://wiki.geany.org/howtos/rubocop) with a _Build Command_.
OK actually I'm not just running Rubocop, but also [Sorbet](https://sorbet.org/). So I wrote a little Bash script which runs `ruby -wc`, Rubocop and Sorbet successively. And I adjusted the _Build Command_ to run my Bash script instead of Rubocop. That works quite well in general. But the order of errors is messed up, because it's first all the `ruby -wc` errors, than the Rubocop errors and finally the Sorbet errors.
`Menu->Build->Next error` ?
@elextr No, that's completely not what I meant.
I mentioned this in my initial report. If I click `Next error` the cursor jumps to another line. BUT I like to know the error for the line where I put the cursor.
Most editors / IDEs somehow show you the error for the line where your cursor is currently in (if there's an error). Some editors display popups, some highlight the error in a pane below. The thing is, sometimes it's not helpful to go through the errors in order the compiler has given them to you. This is especially true for [Rubocop](https://github.com/rubocop/rubocop) which only reports soft warnings, not hard errors.
In that case, no its not available.
One comment though is that I don't use atom, but some "full fat" IDEs like VS or Eclipse have both actual compile errors from the build system and messages from the real time analysis system. That Atom error looks like one of the analysis errors, CSS doesn't have a "compiler" to build it.
Geany does not provide a pre-compile analysis system since for many languages Geany does not have enough information to do that analysis. Possibly for something like CSS it would be possible, but nobody has suggested how it would be done, let alone provided a PR.
Geany does not provide a pre-compile analysis system since for many languages Geany does not have enough information to do that analysis.
I'm actually not asking for all of this. Maybe that screenshot was the wrong way to explain what I'm looking for...
What I'm asking for is:
Geany clearly has knowledge about which error in the compiler tab belongs to which line in the text buffer. There must be this knowledge, because if I click an error in the compiler tab Geany brings the cursor to the corresponding line in the text buffer. Same if I click `Menu->Build->Next error`.
So I'm just asking for the other way around. When I click a line in the text buffer, Geany could search if there's an corresponding error in the compiler tab. And if yes, bring the compiler tab to foreground and let it scroll to the corresponding error.
This should be possible using the existing knowledge Geany has about the errors in the compiler tab and the corresponding lines in the text buffer. No extra UI elements are needed. Just bring the compiler tab to foreground and let it scroll to the first error which corresponds with the active line in the text buffer.
P.S.
This is a humble wish and I know there's no obligation to fulfil it :-) And yes, I don't even have a pull request.
I'm not much into C(++) development. But I've taken a first look into the code.
I'm still searching for a way to register an event if the cursor is being moved to a new line. --> Can you give me a hint where I can register such a handler and get the number of the current line?
In that event handler, the compiler output should be searched for that line. I've taken a look at the function `msgwin_goto_compiler_file_line` in `msgwindow.c`. Like there this would need access to `GTK_TREE_VIEW(msgwindow.tree_compiler)` and it's `model`. But it should loop on calling `msgwin_parse_compiler_error_line` until `line` matches the current line of the cursor. _(maybe this can be optimized later on with a caching data structure, because calling `msgwin_parse_compiler_error_line` often is probably CPU consuming)_
And finally `gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);` and `goto_compiler_file_line(filename, line, focus_editor);` should be called. Similar to when `Next error` is being clicked. But in this case it's not the "next" error, but the error for the line the cursor is currently in.
I've taken some time and hacked something together :-) This is JUST FOR DEMONSTRATION and clearly not meant as a pull request.
I've rewritten the `Next error` feature for demonstration purposes to resemble the feature I'm looking for. If you run this, you can move then cursor to a red line. And if you then activate `Next error` the compiler tab will show the error for the line you're currently in.
That's pretty much what I'm looking for. Except, that I like this routine to be executed automatically if the cursor is being moved to a red line.
<details> <summary>show code diff</summary>
Based on [commit fef873d](https://github.com/geany/geany/commit/fef873d485040bd3a679439802e5067ad9447d...).
``` diff --git a/src/msgwindow.c b/src/msgwindow.c index 350ec3880..2962c5df5 100644 --- a/src/msgwindow.c +++ b/src/msgwindow.c @@ -820,7 +820,10 @@ gboolean msgwin_goto_compiler_file_line(gboolean focus_editor) gint line; gchar *filename, *dir; GtkTreePath *path; - gboolean ret; + gboolean ret = FALSE; + GeanyDocument *doc = document_get_current(); + gint pos = sci_get_current_position(doc->editor->sci); + guint cur_line = sci_get_line_from_position(doc->editor->sci, pos) + 1;
path = gtk_tree_model_get_path(model, &iter); find_prev_build_dir(path, model, &dir); @@ -829,7 +832,8 @@ gboolean msgwin_goto_compiler_file_line(gboolean focus_editor) g_free(string); g_free(dir);
- ret = goto_compiler_file_line(filename, line, focus_editor); + if (line == cur_line) + ret = goto_compiler_file_line(filename, line, focus_editor); g_free(filename); return ret; } diff --git a/src/ui_utils.c b/src/ui_utils.c index 729feae94..50c6b221b 100644 --- a/src/ui_utils.c +++ b/src/ui_utils.c @@ -1777,15 +1777,10 @@ static gboolean tree_view_find(GtkTreeView *treeview, TVMatchCallback cb, gboole treesel = gtk_tree_view_get_selection(treeview); if (gtk_tree_selection_get_selected(treesel, &model, &iter)) { - /* get the next selected item */ - if (! tree_model_iter_get_next(model, &iter, down)) - return FALSE; /* no more items */ - } - else /* no selection */ - { - if (! gtk_tree_model_get_iter_first(model, &iter)) - return TRUE; /* no items */ + tree_model_iter_get_next(model, &iter, down); } + if (! gtk_tree_model_get_iter_first(model, &iter)) + return TRUE; /* no items */ while (TRUE) { gtk_tree_selection_select_iter(treesel, &iter); ```
</details>
This should be possible using the existing knowledge Geany has about the errors in the compiler tab and the corresponding lines in the text buffer. No extra UI elements are needed. Just bring the compiler tab to foreground and let it scroll to the first error which corresponds with the active line in the text buffer.
Geany does not keep file/line information for the compiler messages, when the user clicks a red line in the compiler tab it parses _that line_ for the file/line info. To go the other way would mean parsing all compiler error lines until the current line is found every time its needed, probably ok on a user command like "show current line error" menu and keybinding, but not every line movement in the editor, some compilers can generate a lot of errors to search, and not in order either. If a map of file/line to treeview item was created that would be better (but Geany is not C++ where std::map would be easy, its "plain olde C" so more effort is needed) and of course it needs clearing and re-creating on compile.
Similarly changing the message window tab and scrolling the treeview should be on user command, not just edit window movement, the user may want to keep looking at whichever tab they have open whilst editing, or keep the error message they are working on whilst looking at another line of code they think is involved, and Geany should not change it uncommanded.
If "somebody" (that doesn't necessarily mean you, but it means not me ;-) makes a neat PR based on the above menu and keybinding command and map design I would think it could be accepted.
This proposal shouldn't be too bad. But please tell me if you see thing that should be changed.
If it's OK, I will make a proper pull request.
<details> <summary>show code diff</summary>
```c diff --git a/src/build.c b/src/build.c index 56ec9d613..4e7a5a48b 100644 --- a/src/build.c +++ b/src/build.c @@ -168,6 +168,7 @@ static void run_exit_cb(GPid child_pid, gint status, gpointer user_data); static void on_set_build_commands_activate(GtkWidget *w, gpointer u); static void on_build_next_error(GtkWidget *menuitem, gpointer user_data); static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data); +static void on_build_current_line_error(GtkWidget *menuitem, gpointer user_data); static void kill_process(GPid *pid); static void show_build_result_message(gboolean failure); static void process_build_output_line(gchar *msg, gint color); @@ -1030,6 +1031,7 @@ static void process_build_output_line(gchar *msg, gint color) { gtk_widget_set_sensitive(build_get_menu_items(-1)->menu_item[GBG_FIXED][GBF_NEXT_ERROR], TRUE); gtk_widget_set_sensitive(build_get_menu_items(-1)->menu_item[GBG_FIXED][GBF_PREV_ERROR], TRUE); + gtk_widget_set_sensitive(build_get_menu_items(-1)->menu_item[GBG_FIXED][GBF_CUR_LINE_ERROR], TRUE); } } g_free(filename); @@ -1318,7 +1320,8 @@ static void on_build_menu_item(GtkWidget *w, gpointer user_data) /* the fixed items */ #define MENU_NEXT_ERROR (MENU_SEPARATOR + 1) #define MENU_PREV_ERROR (MENU_NEXT_ERROR + 1) -#define MENU_COMMANDS (MENU_PREV_ERROR + 1) +#define MENU_CUR_LINE_ERROR (MENU_PREV_ERROR + 1) +#define MENU_COMMANDS (MENU_CUR_LINE_ERROR + 1) #define MENU_DONE (MENU_COMMANDS + 1)
@@ -1352,6 +1355,8 @@ static struct BuildMenuItemSpec { GBF_NEXT_ERROR, N_("_Next Error"), on_build_next_error}, {GTK_STOCK_GO_UP, GEANY_KEYS_BUILD_PREVIOUSERROR, MENU_PREV_ERROR, GBF_PREV_ERROR, N_("_Previous Error"), on_build_previous_error}, + {NULL, GEANY_KEYS_BUILD_CURRENTLINEERROR, MENU_CUR_LINE_ERROR, + GBF_CUR_LINE_ERROR, N_("_Current Line Error"), on_build_current_line_error}, {NULL, -1, MENU_SEPARATOR, GBF_SEP_3, NULL, NULL}, {GTK_STOCK_EXECUTE, GEANY_KEYS_BUILD_RUN, GBO_TO_GBG(GEANY_GBO_EXEC), @@ -1489,6 +1494,7 @@ void build_menu_update(GeanyDocument *doc) break; case MENU_NEXT_ERROR: case MENU_PREV_ERROR: + case MENU_CUR_LINE_ERROR: gtk_widget_set_sensitive(menu_items.menu_item[GBG_FIXED][bs->build_cmd], have_errors); vis |= TRUE; break; @@ -1702,6 +1708,18 @@ static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data) }
+static void on_build_current_line_error(GtkWidget *menuitem, gpointer user_data) +{ + if (ui_tree_view_find_cur_line(GTK_TREE_VIEW(msgwindow.tree_compiler), + msgwin_goto_compiler_file_line)) + { + gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER); + } + else + ui_set_statusbar(FALSE, _("No more build errors.")); +} + + void build_toolbutton_build_clicked(GtkAction *action, gpointer unused) { if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_BUILD)) @@ -2807,6 +2825,9 @@ gboolean build_keybinding(guint key_id) case GEANY_KEYS_BUILD_PREVIOUSERROR: item = menu_items->menu_item[GBG_FIXED][GBF_PREV_ERROR]; break; + case GEANY_KEYS_BUILD_CURRENTLINEERROR: + item = menu_items->menu_item[GBG_FIXED][GBF_CUR_LINE_ERROR]; + break; case GEANY_KEYS_BUILD_RUN: item = menu_items->menu_item[GEANY_GBG_EXEC][GBO_TO_CMD(GEANY_GBO_EXEC)]; break; diff --git a/src/build.h b/src/build.h index f9d720d06..eab27bfe2 100644 --- a/src/build.h +++ b/src/build.h @@ -82,6 +82,7 @@ enum GeanyBuildFixedMenuItems { GBF_NEXT_ERROR, GBF_PREV_ERROR, + GBF_CUR_LINE_ERROR, GBF_COMMANDS, GBF_SEP_1, GBF_SEP_2, diff --git a/src/keybindings.c b/src/keybindings.c index 1721fcd1b..439a212d4 100644 --- a/src/keybindings.c +++ b/src/keybindings.c @@ -705,6 +705,8 @@ static void init_default_kb(void) 0, 0, "build_nexterror", _("Next error"), NULL); add_kb(group, GEANY_KEYS_BUILD_PREVIOUSERROR, NULL, 0, 0, "build_previouserror", _("Previous error"), NULL); + add_kb(group, GEANY_KEYS_BUILD_CURRENTLINEERROR, NULL, + GDK_KEY_F3, 0, "build_currentlineerror", _("Current line error"), NULL); add_kb(group, GEANY_KEYS_BUILD_RUN, NULL, GDK_KEY_F5, 0, "build_run", _("Run"), NULL); add_kb(group, GEANY_KEYS_BUILD_OPTIONS, NULL, diff --git a/src/keybindings.h b/src/keybindings.h index e88bebc7f..787c0ff41 100644 --- a/src/keybindings.h +++ b/src/keybindings.h @@ -229,6 +229,7 @@ enum GeanyKeyBindingID GEANY_KEYS_SELECT_ALL, /**< Keybinding. */ GEANY_KEYS_DOCUMENT_RELOADTAGLIST, /**< Keybinding. */ GEANY_KEYS_BUILD_NEXTERROR, /**< Keybinding. */ + GEANY_KEYS_BUILD_CURRENTLINEERROR, /**< Keybinding. */ GEANY_KEYS_NOTEBOOK_MOVETABLAST, /**< Keybinding. */ GEANY_KEYS_SELECT_PARAGRAPH, /**< Keybinding. */ GEANY_KEYS_EDITOR_DELETELINE, /**< Keybinding. */ diff --git a/src/msgwindow.c b/src/msgwindow.c index 350ec3880..94b30bf89 100644 --- a/src/msgwindow.c +++ b/src/msgwindow.c @@ -180,7 +180,7 @@ static gboolean on_msgwin_key_press_event(GtkWidget *widget, GdkEventKey *event, { case MSG_COMPILER: { /* key press in the compiler treeview */ - msgwin_goto_compiler_file_line(enter_or_return); + msgwin_goto_compiler_file_line(enter_or_return, FALSE); break; } case MSG_MESSAGE: @@ -793,7 +793,7 @@ static gboolean goto_compiler_file_line(const gchar *fname, gint line, gboolean }
-gboolean msgwin_goto_compiler_file_line(gboolean focus_editor) +gboolean msgwin_goto_compiler_file_line(gboolean focus_editor, gboolean cur_line_mode) { GtkTreeIter iter; GtkTreeModel *model; @@ -821,6 +821,9 @@ gboolean msgwin_goto_compiler_file_line(gboolean focus_editor) gchar *filename, *dir; GtkTreePath *path; gboolean ret; + GeanyDocument *doc = document_get_current(); + gint pos = sci_get_current_position(doc->editor->sci); + guint cur_line = sci_get_line_from_position(doc->editor->sci, pos) + 1;
path = gtk_tree_model_get_path(model, &iter); find_prev_build_dir(path, model, &dir); @@ -829,7 +832,18 @@ gboolean msgwin_goto_compiler_file_line(gboolean focus_editor) g_free(string); g_free(dir);
- ret = goto_compiler_file_line(filename, line, focus_editor); + if (cur_line_mode) + { + ret = FALSE; + if (line == cur_line) + { + ret = goto_compiler_file_line(filename, line, focus_editor); + } + } + else + { + ret = goto_compiler_file_line(filename, line, focus_editor); + } g_free(filename); return ret; } @@ -1218,7 +1232,7 @@ static gboolean on_msgwin_button_press_event(GtkWidget *widget, GdkEventButton * { case MSG_COMPILER: { /* mouse click in the compiler treeview */ - msgwin_goto_compiler_file_line(double_click); + msgwin_goto_compiler_file_line(double_click, FALSE); break; } case MSG_MESSAGE: diff --git a/src/msgwindow.h b/src/msgwindow.h index 07b06dbc7..9f38f0b6d 100644 --- a/src/msgwindow.h +++ b/src/msgwindow.h @@ -100,7 +100,7 @@ void msgwin_show_hide_tabs(void);
void msgwin_menu_add_common_items(GtkMenu *menu);
-gboolean msgwin_goto_compiler_file_line(gboolean focus_editor); +gboolean msgwin_goto_compiler_file_line(gboolean focus_editor, gboolean cur_line);
void msgwin_parse_compiler_error_line(const gchar *string, const gchar *dir, gchar **filename, gint *line); diff --git a/src/ui_utils.c b/src/ui_utils.c index 729feae94..a2ae7103c 100644 --- a/src/ui_utils.c +++ b/src/ui_utils.c @@ -1768,28 +1768,41 @@ static gboolean tree_model_iter_get_next(GtkTreeModel *model, GtkTreeIter *iter,
/* note: the while loop might be more efficient when searching upwards if it * used tree paths instead of tree iters, but in practice it probably doesn't matter much. */ -static gboolean tree_view_find(GtkTreeView *treeview, TVMatchCallback cb, gboolean down) +static gboolean tree_view_find(GtkTreeView *treeview, TVMatchCallback cb, + GeanyMenuCompileErrorSearchMode err_search_mode) { GtkTreeSelection *treesel; GtkTreeIter iter; GtkTreeModel *model;
treesel = gtk_tree_view_get_selection(treeview); - if (gtk_tree_selection_get_selected(treesel, &model, &iter)) + gboolean active_selection = gtk_tree_selection_get_selected(treesel, &model, &iter); + gboolean down = err_search_mode == GEANY_MENU_COMPILE_NEXT_ERROR || GEANY_MENU_COMPILE_CUR_LINE_ERROR; + if (err_search_mode == GEANY_MENU_COMPILE_CUR_LINE_ERROR) { - /* get the next selected item */ - if (! tree_model_iter_get_next(model, &iter, down)) - return FALSE; /* no more items */ + if (active_selection) + tree_model_iter_get_next(model, &iter, TRUE); + if (! gtk_tree_model_get_iter_first(model, &iter)) + return TRUE; } - else /* no selection */ + else { - if (! gtk_tree_model_get_iter_first(model, &iter)) - return TRUE; /* no items */ + if (active_selection) + { + /* get the next selected item */ + if (! tree_model_iter_get_next(model, &iter, down)) + return FALSE; /* no more items */ + } + else /* no selection */ + { + if (! gtk_tree_model_get_iter_first(model, &iter)) + return TRUE; /* no items */ + } } while (TRUE) { gtk_tree_selection_select_iter(treesel, &iter); - if (cb(FALSE)) + if (cb(FALSE, err_search_mode == GEANY_MENU_COMPILE_CUR_LINE_ERROR)) break; /* found next message */
if (! tree_model_iter_get_next(model, &iter, down)) @@ -1811,14 +1824,21 @@ static gboolean tree_view_find(GtkTreeView *treeview, TVMatchCallback cb, gboole /* Returns FALSE if the treeview has items but no matching next item. */ gboolean ui_tree_view_find_next(GtkTreeView *treeview, TVMatchCallback cb) { - return tree_view_find(treeview, cb, TRUE); + return tree_view_find(treeview, cb, GEANY_MENU_COMPILE_NEXT_ERROR); }
/* Returns FALSE if the treeview has items but no matching next item. */ gboolean ui_tree_view_find_previous(GtkTreeView *treeview, TVMatchCallback cb) { - return tree_view_find(treeview, cb, FALSE); + return tree_view_find(treeview, cb, GEANY_MENU_COMPILE_PREV_ERROR); +} + + +/* Returns FALSE if the treeview has items but no matching next item. */ +gboolean ui_tree_view_find_cur_line(GtkTreeView *treeview, TVMatchCallback cb) +{ + return tree_view_find(treeview, cb, GEANY_MENU_COMPILE_CUR_LINE_ERROR); }
diff --git a/src/ui_utils.h b/src/ui_utils.h index f71790a54..eb0a5f94e 100644 --- a/src/ui_utils.h +++ b/src/ui_utils.h @@ -232,6 +232,15 @@ typedef enum GeanyUIEditorFeatures;
+typedef enum +{ + GEANY_MENU_COMPILE_NEXT_ERROR, + GEANY_MENU_COMPILE_PREV_ERROR, + GEANY_MENU_COMPILE_CUR_LINE_ERROR +} +GeanyMenuCompileErrorSearchMode; + + void ui_widget_show_hide(GtkWidget *widget, gboolean show);
gchar *ui_menu_item_get_text(GtkMenuItem *menu_item); @@ -340,7 +349,7 @@ void ui_update_recent_project_menu(void); void ui_update_tab_status(GeanyDocument *doc);
-typedef gboolean TVMatchCallback(gboolean); +typedef gboolean TVMatchCallback(gboolean, gboolean);
gboolean ui_tree_view_find_next(GtkTreeView *treeview, TVMatchCallback cb);
```
</details>
As I'm pretty happy with my implementation and I'd like to make a pull request soon: Can someone give me a hint where to find guidelines for adding translations? Is is obligatory to run some automated tool like `xgettext`?
Else I'd simply add a ``` msgid "_Current Line Error" msgstr "_..."
``` below the `Previous Error` translation to each `po/*.po` file. And I would use some automated translation system (e.g. Google Translate) for most of the languages.
github-comments@lists.geany.org