On Sat, 11 Jan 2014 21:28:42 +1100 Lex Trotman <ele...@gmail.com> wrote:
> On 11 January 2014 21:00, Thomas Martitz <ku...@rockbox.org> wrote: > > > Not sure is the ^ really useful to us in geany? I'm not sure because > > clicking on the line moves the editor to that location anyway. > > > Yes clicking goes to the line, but doesn't indicate the column, which is > what ^ does. > > It would be better if it *did* go to the column but that needs focus forced > to the editing widget to show the new cursor position. Focussing by > clicking unfortunately makes the cursor go where you click :) > > I just (belatedly) checked and there is PR #191 and patch #11 both > purporting to handle column numbers, not sure of their status. Patch 11 works without changes since Mar-2012. It handles the buildin parsing, and explains in geany.txt the optional 3rd regex for column. Generally, it's a simple extension to the current parsing mechanism. > #191 is quite big but on quick glance I didn't actually see where the > column was handled, and sf is so slow I ran out of patience looking > for the patch 11. I attached a fresh copy of patch 11 which applies without offsets. -- E-gards: Jimmy
>From b1aaae2a12aadc0364917295b8be955639cc60ce Mon Sep 17 00:00:00 2001 From: Dimitar Zhekov <dimitar.zhe...@gmail.com> Date: Sun, 12 Jan 2014 13:56:54 +0200 Subject: [PATCH] Parse an optional column # in the Compiler messages --- doc/geany.txt | 14 ++++++++------ src/build.c | 4 ++-- src/filetypes.c | 47 +++++++++++++++++++++++--------------------- src/filetypes.h | 2 +- src/highlighting.c | 14 ++------------ src/msgwindow.c | 57 +++++++++++++++++++++++++++++++++--------------------- src/msgwindow.h | 2 +- src/navqueue.c | 24 +++++++++++++++++++++-- src/navqueue.h | 3 +++ src/utils.c | 17 ++++++++++++++++ src/utils.h | 2 ++ 11 files changed, 118 insertions(+), 68 deletions(-) diff --git a/doc/geany.txt b/doc/geany.txt index 2e4ca08..fa5663b 100644 --- a/doc/geany.txt +++ b/doc/geany.txt @@ -4236,18 +4236,20 @@ As of Geany 0.19 this section is supplemented by the `[build-menu] section`_. Values that are set in the [build-menu] section will override those in this section. error_regex - This is a regular expression to parse a filename - and line number from build output. If undefined, Geany will fall + This is a GNU-style extended regular expression to parse a filename, a line + number and an optional column from build output. If undefined, Geany will fall back to its default error message parsing. - Only the first two matches will be read by Geany. Geany will look for - a match that is purely digits, and use this for the line number. The - remaining match will be used as the filename. + Geany will read the first two (or three, if there is a third) matches, and check + whether the first or second match is purely digits. If so, it'll try to use the + matches as <line> [column] <filename>, or as <filename> <line> [column]. - *Example:* ``error_regex=(.+):([0-9]+):[0-9]+`` + *Example:* ``error_regex=([^:]+):([0-9]+):([0-9]+): |([^:]+):([0-9]+): `` This will parse a message such as: ``test.py:7:24: E202 whitespace before ']'`` + or (if the first | part does not match) as: + ``test.py:7: E202 whitespace before ']'`` **Build commands** diff --git a/src/build.c b/src/build.c index f3343ab..0057483 100644 --- a/src/build.c +++ b/src/build.c @@ -1061,7 +1061,7 @@ static void process_build_output_line(const gchar *str, gint color) { gchar *msg, *tmp; gchar *filename; - gint line; + gint line, col; msg = g_strdup(str); @@ -1077,7 +1077,7 @@ static void process_build_output_line(const gchar *str, gint color) { SETPTR(current_dir_entered, tmp); } - msgwin_parse_compiler_error_line(msg, current_dir_entered, &filename, &line); + msgwin_parse_compiler_error_line(msg, current_dir_entered, &filename, &line, &col); if (line != -1 && filename != NULL) { diff --git a/src/filetypes.c b/src/filetypes.c index a971a81..d43b724 100644 --- a/src/filetypes.c +++ b/src/filetypes.c @@ -1578,7 +1578,7 @@ static void compile_regex(GeanyFiletype *ft, gchar *regstr) gboolean filetypes_parse_error_message(GeanyFiletype *ft, const gchar *message, - gchar **filename, gint *line) + gchar **filename, gint *line, gint *col) { gchar *regstr; gchar **tmp; @@ -1597,7 +1597,7 @@ gboolean filetypes_parse_error_message(GeanyFiletype *ft, const gchar *message, regstr = *tmp; *filename = NULL; - *line = -1; + *line = *col = -1; if (G_UNLIKELY(EMPTY(regstr))) return FALSE; @@ -1617,33 +1617,36 @@ gboolean filetypes_parse_error_message(GeanyFiletype *ft, const gchar *message, } if (g_match_info_get_match_count(minfo) >= 3) { - gchar *first, *second, *end; - glong l; + gchar *match[3]; + int line_idx; + gboolean has_column = g_match_info_get_match_count(minfo) >= 4; - first = g_match_info_fetch(minfo, 1); - second = g_match_info_fetch(minfo, 2); - l = strtol(first, &end, 10); - if (*end == '\0') /* first is purely decimals */ + match[0] = g_match_info_fetch(minfo, 1); + match[1] = g_match_info_fetch(minfo, 2); + match[2] = has_column ? g_match_info_fetch(minfo, 3) : NULL; + + if (utils_str_to_pos(match[0], line, TRUE) != NULL) /* first is purely decimals */ { - *line = l; - g_free(first); - *filename = second; + line_idx = 0; + *filename = match[1 + has_column]; } else { - l = strtol(second, &end, 10); - if (*end == '\0') - { - *line = l; - g_free(second); - *filename = first; - } + line_idx = 1; + if (utils_str_to_pos(match[1], line, TRUE) != NULL) + *filename = match[0]; else - { - g_free(first); - g_free(second); - } + g_free(match[0]); } + + if (has_column) + { + if (*filename != NULL) + utils_str_to_pos(match[line_idx + 1], col, TRUE); + g_free(match[line_idx + 1]); + } + + g_free(match[line_idx]); } g_match_info_free(minfo); return *filename != NULL; diff --git a/src/filetypes.h b/src/filetypes.h index 49fff2f..d39710e 100644 --- a/src/filetypes.h +++ b/src/filetypes.h @@ -213,7 +213,7 @@ GtkFileFilter *filetypes_create_file_filter_all_source(void); gboolean filetype_has_tags(GeanyFiletype *ft); gboolean filetypes_parse_error_message(GeanyFiletype *ft, const gchar *message, - gchar **filename, gint *line); + gchar **filename, gint *line, gint *col); gboolean filetype_get_comment_open_close(const GeanyFiletype *ft, gboolean single_first, const gchar **co, const gchar **cc); diff --git a/src/highlighting.c b/src/highlighting.c index d84b489..d7f2e5c 100644 --- a/src/highlighting.c +++ b/src/highlighting.c @@ -319,16 +319,6 @@ static void get_keyfile_style(GKeyFile *config, GKeyFile *configh, } -static void convert_int(const gchar *int_str, gint *val) -{ - gchar *end; - gint v = strtol(int_str, &end, 10); - - if (int_str != end) - *val = v; -} - - /* Get first and second integer numbers, store in foreground and background fields of @a style. */ static void get_keyfile_int(GKeyFile *config, GKeyFile *configh, const gchar *section, const gchar *key, gint fdefault_val, gint sdefault_val, @@ -353,10 +343,10 @@ static void get_keyfile_int(GKeyFile *config, GKeyFile *configh, const gchar *se if (list[0]) { - convert_int(list[0], &style->foreground); + utils_str_to_pos(list[0], &style->foreground, TRUE); if (list[1]) { - convert_int(list[1], &style->background); + utils_str_to_pos(list[1], &style->background, TRUE); } } g_strfreev(list); diff --git a/src/msgwindow.c b/src/msgwindow.c index f423267..da83430 100644 --- a/src/msgwindow.c +++ b/src/msgwindow.c @@ -58,6 +58,7 @@ typedef struct const gchar *pattern; /* pattern to split the error message into some fields */ guint min_fields; /* used to detect errors after parsing */ guint line_idx; /* idx of the field where the line is */ + gint col_idx; /* idx of the field where the column is or line_idx or -1 */ gint file_idx; /* idx of the field where the filename is or -1 */ } ParseData; @@ -625,7 +626,8 @@ find_prev_build_dir(GtkTreePath *cur, GtkTreeModel *model, gchar **prefix) } -static gboolean goto_compiler_file_line(const gchar *filename, gint line, gboolean focus_editor) +static gboolean goto_compiler_file_line(const gchar *filename, gint line, gint col, + gboolean focus_editor) { if (!filename || line <= -1) return FALSE; @@ -674,7 +676,7 @@ static gboolean goto_compiler_file_line(const gchar *filename, gint line, gboole if (! doc->changed && editor_prefs.use_indicators) /* if modified, line may be wrong */ editor_indicator_set_on_line(doc->editor, GEANY_INDICATOR_ERROR, line - 1); - ret = navqueue_goto_line(old_doc, doc, line); + ret = navqueue_goto_line_col(old_doc, doc, line, col); if (ret && focus_editor) gtk_widget_grab_focus(GTK_WIDGET(doc->editor->sci)); @@ -709,7 +711,7 @@ gboolean msgwin_goto_compiler_file_line(gboolean focus_editor) gtk_tree_model_get(model, &iter, 1, &string, -1); if (string != NULL) { - gint line; + gint line, col; gchar *filename, *dir; GtkTreePath *path; gboolean ret; @@ -717,11 +719,11 @@ gboolean msgwin_goto_compiler_file_line(gboolean focus_editor) path = gtk_tree_model_get_path(model, &iter); find_prev_build_dir(path, model, &dir); gtk_tree_path_free(path); - msgwin_parse_compiler_error_line(string, dir, &filename, &line); + msgwin_parse_compiler_error_line(string, dir, &filename, &line, &col); g_free(string); g_free(dir); - ret = goto_compiler_file_line(filename, line, focus_editor); + ret = goto_compiler_file_line(filename, line, col, focus_editor); g_free(filename); return ret; } @@ -753,13 +755,13 @@ static void make_absolute(gchar **filename, const gchar *dir) * relevant file with the error in *filename. * *line will be -1 if no error was found in string. * *filename must be freed unless it is NULL. */ -static void parse_file_line(ParseData *data, gchar **filename, gint *line) +static void parse_file_line(ParseData *data, gchar **filename, gint *line, gint *col) { - gchar *end = NULL; + const gchar *end; gchar **fields; *filename = NULL; - *line = -1; + *line = *col = -1; g_return_if_fail(data->string != NULL); @@ -772,15 +774,24 @@ static void parse_file_line(ParseData *data, gchar **filename, gint *line) return; } - *line = strtol(fields[data->line_idx], &end, 10); + end = utils_str_to_pos(fields[data->line_idx], line, FALSE); - /* if the line could not be read, line is 0 and an error occurred, so we leave */ - if (fields[data->line_idx] == end) + /* if the line could not be read or is invalid, leave */ + if (end == NULL) { g_strfreev(fields); return; } + /* parse the column */ + if (data->col_idx == (gint) data->line_idx) + { + while (*end && !g_ascii_isdigit(*end)) end++; + utils_str_to_pos(end, col, FALSE); + } + else if (data->col_idx != -1 && (gint) g_strv_length(fields) >= data->col_idx) + utils_str_to_pos(fields[data->col_idx], col, FALSE); + /* let's stop here if there is no filename in the error message */ if (data->file_idx == -1) { @@ -798,9 +809,9 @@ static void parse_file_line(ParseData *data, gchar **filename, gint *line) static void parse_compiler_error_line(const gchar *string, - gchar **filename, gint *line) + gchar **filename, gint *line, gint *col) { - ParseData data = {NULL, NULL, 0, 0, 0}; + ParseData data = {NULL, NULL, 0, 0, -1, 0}; data.string = string; @@ -873,6 +884,7 @@ static void parse_compiler_error_line(const gchar *string, data.pattern = "("; data.min_fields = 2; data.line_idx = 1; + data.col_idx = 1; data.file_idx = 0; break; } @@ -926,6 +938,7 @@ static void parse_compiler_error_line(const gchar *string, data.pattern = " "; data.min_fields = 4; data.line_idx = 1; + data.col_idx = 3; data.file_idx = -1; break; } @@ -963,6 +976,7 @@ static void parse_compiler_error_line(const gchar *string, data.pattern = ":"; data.min_fields = 3; data.line_idx = 1; + data.col_idx = 2; data.file_idx = 0; break; } @@ -970,7 +984,7 @@ static void parse_compiler_error_line(const gchar *string, } if (data.pattern != NULL) - parse_file_line(&data, filename, line); + parse_file_line(&data, filename, line, col); } @@ -980,13 +994,13 @@ static void parse_compiler_error_line(const gchar *string, * *line will be -1 if no error was found in string. * *filename must be freed unless it is NULL. */ void msgwin_parse_compiler_error_line(const gchar *string, const gchar *dir, - gchar **filename, gint *line) + gchar **filename, gint *line, gint *col) { GeanyFiletype *ft; gchar *trimmed_string; *filename = NULL; - *line = -1; + *line = *col = -1; if (G_UNLIKELY(string == NULL)) return; @@ -1001,10 +1015,10 @@ void msgwin_parse_compiler_error_line(const gchar *string, const gchar *dir, ft = filetypes[build_info.file_type_id]; /* try parsing with a custom regex */ - if (!filetypes_parse_error_message(ft, trimmed_string, filename, line)) + if (!filetypes_parse_error_message(ft, trimmed_string, filename, line, col)) { /* fallback to default old-style parsing */ - parse_compiler_error_line(trimmed_string, filename, line); + parse_compiler_error_line(trimmed_string, filename, line, col); } make_absolute(filename, dir); g_free(trimmed_string); @@ -1033,10 +1047,9 @@ static void msgwin_parse_generic_line(const gchar *string, gchar **filename, gin /* now the line */ if (fields[1] != NULL) { - gchar *end; + const gchar *end = utils_str_to_pos(fields[1], line, FALSE); - *line = strtol(fields[1], &end, 10); - if (end == fields[1]) + if (end == NULL) *line = -1; else if (*end == ':' || g_ascii_isspace(*end)) { /* if we have a blank or a separator right after the number, assume we really got a @@ -1091,7 +1104,7 @@ gboolean msgwin_goto_messages_file_line(gboolean focus_editor) doc = document_open_file(filename, FALSE, NULL, NULL); if (doc != NULL) { - ret = (line < 0) ? TRUE : navqueue_goto_line(old_doc, doc, line); + ret = navqueue_goto_line(old_doc, doc, line > 1 ? line : 1); if (ret && focus_editor) gtk_widget_grab_focus(GTK_WIDGET(doc->editor->sci)); } diff --git a/src/msgwindow.h b/src/msgwindow.h index 18f487f..49b73fc 100644 --- a/src/msgwindow.h +++ b/src/msgwindow.h @@ -98,7 +98,7 @@ void msgwin_menu_add_common_items(GtkMenu *menu); gboolean msgwin_goto_compiler_file_line(gboolean focus_editor); void msgwin_parse_compiler_error_line(const gchar *string, const gchar *dir, - gchar **filename, gint *line); + gchar **filename, gint *line, gint *col); gboolean msgwin_goto_messages_file_line(gboolean focus_editor); diff --git a/src/navqueue.c b/src/navqueue.c index 1df31da..adee8de 100644 --- a/src/navqueue.c +++ b/src/navqueue.c @@ -143,17 +143,37 @@ static void add_new_position(const gchar *utf8_filename, gint pos) * position is set * @param new_doc The document of the new position, must be valid. * @param line the line number of the new position. It is counted with 1 as the first line, not 0. + * line 0 works as line 1, line > number of lines goes to end of the document. * * @return @c TRUE if the cursor has changed the position to @a line or @c FALSE otherwise. **/ gboolean navqueue_goto_line(GeanyDocument *old_doc, GeanyDocument *new_doc, gint line) { + return navqueue_goto_line_col(old_doc, new_doc, line, 0); +} + + +gboolean navqueue_goto_line_col(GeanyDocument *old_doc, GeanyDocument *new_doc, gint line, + gint col) +{ gint pos; g_return_val_if_fail(new_doc != NULL, FALSE); - g_return_val_if_fail(line >= 1, FALSE); + g_return_val_if_fail(line >= 0, FALSE); + + if (line > 0) /* some compilers, like pdflatex report errors on line 0 */ + line--; /* so only adjust the line number if it is greater than 0 */ + pos = sci_get_position_from_line(new_doc->editor->sci, line); + if (pos < 0) /* returned by get position if line # > number of lines + 1 */ + pos = sci_get_length(new_doc->editor->sci); /* goto EOF like scintilla */ + else if (col > 1) + { + gint eoln = sci_get_line_end_position(new_doc->editor->sci, line); - pos = sci_get_position_from_line(new_doc->editor->sci, line - 1); + pos += col - 1; + if (pos > eoln) + pos = eoln; + } /* first add old file position */ if (old_doc != NULL && old_doc->file_name) diff --git a/src/navqueue.h b/src/navqueue.h index 1f1755a..c25cff0 100644 --- a/src/navqueue.h +++ b/src/navqueue.h @@ -39,6 +39,9 @@ void navqueue_remove_file(const gchar *filename); gboolean navqueue_goto_line(GeanyDocument *old_doc, GeanyDocument *new_doc, gint line); +gboolean navqueue_goto_line_col(GeanyDocument *old_doc, GeanyDocument *new_doc, gint line, + gint col); + void navqueue_go_back(void); void navqueue_go_forward(void); diff --git a/src/utils.c b/src/utils.c index cd196cc..780f3b2 100644 --- a/src/utils.c +++ b/src/utils.c @@ -2081,3 +2081,20 @@ gchar *utils_parse_and_format_build_date(const gchar *input) return g_strdup(input); } + +/* Converts @a s to a positive integer (including zero). + * If @a entire is TRUE, the entire string must be a number. + * On success, stores the value in the location pointed by i, and returns + * a pointer to the first unparsed character. On failure, returns NULL. */ +const gchar *utils_str_to_pos(const gchar *s, gint *i, gboolean entire) +{ + char *end; + unsigned long l = strtoul(s, &end, 10); + + if (end > s && (*end == '\0' || !entire) && l <= G_MAXINT) + { + *i = l; + return end; + } + return NULL; +} diff --git a/src/utils.h b/src/utils.h index 1ea09ed..88e7142 100644 --- a/src/utils.h +++ b/src/utils.h @@ -288,4 +288,6 @@ gchar *utils_parse_and_format_build_date(const gchar *input); G_END_DECLS +const gchar *utils_str_to_pos(const gchar *s, gint *i, gboolean entire); + #endif -- 1.8.5.2
_______________________________________________ Devel mailing list Devel@lists.geany.org https://lists.geany.org/cgi-bin/mailman/listinfo/devel