Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cursor support #48

Merged
merged 8 commits into from
Jul 14, 2015
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 64 additions & 8 deletions autoload/codefmt.vim
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,34 @@ function! codefmt#GetJsBeautifyFormatter() abort
endfunction


function! s:ClangFormatHasAtLeastVersion(minimum_version) abort
if !exists('s:clang_format_version')
let l:executable = s:plugin.Flag('clang_format_executable')
let l:version_output =
\ maktaba#syscall#Create([l:executable, '--version']).Call().stdout
let l:version_string = matchstr(l:version_output, '\v\d+(.\d+)+')
let s:clang_format_version = map(split(l:version_string, '\.'), 'v:val + 0')
endif
let l:length = min([len(a:minimum_version), len(s:clang_format_version)])
for i in range(l:length)
if a:minimum_version[i] < s:clang_format_version[i]
return 1
elseif a:minimum_version[i] > s:clang_format_version[i]
return 0
endif
endfor
return len(a:minimum_version) <= len(s:clang_format_version)
endfunction


""
" @private
" Invalidates the cached clang-format version.
function! codefmt#InvalidateClangFormatVersion() abort
unlet! s:clang_format_version
endfunction


""
" @private
" Formatter: clang-format
Expand All @@ -161,9 +189,14 @@ function! codefmt#GetClangFormatFormatter() abort
endfunction

function l:formatter.AppliesToBuffer() abort
return &filetype is# 'c' || &filetype is# 'cpp' ||
if &filetype is# 'c' || &filetype is# 'cpp' ||
\ &filetype is# 'proto' || &filetype is# 'javascript'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: indent +4 spaces. This seems to be 5?

endfunction
return 1
endif
" Version 3.6 adds support for java
" http://llvm.org/releases/3.6.0/tools/clang/docs/ReleaseNotes.html
return &filetype is# 'java' && s:ClangFormatHasAtLeastVersion([3, 6])
endfunction:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've got a stowaway colon here.


""
" Reformat buffer with clang-format, only targeting [ranges] if given.
Expand All @@ -178,6 +211,10 @@ function! codefmt#GetClangFormatFormatter() abort
\ 'clang_format_style flag must be string or callable. Found %s',
\ string(l:Style_value))
endif
if empty(a:ranges)
return
endif

let l:cmd = [
\ s:plugin.Flag('clang_format_executable'),
\ '-style', l:style]
Expand All @@ -186,19 +223,39 @@ function! codefmt#GetClangFormatFormatter() abort
let l:cmd += ['-assume-filename', l:fname]
endif

if empty(a:ranges)
return
endif
for [l:startline, l:endline] in a:ranges
call maktaba#ensure#IsNumber(l:startline)
call maktaba#ensure#IsNumber(l:endline)
let l:cmd += ['-lines', l:startline . ':' . l:endline]
endfor

" 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: single quotes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

let l:cmd += ['-cursor', string(l:cursor_pos)]
endif

let l:input = join(getline(1, line('$')), "\n")
let l:result = maktaba#syscall#Create(l:cmd).WithStdin(l:input).Call()
let l:formatted = split(l:result.stdout, "\n")
call maktaba#buffer#Overwrite(1, line('$'), l:formatted)

if !l:supports_cursor
call maktaba#buffer#Overwrite(1, line('$'), l:formatted[0:])
else
call maktaba#buffer#Overwrite(1, line('$'), l:formatted[1:])
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any scenarios where either no JSON gets returned or you get JSON w/o Cursor that you might want custom messages for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my understanding is that the JSON is guaranteed to be there and that it is guaranteed to have cursor. I just put this there to be extra double safe.

exec "goto " . l:new_cursor_pos
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest execute 'goto' l:new_cursor_pos. (Long command name, no explicit concatenation.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

catch
call maktaba#error#Warn('Unable to parse clang-format cursor pos: %s',
\ v:exception)
endtry
endif
endfunction

return l:formatter
Expand Down Expand Up @@ -500,8 +557,7 @@ endfunction

""
" @private
" Invalidates the cached autopep8 version detection for testing, forcing the
" autopep8 formatter to check for it again on the next invocation.
" Invalidates the cached autopep8 version detection info.
function! codefmt#InvalidateAutopep8Version() abort
unlet! s:autopep8_supports_range
endfunction
Expand Down
8 changes: 6 additions & 2 deletions instant/flags.vim
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ endif


" Shout if maktaba is too old. Done here to ensure it's always triggered.
if !maktaba#IsAtLeastVersion('1.9.0')
call maktaba#error#Shout('Codefmt requires maktaba version 1.9.0.')
if !maktaba#IsAtLeastVersion('1.10.0')
call maktaba#error#Shout('Codefmt requires maktaba version 1.10.0.')
call maktaba#error#Shout('You have maktaba version %s.', maktaba#VERSION)
call maktaba#error#Shout('Please update your maktaba install.')
endif
Expand All @@ -51,6 +51,10 @@ call s:plugin.flags.autopep8_executable.AddCallback(
""
" The path to the clang-format executable.
call s:plugin.Flag('clang_format_executable', 'clang-format')
" Invalidate cache of detected clang-format version when this is changed, regardless
" of {value} arg.
call s:plugin.flags.clang_format_executable.AddCallback(
\ maktaba#function#FromExpr('codefmt#InvalidateClangFormatVersion()'), 0)

""
" Formatting style for clang-format to use. Either a string or callable that
Expand Down
94 changes: 87 additions & 7 deletions vroom/clangformat.vroom
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,40 @@ briefly.


The clang-format formatter expects the clang-format executable to be installed
on your system (version 3.4 or newer).
on your system.

% f();
:FormatCode clang-format
! clang-format --version .*
$ clang-format version 3.7.0 (tags/testing)
! clang-format .*
$ { "Cursor": 0 }
$ f();

The name or path of the clang-format executable can be configured via the
clang_format_executable flag if the default of "clang-format" doesn't work.
Notice the "clang-format --version" syscall. The clang-format formatter checks
the version of the clang-format executable to detect what features it supports.
It caches the result, so it only does this once per vim session. We'll take a
closer look at that below.

:Glaive codefmt clang_format_executable='clang-format-3.4'
Any time this flag is changed, the cached version is invalidated and checked
fresh on the next format invocation. The name or path of the clang-format
executable can be configured via the clang_format_executable flag if the
default of "clang-format" doesn't work.

:Glaive codefmt clang_format_executable='clang-format-3.9'
:FormatCode clang-format
! clang-format-3.4 .*
! clang-format-3.9 --version .*
$ clang-format version 3.9.0 (tags/testing)
! clang-format-3.9 .*
$ { "Cursor": 0 }
$ f();
:Glaive codefmt clang_format_executable='clang-format'
:FormatCode clang-format
! clang-format --version .*
$ clang-format version 3.9.0 (tags/testing)
! clang-format .*
$ { "Cursor": 0 }
$ f();


You can format any buffer with clang-format specifying the formatter explicitly.
Expand All @@ -43,6 +62,7 @@ You can format any buffer with clang-format specifying the formatter explicitly.

:FormatCode clang-format
! clang-format -style file .*2>.*
$ { "Cursor": 0 }
$ void f() {
$ int i;
$ SomeFunction(parameter, // comment
Expand All @@ -55,25 +75,35 @@ You can format any buffer with clang-format specifying the formatter explicitly.
}
@end

Several filetypes will use the clang-format formatter by default:
c, cpp, javascript, and proto.
Several filetypes will use the clang-format formatter by default: c, cpp,
javascript, and proto. If the version of clang-format is >= 3.6, then it will
also format java files.

@clear
% f();

:set filetype=cpp
:FormatCode
! clang-format .*
$ { "Cursor": 0 }
$ f();

:set filetype=javascript
:FormatCode
! clang-format .*
$ { "Cursor": 0 }
$ f();

:set filetype=proto
:FormatCode
! clang-format .*
$ { "Cursor": 0 }
$ f();

:set filetype=java
:FormatCode
! clang-format .*
$ { "Cursor": 0 }
$ f();

:set filetype=
Expand All @@ -89,6 +119,7 @@ It can format specific line ranges of code using :FormatLines.

:3,4FormatLines clang-format
! clang-format -style file -lines 3:4 .*2>.*
$ { "Cursor": 0 }
$ void f() {
$ int i = 2+2;
$ int i = 3 + 3;
Expand All @@ -103,6 +134,52 @@ It can format specific line ranges of code using :FormatLines.



It will keep the cursor in the correct position when formatting code (assuming
that clang-format has a version >= 3.4).

:Glaive codefmt clang_format_executable='clang-format-3.2'
@clear
% int f() { int i = 1; return 1234567890; }

:FormatCode clang-format
! clang-format-3.2 --version .*
$ clang-format version 3.2.0 (tags/testing)
! clang-format-3.2 -style file .*2>.*
$ int f() {
$ int i = 1;
$ return 1234567890;
$ }
int f() {
int i = 1;
return 1234567890;
}
@end
:Glaive codefmt clang_format_executable='clang-format'
@clear
% int f() { int i = 1; return 1234567890; }

:goto 33
:echomsg getline('.')[col('.')-1]
~ 5
:FormatCode clang-format
! clang-format --version .*
$ clang-format version 3.7.0 (tags/testing)
! clang-format -style file .* -cursor 32 .*2>.*
$ { "Cursor": 36 }
$ int f() {
$ int i = 1;
$ return 1234567890;
$ }
int f() {
int i = 1;
return 1234567890;
}
@end
:echomsg getline('.')[col('.')-1]
~ 5



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
default, "file" is used to indicate that clang-format should respect
Expand All @@ -115,6 +192,7 @@ a string value to use everywhere...
% f();
:FormatCode clang-format
! clang-format -style WebKit .*2>.*
$ { "Cursor": 0 }
$ f();

...or a callable that takes no arguments and returns a string style name for the
Expand All @@ -131,11 +209,13 @@ current buffer.
:silent file /foo/WebKit/foo.cc
:FormatCode clang-format
! clang-format -style WebKit -assume-filename .*foo.cc .*2>.*
$ { "Cursor": 0 }
$ f();

:silent file /foo/foo.cc
:FormatCode clang-format
! clang-format -style file -assume-filename .*foo.cc .*2>.*
$ { "Cursor": 0 }
$ f();

:Glaive codefmt clang_format_style='file'
Expand Down