Skip to content

Commit 307bd77

Browse files
committed
clang-format: work around vim/vim#5930 when positioning cursor
This bug (and a related predecessor) cause `:goto` and `line2bytes()` to do the wrong thing when vim textprops are used. This manifests as "cursor jumps around randomly after formatting" when using plugins that use textprops for diagnostics, highlighting etc. We happen to have all the code in an array when we need to do cursor coordinate conversions, so we do those in vimscript instead of querying vim. Without this patch, The newly added test fails on affected versions of vim including both vim master and debian's 8.1.2269 (in different ways). Vim bug tracker entry: vim/vim#5930 Similar workaround in first-party vim integration: llvm/llvm-project@591be7e
1 parent d3d8b8b commit 307bd77

File tree

2 files changed

+54
-11
lines changed

2 files changed

+54
-11
lines changed

autoload/codefmt/clangformat.vim

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -120,32 +120,49 @@ function! codefmt#clangformat#GetFormatter() abort
120120
let l:cmd += ['-lines', l:startline . ':' . l:endline]
121121
endfor
122122

123+
let l:lines = getline(1, line('$'))
124+
123125
" Version 3.4 introduced support for cursor tracking
124126
" http://llvm.org/releases/3.4/tools/clang/docs/ClangFormat.html
125127
let l:supports_cursor = s:ClangFormatHasAtLeastVersion([3, 4])
126128
if l:supports_cursor
127-
" line2byte counts bytes from 1, and col counts from 1, so -2
128-
let l:cursor_pos = line2byte(line('.')) + col('.') - 2
129+
" Avoid line2byte: https://github.com/vim/vim/issues/5930
130+
let l:cursor_pos = col('.') - 1
131+
for l:i in range(1, line('.') - 1)
132+
let l:cursor_pos += len(l:lines[l:i - 1]) + 1
133+
endfor
129134
let l:cmd += ['-cursor', string(l:cursor_pos)]
130135
endif
131136

132-
let l:input = join(getline(1, line('$')), "\n")
137+
let l:input = join(l:lines, "\n")
133138
let l:result = maktaba#syscall#Create(l:cmd).WithStdin(l:input).Call()
134139
let l:formatted = split(l:result.stdout, "\n")
135140

136-
if !l:supports_cursor
137-
call maktaba#buffer#Overwrite(1, line('$'), l:formatted[0:])
138-
else
139-
call maktaba#buffer#Overwrite(1, line('$'), l:formatted[1:])
141+
if l:supports_cursor
142+
" With -cursor, the first line is a JSON object.
143+
let l:header = l:formatted[0]
144+
let l:formatted = l:formatted[1:]
145+
call maktaba#buffer#Overwrite(1, line('$'), l:formatted)
140146
try
141-
let l:clang_format_output_json = maktaba#json#Parse(l:formatted[0])
142-
let l:new_cursor_pos =
143-
\ maktaba#ensure#IsNumber(l:clang_format_output_json.Cursor) + 1
144-
execute 'goto' l:new_cursor_pos
147+
let l:header_json = maktaba#json#Parse(l:header)
148+
let l:offset = maktaba#ensure#IsNumber(l:header_json.Cursor)
149+
" Compute line/col, avoid goto: https://github.com/vim/vim/issues/5930
150+
let l:new_line = 0
151+
for l:line in l:formatted
152+
let l:offset_after_next = l:offset - len(l:line) - 1
153+
if l:offset_after_next < 0
154+
break
155+
endif
156+
let l:offset = l:offset_after_next
157+
let l:new_line += 1
158+
endfor
159+
call cursor(l:new_line + 1, l:offset + 1)
145160
catch
146161
call maktaba#error#Warn('Unable to parse clang-format cursor pos: %s',
147162
\ v:exception)
148163
endtry
164+
else
165+
call maktaba#buffer#Overwrite(1, line('$'), l:formatted)
149166
endif
150167
endfunction
151168

vroom/clangformat.vroom

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,33 @@ that clang-format has a version >= 3.4).
178178
:echomsg getline('.')[col('.')-1]
179179
~ 5
180180

181+
This should work when with textprops set in the file, despite various functions
182+
like line2byte() and :goto being buggy in that case.
183+
(See https://github.com/vim/vim/issues/5930 for bug details)
181184

185+
@clear
186+
% int f() {<CR>
187+
| int i=1;<CR>
188+
| return 1234567890; }<CR>
189+
:call prop_type_add('keyword', {})
190+
:call prop_add(1, 1, {'length': 3, 'type': 'keyword'})
191+
:call cursor(2, 10)
192+
:echomsg getline('.')[col('.')-1]
193+
~ =
194+
:FormatCode clang-format
195+
! clang-format -style file .* -cursor 23 .*2>.*
196+
$ { "Cursor": 18 }
197+
$ int f() {
198+
$ int i = 1;
199+
$ return 1234567890;
200+
$ }
201+
int f() {
202+
int i = 1;
203+
return 1234567890;
204+
}
205+
@end
206+
:echomsg getline('.')[col('.')-1]
207+
~ =
182208

183209
You might have wondered where the "-style file" above comes from. The
184210
clang-format tool accepts a "style" option to control the formatting style. By

0 commit comments

Comments
 (0)