Skip to content

Commit

Permalink
Merge pull request #48 from fowles/master
Browse files Browse the repository at this point in the history
Add cursor support
  • Loading branch information
fowles committed Jul 14, 2015
2 parents 654a3d3 + 6638c13 commit c426e43
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 30 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ language: generic
env:
matrix:
# This Maktaba version should match the minimum required in instant/flags.vim.
- CI_TARGET=vim MAKTABA_VERSION=1.9.0
- CI_TARGET=vim MAKTABA_VERSION=1.10.0
- CI_TARGET=vim MAKTABA_VERSION=master
- CI_TARGET=neovim MAKTABA_VERSION=master
before_script:
Expand Down
88 changes: 68 additions & 20 deletions autoload/codefmt.vim
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@ function! codefmt#EnsureFormatter(formatter) abort
" Throw BadValue if the wrong number of format functions are provided.
let l:available_format_functions = ['Format', 'FormatRange', 'FormatRanges']
let l:format_functions = filter(copy(l:available_format_functions),
\ 'has_key(a:formatter, v:val)')
\ 'has_key(a:formatter, v:val)')
if empty(l:format_functions)
throw maktaba#error#BadValue('Formatter ' . a:formatter.name .
\ ' has no format functions. It must have at least one of ' .
\ join(l:available_format_functions, ', '))
\ ' has no format functions. It must have at least one of ' .
\ join(l:available_format_functions, ', '))
endif

" TODO(dbarnett): Check types.
Expand Down Expand Up @@ -120,8 +120,7 @@ function! codefmt#GetJsBeautifyFormatter() abort
" {endline}.
" @throws ShellError
function l:formatter.FormatRange(startline, endline) abort
let l:cmd = [ s:plugin.Flag('js_beautify_executable'),
\'-f', '-' ]
let l:cmd = [s:plugin.Flag('js_beautify_executable'), '-f', '-']
if &filetype != ""
let l:cmd = l:cmd + ['--type', &filetype]
endif
Expand All @@ -146,6 +145,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 @@ -162,7 +189,7 @@ function! codefmt#GetClangFormatFormatter() abort

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

""
Expand All @@ -178,6 +205,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 +217,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
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
execute 'goto' l:new_cursor_pos
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 @@ -317,12 +368,10 @@ function! codefmt#GetAutopep8Formatter() abort
let l:lines = getline(1, line('$'))

if s:autopep8_supports_range
let l:cmd = [ l:executable,
\ '--range', ''.a:startline, ''.a:endline,
\ '-' ]
let l:cmd = [l:executable, '--range', ''.a:startline, ''.a:endline, '-']
let l:input = join(l:lines, "\n")
else
let l:cmd = [ l:executable, '-' ]
let l:cmd = [l:executable, '-']
" Hack range formatting by formatting range individually, ignoring context.
let l:input = join(l:lines[a:startline - 1 : a:endline - 1], "\n")
endif
Expand Down Expand Up @@ -364,7 +413,7 @@ function! codefmt#IsFormatterAvailable() abort
let l:formatters = copy(s:registry.GetExtensions())
let l:is_available = 'v:val.AppliesToBuffer() && s:IsAvailable(v:val)'
return !empty(filter(l:formatters, l:is_available)) ||
\ !empty(get(b:, 'codefmt_formatter'))
\ !empty(get(b:, 'codefmt_formatter'))
endfunction

function! s:GetSetupInstructions(formatter) abort
Expand Down Expand Up @@ -410,13 +459,13 @@ function! s:GetFormatter(...) abort
" Check if we have formatters that are not available for some reason.
" Report a better error message in that case.
let l:unavailable_formatters = filter(
\ copy(l:formatters), 'v:val.AppliesToBuffer()')
\ copy(l:formatters), 'v:val.AppliesToBuffer()')
if !empty(l:unavailable_formatters)
let l:error = join(map(copy(l:unavailable_formatters),
\ 's:GetSetupInstructions(v:val)'), "\n")
\ 's:GetSetupInstructions(v:val)'), "\n")
else
let l:error = 'Not available. codefmt doesn''t have a default ' .
\ 'formatter for this buffer.'
\ 'formatter for this buffer.'
endif
call maktaba#error#Shout(l:error)
return
Expand Down Expand Up @@ -500,8 +549,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
2 changes: 2 additions & 0 deletions vroom/autocmd.vroom
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ or whatever conditions you want to trigger on.
|i);}

:doautocmd BufWritePre
! clang-format --version .*
$ clang-format version 3.3.0 (tags/testing)
! clang-format .*
$ void f() {
$ int i;
Expand Down
87 changes: 80 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,28 @@ 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.

@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=
Expand All @@ -89,6 +112,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 +127,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.3'
@clear
% int f() { int i = 1; return 1234567890; }

:FormatCode clang-format
! clang-format-3.3 --version .*
$ clang-format version 3.3.0 (tags/testing)
! clang-format-3.3 -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 +185,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 +202,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
2 changes: 2 additions & 0 deletions vroom/jsbeautify.vroom
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ javascript, json, html and css.

:set filetype=javascript
:FormatCode
! clang-format --version .*
$ clang-format version 3.3.0 (tags/testing)
! clang-format .*
$ f();

Expand Down
Loading

0 comments on commit c426e43

Please sign in to comment.