├── .github └── workflows │ └── ci.yml ├── README.md ├── autoload ├── clang_format.vim └── operator │ └── clang_format.vim ├── doc └── clang-format.txt ├── plugin └── clang_format.vim └── test ├── .gitignore ├── Gemfile ├── Gemfile.lock ├── Guardfile ├── README.md ├── Rakefile ├── VimFlavor ├── VimFlavor.lock └── t ├── clang-format-dummy.sh ├── clang_format_spec.vim ├── test.cpp └── test.js /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | unit-tests: 6 | name: Unit tests 7 | strategy: 8 | matrix: 9 | os: [ubuntu-latest, macos-latest] 10 | runs-on: ${{ matrix.os }} 11 | steps: 12 | - run: sudo apt install -y --no-install-recommends clang-format 13 | if: ${{ matrix.os == 'ubuntu-latest' }} 14 | - run: brew install clang-format 15 | if: ${{ matrix.os == 'macos-latest' }} 16 | - uses: actions/checkout@v2 17 | - uses: ruby/setup-ruby@v1 18 | with: 19 | ruby-version: 3 20 | bundler-cache: true 21 | - uses: rhysd/action-setup-vim@v1 22 | - name: Run unit tests 23 | run: | 24 | cd ./test 25 | bundle --version 26 | bundle install 27 | bundle exec vim-flavor test 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Format your C family code 2 | ======================================= 3 | [![CI](https://github.com/rhysd/vim-clang-format/actions/workflows/ci.yml/badge.svg)](https://github.com/rhysd/vim-clang-format/actions/workflows/ci.yml) 4 | 5 | This plugin formats your code with specific coding style using [clang-format](http://clang.llvm.org/docs/ClangFormat.html). 6 | 7 | Automatic formatting is provided for the following languages by default: 8 | 9 | - C 10 | - C++ 11 | - Objective-C 12 | - JavaScript 13 | - Java 14 | - TypeScript 15 | - Protobuf 16 | - Cuda 17 | - Vala 18 | 19 | ## Screenshot 20 | 21 | ![Screenshot](https://raw.githubusercontent.com/rhysd/ss/master/vim-clang-format/main.gif) 22 | 23 | ## Requirements 24 | 25 | - `clang-format` command (**3.4 or later**), which is bundled in Clang extra tools 26 | - [vim-operator-user](https://github.com/kana/vim-operator-user)(highly recommended) 27 | - [vimproc.vim](https://github.com/Shougo/vimproc.vim)(recommended in Windows) 28 | 29 | ## Installation 30 | 31 | Copy `plugin`, `doc` and `autoload` directories into your `~/.vim` or use `:packadd` in Vim8. Or please use your favorite plugin manager to install this plugin. I recommend latter. 32 | 33 | ## Usage 34 | 35 | `:ClangFormat` command is available. 36 | If you use it in normal mode, the whole code will be formatted. If you use it in visual mode, the selected code will be formatted. 37 | It is more convenient to map `:ClangFormat` to your favorite key mapping in normal mode and visual mode. 38 | 39 | If you install [vim-operator-user](https://github.com/kana/vim-operator-user) in advance, you can also map `(operator-clang-format)` to your favorite key bind. 40 | 41 | `:ClangFormatAutoToggle` command toggles the auto formatting on buffer write. 42 | `:ClangFormatAutoEnable` command enables the auto formatting on buffer write. Useful for automatically enabling the auto format through a vimrc. `:ClangFormatAutoDisable` turns it off. 43 | 44 | ## What is the difference from `clang-format.py`? 45 | 46 | `clang-format.py` is Python script to use clang-format from Vim, which is installed with clang-format. 47 | The usage is [here](http://clang.llvm.org/docs/ClangFormat.html#vim-integration). 48 | Against `clang-format.py`, vim-clang-format has below advantages. 49 | 50 | - Style options are highly customizable in `.vimrc`. `clang-format.py` requires `.clang-format` file to customize a style. 51 | - vim-clang-format provides an operator mapping. 52 | - vim-clang-format doesn't need python interface. 53 | 54 | In short, vim-clang-format has better Vim integration than `clang-format.py`. 55 | 56 | ## Customization 57 | 58 | You can customize formatting using some variables. 59 | 60 | - `g:clang_format#code_style` 61 | 62 | `g:clang_format#code_style` is a base style. 63 | `llvm`, `google`, `chromium`, `mozilla` is supported. 64 | The default value is `google`. 65 | 66 | - `g:clang_format#style_options` 67 | 68 | Coding style options as dictionary. 69 | 70 | An example is below: 71 | 72 | ```vim 73 | let g:clang_format#style_options = { 74 | \ "AccessModifierOffset" : -4, 75 | \ "AllowShortIfStatementsOnASingleLine" : "true", 76 | \ "AlwaysBreakTemplateDeclarations" : "true", 77 | \ "Standard" : "C++11", 78 | \ "BreakBeforeBraces" : "Stroustrup"} 79 | ``` 80 | 81 | For config information, execute `clang-format -dump-config` command. 82 | 83 | - `g:clang_format#command` 84 | 85 | Name of `clang-format`. If the name of command is not `clang-format` 86 | or you want to specify a command by absolute path, set this variable. 87 | Default value is `clang-format`. 88 | 89 | - `g:clang_format#extra_args` 90 | 91 | You can specify more extra options in `g:clang_format#extra_args` as String or List of String. 92 | 93 | - `g:clang_format#detect_style_file` 94 | 95 | When this variable's value is `1`, vim-clang-format automatically detects the style file like 96 | `.clang-format` or `_clang-format` and applies the style to formatting. 97 | 98 | - `g:clang_format#auto_format` 99 | 100 | When the value is 1, a current buffer is automatically formatted on saving the buffer. 101 | Formatting is executed on `BufWritePre` event. 102 | 103 | - `g:clang_format#auto_format_on_insert_leave` 104 | 105 | When the value is 1, inserted lines are automatically formatted on leaving insert mode. 106 | Formatting is executed on `InsertLeave` event. 107 | 108 | - `g:clang_format#auto_formatexpr` 109 | 110 | When the value is 1, `formatexpr` option is set by vim-clang-format automatically in C, C++ and ObjC codes. 111 | Vim's format mappings (e.g. `gq`) get to use `clang-format` to format. This 112 | option is not comptabile with Vim's `textwidth` feature. You must set 113 | `textwidth` to `0` when the `formatexpr` is set. 114 | 115 | - `g:clang_format#auto_filetypes` 116 | 117 | List of file types to which `g:clang_format#auto_format`, `g:clang_format#auto_format_on_insert_leave`, 118 | and `g:clang_format#auto_formatexpr` should be applied. 119 | The default value is `["c", "cpp", "objc", "java", "javascript", "typescript", "proto", "arduino"]`. 120 | 121 | - `g:clang_format#enable_fallback_style` 122 | 123 | When the value is 0, `-fallback-style=none` option is added on executing clang-format command. 124 | It means that vim-clang-format does nothing when `.clang-format` is not found. 125 | The default value is 1. 126 | 127 | ## Vimrc Example 128 | 129 | ```vim 130 | let g:clang_format#style_options = { 131 | \ "AccessModifierOffset" : -4, 132 | \ "AllowShortIfStatementsOnASingleLine" : "true", 133 | \ "AlwaysBreakTemplateDeclarations" : "true", 134 | \ "Standard" : "C++11"} 135 | 136 | " map to cf in C++ code 137 | autocmd FileType c,cpp,objc nnoremap cf :ClangFormat 138 | autocmd FileType c,cpp,objc vnoremap cf :ClangFormat 139 | " if you install vim-operator-user 140 | autocmd FileType c,cpp,objc map x (operator-clang-format) 141 | " Toggle auto formatting: 142 | nmap C :ClangFormatAutoToggle 143 | ``` 144 | 145 | ### Auto-enabling auto-formatting 146 | 147 | ```vim 148 | autocmd FileType c ClangFormatAutoEnable 149 | ``` 150 | 151 | ## For More Information 152 | 153 | ``` 154 | $ clang-format -help 155 | ``` 156 | 157 | ``` 158 | $ clang-format -dump-config 159 | ``` 160 | 161 | clang-format's documentation and API documentation is useful in some cases. 162 | In particular, the following link is useful to know the information of a key and its value of a style setting. 163 | [CLANG-FORMAT STYLE OPTIONS](http://clang.llvm.org/docs/ClangFormatStyleOptions.html) 164 | 165 | ## License 166 | 167 | The MIT License (MIT) 168 | 169 | Copyright (c) 2013-2021 rhysd 170 | 171 | Permission is hereby granted, free of charge, to any person obtaining a copy 172 | of this software and associated documentation files (the "Software"), to deal 173 | in the Software without restriction, including without limitation the rights 174 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 175 | copies of the Software, and to permit persons to whom the Software is 176 | furnished to do so, subject to the following conditions: 177 | 178 | The above copyright notice and this permission notice shall be included in 179 | all copies or substantial portions of the Software. 180 | 181 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 182 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 183 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 184 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 185 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 186 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 187 | THE SOFTWARE. 188 | -------------------------------------------------------------------------------- /autoload/clang_format.vim: -------------------------------------------------------------------------------- 1 | let s:save_cpo = &cpo 2 | set cpo&vim 3 | 4 | let s:on_windows = has('win32') || has('win64') 5 | let s:dict_t = type({}) 6 | let s:list_t = type([]) 7 | if exists('v:true') 8 | let s:bool_t = type(v:true) 9 | else 10 | let s:bool_t = -1 11 | endif 12 | 13 | " helper functions {{{ 14 | function! s:has_vimproc() abort 15 | if !exists('s:exists_vimproc') 16 | try 17 | silent call vimproc#version() 18 | let s:exists_vimproc = 1 19 | catch 20 | let s:exists_vimproc = 0 21 | endtry 22 | endif 23 | return s:exists_vimproc 24 | endfunction 25 | 26 | function! s:system(str, ...) abort 27 | let command = a:str 28 | let input = a:0 >= 1 ? a:1 : '' 29 | 30 | if a:0 == 0 || a:1 ==# '' 31 | silent let output = s:has_vimproc() ? 32 | \ vimproc#system(command) : system(command) 33 | elseif a:0 == 1 34 | silent let output = s:has_vimproc() ? 35 | \ vimproc#system(command, input) : system(command, input) 36 | else 37 | " ignores 3rd argument unless you have vimproc. 38 | silent let output = s:has_vimproc() ? 39 | \ vimproc#system(command, input, a:2) : system(command, input) 40 | endif 41 | 42 | return output 43 | endfunction 44 | 45 | function! s:create_keyvals(key, val) abort 46 | if type(a:val) == s:dict_t 47 | return a:key . ': {' . s:stringize_options(a:val) . '}' 48 | elseif type(a:val) == s:bool_t 49 | return a:key . (a:val == v:true ? ': true' : ': false') 50 | elseif type(a:val) == s:list_t 51 | return a:key . ': [' . join(a:val,',') . ']' 52 | else 53 | return a:key . ': ''' . escape(a:val, '''') . '''' 54 | endif 55 | endfunction 56 | 57 | function! s:stringize_options(opts) abort 58 | let keyvals = map(items(a:opts), 's:create_keyvals(v:val[0], v:val[1])') 59 | return join(keyvals, ',') 60 | endfunction 61 | 62 | function! s:build_extra_options() abort 63 | let opts = copy(g:clang_format#style_options) 64 | if has_key(g:clang_format#filetype_style_options, &ft) 65 | call extend(opts, g:clang_format#filetype_style_options[&ft]) 66 | endif 67 | 68 | let extra_options = s:stringize_options(opts) 69 | if !empty(extra_options) 70 | let extra_options = ', ' . extra_options 71 | endif 72 | 73 | return extra_options 74 | endfunction 75 | 76 | function! s:make_style_options() abort 77 | let extra_options = s:build_extra_options() 78 | return printf("{BasedOnStyle: %s, IndentWidth: %d, UseTab: %s%s}", 79 | \ g:clang_format#code_style, 80 | \ (exists('*shiftwidth') ? shiftwidth() : &l:shiftwidth), 81 | \ &l:expandtab==1 ? 'false' : 'true', 82 | \ extra_options) 83 | endfunction 84 | 85 | function! s:success(result) abort 86 | let exit_success = (s:has_vimproc() ? vimproc#get_last_status() : v:shell_error) == 0 87 | return exit_success && a:result !~# '^YAML:\d\+:\d\+: error: unknown key ' 88 | endfunction 89 | 90 | function! s:error_message(result) abort 91 | echoerr 'clang-format has failed to format.' 92 | if a:result =~# '^YAML:\d\+:\d\+: error: unknown key ' 93 | echohl ErrorMsg 94 | for l in split(a:result, "\n")[0:1] 95 | echomsg l 96 | endfor 97 | echohl None 98 | endif 99 | endfunction 100 | 101 | function! clang_format#get_version() abort 102 | if &shell =~# 'csh$' && executable('/bin/bash') 103 | let shell_save = &shell 104 | set shell=/bin/bash 105 | endif 106 | try 107 | let version_output = s:system(s:shellescape(g:clang_format#command).' --version 2>&1') 108 | if stridx(version_output, 'NPM') != -1 109 | " Note: 110 | " When clang-format is installed with npm, version string is changed (#39). 111 | return matchlist(version_output, 'NPM version \d\+\.\d\+\.\(\d\)\(\d\+\)')[1:2] 112 | else 113 | return matchlist(version_output, '\(\d\+\)\.\(\d\+\)')[1:2] 114 | endif 115 | finally 116 | if exists('l:shell_save') 117 | let &shell = shell_save 118 | endif 119 | endtry 120 | endfunction 121 | 122 | function! clang_format#is_invalid() abort 123 | if !exists('s:command_available') 124 | if !executable(g:clang_format#command) 125 | return 1 126 | endif 127 | let s:command_available = 1 128 | endif 129 | 130 | if !exists('s:version') 131 | let v = clang_format#get_version() 132 | if len(v) < 2 133 | " XXX: Give up checking version 134 | return 0 135 | endif 136 | if v[0] < 3 || (v[0] == 3 && v[1] < 4) 137 | return 2 138 | endif 139 | let s:version = v 140 | endif 141 | 142 | if g:clang_format#auto_format_git_diff && 143 | \ !exists('s:git_available') 144 | if !executable(g:clang_format#git) 145 | return 1 146 | endif 147 | let s:git_available = 1 148 | endif 149 | 150 | return 0 151 | endfunction 152 | 153 | function! s:verify_command() abort 154 | let invalidity = clang_format#is_invalid() 155 | if invalidity == 1 156 | echoerr "clang-format is not found. check g:clang_format#command." 157 | elseif invalidity == 2 158 | echoerr 'clang-format 3.3 or earlier is not supported for the lack of aruguments' 159 | endif 160 | endfunction 161 | 162 | function! s:shellescape(str) abort 163 | if s:on_windows && (&shell =~? 'cmd\.exe') 164 | " shellescape() surrounds input with single quote when 'shellslash' is on. But cmd.exe 165 | " requires double quotes. Temporarily set it to 0. 166 | let shellslash = &shellslash 167 | set noshellslash 168 | try 169 | return shellescape(a:str) 170 | finally 171 | let &shellslash = shellslash 172 | endtry 173 | endif 174 | return shellescape(a:str) 175 | endfunction 176 | 177 | " }}} 178 | 179 | " variable definitions {{{ 180 | function! s:getg(name, default) abort 181 | " backward compatibility 182 | if exists('g:operator_'.substitute(a:name, '#', '_', '')) 183 | echoerr 'g:operator_'.substitute(a:name, '#', '_', '').' is deprecated. Please use g:'.a:name 184 | return g:operator_{substitute(a:name, '#', '_', '')} 185 | else 186 | return get(g:, a:name, a:default) 187 | endif 188 | endfunction 189 | 190 | let g:clang_format#command = s:getg('clang_format#command', 'clang-format') 191 | let g:clang_format#extra_args = s:getg('clang_format#extra_args', "") 192 | if type(g:clang_format#extra_args) == type([]) 193 | let g:clang_format#extra_args = join(g:clang_format#extra_args, " ") 194 | endif 195 | let g:clang_format#git = s:getg('clang_format#git', 'git') 196 | 197 | let g:clang_format#code_style = s:getg('clang_format#code_style', 'google') 198 | let g:clang_format#style_options = s:getg('clang_format#style_options', {}) 199 | let g:clang_format#filetype_style_options = s:getg('clang_format#filetype_style_options', {}) 200 | 201 | let g:clang_format#detect_style_file = s:getg('clang_format#detect_style_file', 1) 202 | let g:clang_format#enable_fallback_style = s:getg('clang_format#enable_fallback_style', 1) 203 | 204 | let g:clang_format#auto_format = s:getg('clang_format#auto_format', 0) 205 | let g:clang_format#auto_format_git_diff = s:getg('clang_format#auto_format_git_diff', 0) 206 | let g:clang_format#auto_format_git_diff_fallback = s:getg('clang_format#auto_format_git_diff_fallback', 'file') 207 | let g:clang_format#auto_format_on_insert_leave = s:getg('clang_format#auto_format_on_insert_leave', 0) 208 | let g:clang_format#auto_formatexpr = s:getg('clang_format#auto_formatexpr', 0) 209 | let g:clang_format#auto_filetypes = s:getg( 'clang_format#auto_filetypes', 210 | \ [ 'c', 'cpp', 'objc', 'java', 'javascript', 'typescript', 211 | \ 'proto', 'arduino', 'cuda', 'vala' ] ) 212 | " }}} 213 | 214 | " format codes {{{ 215 | function! s:detect_style_file() abort 216 | let dirname = fnameescape(expand('%:p:h')) 217 | return findfile('.clang-format', dirname.';') != '' || findfile('_clang-format', dirname.';') != '' 218 | endfunction 219 | 220 | " clang_format#format_ranges is were the magic happends. 221 | " ranges is a list of pairs, like [[start1,end1],[start2,end2]...] 222 | function! clang_format#format_ranges(ranges) abort 223 | let args = '' 224 | for range in a:ranges 225 | let args .= printf(' -lines=%d:%d', range[0], range[1]) 226 | endfor 227 | if ! (g:clang_format#detect_style_file && s:detect_style_file()) 228 | if g:clang_format#enable_fallback_style 229 | let args .= ' ' . s:shellescape(printf('-style=%s', s:make_style_options())) . ' ' 230 | else 231 | let args .= ' -fallback-style=none ' 232 | endif 233 | else 234 | let args .= ' -style=file ' 235 | endif 236 | let filename = expand('%') 237 | if filename !=# '' 238 | let args .= printf('-assume-filename=%s ', s:shellescape(escape(filename, " \t"))) 239 | endif 240 | let args .= g:clang_format#extra_args 241 | let clang_format = printf('%s %s --', s:shellescape(g:clang_format#command), args) 242 | let source = join(getline(1, '$'), "\n") 243 | return s:system(clang_format, source) 244 | endfunction 245 | 246 | function! clang_format#format(line1, line2) abort 247 | return clang_format#format_ranges([[a:line1, a:line2]]) 248 | endfunction 249 | " }}} 250 | 251 | " replace buffer {{{ 252 | function! clang_format#replace_ranges(ranges, ...) abort 253 | call s:verify_command() 254 | 255 | let pos_save = a:0 >= 1 ? a:1 : getpos('.') 256 | let formatted = clang_format#format_ranges(a:ranges) 257 | if !s:success(formatted) 258 | call s:error_message(formatted) 259 | return 260 | endif 261 | 262 | let winview = winsaveview() 263 | let splitted = split(formatted, '\n', 1) 264 | 265 | silent! undojoin 266 | if line('$') > len(splitted) 267 | execute len(splitted) .',$delete' '_' 268 | endif 269 | call setline(1, splitted) 270 | call winrestview(winview) 271 | call setpos('.', pos_save) 272 | endfunction 273 | 274 | function! clang_format#replace(line1, line2, ...) abort 275 | call call(function("clang_format#replace_ranges"), [[[a:line1, a:line2]], a:000]) 276 | endfunction 277 | " }}} 278 | 279 | " auto formatting on insert leave {{{ 280 | let s:pos_on_insertenter = [] 281 | 282 | function! s:format_inserted_area() abort 283 | let pos = getpos('.') 284 | " When in the same buffer 285 | if &modified && ! empty(s:pos_on_insertenter) && s:pos_on_insertenter[0] == pos[0] 286 | call clang_format#replace(s:pos_on_insertenter[1], line('.')) 287 | let s:pos_on_insertenter = [] 288 | endif 289 | endfunction 290 | 291 | function! clang_format#enable_format_on_insert() abort 292 | augroup plugin-clang-format-auto-format-insert 293 | autocmd! * 294 | autocmd InsertEnter let s:pos_on_insertenter = getpos('.') 295 | autocmd InsertLeave call s:format_inserted_area() 296 | augroup END 297 | endfunction 298 | " }}} 299 | 300 | " toggle auto formatting {{{ 301 | function! clang_format#toggle_auto_format() abort 302 | let g:clang_format#auto_format = !g:clang_format#auto_format 303 | if g:clang_format#auto_format 304 | echo 'Auto clang-format: enabled' 305 | else 306 | echo 'Auto clang-format: disabled' 307 | endif 308 | endfunction 309 | " }}} 310 | 311 | " enable auto formatting {{{ 312 | function! clang_format#enable_auto_format() abort 313 | let g:clang_format#auto_format = 1 314 | endfunction 315 | " }}} 316 | 317 | " disable auto formatting {{{ 318 | function! clang_format#disable_auto_format() abort 319 | let g:clang_format#auto_format = 0 320 | endfunction 321 | 322 | " s:strip: helper function to strip a string 323 | function! s:strip(string) 324 | return substitute(a:string, '^\s*\(.\{-}\)\s*\r\=\n\=$', '\1', '') 325 | endfunction 326 | 327 | " clang_format#get_git_diff 328 | " a:file must be an absolute path to the file to be processed 329 | " this function compares the current buffer content against the 330 | " git index content of the file. 331 | " this function returns a list of pair of ranges if the file is tracked 332 | " and has changes, an empty list otherwise 333 | function! clang_format#get_git_diff(cur_file) 334 | let file_path = isdirectory(a:cur_file) ? a:cur_file : 335 | \ fnamemodify(a:cur_file, ":h") 336 | let top_dir=s:strip(system( 337 | \ g:clang_format#git." -C ".shellescape(file_path). 338 | \ " rev-parse --show-toplevel")) 339 | if v:shell_error != 0 340 | return [] 341 | endif 342 | let cur_file = s:strip(s:system( 343 | \ g:clang_format#git." -C ".shellescape(top_dir). 344 | \ " ls-files --error-unmatch ".shellescape(a:cur_file))) 345 | if v:shell_error != 0 346 | return [] 347 | endif 348 | let source = join(getline(1, '$'), "\n") 349 | " git show :file shows the staged content of the file: 350 | " - content in index if any (staged but not commmited) 351 | " - else content in HEAD 352 | " this solution also solves the problem for 'git mv'ed file: 353 | " - if the current buffer has been renamed by simple mv (without git 354 | " add), the file is considered as untracked 355 | " - if the renamed file has been git added or git mv, git show :file 356 | " will show the expected content. 357 | " this barbarian command does the following: 358 | " - diff --*-group-* options will return ranges (start,end) for each 359 | " diff chunk 360 | " - <(git show :file) is a process substitution, using /dev/fd/ as 361 | " temporary file for the output 362 | " - - is stdin, which is current buffer content in variable 'source' 363 | let diff_cmd = 364 | \ 'diff <('.g:clang_format#git.' show :'.shellescape(cur_file).') - '. 365 | \ '--old-group-format="" --unchanged-group-format="" '. 366 | \ '--new-group-format="%dF-%dL%c''\\012''" '. 367 | \ '--changed-group-format="%dF-%dL%c''\\012''"' 368 | let ranges = s:system(diff_cmd, source) 369 | if !(v:shell_error == 0 || v:shell_error == 1) 370 | throw printf("clang-format: git diff failed `%s` for ranges %s", 371 | \ diff_cmd, ranges) 372 | endif 373 | let ranges = split(ranges, '\n') 374 | " ranges is now a list of pairs [[start1, end1],[start2,end2]...] 375 | let ranges = map(ranges, "split(v:val, '-')") 376 | return ranges 377 | endfunction 378 | 379 | " this function will try to format only buffer lines diffing from git index 380 | " content. 381 | " If the file is untracked (not in a git repo or not tracked in a git repo), 382 | " it returns 1. 383 | " If the format succeeds, it returns 0. 384 | function! clang_format#do_auto_format_git_diff() 385 | let cur_file = expand("%:p") 386 | let ranges = clang_format#get_git_diff(cur_file) 387 | if !empty(ranges) 388 | call clang_format#replace_ranges(ranges) 389 | return 0 390 | else 391 | return 1 392 | endif 393 | endfunction 394 | 395 | function! clang_format#do_auto_format() 396 | if g:clang_format#auto_format_git_diff 397 | let ret = clang_format#do_auto_format_git_diff() 398 | if ret == 0 || 399 | \ g:clang_format#auto_format_git_diff_fallback != 'file' 400 | return 401 | endif 402 | endif 403 | call clang_format#replace_ranges([[1, line('$')]]) 404 | endfunction 405 | 406 | " }}} 407 | let &cpo = s:save_cpo 408 | unlet s:save_cpo 409 | -------------------------------------------------------------------------------- /autoload/operator/clang_format.vim: -------------------------------------------------------------------------------- 1 | function! s:is_empty_region(begin, end) abort 2 | return a:begin[1] > a:end[1] || (a:begin[1] == a:end[1] && a:end[2] < a:begin[2]) 3 | endfunction 4 | 5 | function! operator#clang_format#do(motion_wise) abort 6 | if s:is_empty_region(getpos("'["), getpos("']")) 7 | return 8 | endif 9 | 10 | " Do not move cursor and screen 11 | if exists('g:operator#clang_format#save_pos') && exists('g:operator#clang_format#save_screen_pos') 12 | call clang_format#replace(getpos("'[")[1], getpos("']")[1], g:operator#clang_format#save_pos, g:operator#clang_format#save_screen_pos) 13 | unlet g:operator#clang_format#save_pos 14 | unlet g:operator#clang_format#save_screen_pos 15 | else 16 | call clang_format#replace(getpos("'[")[1], getpos("']")[1]) 17 | endif 18 | endfunction 19 | -------------------------------------------------------------------------------- /doc/clang-format.txt: -------------------------------------------------------------------------------- 1 | *clang-format.txt* Format C, C++, Objective-C using clang-format 2 | 3 | Author : rhysd 4 | 5 | CONTENTS *vim-clang-format-contents* 6 | 7 | Introduction |vim-clang-format-introduction| 8 | Usage |vim-clang-format-usage| 9 | Install |vim-clang-format-install| 10 | Dependency |vim-clang-format-dependency| 11 | Commands |vim-clang-format-commands| 12 | Mappings |vim-clang-format-mappings| 13 | Functions |vim-clang-format-functions| 14 | Variables |vim-clang-format-variables| 15 | Setup Example |vim-clang-format-setup-example| 16 | Repository Page |vim-clang-format-repository-page| 17 | License |vim-clang-format-license| 18 | 19 | ============================================================================== 20 | INTRODUCTION *vim-clang-format-introduction* 21 | 22 | *vim-clang-format* formats your C, C++ and Objective-C code with specific coding 23 | style using clang. Automatic formatting is supported for the following file 24 | types by default. 25 | 26 | * c 27 | * cpp 28 | * objc 29 | * javascript 30 | * java 31 | * typescript 32 | * proto 33 | * arduino 34 | 35 | Screenshot: 36 | http://gifzo.net/BIteGJ9Vasg.gif 37 | 38 | 39 | 40 | ============================================================================== 41 | USAGE *vim-clang-format-usage* 42 | 43 | |:ClangFormat| command is available. If you use it in normal mode, the whole 44 | code will be formatted. If you use it in visual mode, the selected code will 45 | be formatted. It is more convenient to map |:ClangFormat| to your favorite key 46 | mapping in normal mode and visual mode. 47 | |'shiftwidth'| and |'expandtab'| are used for formatting. 48 | 49 | In addition, |(operator-clang-format)| is an operator mapping to format 50 | the |text-objects|. It is also very useful for expert users. 51 | 52 | 53 | 54 | ============================================================================== 55 | DEPENDENCY *vim-clang-format-dependency* 56 | 57 | |clang-format| (3.4 or later is required) 58 | http://clang.llvm.org/docs/ClangFormat.html 59 | 60 | Note: 61 | clang-format 3.4 in Ubuntu 13.04 seems to be old. Please use LLVM's 62 | official apt repository. 63 | http://llvm.org/apt/ 64 | 65 | |vim-operator-user| (highly recommended) 66 | https://github.com/kana/vim-operator-user 67 | 68 | |vimproc| (recommended in Windows) 69 | https://github.com/Shougo/vimproc.vim 70 | 71 | 72 | 73 | ============================================================================== 74 | INSTALL *vim-clang-format-install* 75 | 76 | Using Vim plugin package manager is recommended. I use |neobundle| and |vundle| 77 | seems the most famous. 78 | If you want to install manually, it is not recommended, copy files and 79 | directories in autoload, doc and plugin directories to your vim config 80 | directory. Vim config directory is usually $HOME/vimfiles on Windows or 81 | ~/.vim in other operating systems. 82 | 83 | 84 | 85 | ============================================================================== 86 | COMMANDS *vim-clang-format-commands* 87 | 88 | :[range]ClangFormat *:ClangFormat* 89 | Format a entire current buffer with clang-format. If [range] is 90 | given, |:ClangFormat| formats the range. In visual mode, |:ClangFormat| 91 | formats the selected region with linewise way. 92 | 93 | :[range]ClangFormatEchoFormattedCode *:ClangFormatEchoFormattedCode* 94 | Echo version of |:Clangformat|. Instead of editing buffer directly, echo 95 | the formatted text. 96 | 97 | 98 | 99 | ============================================================================== 100 | MAPPINGS *vim-clang-format-mappings* 101 | 102 | (operator-clang-format) *(operator-clang-format)* 103 | 104 | Operator mapping to format source code with clang-format. To use this 105 | mapping, |operator-user| is required. 106 | 107 | 108 | 109 | ============================================================================== 110 | FUNCTIONS *vim-clang-format-functions* 111 | 112 | clang_format#replace({start_line}, {last_line}) *clang_format#replace()* 113 | 114 | This is function version of |:ClangFormat|. {start_line} and {last_line} 115 | are required. 116 | 117 | clang_format#format({start_line}, {last_line}) *clang_format#format()* 118 | 119 | This function returns a formatted code as |String|. 120 | 121 | 122 | 123 | ============================================================================== 124 | VARIABLES *vim-clang-format-variables* 125 | 126 | g:clang_format#command *g:clang_format#command* 127 | 128 | Name of clang-format command. 129 | The default value is "clang-format". 130 | 131 | g:clang_format#git *g:clang_format#git* 132 | 133 | Name of the git command. 134 | The default value is "git". 135 | 136 | g:clang_format#code_style *g:clang_format#code_style* 137 | 138 | Base coding style for formatting. Available coding styles are "llvm", 139 | "google", "chromium" and "mozilla". 140 | The default value is "google". 141 | 142 | g:clang_format#style_options *g:clang_format#style_options* 143 | 144 | Coding style options to specify the rule of format in more detail. For 145 | example, using {} in short if statement or not, use C++11 feature or not, 146 | access modifier offset and so on. This value is a |Dictionary| whose key 147 | and value are |String| or |Number|. 148 | The key and the value are options passed to clang-format's -stryle option. 149 | You can look up them by executing `clang-format -dump-config` command. If 150 | you want to know all of the keys and values, clang-format's documentation 151 | and API documentation is useful. In paticular, the following link is 152 | useful to know. 153 | 154 | CLANG-FORMAT STYLE OPTIONS 155 | http://clang.llvm.org/docs/ClangFormatStyleOptions.html 156 | 157 | g:clang_format#filetype_style_options 158 | *g:clang_format#filetype_style_options* 159 | 160 | If you want to use language-specific style options, you can use this 161 | variable. This variable is a |Dictionary| and its key is |filetype|. You 162 | can specify the filetype specific style options as a value of the key. 163 | The format to specify options is the same as |g:clang_format#style_options|. 164 | 165 | For example, if you want to set "C++11" to "Standard", you can specify it 166 | only when the filetype is "cpp" as below: 167 | > 168 | " Your common style options 169 | let g:clang_format#style_options = { 170 | \ 'AllowShortIfStatementsOnASingleLine' : 'true', 171 | \ } 172 | 173 | " Your filetype specific options 174 | let g:clang_format#filetype_style_options = { 175 | \ 'cpp' : {"Standard" : "C++11"}, 176 | \ } 177 | < 178 | g:clang_format#extra_args *g:clang_format#extra_args* 179 | 180 | Extra arguments for clang-format. This |String| value is passed to 181 | clang-format directly. 182 | 183 | g:clang_format#detect_style_file *g:clang_format#detect_style_file* 184 | 185 | When the value is 1, |vim-clang-format| automatically detects the style file 186 | like .clang-format or _clang-format and applies the style to formatting. 187 | The default value is 1. 188 | 189 | g:clang_format#auto_format *g:clang_format#auto_format* 190 | 191 | When the value is 1, |vim-clang-format| automatically formats a current 192 | buffer on saving the buffer. Formatting is executed at |BufWritePre| event. 193 | The default value is 0. 194 | 195 | g:clang_format#auto_format_git_diff *g:clang_format#auto_format_git_diff* 196 | 197 | When this value is 1, and when g:clang_format#auto_format is 1, the auto 198 | format only formats modified lines is the file is tracked in git. 199 | If the file is not tracked, or even not in a git project, fallback 200 | behavior depends on |g:clang_format#auto_format_git_diff_fallback|. 201 | WARNING: this option should not be used with 202 | |g:clang_format#auto_format_on_insert_leave|. 203 | The default value is 0. 204 | 205 | g:clang_format#auto_format_git_diff_fallback 206 | *g:clang_format#auto_format_git_diff_fallback* 207 | 208 | Fallback behavior when |g:clang_format#auto_format_git_diff| is 1 and the 209 | current file is not tracked. The value can be: 210 | - 'file': the whole file is formatted (which is the default 211 | |g:clang_format#auto_format| behavior). 212 | - 'pass': the file is not formatted. 213 | 214 | g:clang_format#auto_format_on_insert_leave 215 | *g:clang_format#auto_format_on_insert_leave* 216 | 217 | When the value is 1, the inserted lines are automatically formatted on 218 | leaving insert mode. Formatting is executed on |InsertLeave| event. 219 | 220 | g:clang_format#auto_formatexpr *g:clang_format#auto_formatexpr* 221 | 222 | When the value is 1, 'formatexpr' option is set automatically in |c|, |cpp| 223 | and |objc| codes. Vim's format mappings (e.g. |gq|) get to use |clang-format| 224 | to format. This option is not comptabile with Vim's `textwidth` feature. You 225 | must set `textwidth` to `0` when the `formatexpr` is set. 226 | 227 | g:clang_format#auto_filetypes *g:clang_format#auto_filetypes* 228 | 229 | List of file types to which |g:clang_format#auto_format|, 230 | |g:clang_format#auto_format_on_insert_leave|, and 231 | |g:clang_format#auto_formatexpr| should be applied. 232 | 233 | The default value is [ "c", "cpp", "objc", "java", 234 | \ "javascript", "typescript", "proto", "arduino" ]. 235 | 236 | g:clang_format#enable_fallback_style *g:clang_format#enable_fallback_style* 237 | 238 | When the value is 0, "-fallback-style=none" option is added on executing 239 | clang-format command. It means that vim-clang-format does nothing when 240 | ".clang-format" is not found. 241 | The default value is 1. 242 | 243 | 244 | 245 | ============================================================================== 246 | SETUP EXAMPLE *vim-clang-format-setup-example* 247 | 248 | Below is an example for .vimrc. 249 | > 250 | " your favorite style options 251 | let g:clang_format#style_options = { 252 | \ "AccessModifierOffset" : -4, 253 | \ "AllowShortIfStatementsOnASingleLine" : "true", 254 | \ "AlwaysBreakTemplateDeclarations" : "true", 255 | \ "Standard" : "C++11"} 256 | 257 | augroup ClangFormatSettings 258 | autocmd! 259 | " map to cf in C++ code 260 | autocmd FileType c,cpp,objc nnoremap cf :ClangFormat 261 | autocmd FileType c,cpp,objc vnoremap cf :ClangFormat 262 | " if you install vim-operator-user 263 | autocmd FileType c,cpp,objc map x (operator-clang-format) 264 | augroup END 265 | < 266 | 267 | 268 | ============================================================================== 269 | REPOSITORY PAGE *vim-clang-format-repository-page* 270 | 271 | The latest version of |vim-clang-format| is available at 272 | https://github.com/rhysd/vim-clang-format 273 | 274 | Contributions (pull requests) are welcome. None of them is too short. 275 | Especially, English check is very helpful because I'm poor at English :( 276 | 277 | 278 | 279 | ============================================================================== 280 | LICENSE *vim-clang-format-license* 281 | 282 | |vim-clang-format| is distributed under MIT license. 283 | 284 | Copyright (c) 2013-2021 rhysd 285 | 286 | Permission is hereby granted, free of charge, to any person obtaining 287 | a copy of this software and associated documentation files (the 288 | "Software"), to deal in the Software without restriction, including 289 | without limitation the rights to use, copy, modify, merge, publish, 290 | distribute, sublicense, and/or sell copies of the Software, and to 291 | permit persons to whom the Software is furnished to do so, subject to 292 | the following conditions: 293 | The above copyright notice and this permission notice shall be 294 | included in all copies or substantial portions of the Software. 295 | 296 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 297 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 298 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 299 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 300 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 301 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 302 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 303 | 304 | 305 | 306 | ============================================================================== 307 | vim:tw=78:colorcolumn=78:ts=8:ft=help:norl:noet:fen:fdl=0: 308 | -------------------------------------------------------------------------------- /plugin/clang_format.vim: -------------------------------------------------------------------------------- 1 | if exists('g:loaded_clang_format') 2 | finish 3 | endif 4 | 5 | try 6 | call operator#user#define( 7 | \ 'clang-format', 8 | \ 'operator#clang_format#do', 9 | \ 'let g:operator#clang_format#save_pos = getpos(".") \| let g:operator#clang_format#save_screen_pos = line("w0")' 10 | \ ) 11 | catch /^Vim\%((\a\+)\)\=:E117/ 12 | " vim-operator-user is not installed 13 | endtry 14 | 15 | command! -range=% -nargs=0 ClangFormat call clang_format#replace(, ) 16 | 17 | command! -range=% -nargs=0 ClangFormatEchoFormattedCode echo clang_format#format(, ) 18 | 19 | augroup plugin-clang-format-auto-format 20 | autocmd! 21 | autocmd BufWritePre * 22 | \ if index(g:clang_format#auto_filetypes, &ft) >= 0 && 23 | \ g:clang_format#auto_format && 24 | \ !clang_format#is_invalid() | 25 | \ call clang_format#do_auto_format() | 26 | \ endif 27 | autocmd FileType * 28 | \ if index(g:clang_format#auto_filetypes, &ft) >= 0 && 29 | \ g:clang_format#auto_format_on_insert_leave && 30 | \ !clang_format#is_invalid() | 31 | \ call clang_format#enable_format_on_insert() | 32 | \ endif 33 | autocmd FileType * 34 | \ if index(g:clang_format#auto_filetypes, &ft) >= 0 && 35 | \ g:clang_format#auto_formatexpr && 36 | \ !clang_format#is_invalid() | 37 | \ setlocal formatexpr=clang_format#replace(v:lnum,v:lnum+v:count-1) | 38 | \ endif 39 | augroup END 40 | 41 | command! ClangFormatAutoToggle call clang_format#toggle_auto_format() 42 | command! ClangFormatAutoEnable call clang_format#enable_auto_format() 43 | command! ClangFormatAutoDisable call clang_format#disable_auto_format() 44 | 45 | let g:loaded_clang_format = 1 46 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | .vim-flavor 2 | .bundle 3 | -------------------------------------------------------------------------------- /test/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'vim-flavor', '~> 1.1' 4 | -------------------------------------------------------------------------------- /test/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | parslet (1.8.2) 5 | thor (0.20.3) 6 | vim-flavor (1.1.5) 7 | parslet (~> 1.0) 8 | thor (~> 0.14) 9 | 10 | PLATFORMS 11 | x86_64-darwin-19 12 | 13 | DEPENDENCIES 14 | vim-flavor (~> 1.1) 15 | 16 | BUNDLED WITH 17 | 2.2.22 18 | -------------------------------------------------------------------------------- /test/Guardfile: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | def which cmd 4 | dir = ENV['PATH'].split(':').find {|p| File.executable? File.join(p, cmd)} 5 | File.join(dir, cmd) unless dir.nil? 6 | end 7 | 8 | def notify m 9 | msg = "'#{m}\\n#{Time.now.to_s}'" 10 | case 11 | when which('terminal-notifier') 12 | `terminal-notifier -message #{msg}` 13 | when which('notify-send') 14 | `notify-send #{msg}` 15 | when which('tmux') 16 | `tmux display-message #{msg}` if `tmux list-clients 1>/dev/null 2>&1` && $?.success? 17 | end 18 | end 19 | 20 | guard :shell do 21 | watch /^(\.\.\/autoload|plugin|t)\/.+\.vim$/ do 22 | system "rake test" 23 | unless $? 24 | notify "test(s) failed" 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | How to run the tests 2 | ==================== 3 | 4 | ## Prerequisities 5 | 6 | - Ruby 1.9 or higher 7 | - Rake 8 | - Bundler 9 | 10 | ## How to run tests 11 | 12 | Following command does everything to run tests. 13 | 14 | ``` 15 | $ rake test 16 | ``` 17 | 18 | Or you can also run `vim-flavor` directly. 19 | 20 | ``` 21 | $ bundle install --path=.bundle 22 | $ bundle exec vim-flavor test 23 | ``` 24 | 25 | ## How to watch file changes and run tests automatically 26 | 27 | ``` 28 | $ rake watch 29 | ``` 30 | 31 | It requires guard gem. 32 | -------------------------------------------------------------------------------- /test/Rakefile: -------------------------------------------------------------------------------- 1 | task :test do 2 | sh 'bundle install --path=.bundle' 3 | sh 'bundle exec vim-flavor test' 4 | end 5 | 6 | task :watch do 7 | sh 'guard -w ..' 8 | end 9 | -------------------------------------------------------------------------------- /test/VimFlavor: -------------------------------------------------------------------------------- 1 | flavor 'rhysd/vim-vspec-matchers' 2 | flavor 'kana/vim-operator-user' 3 | -------------------------------------------------------------------------------- /test/VimFlavor.lock: -------------------------------------------------------------------------------- 1 | kana/vim-operator-user (0.1.0) 2 | kana/vim-vspec (1.6.1) 3 | rhysd/vim-vspec-matchers (0.0.4) 4 | -------------------------------------------------------------------------------- /test/t/clang-format-dummy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo 'Ubuntu clang-format version 3.2.0-1~exp1 (trunk) (based on LLVM 3.2.0)' 4 | -------------------------------------------------------------------------------- /test/t/clang_format_spec.vim: -------------------------------------------------------------------------------- 1 | " test with vim-vspec 2 | " https://github.com/kana/vim-vspec 3 | 4 | " helpers "{{{ 5 | " clang-format detection 6 | function! s:detect_clang_format() abort 7 | if $CLANG_FORMAT !=# '' && executable($CLANG_FORMAT) 8 | return $CLANG_FORMAT 9 | endif 10 | 11 | for suffix in ['-HEAD', '-5.0', '-4.0', '-3.8', '-3.7', '-3.6', '-3.5', '-3.4', ''] 12 | let c = 'clang-format' . suffix 13 | if executable(c) 14 | return c 15 | endif 16 | endfor 17 | throw 'not ok because detect clang-format could not be found in $PATH' 18 | endfunction 19 | let g:clang_format#command = s:detect_clang_format() 20 | 21 | function! Chomp(s) abort 22 | return a:s =~# '\n$' 23 | \ ? a:s[0:len(a:s)-2] 24 | \ : a:s 25 | endfunction 26 | 27 | function! ChompHead(s) abort 28 | return a:s =~# '^\n' 29 | \ ? a:s[1:len(a:s)-1] 30 | \ : a:s 31 | endfunction 32 | 33 | function! GetBuffer() abort 34 | return join(getline(1, '$'), "\n") 35 | endfunction 36 | 37 | function! ClangFormat(line1, line2, ...) abort 38 | let opt = printf("-lines=%d:%d '-style={BasedOnStyle: Google, IndentWidth: %d, UseTab: %s", a:line1, a:line2, &l:shiftwidth, &l:expandtab==1 ? "false" : "true") 39 | let file = 'test.cpp' 40 | 41 | for a in a:000 42 | if a =~# '^test\.\w\+$' 43 | let file = a 44 | else 45 | let opt .= a 46 | endif 47 | endfor 48 | let opt .= "}'" 49 | 50 | let cmd = printf('%s %s ./t/%s --', g:clang_format#command, opt, file) 51 | let result = Chomp(system(cmd)) 52 | if v:shell_error 53 | throw "Error on system(): clang-format exited with non-zero.\nCommand: " . cmd . "\nOutput: " . result 54 | endif 55 | return Chomp(system(cmd)) 56 | endfunction 57 | "}}} 58 | 59 | " setup {{{ 60 | let s:root_dir = resolve(getcwd() . '/..') 61 | execute 'set' 'rtp +=' . s:root_dir 62 | 63 | runtime! plugin/clang_format.vim 64 | 65 | call vspec#customize_matcher('to_be_empty', function('empty')) 66 | 67 | function! RaisesException(cmd) abort 68 | try 69 | execute a:cmd 70 | return 0 71 | catch 72 | return 1 73 | endtry 74 | endfunction 75 | 76 | call vspec#customize_matcher('to_throw_exception', function('RaisesException')) 77 | 78 | "}}} 79 | 80 | " test for default settings {{{ 81 | describe 'default settings' 82 | it 'provide a default mapping' 83 | Expect maparg('(operator-clang-format)') not to_be_empty 84 | end 85 | 86 | it 'provide autoload functions' 87 | " load autload script 88 | silent! call clang_format#get_version() 89 | silent! call operator#clang_format#do() 90 | Expect exists('*operator#clang_format#do') to_be_true 91 | Expect exists('*clang_format#format') to_be_true 92 | Expect exists('*clang_format#get_version') to_be_true 93 | end 94 | 95 | it 'provide variables to customize this plugin' 96 | Expect exists('g:clang_format#extra_args') to_be_true 97 | Expect exists('g:clang_format#code_style') to_be_true 98 | Expect exists('g:clang_format#style_options') to_be_true 99 | Expect exists('g:clang_format#filetype_style_options') to_be_true 100 | Expect exists('g:clang_format#command') to_be_true 101 | Expect exists('g:clang_format#detect_style_file') to_be_true 102 | Expect exists('g:clang_format#auto_format') to_be_true 103 | Expect exists('g:clang_format#auto_format_on_insert_leave') to_be_true 104 | Expect g:clang_format#extra_args to_be_empty 105 | Expect g:clang_format#code_style ==# 'google' 106 | Expect g:clang_format#style_options to_be_empty 107 | Expect g:clang_format#filetype_style_options to_be_empty 108 | Expect executable(g:clang_format#command) to_be_true 109 | Expect g:clang_format#detect_style_file to_be_true 110 | end 111 | 112 | it 'provide commands' 113 | Expect exists(':ClangFormat') to_be_true 114 | Expect exists(':ClangFormatEchoFormattedCode') to_be_true 115 | end 116 | end 117 | "}}} 118 | 119 | " test for clang_format#format() {{{ 120 | function! s:expect_the_same_output(line1, line2) abort 121 | Expect clang_format#format(a:line1, a:line2) ==# ClangFormat(a:line1, a:line2) 122 | endfunction 123 | 124 | describe 'clang_format#format()' 125 | 126 | before 127 | let g:clang_format#detect_style_file = 0 128 | new 129 | execute 'silent' 'edit!' './t/test.cpp' 130 | end 131 | 132 | after 133 | bdelete! 134 | end 135 | 136 | it 'formats whole t/test.cpp' 137 | call s:expect_the_same_output(1, line('$')) 138 | end 139 | 140 | it 'formats too long macro definitions' 141 | call s:expect_the_same_output(3, 3) 142 | end 143 | 144 | it 'formats one line functions' 145 | call s:expect_the_same_output(5, 5) 146 | end 147 | 148 | it 'formats initilizer list definition' 149 | call s:expect_the_same_output(9, 9) 150 | end 151 | 152 | it 'formats for statement' 153 | call s:expect_the_same_output(11, 13) 154 | end 155 | 156 | it 'formats too long string to multiple lines' 157 | call s:expect_the_same_output(17, 17) 158 | end 159 | 160 | it 'doesn''t move cursor' 161 | execute 'normal!' (1+line('$')).'gg' 162 | let pos = getpos('.') 163 | call s:expect_the_same_output(1, line('$')) 164 | Expect pos == getpos('.') 165 | end 166 | 167 | describe 'g:clang_format#style_options' 168 | before 169 | let g:clang_format#detect_style_file = 0 170 | new 171 | execute 'silent' 'edit!' './t/test.cpp' 172 | 173 | let s:saved_styles = [g:clang_format#style_options, &expandtab, &shiftwidth] 174 | set expandtab 175 | set shiftwidth=4 176 | end 177 | 178 | after 179 | close! 180 | let [g:clang_format#style_options, &expandtab, &shiftwidth] = s:saved_styles 181 | end 182 | 183 | it 'customizes code styles' 184 | let g:clang_format#style_options = {'UseTab' : 'false', 'IndentWidth' : 4} 185 | call s:expect_the_same_output(1, line('$')) 186 | end 187 | 188 | it 'can contain v:true/v:false' 189 | if exists('v:false') 190 | let g:clang_format#style_options = {'UseTab' : v:false, 'IndentWidth' : 4} 191 | call s:expect_the_same_output(1, line('$')) 192 | endif 193 | end 194 | end 195 | 196 | it 'ensures to fix issue #38' 197 | let saved = g:clang_format#style_options 198 | try 199 | let g:clang_format#style_options = { 200 | \ "BraceWrapping" : { 201 | \ "AfterControlStatement" : "true" , 202 | \ "AfterClass " : "true", 203 | \ }, 204 | \ } 205 | try 206 | call clang_format#format(1, line('$')) 207 | catch /^YAML:\d\+:\d\+/ 208 | " OK 209 | endtry 210 | finally 211 | let g:clang_format#style_options = saved 212 | endtry 213 | end 214 | end 215 | 216 | describe 'clang_format#replace()' 217 | before 218 | let g:clang_format#detect_style_file = 0 219 | new 220 | execute 'silent' 'edit!' './t/test.cpp' 221 | let s:cmd_tmp = g:clang_format#command 222 | end 223 | 224 | after 225 | bdelete! 226 | let g:clang_format#command = s:cmd_tmp 227 | end 228 | 229 | it 'throws an error when command is not found' 230 | let g:clang_format#command = "clang_format_not_exist" 231 | Expect "call clang_format#replace(1, line('$'))" to_throw_exception 232 | end 233 | 234 | it 'throws an error when command is not found' 235 | let g:clang_format#command = './t/clang-format-dummy.sh' 236 | Expect "call clang_format#replace(1, line('$'))" to_throw_exception 237 | end 238 | end 239 | " }}} 240 | 241 | " test for (operator-clang-format) {{{ 242 | describe '(operator-clang-format)' 243 | 244 | before 245 | let g:clang_format#detect_style_file = 0 246 | new 247 | execute 'silent' 'edit!' './t/test.cpp' 248 | map x (operator-clang-format) 249 | end 250 | 251 | after 252 | bdelete! 253 | end 254 | 255 | it 'formats in visual mode' 256 | let by_clang_format_command = ClangFormat(1, line('$')) 257 | normal ggVGx 258 | let buffer = GetBuffer() 259 | Expect by_clang_format_command ==# buffer 260 | end 261 | 262 | it 'formats a text object' 263 | " format for statement 264 | let by_clang_format_command = ClangFormat(11, 13) 265 | " move to for statement block 266 | execute 12 267 | " do format a text object {} 268 | normal xa{ 269 | let buffer = GetBuffer() 270 | Expect by_clang_format_command ==# buffer 271 | end 272 | end 273 | " }}} 274 | 275 | " test for :ClangFormat {{{ 276 | describe ':ClangFormat' 277 | 278 | describe 'with filetype cpp' 279 | 280 | before 281 | let g:clang_format#detect_style_file = 0 282 | new 283 | execute 'silent' 'edit!' './t/test.cpp' 284 | end 285 | 286 | after 287 | bdelete! 288 | end 289 | 290 | it 'formats the whole code in normal mode' 291 | let by_clang_format_command = ClangFormat(1, line('$')) 292 | ClangFormat 293 | let buffer = GetBuffer() 294 | Expect by_clang_format_command ==# buffer 295 | end 296 | 297 | it 'formats selected code in visual mode' 298 | " format for statement 299 | let by_clang_format_command = ClangFormat(11, 13) 300 | " move to for statement block 301 | execute 11 302 | normal! VjjV 303 | '<,'>ClangFormat 304 | let buffer = GetBuffer() 305 | Expect by_clang_format_command ==# buffer 306 | end 307 | 308 | it 'doesn''t move cursor' 309 | execute 'normal!' (1+line('$')).'gg' 310 | let pos = getpos('.') 311 | ClangFormat 312 | Expect pos == getpos('.') 313 | end 314 | 315 | it 'maintains screen position while formatting' 316 | execute 'normal!' "3\" 317 | let prev = line('w0') 318 | ClangFormat 319 | let current = line('w0') 320 | Expect prev ==# current 321 | end 322 | end 323 | 324 | describe 'with filetype javascript' 325 | 326 | before 327 | let g:clang_format#detect_style_file = 0 328 | new 329 | execute 'silent' 'edit!' './t/test.js' 330 | end 331 | 332 | after 333 | bdelete! 334 | end 335 | 336 | it 'formats the whole code in normal mode' 337 | let by_clang_format_command = ClangFormat(1, line('$'), 'test.js') 338 | ClangFormat 339 | let buffer = GetBuffer() 340 | Expect by_clang_format_command ==# buffer 341 | end 342 | 343 | end 344 | 345 | describe 'hard tab' 346 | 347 | before 348 | let g:clang_format#detect_style_file = 0 349 | let s:saved_expandtab = &expandtab 350 | let s:saved_shiftwidth = &shiftwidth 351 | set noexpandtab shiftwidth=8 352 | new 353 | execute 'silent' 'edit!' './t/test.cpp' 354 | end 355 | 356 | after 357 | let &expandtab = s:saved_expandtab 358 | let &shiftwidth = s:saved_shiftwidth 359 | end 360 | 361 | it 'does not break formatting' 362 | let by_clang_format_command = ClangFormat(1, line('$'), 'test.cpp') 363 | ClangFormat 364 | let buffer = GetBuffer() 365 | Expect by_clang_format_command ==# buffer 366 | end 367 | 368 | end 369 | end 370 | " }}} 371 | 372 | " test for auto formatting {{{ 373 | describe 'g:clang_format#auto_format' 374 | 375 | before 376 | let g:clang_format#auto_format = 1 377 | new 378 | execute 'silent' 'edit!' './t/test.cpp' 379 | end 380 | 381 | after 382 | bdelete! 383 | end 384 | 385 | it 'formats a current buffer on BufWritePre if the value is 1' 386 | SKIP 'because somehow BufWritePre event isn''t fired' 387 | let formatted = clang_format#format(1, line('$')) 388 | doautocmd BufWritePre 389 | let auto_formatted = join(getline(1, line('$')), "\n") 390 | Expect auto_formatted ==# formatted 391 | end 392 | end 393 | " }}} 394 | 395 | " test for auto formatting on insert leave {{{ 396 | describe 'g:clang_format#auto_format_on_insert_leave' 397 | 398 | before 399 | let g:clang_format#auto_format_on_insert_leave = 1 400 | new 401 | execute 'silent' 'edit!' './t/test.cpp' 402 | end 403 | 404 | after 405 | bdelete! 406 | end 407 | 408 | it 'formats a inserted area on InsertLeave if the value is 1' 409 | SKIP 'because somehow InsertEnter and InsertLeave events aren''t fired' 410 | execute 10 411 | execute 'normal' "iif(1+2)return 4;\" 412 | Expect getline('.') ==# ' if (1 + 2) return 4;' 413 | end 414 | end 415 | 416 | " }}} 417 | 418 | " test for auto 'formatexpr' setting feature {{{ 419 | describe 'g:clang_format#auto_formatexpr' 420 | before 421 | let g:clang_format#auto_formatexpr = 1 422 | new 423 | execute 'silent' 'edit!' './t/test.cpp' 424 | end 425 | 426 | after 427 | bdelete! 428 | end 429 | 430 | it 'formats the text object using gq operator' 431 | SKIP 'because of unknown backslash on formatting too long macros' 432 | doautocmd Filetype cpp 433 | let expected = ClangFormat(1, line('$')) 434 | normal ggVGgq 435 | let actual = GetBuffer() 436 | Expect expected ==# actual 437 | end 438 | end 439 | " }}} 440 | 441 | " test for undo {{{ 442 | describe 'undo formatting text' 443 | before 444 | let g:clang_format#detect_style_file = 0 445 | new 446 | execute 'silent' 'edit!' './t/test.cpp' 447 | end 448 | 449 | after 450 | bdelete! 451 | end 452 | 453 | it 'restores previous text as editing buffer normally' 454 | let prev = GetBuffer() 455 | ClangFormat 456 | undo 457 | let buf = GetBuffer() 458 | Expect prev ==# buf 459 | end 460 | end 461 | " }}} 462 | 463 | " test for empty buffer {{{ 464 | describe 'empty buffer' 465 | before 466 | let g:clang_format#detect_style_file = 0 467 | new 468 | end 469 | 470 | after 471 | bdelete! 472 | end 473 | 474 | it 'can format empty buffer' 475 | ClangFormat 476 | end 477 | 478 | it 'can format a buffer containing only new lines' 479 | call setline('.', ['', '']) 480 | ClangFormat 481 | call setline('.', ['', '', '']) 482 | ClangFormat 483 | end 484 | end 485 | " }}}" 486 | -------------------------------------------------------------------------------- /test/t/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define TEST_CPP_FOR_OPERATOR_CLANG_FORMAT_LONG_MACRO " CPP for vim-operator-clang-complete" 4 | 5 | void f(){ std::cout << "hello\n"; } 6 | 7 | int main() 8 | { 9 | int * hoge = {1,3,5,7}; 10 | 11 | for(int i=0;i<4;++i){ 12 | if(i%2==0) std::cout << hoge[i] << std::endl; 13 | } 14 | 15 | std::cout << "this is very very long one-line string. so this line go over 80 column .\n"; 16 | 17 | std::cout << "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:"; 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /test/t/test.js: -------------------------------------------------------------------------------- 1 | function(){console.log('this');console.log('is');console.log('test')} 2 | --------------------------------------------------------------------------------