patch 9.2.0547: "%v" in 'errorformat' is affected by 'tabstop'

Problem:  The "%v" item in 'errorformat' interprets the reported
          screen column using the buffer's 'tabstop', so the cursor
          jumps to the wrong column when 'tabstop' is not 8
          (vimpostor).
Solution: When resolving a "%v" column, always count a <tab> as 8
          screen columns, independent of 'tabstop', matching the
          column numbers reported by compilers; keep the multi-byte
          handling.  Also use "%v" in the gcc compiler file and
          update the documentation (Hirohito Higashi).

fixes:  #20321
closes: #20359

Co-Authored-By: vimpostor <21310755+vimpostor@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Hirohito Higashi <h.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Hirohito Higashi
2026-05-28 19:18:38 +00:00
committed by Christian Brabandt
parent 627e4355ac
commit 44dcad20f2
5 changed files with 65 additions and 8 deletions
+4 -3
View File
@@ -7,6 +7,7 @@
" added line suggested by Anton Lindqvist 2016 Mar 31
" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition)
" 2025 Dec 17 by The Vim Project (correctly parse: 'make: *** [Makefile:2: all] Error 1')
" 2026 May 28 by The Vim Project (Use %v to parse column number)
if exists("current_compiler")
finish
@@ -24,9 +25,9 @@ CompilerSet errorformat=
\\"%f\"%*\\D%l:\ %m,
\%-G%f:%l:\ %trror:\ (Each\ undeclared\ identifier\ is\ reported\ only\ once,
\%-G%f:%l:\ %trror:\ for\ each\ function\ it\ appears\ in.),
\%f:%l:%c:\ %trror:\ %m,
\%f:%l:%c:\ %tarning:\ %m,
\%f:%l:%c:\ %m,
\%f:%l:%v:\ %trror:\ %m,
\%f:%l:%v:\ %tarning:\ %m,
\%f:%l:%v:\ %m,
\%f:%l:\ %trror:\ %m,
\%f:%l:\ %tarning:\ %m,
\%f:%l:\ %m,
+4 -4
View File
@@ -1,4 +1,4 @@
*quickfix.txt* For Vim version 9.2. Last change: 2026 Feb 14
*quickfix.txt* For Vim version 9.2. Last change: 2026 May 28
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -1821,9 +1821,9 @@ Basic items
%c column number (finds a number representing character
column of the error, byte index, a <tab> is 1
character column)
%v virtual column number (finds a number representing
screen column of the error (1 <tab> == 8 screen
columns))
%v virtual column number (finds a number representing the
screen column of the error, where a <tab> is always 8
screen columns regardless of 'tabstop')
%k end column number (finds a number representing
the character column of the error, byte index, or a
number representing screen end column of the error if
+27 -1
View File
@@ -3626,6 +3626,32 @@ qf_jump_edit_buffer(
return retval;
}
/*
* Return the byte index in the current line for screen column "vcol"
* (zero-based). A <tab> is always counted as 8 screen columns, matching the
* column numbers compilers report for the "%v" item in 'errorformat',
* regardless of the buffer's 'tabstop'.
*/
static int
qf_screen_col_to_idx(colnr_T vcol)
{
char_u *line = ml_get_curline();
char_u *p = line;
colnr_T col = 0;
while (*p != NUL && col < vcol)
{
if (*p == TAB)
col += 8 - (col % 8);
else
col += ptr2cells(p);
if (col > vcol)
break;
MB_PTR_ADV(p);
}
return (int)(p - line);
}
/*
* Go to the error line in the current file using either line/column number or
* a search pattern.
@@ -3653,7 +3679,7 @@ qf_jump_goto_line(
{
curwin->w_cursor.coladd = 0;
if (qf_viscol == TRUE)
coladvance(qf_col - 1);
curwin->w_cursor.col = qf_screen_col_to_idx(qf_col - 1);
else
curwin->w_cursor.col = qf_col - 1;
curwin->w_set_curswant = true;
+28
View File
@@ -5004,6 +5004,34 @@ func Test_viscol()
set efm&
endfunc
" Test that '%v' is not affected by 'tabstop': a <tab> is always counted as
" 8 screen columns, matching the column numbers reported by compilers.
func Test_viscol_tabstop()
enew
call writefile(["\tABCDEFGH"], 'Xfile1', 'D')
edit Xfile1
set efm=%f:%l:%v:%m
" gcc reports column 9 for 'A' (the <tab> expands to 8 columns). The jump
" must land on 'A' (byte 2) for any 'tabstop' value.
for ts in [8, 4, 2, 13]
exe 'setlocal tabstop=' .. ts
cexpr "Xfile1:1:9:XX"
call assert_equal(2, col('.'), 'tabstop=' .. ts)
endfor
" A multi-byte character after the tab: 'ä' is 2 bytes but 1 screen cell,
" so screen column 10 is the next character 'b' (byte 4).
call writefile(["\täbc"], 'Xfile1')
edit! Xfile1
setlocal tabstop=4
cexpr "Xfile1:1:10:XX"
call assert_equal(4, col('.'))
enew | only
set efm&
endfunc
" Test for the quickfix window buffer
func Xqfbuf_test(cchar)
call s:setup_commands(a:cchar)
+2
View File
@@ -729,6 +729,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
547,
/**/
546,
/**/