vim-patch:9.2.0547: "%v" in 'errorformat' is affected by 'tabstop' (#40040)

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:  vim/vim#20321
closes: vim/vim#20359

https://github.com/vim/vim/commit/44dcad20f2781ce5c1bd9a851212377859cc5bea

Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
Co-authored-by: vimpostor <21310755+vimpostor@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
authored and GitHub committed 2026-05-29 09:35:37 +08:00
1 parent aa18f3c796
commit 6d2eea9345
4 files changed
+61 -9

No files matched your search

+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,
+3 -3
View File
@@ -1815,9 +1815,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
+25 -1
View File
@@ -3041,6 +3041,30 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int
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)
{
const char *line = get_cursor_line_ptr();
const char *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.
static void qf_jump_goto_line(linenr_T qf_lnum, int qf_col, char qf_viscol, char *qf_pattern)
@@ -3055,7 +3079,7 @@ static void qf_jump_goto_line(linenr_T qf_lnum, int qf_col, char qf_viscol, char
if (qf_col > 0) {
curwin->w_cursor.coladd = 0;
if (qf_viscol == true) {
coladvance(curwin, qf_col - 1);
curwin->w_cursor.col = qf_screen_col_to_idx(qf_col - 1);
} else {
curwin->w_cursor.col = qf_col - 1;
}
+29 -2
View File
@@ -4948,7 +4948,7 @@ endfunc
" Test for parsing entries using visual screen column
func Test_viscol()
enew
call writefile(["Col1\tCol2\tCol3"], 'Xfile1')
call writefile(["Col1\tCol2\tCol3"], 'Xfile1', 'D')
edit Xfile1
" Use byte offset for column number
@@ -5013,7 +5013,34 @@ func Test_viscol()
enew | only
set efm&
call delete('Xfile1')
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