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

Reply via email to