2 " File: clang_complete.vim
3 " Author: Xavier Deguillard <deguilx@gmail.com>
5 " Description: Use of clang to complete in C/C++.
7 " Help: Use :help clang_complete
10 au FileType c,cpp,objc,objcpp call <SID>ClangCompleteInit()
12 let b:clang_parameters = ''
13 let b:clang_user_options = ''
14 let b:my_changedtick = 0
16 " Store plugin path, as this is available only when sourcing the file,
17 " not during a function call.
18 let s:plugin_path = escape(expand('<sfile>:p:h'), '\')
20 function! s:ClangCompleteInit()
21 if !exists('g:clang_auto_select')
22 let g:clang_auto_select = 0
25 if !exists('g:clang_complete_auto')
26 let g:clang_complete_auto = 1
29 if !exists('g:clang_complete_copen')
30 let g:clang_complete_copen = 0
33 if !exists('g:clang_hl_errors')
34 let g:clang_hl_errors = 1
37 if !exists('g:clang_periodic_quickfix')
38 let g:clang_periodic_quickfix = 0
41 if !exists('g:clang_snippets')
42 let g:clang_snippets = 0
45 if !exists('g:clang_snippets_engine')
46 let g:clang_snippets_engine = 'clang_complete'
49 if !exists('g:clang_exec')
50 let g:clang_exec = 'clang'
53 if !exists('g:clang_user_options')
54 let g:clang_user_options = ''
57 " Only use libclang if the user clearly show intent to do so for now
58 if !exists('g:clang_use_library')
59 let g:clang_use_library = (has('python') && exists('g:clang_library_path'))
62 if !exists('g:clang_complete_macros')
63 let g:clang_complete_macros = 0
66 if !exists('g:clang_complete_patterns')
67 let g:clang_complete_patterns = 0
70 if !exists('g:clang_debug')
74 if !exists('g:clang_sort_algo')
75 let g:clang_sort_algo = 'priority'
78 if !exists('g:clang_auto_user_options')
79 let g:clang_auto_user_options = 'path, .clang_complete'
82 call LoadUserOptions()
84 inoremap <expr> <buffer> <C-X><C-U> <SID>LaunchCompletion()
85 inoremap <expr> <buffer> . <SID>CompleteDot()
86 inoremap <expr> <buffer> > <SID>CompleteArrow()
87 inoremap <expr> <buffer> : <SID>CompleteColon()
88 inoremap <expr> <buffer> <CR> <SID>HandlePossibleSelectionEnter()
90 if g:clang_snippets == 1
91 call g:ClangSetSnippetEngine(g:clang_snippets_engine)
94 " Force menuone. Without it, when there's only one completion result,
95 " it can be confusing (not completing and no popup)
96 if g:clang_auto_select != 2
98 set completeopt+=menuone
101 " Disable every autocmd that could have been set.
102 augroup ClangComplete
106 let b:should_overload = 0
107 let b:my_changedtick = b:changedtick
108 let b:clang_parameters = '-x c'
110 if &filetype == 'objc'
111 let b:clang_parameters = '-x objective-c'
114 if &filetype == 'cpp' || &filetype == 'objcpp'
115 let b:clang_parameters .= '++'
118 if expand('%:e') =~ 'h*'
119 let b:clang_parameters .= '-header'
122 let g:clang_complete_lib_flags = 0
124 if g:clang_complete_macros == 1
125 let b:clang_parameters .= ' -code-completion-macros'
126 let g:clang_complete_lib_flags = 1
129 if g:clang_complete_patterns == 1
130 let b:clang_parameters .= ' -code-completion-patterns'
131 let g:clang_complete_lib_flags += 2
134 setlocal completefunc=ClangComplete
135 setlocal omnifunc=ClangComplete
137 if g:clang_periodic_quickfix == 1
138 augroup ClangComplete
139 au CursorHold,CursorHoldI <buffer> call <SID>DoPeriodicQuickFix()
143 " Load the python bindings of libclang
144 if g:clang_use_library == 1
146 exe s:initClangCompletePython()
148 echoe 'clang_complete: No python support available.'
149 echoe 'Cannot use clang library, using executable'
150 echoe 'Compile vim with python support to use libclang'
151 let g:clang_use_library = 0
157 function! LoadUserOptions()
158 let b:clang_user_options = ''
160 let l:option_sources = split(g:clang_auto_user_options, ',')
161 let l:remove_spaces_cmd = 'substitute(v:val, "\\s*\\(.*\\)\\s*", "\\1", "")'
162 let l:option_sources = map(l:option_sources, l:remove_spaces_cmd)
164 for l:source in l:option_sources
165 if l:source == 'path'
166 call s:parsePathOption()
167 elseif l:source == '.clang_complete'
173 function! s:parseConfig()
174 let l:local_conf = findfile('.clang_complete', getcwd() . ',.;')
175 if l:local_conf == '' || !filereadable(l:local_conf)
179 let l:opts = readfile(l:local_conf)
181 " Better handling of absolute path
182 " I don't know if those pattern will work on windows
184 if matchstr(l:opt, '\C-I\s*/') != ''
185 let l:opt = substitute(l:opt, '\C-I\s*\(/\%(\w\|\\\s\)*\)',
188 let l:opt = substitute(l:opt, '\C-I\s*\(\%(\w\|\\\s\)*\)',
189 \ '-I' . l:local_conf[:-16] . '\1', 'g')
191 let b:clang_user_options .= ' ' . l:opt
195 function! s:parsePathOption()
196 let l:dirs = split(&path, ',')
198 if len(l:dir) == 0 || !isdirectory(l:dir)
202 " Add only absolute paths
203 if matchstr(l:dir, '\s*/') != ''
204 let l:opt = '-I' . l:dir
205 let b:clang_user_options .= ' ' . l:opt
210 function! s:initClangCompletePython()
211 " Only parse the python library once
212 if !exists('s:libclang_loaded')
214 if exists('g:clang_library_path')
215 " Load the library from the given library path.
216 exe 'python sys.argv = ["' . escape(g:clang_library_path, '\') . '"]'
218 " By setting argv[0] to '' force the python bindings to load the library
219 " from the normal system search path.
220 python sys.argv[0] = ''
223 exe 'python sys.path = ["' . s:plugin_path . '"] + sys.path'
224 exe 'pyfile ' . s:plugin_path . '/libclang.py'
225 python initClangComplete(vim.eval('g:clang_complete_lib_flags'))
226 let s:libclang_loaded = 1
231 function! s:GetKind(proto)
235 let l:ret = match(a:proto, '^\[#')
236 let l:params = match(a:proto, '(')
237 if l:ret == -1 && l:params == -1
240 if l:ret != -1 && l:params == -1
249 function! s:CallClangBinaryForDiagnostics(tempfile)
250 let l:escaped_tempfile = shellescape(a:tempfile)
251 let l:buf = getline(1, '$')
253 call writefile(l:buf, a:tempfile)
254 catch /^Vim\%((\a\+)\)\=:E482/
258 let l:command = g:clang_exec . ' -cc1 -fsyntax-only'
259 \ . ' -fno-caret-diagnostics -fdiagnostics-print-source-range-info'
260 \ . ' ' . l:escaped_tempfile
261 \ . ' ' . b:clang_parameters . ' ' . b:clang_user_options . ' ' . g:clang_user_options
263 let l:clang_output = split(system(l:command), "\n")
264 call delete(a:tempfile)
265 return l:clang_output
268 function! s:CallClangForDiagnostics(tempfile)
269 if g:clang_use_library == 1
270 python updateCurrentDiagnostics()
272 return s:CallClangBinaryForDiagnostics(a:tempfile)
276 function! s:DoPeriodicQuickFix()
277 " Don't do any superfluous reparsing.
278 if b:my_changedtick == b:changedtick
281 let b:my_changedtick = b:changedtick
283 " Create tempfile name for clang/clang++ executable mode
284 let b:my_changedtick = b:changedtick
285 let l:tempfile = expand('%:p:h') . '/' . localtime() . expand('%:t')
287 let l:clang_output = s:CallClangForDiagnostics(l:tempfile)
289 call s:ClangQuickFix(l:clang_output, l:tempfile)
292 function! s:ClangQuickFix(clang_output, tempfname)
293 " Clear the bad spell, the user may have corrected them.
294 syntax clear SpellBad
295 syntax clear SpellLocal
297 if g:clang_use_library == 0
298 let l:list = s:ClangUpdateQuickFix(a:clang_output, a:tempfname)
300 python vim.command('let l:list = ' + str(getCurrentQuickFixList()))
301 python highlightCurrentDiagnostics()
304 if g:clang_complete_copen == 1
305 " We should get back to the original buffer
306 let l:bufnr = bufnr('%')
309 " http://vim.1045645.n5.nabble.com/setqflist-inconsistency-td1211423.html
316 let l:winbufnr = bufwinnr(l:bufnr)
317 exe l:winbufnr . 'wincmd w'
319 call setqflist(l:list)
320 silent doautocmd QuickFixCmdPost make
323 function! s:ClangUpdateQuickFix(clang_output, tempfname)
325 for l:line in a:clang_output
326 let l:erridx = match(l:line, '\%(error\|warning\|note\): ')
328 " Error are always at the beginning.
329 if l:line[:11] == 'COMPLETION: ' || l:line[:9] == 'OVERLOAD: '
334 let l:pattern = '^\(.*\):\(\d*\):\(\d*\):\(\%({\d\+:\d\+-\d\+:\d\+}\)*\)'
335 let l:tmp = matchstr(l:line, l:pattern)
336 let l:fname = substitute(l:tmp, l:pattern, '\1', '')
337 if l:fname == a:tempfname
340 let l:bufnr = bufnr(l:fname, 1)
341 let l:lnum = substitute(l:tmp, l:pattern, '\2', '')
342 let l:col = substitute(l:tmp, l:pattern, '\3', '')
343 let l:errors = substitute(l:tmp, l:pattern, '\4', '')
344 if l:line[l:erridx] == 'e'
345 let l:text = l:line[l:erridx + 7:]
347 let l:hlgroup = ' SpellBad '
348 elseif l:line[l:erridx] == 'w'
349 let l:text = l:line[l:erridx + 9:]
351 let l:hlgroup = ' SpellLocal '
353 let l:text = l:line[l:erridx + 6:]
363 let l:list = add(l:list, l:item)
365 if g:clang_hl_errors == 0 || l:fname != '%' || l:type == 'I'
370 let l:pat = '/\%' . l:lnum . 'l' . '\%' . l:col . 'c./'
371 exe 'syntax match' . l:hlgroup . l:pat
376 let l:ranges = split(l:errors, '}')
377 for l:range in l:ranges
378 " Doing precise error and warning handling.
379 " The highlight will be the same as clang's carets.
380 let l:pattern = '{\%(\d\+\):\(\d\+\)-\%(\d\+\):\(\d\+\)'
381 let l:tmp = matchstr(l:range, l:pattern)
382 let l:startcol = substitute(l:tmp, l:pattern, '\1', '')
383 let l:endcol = substitute(l:tmp, l:pattern, '\2', '')
384 " Highlighting the ~~~~
385 let l:pat = '/\%' . l:lnum . 'l'
386 \ . '\%' . l:startcol . 'c'
388 \ . '\%' . l:endcol . 'c/'
389 exe 'syntax match' . l:hlgroup . l:pat
395 function! s:DemangleProto(prototype)
396 let l:proto = substitute(a:prototype, '\[#[^#]*#\]', '', 'g')
397 let l:proto = substitute(l:proto, '{#.*#}', '', 'g')
403 function! s:ClangCompleteBinary(base)
404 let l:buf = getline(1, '$')
405 let l:tempfile = expand('%:p:h') . '/' . localtime() . expand('%:t')
407 call writefile(l:buf, l:tempfile)
408 catch /^Vim\%((\a\+)\)\=:E482/
411 let l:escaped_tempfile = shellescape(l:tempfile)
413 let l:command = g:clang_exec . ' -cc1 -fsyntax-only'
414 \ . ' -fno-caret-diagnostics -fdiagnostics-print-source-range-info'
415 \ . ' -code-completion-at=' . l:escaped_tempfile . ':'
416 \ . line('.') . ':' . b:col . ' ' . l:escaped_tempfile
417 \ . ' ' . b:clang_parameters . ' ' . b:clang_user_options . ' ' . g:clang_user_options
418 let l:clang_output = split(system(l:command), "\n")
419 call delete(l:tempfile)
421 call s:ClangQuickFix(l:clang_output, l:tempfile)
425 if l:clang_output == []
429 let l:filter_str = "v:val =~ '^COMPLETION: " . a:base . "\\|^OVERLOAD: '"
430 call filter(l:clang_output, l:filter_str)
433 for l:line in l:clang_output
435 if l:line[:11] == 'COMPLETION: ' && b:should_overload != 1
437 let l:value = l:line[12:]
439 let l:colonidx = stridx(l:value, ' : ')
441 let l:wabbr = s:DemangleProto(l:value)
443 let l:proto = l:value
445 let l:word = l:value[:l:colonidx - 1]
447 if l:word =~ '(Hidden)'
448 let l:word = l:word[:-10]
451 let l:proto = l:value[l:colonidx + 3:]
454 let l:kind = s:GetKind(l:proto)
455 if l:kind == 't' && b:clang_complete_type == 0
461 let l:proto = s:DemangleProto(l:proto)
463 elseif l:line[:9] == 'OVERLOAD: ' && b:should_overload == 1
465 let l:value = l:line[10:]
466 if match(l:value, '<#') == -1
469 let l:word = substitute(l:value, '.*<#', '<#', 'g')
470 let l:word = substitute(l:word, '#>.*', '#>', 'g')
471 let l:wabbr = substitute(l:word, '<#\([^#]*\)#>', '\1', 'g')
472 let l:proto = l:value
473 let l:proto = s:DemangleProto(l:value)
480 if g:clang_snippets == 1
481 let l:startidx = match(l:proto, '<#')
482 while l:startidx != -1
483 let l:proto = substitute(l:proto, '<#', '', '')
484 let l:endidx = match(l:proto, '#>')
485 let l:proto = substitute(l:proto, '#>', '', '')
486 let l:args_pos += [[ l:startidx, l:endidx ]]
487 let l:startidx = match(l:proto, '<#')
498 \ 'args_pos': l:args_pos }
500 call add(l:res, l:item)
505 function! ClangComplete(findstart, base)
507 let l:line = getline('.')
508 let l:start = col('.') - 1
509 let b:clang_complete_type = 1
510 let l:wsstart = l:start
511 if l:line[l:wsstart - 1] =~ '\s'
512 while l:wsstart > 0 && l:line[l:wsstart - 1] =~ '\s'
516 if l:line[l:wsstart - 1] =~ '[(,]'
517 let b:should_overload = 1
518 let b:col = l:wsstart + 1
521 let b:should_overload = 0
522 while l:start > 0 && l:line[l:start - 1] =~ '\i'
525 if l:line[l:start - 2:] =~ '->' || l:line[l:start - 1] == '.'
526 let b:clang_complete_type = 0
528 let b:col = l:start + 1
531 if g:clang_debug == 1
532 let l:time_start = reltime()
535 if g:clang_snippets == 1
539 if g:clang_use_library == 1
540 python vim.command('let l:res = ' + str(getCurrentCompletions(vim.eval('a:base'))))
542 let l:res = s:ClangCompleteBinary(a:base)
546 if g:clang_snippets == 1
547 let item['word'] = b:AddSnip(item['info'], item['args_pos'])
549 let item['word'] = item['abbr']
552 if g:clang_snippets == 1
553 inoremap <expr> <buffer> <C-Y> <SID>HandlePossibleSelectionCtrlY()
554 augroup ClangComplete
555 au CursorMovedI <buffer> call <SID>TriggerSnippet()
557 let b:snippet_chosen = 0
560 if g:clang_debug == 1
561 echom 'clang_complete: completion time (' . (g:clang_use_library == 1 ? 'library' : 'binary') . ') '. split(reltimestr(reltime(l:time_start)))[0]
567 function! s:HandlePossibleSelectionEnter()
569 let b:snippet_chosen = 1
575 function! s:HandlePossibleSelectionCtrlY()
577 let b:snippet_chosen = 1
582 function! s:TriggerSnippet()
583 " Dont bother doing anything until we're sure the user exited the menu
588 " Stop monitoring as we'll trigger a snippet
589 silent! iunmap <buffer> <C-Y>
590 augroup ClangComplete
591 au! CursorMovedI <buffer>
594 " Trigger the snippet
598 function! s:ShouldComplete()
599 if (getline('.') =~ '#\s*\(include\|import\)')
605 for l:id in synstack(line('.'), col('.') - 1)
606 if match(synIDattr(l:id, 'name'), '\CComment\|String\|Number')
615 function! s:LaunchCompletion()
617 if s:ShouldComplete()
618 let l:result = "\<C-X>\<C-U>"
619 if g:clang_auto_select != 2
620 let l:result .= "\<C-P>"
622 if g:clang_auto_select == 1
623 let l:result .= "\<C-R>=(pumvisible() ? \"\\<Down>\" : '')\<CR>"
629 function! s:CompleteDot()
630 if g:clang_complete_auto == 1
631 return '.' . s:LaunchCompletion()
636 function! s:CompleteArrow()
637 if g:clang_complete_auto != 1 || getline('.')[col('.') - 2] != '-'
640 return '>' . s:LaunchCompletion()
643 function! s:CompleteColon()
644 if g:clang_complete_auto != 1 || getline('.')[col('.') - 2] != ':'
647 return ':' . s:LaunchCompletion()
650 " May be used in a mapping to update the quickfix window.
651 function! g:ClangUpdateQuickFix()
652 call s:DoPeriodicQuickFix()
656 function! g:ClangSetSnippetEngine(engine_name)
658 call eval('snippets#' . a:engine_name . '#init()')
659 let b:AddSnip = function('snippets#' . a:engine_name . '#add_snippet')
660 let b:ResetSnip = function('snippets#' . a:engine_name . '#reset')
661 let b:TriggerSnip = function('snippets#' . a:engine_name . '#trigger')
662 catch /^Vim\%((\a\+)\)\=:E117/
663 echoe 'Snippets engine ' . a:engine_name . ' not found.'
664 let g:clang_snippets = 0
668 " vim: set ts=2 sts=2 sw=2 expandtab :