changeset 13821:98274127d675 v8.0.1782

patch 8.0.1782: no simple way to label quickfix entries commit https://github.com/vim/vim/commit/d76ce852668635d81778cedacc2d3f021ed4e475 Author: Bram Moolenaar <Bram@vim.org> Date: Tue May 1 15:02:04 2018 +0200 patch 8.0.1782: no simple way to label quickfix entries Problem: No simple way to label quickfix entries. Solution: Add the "module" item, to be used instead of the file name for display purposes. (Martin Szamotulski, closes #1757)
author Christian Brabandt <cb@256bit.org>
date Tue, 01 May 2018 15:15:07 +0200
parents 2e78af0dc866
children 46912abff012
files runtime/doc/eval.txt runtime/doc/quickfix.txt src/alloc.h src/quickfix.c src/testdir/test_quickfix.vim src/version.c
diffstat 6 files changed, 101 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -4737,6 +4737,7 @@ getqflist([{what}])					*getqflist()*
 		list item is a dictionary with these entries:
 			bufnr	number of buffer that has the file name, use
 				bufname() to get the name
+			module	module name
 			lnum	line number in the buffer (first line is 1)
 			col	column number (first column is 1)
 			vcol	|TRUE|: "col" is visual column
@@ -7221,6 +7222,8 @@ setqflist({list} [, {action} [, {what}]]
 				buffer
 		    filename	name of a file; only used when "bufnr" is not
 				present or it is invalid.
+		    module	name of a module; if given it will be used in
+				quickfix error window instead of the filename.
 		    lnum	line number in the file
 		    pattern	search pattern used to locate the error
 		    col		column number
--- a/runtime/doc/quickfix.txt
+++ b/runtime/doc/quickfix.txt
@@ -1220,6 +1220,7 @@ you want to match case, add "\C" to the 
 Basic items
 
 	%f		file name (finds a string)
+	%o		module name (finds a string)
 	%l		line number (finds a number)
 	%c		column number (finds a number representing character
 			column of the error, (1 <tab> == 1 character column))
@@ -1264,6 +1265,11 @@ conversion can be used to locate lines w
 output.  Like the output of the "grep" shell command.
 When the pattern is present the line number will not be used.
 
+The "%o" conversion specifies the module name in quickfix entry.  If present
+it will be used in quickfix error window instead of the filename.  The module
+name is used only for displaying purposes, the file name is used when jumping
+to the file.
+
 Changing directory
 
 The following uppercase conversion characters specify the type of special
--- a/src/alloc.h
+++ b/src/alloc.h
@@ -15,6 +15,7 @@ typedef enum {
 	aid_qf_dirname_start,
 	aid_qf_dirname_now,
 	aid_qf_namebuf,
+	aid_qf_module,
 	aid_qf_errmsg,
 	aid_qf_pattern,
 	aid_last
--- a/src/quickfix.c
+++ b/src/quickfix.c
@@ -33,6 +33,7 @@ struct qfline_S
     int		qf_fnum;	/* file number for the line */
     int		qf_col;		/* column where the error occurred */
     int		qf_nr;		/* error number */
+    char_u	*qf_module;	/* module name for this error */
     char_u	*qf_pattern;	/* search pattern for the error */
     char_u	*qf_text;	/* description of the error */
     char_u	qf_viscol;	/* set to TRUE if qf_col is screen column */
@@ -101,7 +102,7 @@ struct qf_info_S
 static qf_info_T ql_info;	/* global quickfix list */
 static int_u last_qf_id = 0;	/* Last used quickfix list id */
 
-#define FMT_PATTERNS 10		/* maximum number of % recognized */
+#define FMT_PATTERNS 11		/* maximum number of % recognized */
 
 /*
  * Structure used to hold the info of one part of 'errorformat'
@@ -135,7 +136,8 @@ static efm_T	*fmt_start = NULL; /* cache
 
 static int	qf_init_ext(qf_info_T *qi, int qf_idx, char_u *efile, buf_T *buf, typval_T *tv, char_u *errorformat, int newlist, linenr_T lnumfirst, linenr_T lnumlast, char_u *qf_title, char_u *enc);
 static void	qf_new_list(qf_info_T *qi, char_u *qf_title);
-static int	qf_add_entry(qf_info_T *qi, int qf_idx, char_u *dir, char_u *fname, int bufnum, char_u *mesg, long lnum, int col, int vis_col, char_u *pattern, int nr, int type, int valid);
+static int	qf_add_entry(qf_info_T *qi, int qf_idx, char_u *dir, char_u *fname, char_u *module, int bufnum, char_u *mesg, long lnum, int col, int vis_col, char_u *pattern, int nr, int type, int valid);
+static qf_info_T *ll_new_list(void);
 static void	qf_free(qf_info_T *qi, int idx);
 static char_u	*qf_types(int, int);
 static int	qf_get_fnum(qf_info_T *qi, int qf_idx, char_u *, char_u *);
@@ -221,7 +223,8 @@ static struct fmtpattern
 		    {'r', ".*"},
 		    {'p', "[- 	.]*"},
 		    {'v', "\\d\\+"},
-		    {'s', ".\\+"}
+		    {'s', ".\\+"},
+		    {'o', ".\\+"}
 		};
 
 /*
@@ -809,6 +812,7 @@ qf_get_nextline(qfstate_T *state)
 
 typedef struct {
     char_u	*namebuf;
+    char_u	*module;
     char_u	*errmsg;
     int		errmsglen;
     long	lnum;
@@ -868,6 +872,7 @@ restofline:
 	if (qfl->qf_multiscan && vim_strchr((char_u *)"OPQ", idx) == NULL)
 	    continue;
 	fields->namebuf[0] = NUL;
+	fields->module[0] = NUL;
 	fields->pattern[0] = NUL;
 	if (!qfl->qf_multiscan)
 	    fields->errmsg[0] = NUL;
@@ -1008,6 +1013,15 @@ restofline:
 		fields->pattern[len + 4] = '$';
 		fields->pattern[len + 5] = NUL;
 	    }
+	    if ((i = (int)fmt_ptr->addr[10]) > 0)		/* %o */
+	    {
+		if (regmatch.startp[i] == NULL)
+		    continue;
+		len = (int)(regmatch.endp[i] - regmatch.startp[i]);
+		if (len > CMDBUFFSIZE)
+		    len = CMDBUFFSIZE;
+		STRNCAT(fields->module, regmatch.startp[i], len);
+	    }
 	    break;
 	}
     }
@@ -1181,11 +1195,12 @@ qf_init_ext(
 	convert_setup(&state.vc, enc, p_enc);
 #endif
     fields.namebuf = alloc_id(CMDBUFFSIZE + 1, aid_qf_namebuf);
+    fields.module = alloc_id(CMDBUFFSIZE + 1, aid_qf_module);
     fields.errmsglen = CMDBUFFSIZE + 1;
     fields.errmsg = alloc_id(fields.errmsglen, aid_qf_errmsg);
     fields.pattern = alloc_id(CMDBUFFSIZE + 1, aid_qf_pattern);
     if (fields.namebuf == NULL || fields.errmsg == NULL
-		|| fields.pattern == NULL)
+		|| fields.pattern == NULL || fields.module == NULL)
 	goto qf_init_end;
 
     if (efile != NULL && (state.fd = mch_fopen((char *)efile, "r")) == NULL)
@@ -1282,6 +1297,7 @@ qf_init_ext(
 			    ? fields.namebuf
 			    : ((qfl->qf_currfile != NULL && fields.valid)
 				? qfl->qf_currfile : (char_u *)NULL),
+			fields.module,
 			0,
 			fields.errmsg,
 			fields.lnum,
@@ -1327,6 +1343,7 @@ qf_init_end:
     if (state.fd != NULL)
 	fclose(state.fd);
     vim_free(fields.namebuf);
+    vim_free(fields.module);
     vim_free(fields.errmsg);
     vim_free(fields.pattern);
     vim_free(state.growbuf);
@@ -1444,6 +1461,7 @@ qf_add_entry(
     int		qf_idx,		/* list index */
     char_u	*dir,		/* optional directory name */
     char_u	*fname,		/* file name or NULL */
+    char_u	*module,	/* module name or NULL */
     int		bufnum,		/* buffer number or zero */
     char_u	*mesg,		/* message */
     long	lnum,		/* line number */
@@ -1486,6 +1504,15 @@ qf_add_entry(
 	vim_free(qfp);
 	return FAIL;
     }
+    if (module == NULL || *module == NUL)
+	qfp->qf_module = NULL;
+    else if ((qfp->qf_module = vim_strsave(module)) == NULL)
+    {
+	vim_free(qfp->qf_text);
+	vim_free(qfp->qf_pattern);
+	vim_free(qfp);
+	return FAIL;
+    }
     qfp->qf_nr = nr;
     if (type != 1 && !vim_isprintc(type)) /* only printable chars allowed */
 	type = 0;
@@ -1635,6 +1662,7 @@ copy_loclist(win_T *from, win_T *to)
 				 to->w_llist->qf_curlist,
 				 NULL,
 				 NULL,
+				 from_qfp->qf_module,
 				 0,
 				 from_qfp->qf_text,
 				 from_qfp->qf_lnum,
@@ -2765,18 +2793,22 @@ qf_list(exarg_T *eap)
 		break;
 
 	    fname = NULL;
-	    if (qfp->qf_fnum != 0
-			      && (buf = buflist_findnr(qfp->qf_fnum)) != NULL)
-	    {
-		fname = buf->b_fname;
-		if (qfp->qf_type == 1)	/* :helpgrep */
-		    fname = gettail(fname);
+	    if (qfp->qf_module != NULL && *qfp->qf_module != NUL)
+		vim_snprintf((char *)IObuff, IOSIZE, "%2d %s", i, (char *)qfp->qf_module);
+	    else {
+		if (qfp->qf_fnum != 0
+				&& (buf = buflist_findnr(qfp->qf_fnum)) != NULL)
+		{
+		    fname = buf->b_fname;
+		    if (qfp->qf_type == 1)	/* :helpgrep */
+			fname = gettail(fname);
+		}
+		if (fname == NULL)
+		    sprintf((char *)IObuff, "%2d", i);
+		else
+		    vim_snprintf((char *)IObuff, IOSIZE, "%2d %s",
+								i, (char *)fname);
 	    }
-	    if (fname == NULL)
-		sprintf((char *)IObuff, "%2d", i);
-	    else
-		vim_snprintf((char *)IObuff, IOSIZE, "%2d %s",
-							    i, (char *)fname);
 	    msg_outtrans_attr(IObuff, i == qi->qf_lists[qi->qf_curlist].qf_index
 					   ? HL_ATTR(HLF_QFL) : qfFileAttr);
 
@@ -2957,9 +2989,10 @@ qf_free_items(qf_info_T *qi, int idx)
 	qfpnext = qfp->qf_next;
 	if (!stop)
 	{
+	    vim_free(qfp->qf_module);
 	    vim_free(qfp->qf_text);
+	    vim_free(qfp->qf_pattern);
 	    stop = (qfp == qfpnext);
-	    vim_free(qfp->qf_pattern);
 	    vim_free(qfp);
 	    if (stop)
 		/* Somehow qf_count may have an incorrect value, set it to 1
@@ -3562,7 +3595,12 @@ qf_fill_buffer(qf_info_T *qi, buf_T *buf
 	}
 	while (lnum < qi->qf_lists[qi->qf_curlist].qf_count)
 	{
-	    if (qfp->qf_fnum != 0
+	    if (qfp->qf_module != NULL)
+	    {
+		STRCPY(IObuff, qfp->qf_module);
+		len = (int)STRLEN(IObuff);
+	    }
+	    else if (qfp->qf_fnum != 0
 		    && (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL
 		    && errbuf->b_fname != NULL)
 	    {
@@ -4367,6 +4405,7 @@ vgr_match_buflines(
 			qi->qf_curlist,
 			NULL,       /* dir */
 			fname,
+			NULL,
 			duplicate_name ? 0 : buf->b_fnum,
 			ml_get_buf(buf,
 			    regmatch->startpos[0].lnum + lnum, FALSE),
@@ -4934,6 +4973,8 @@ get_errorlist(qf_info_T *qi_arg, win_T *
 	  || dict_add_nr_str(dict, "col",   (long)qfp->qf_col, NULL) == FAIL
 	  || dict_add_nr_str(dict, "vcol",  (long)qfp->qf_viscol, NULL) == FAIL
 	  || dict_add_nr_str(dict, "nr",    (long)qfp->qf_nr, NULL) == FAIL
+	  || dict_add_nr_str(dict, "module",  0L,
+		   qfp->qf_module == NULL ? (char_u *)"" : qfp->qf_module) == FAIL
 	  || dict_add_nr_str(dict, "pattern",  0L,
 	     qfp->qf_pattern == NULL ? (char_u *)"" : qfp->qf_pattern) == FAIL
 	  || dict_add_nr_str(dict, "text",  0L,
@@ -5312,7 +5353,7 @@ qf_add_entries(
 {
     listitem_T	*li;
     dict_T	*d;
-    char_u	*filename, *pattern, *text, *type;
+    char_u	*filename, *module, *pattern, *text, *type;
     int		bufnum;
     long	lnum;
     int		col, nr;
@@ -5347,6 +5388,7 @@ qf_add_entries(
 	    continue;
 
 	filename = get_dict_string(d, (char_u *)"filename", TRUE);
+	module = get_dict_string(d, (char_u *)"module", TRUE);
 	bufnum = (int)get_dict_number(d, (char_u *)"bufnr");
 	lnum = (int)get_dict_number(d, (char_u *)"lnum");
 	col = (int)get_dict_number(d, (char_u *)"col");
@@ -5383,6 +5425,7 @@ qf_add_entries(
 			       qf_idx,
 			       NULL,	    /* dir */
 			       filename,
+			       module,
 			       bufnum,
 			       text,
 			       lnum,
@@ -5394,6 +5437,7 @@ qf_add_entries(
 			       valid);
 
 	vim_free(filename);
+	vim_free(module);
 	vim_free(pattern);
 	vim_free(text);
 	vim_free(type);
@@ -6040,6 +6084,7 @@ hgr_search_file(
 			qi->qf_curlist,
 			NULL,	/* dir */
 			fname,
+			NULL,
 			0,
 			line,
 			lnum,
@@ -6104,7 +6149,7 @@ hgr_search_files_in_dir(
 	    /* Skip files for a different language. */
 	    if (lang != NULL
 		    && STRNICMP(lang, fnames[fi]
-			+ STRLEN(fnames[fi]) - 3, 2) != 0
+				    + STRLEN(fnames[fi]) - 3, 2) != 0
 		    && !(STRNICMP(lang, "en", 2) == 0
 			&& STRNICMP("txt", fnames[fi]
 			    + STRLEN(fnames[fi]) - 3, 3) == 0))
--- a/src/testdir/test_quickfix.vim
+++ b/src/testdir/test_quickfix.vim
@@ -138,6 +138,16 @@ func XlistTests(cchar)
 	      \ ' 4:40 col 20 x  44: Other',
 	      \ ' 5:50 col 25  55: one'], l)
 
+  " Test for module names, one needs to explicitly set `'valid':v:true` so
+  call g:Xsetlist([
+	\ {'lnum':10,'col':5,'type':'W','module':'Data.Text','text':'ModuleWarning','nr':11,'valid':v:true},
+	\ {'lnum':20,'col':10,'type':'W','module':'Data.Text','filename':'Data/Text.hs','text':'ModuleWarning','nr':22,'valid':v:true},
+	\ {'lnum':30,'col':15,'type':'W','filename':'Data/Text.hs','text':'FileWarning','nr':33,'valid':v:true}])
+  let l = split(execute('Xlist', ""), "\n")
+  call assert_equal([' 1 Data.Text:10 col 5 warning  11: ModuleWarning',
+	\ ' 2 Data.Text:20 col 10 warning  22: ModuleWarning',
+	\ ' 3 Data/Text.hs:30 col 15 warning  33: FileWarning'], l)
+
   " Error cases
   call assert_fails('Xlist abc', 'E488:')
 endfunc
@@ -1142,6 +1152,21 @@ func Test_efm2()
   call assert_equal(1, l[4].valid)
   call assert_equal('unittests/dbfacadeTest.py', bufname(l[4].bufnr))
 
+  " Test for %o
+  set efm=%f(%o):%l\ %m
+  cgetexpr ['Xtestfile(Language.PureScript.Types):20 Error']
+  call writefile(['Line1'], 'Xtestfile')
+  let l = getqflist()
+  call assert_equal(1, len(l), string(l))
+  call assert_equal('Language.PureScript.Types', l[0].module)
+  copen
+  call assert_equal('Language.PureScript.Types|20| Error', getline(1))
+  call feedkeys("\<CR>", 'xn')
+  call assert_equal('Xtestfile', expand('%:t'))
+  cclose
+  bd
+  call delete("Xtestfile")
+
   " The following sequence of commands used to crash Vim
   set efm=%W%m
   cgetexpr ['msg1']
--- a/src/version.c
+++ b/src/version.c
@@ -762,6 +762,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1782,
+/**/
     1781,
 /**/
     1780,