diff --git a/autoload/codefmt/clangformat.vim b/autoload/codefmt/clangformat.vim index 6835dd4..536b968 100644 --- a/autoload/codefmt/clangformat.vim +++ b/autoload/codefmt/clangformat.vim @@ -55,6 +55,36 @@ function! s:ClangFormatHasAtLeastVersion(minimum_version) abort endfunction +" Inputs are 1-based (row, col) coordinates into lines. +" Returns the corresponding zero-based offset into lines->join("\n") +function! s:PositionToOffset(row, col, lines) abort + let l:offset = a:col - 1 " 1-based to 0-based + if a:row > 1 + for l:line in a:lines[0 : a:row - 2] " 1-based to 0-based, exclude current + let l:offset += len(l:line) + 1 " +1 for newline + endfor + endif + return l:offset +endfunction + + +" Input is zero-based offset into lines->join("\n") +" Returns the 1-based [row, col] coordinates into lines. +function! s:OffsetToPosition(offset, lines) abort + let l:lines_consumed = 0 + let l:chars_left = a:offset + for l:line in a:lines + let l:line_len = len(l:line) + 1 " +1 for newline + if l:chars_left < l:line_len + break + endif + let l:chars_left -= l:line_len + let l:lines_consumed += 1 + endfor + return [l:lines_consumed + 1, l:chars_left + 1] " 0-based to 1-based +endfunction + + "" " @private " Invalidates the cached clang-format version. @@ -120,32 +150,37 @@ function! codefmt#clangformat#GetFormatter() abort let l:cmd += ['-lines', l:startline . ':' . l:endline] endfor + let l:lines = getline(1, line('$')) + " Version 3.4 introduced support for cursor tracking " http://llvm.org/releases/3.4/tools/clang/docs/ClangFormat.html let l:supports_cursor = s:ClangFormatHasAtLeastVersion([3, 4]) if l:supports_cursor - " line2byte counts bytes from 1, and col counts from 1, so -2 - let l:cursor_pos = line2byte(line('.')) + col('.') - 2 + " Avoid line2byte: https://github.com/vim/vim/issues/5930 + let l:cursor_pos = s:PositionToOffset(line('.'), col('.'), l:lines) let l:cmd += ['-cursor', string(l:cursor_pos)] endif - let l:input = join(getline(1, line('$')), "\n") + let l:input = join(l:lines, "\n") let l:result = maktaba#syscall#Create(l:cmd).WithStdin(l:input).Call() let l:formatted = split(l:result.stdout, "\n") - if !l:supports_cursor - call maktaba#buffer#Overwrite(1, line('$'), l:formatted[0:]) - else - call maktaba#buffer#Overwrite(1, line('$'), l:formatted[1:]) + if l:supports_cursor + " With -cursor, the first line is a JSON object. + let l:header = remove(l:formatted, 0) + call maktaba#buffer#Overwrite(1, line('$'), l:formatted) try - let l:clang_format_output_json = maktaba#json#Parse(l:formatted[0]) - let l:new_cursor_pos = - \ maktaba#ensure#IsNumber(l:clang_format_output_json.Cursor) + 1 - execute 'goto' l:new_cursor_pos + let l:header_json = maktaba#json#Parse(l:header) + let l:offset = maktaba#ensure#IsNumber(l:header_json.Cursor) + " Compute line/col, avoid goto: https://github.com/vim/vim/issues/5930 + let [l:new_line, l:new_col] = s:OffsetToPosition(l:offset, l:formatted) + call cursor(l:new_line, l:new_col) catch call maktaba#error#Warn('Unable to parse clang-format cursor pos: %s', \ v:exception) endtry + else + call maktaba#buffer#Overwrite(1, line('$'), l:formatted) endif endfunction diff --git a/vroom/clangformat.vroom b/vroom/clangformat.vroom index 2fc012d..e5d7ea7 100644 --- a/vroom/clangformat.vroom +++ b/vroom/clangformat.vroom @@ -178,7 +178,33 @@ that clang-format has a version >= 3.4). :echomsg getline('.')[col('.')-1] ~ 5 +This should work when with textprops set in the file, despite various functions +like line2byte() and :goto being buggy in that case. +(See https://github.com/vim/vim/issues/5930 for bug details) + @clear + % int f() { + | int i=1; + | return 1234567890; } + :call prop_type_add('keyword', {}) + :call prop_add(1, 1, {'length': 3, 'type': 'keyword'}) + :call cursor(2, 10) + :echomsg getline('.')[col('.')-1] + ~ = + :FormatCode clang-format + ! clang-format -style file .* -cursor 23 .*2>.* + $ { "Cursor": 18 } + $ int f() { + $ int i = 1; + $ return 1234567890; + $ } + int f() { + int i = 1; + return 1234567890; + } + @end + :echomsg getline('.')[col('.')-1] + ~ = You might have wondered where the "-style file" above comes from. The clang-format tool accepts a "style" option to control the formatting style. By