1
0

clang_complete.vim 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. "
  2. " File: clang_complete.vim
  3. " Author: Xavier Deguillard <deguilx@gmail.com>
  4. "
  5. " Description: Use of clang to complete in C/C++.
  6. "
  7. " Configuration: Each project can have a .clang_complete at his root,
  8. " containing the compiler options. This is useful if
  9. " you're using some non-standard include paths.
  10. " For simplicity, please don't put relative and
  11. " absolute include path on the same line. It is not
  12. " currently correctly handled.
  13. "
  14. " Options:
  15. " - g:clang_complete_auto:
  16. " if equal to 1, automatically complete after ->, ., ::
  17. " Default: 1
  18. "
  19. " - g:clang_complete_copen:
  20. " if equal to 1, open quickfix window on error.
  21. " Default: 0
  22. "
  23. " - g:clang_hl_errors:
  24. " if equal to 1, it will highlight the warnings and errors the
  25. " same way clang does it.
  26. " Default: 1
  27. "
  28. " - g:clang_periodic_quickfix:
  29. " if equal to 1, it will periodically update the quickfix window
  30. " Note: You could use the g:ClangUpdateQuickFix() to do the same
  31. " with a mapping.
  32. " Default: 0
  33. "
  34. " - g:clang_snippets:
  35. " if equal to 1, it will do some snippets magic after a ( or a ,
  36. " inside function call. Not currently fully working.
  37. " Default: 0
  38. "
  39. " - g:clang_conceal_snippets:
  40. " if equal to 1, vim will use vim 7.3 conceal feature to hide <#
  41. " and #> which delimit a snippets.
  42. " Note: See concealcursor and conceallevel for conceal configuration.
  43. " Default: 1 (0 if conceal not available)
  44. "
  45. " - g:clang_exec:
  46. " Name or path of clang executable.
  47. " Note: Use this if clang has a non-standard name, or isn't in the
  48. " path.
  49. " Default: 'clang'
  50. "
  51. " - g:clang_user_options:
  52. " Option added at the end of clang command. Useful if you want to
  53. " filter the result, or if you want to ignore the error code
  54. " returned by clang: on error, the completion is not shown.
  55. " Default: ''
  56. " Example: '|| exit 0' (it will discard clang return value)
  57. "
  58. " Todo: - Fix bugs
  59. " - Parse fix-its and do something useful with it.
  60. " - -code-completion-macros -code-completion-patterns
  61. "
  62. au FileType c,cpp,objc,objcpp call s:ClangCompleteInit()
  63. let b:clang_parameters = ''
  64. let b:clang_user_options = ''
  65. let b:my_changedtick = 0
  66. function s:ClangCompleteInit()
  67. let l:local_conf = findfile(".clang_complete", '.;')
  68. let b:clang_user_options = ''
  69. if l:local_conf != ""
  70. let l:opts = readfile(l:local_conf)
  71. for l:opt in l:opts
  72. " Better handling of absolute path
  73. " I don't know if those pattern will work on windows
  74. " platform
  75. if matchstr(l:opt, '\C-I\s*/') != ""
  76. let l:opt = substitute(l:opt, '\C-I\s*\(/\%(\w\|\\\s\)*\)',
  77. \ '-I' . '\1', "g")
  78. else
  79. let l:opt = substitute(l:opt, '\C-I\s*\(\%(\w\|\\\s\)*\)',
  80. \ '-I' . l:local_conf[:-16] . '\1', "g")
  81. endif
  82. let b:clang_user_options .= " " . l:opt
  83. endfor
  84. endif
  85. if !exists('g:clang_complete_auto')
  86. let g:clang_complete_auto = 1
  87. endif
  88. if !exists('g:clang_complete_copen')
  89. let g:clang_complete_copen = 0
  90. endif
  91. if !exists('g:clang_hl_errors')
  92. let g:clang_hl_errors = 1
  93. endif
  94. if !exists('g:clang_periodic_quickfix')
  95. let g:clang_periodic_quickfix = 0
  96. endif
  97. if !exists('g:clang_snippets')
  98. let g:clang_snippets = 0
  99. endif
  100. if !exists('g:clang_conceal_snippets')
  101. let g:clang_conceal_snippets = has("conceal")
  102. endif
  103. if !exists('g:clang_exec')
  104. let g:clang_exec = 'clang'
  105. endif
  106. if !exists('g:clang_user_options')
  107. let g:clang_user_options = ''
  108. endif
  109. let b:clang_user_options .= ' ' . g:clang_user_options
  110. if g:clang_complete_auto == 1
  111. inoremap <expr> <buffer> <C-X><C-U> LaunchCompletion()
  112. inoremap <expr> <buffer> . CompleteDot()
  113. inoremap <expr> <buffer> > CompleteArrow()
  114. inoremap <expr> <buffer> : CompleteColon()
  115. endif
  116. if g:clang_snippets == 1
  117. noremap <expr> <buffer> <tab> UpdateSnips()
  118. snoremap <expr> <buffer> <tab> UpdateSnips()
  119. if g:clang_conceal_snippets == 1
  120. syntax match Conceal /<#/ conceal
  121. syntax match Conceal /#>/ conceal
  122. endif
  123. endif
  124. " Disable every autocmd that could have been set.
  125. augroup ClangComplete
  126. autocmd!
  127. augroup end
  128. let b:should_overload = 0
  129. let b:my_changedtick = b:changedtick
  130. let b:clang_parameters = '-x c'
  131. if &filetype == 'objc'
  132. let b:clang_parameters = '-x objective-c'
  133. endif
  134. if &filetype == 'cpp' || &filetype == 'objcpp'
  135. let b:clang_parameters .= '++'
  136. endif
  137. if expand('%:e') =~ 'h*'
  138. let b:clang_parameters .= '-header'
  139. endif
  140. setlocal completefunc=ClangComplete
  141. if g:clang_periodic_quickfix == 1
  142. augroup ClangComplete
  143. au CursorHold,CursorHoldI <buffer> call s:DoPeriodicQuickFix()
  144. augroup end
  145. endif
  146. endfunction
  147. function s:GetKind(proto)
  148. if a:proto == ""
  149. return 't'
  150. endif
  151. let l:ret = match(a:proto, '^\[#')
  152. let l:params = match(a:proto, '(')
  153. if l:ret == -1 && l:params == -1
  154. return 't'
  155. endif
  156. if l:ret != -1 && l:params == -1
  157. return 'v'
  158. endif
  159. if l:params != -1
  160. return 'f'
  161. endif
  162. return 'm'
  163. endfunction
  164. function s:DoPeriodicQuickFix()
  165. " Don't do any superfluous reparsing.
  166. if b:my_changedtick == b:changedtick
  167. return
  168. endif
  169. let b:my_changedtick = b:changedtick
  170. let l:buf = getline(1, '$')
  171. let l:tempfile = expand('%:p:h') . '/' . localtime() . expand('%:t')
  172. try
  173. call writefile(l:buf, l:tempfile)
  174. catch /^Vim\%((\a\+)\)\=:E482/
  175. return
  176. endtry
  177. let l:escaped_tempfile = shellescape(l:tempfile)
  178. let l:command = g:clang_exec . " -cc1 -fsyntax-only"
  179. \ . " -fno-caret-diagnostics -fdiagnostics-print-source-range-info"
  180. \ . " " . l:escaped_tempfile
  181. \ . " " . b:clang_parameters . " " . b:clang_user_options
  182. let l:clang_output = split(system(l:command), "\n")
  183. call delete(l:tempfile)
  184. call s:ClangQuickFix(l:clang_output, l:tempfile)
  185. endfunction
  186. function s:ClangQuickFix(clang_output, tempfname)
  187. " Clear the bad spell, the user may have corrected them.
  188. syntax clear SpellBad
  189. syntax clear SpellLocal
  190. let l:list = []
  191. for l:line in a:clang_output
  192. let l:erridx = match(l:line, '\%(error\|warning\): ')
  193. if l:erridx == -1
  194. " Error are always at the beginning.
  195. if l:line[:11] == 'COMPLETION: ' || l:line[:9] == 'OVERLOAD: '
  196. break
  197. endif
  198. continue
  199. endif
  200. let l:pattern = '^\(.*\):\(\d*\):\(\d*\):\(\%({\d\+:\d\+-\d\+:\d\+}\)*\)'
  201. let l:tmp = matchstr(l:line, l:pattern)
  202. let l:fname = substitute(l:tmp, l:pattern, '\1', '')
  203. if l:fname == a:tempfname
  204. let l:fname = "%"
  205. endif
  206. let l:bufnr = bufnr(l:fname, 1)
  207. let l:lnum = substitute(l:tmp, l:pattern, '\2', '')
  208. let l:col = substitute(l:tmp, l:pattern, '\3', '')
  209. let l:errors = substitute(l:tmp, l:pattern, '\4', '')
  210. if l:line[l:erridx] == 'e'
  211. let l:text = l:line[l:erridx + 7:]
  212. let l:type = 'E'
  213. let l:hlgroup = " SpellBad "
  214. else
  215. let l:text = l:line[l:erridx + 9:]
  216. let l:type = 'W'
  217. let l:hlgroup = " SpellLocal "
  218. endif
  219. let l:item = {
  220. \ "bufnr": l:bufnr,
  221. \ "lnum": l:lnum,
  222. \ "col": l:col,
  223. \ "text": l:text,
  224. \ "type": l:type }
  225. let l:list = add(l:list, l:item)
  226. if g:clang_hl_errors == 0 || l:fname != "%"
  227. continue
  228. endif
  229. " Highlighting the ^
  230. let l:pat = '/\%' . l:lnum . 'l' . '\%' . l:col . 'c./'
  231. exe "syntax match" . l:hlgroup . l:pat
  232. if l:errors == ""
  233. continue
  234. endif
  235. let l:ranges = split(l:errors, '}')
  236. for l:range in l:ranges
  237. " Doing precise error and warning handling.
  238. " The highlight will be the same as clang's carets.
  239. let l:pattern = '{\%(\d\+\):\(\d\+\)-\%(\d\+\):\(\d\+\)'
  240. let l:tmp = matchstr(l:range, l:pattern)
  241. let l:startcol = substitute(l:tmp, l:pattern, '\1', '')
  242. let l:endcol = substitute(l:tmp, l:pattern, '\2', '')
  243. " Highlighting the ~~~~
  244. let l:pat = '/\%' . l:lnum . 'l'
  245. \ . '\%' . l:startcol . 'c'
  246. \ . '.*'
  247. \ . '\%' . l:endcol . 'c/'
  248. exe "syntax match" . l:hlgroup . l:pat
  249. endfor
  250. endfor
  251. call setqflist(l:list)
  252. doautocmd QuickFixCmdPost make
  253. if g:clang_complete_copen == 1
  254. " We should get back to the original buffer
  255. let l:bufnr = bufnr("%")
  256. cwindow
  257. let l:winbufnr = bufwinnr(l:bufnr)
  258. exe l:winbufnr . "wincmd w"
  259. endif
  260. endfunction
  261. function s:DemangleProto(prototype)
  262. let l:proto = substitute(a:prototype, '[#', "", "g")
  263. let l:proto = substitute(l:proto, '#]', ' ', "g")
  264. let l:proto = substitute(l:proto, '#>', "", "g")
  265. let l:proto = substitute(l:proto, '<#', "", "g")
  266. " TODO: add a candidate for each optional parameter
  267. let l:proto = substitute(l:proto, '{#', "", "g")
  268. let l:proto = substitute(l:proto, '#}', "", "g")
  269. return l:proto
  270. endfunction
  271. let b:col = 0
  272. function ClangComplete(findstart, base)
  273. if a:findstart
  274. let l:line = getline('.')
  275. let l:start = col('.') - 1
  276. let l:wsstart = l:start
  277. if l:line[l:wsstart - 1] =~ '\s'
  278. while l:wsstart > 0 && l:line[l:wsstart - 1] =~ '\s'
  279. let l:wsstart -= 1
  280. endwhile
  281. endif
  282. if l:line[l:wsstart - 1] =~ '[(,]'
  283. let b:should_overload = 1
  284. let b:col = l:wsstart + 1
  285. return l:wsstart
  286. endif
  287. let b:should_overload = 0
  288. while l:start > 0 && l:line[l:start - 1] =~ '\i'
  289. let l:start -= 1
  290. endwhile
  291. let b:col = l:start + 1
  292. return l:start
  293. else
  294. let l:buf = getline(1, '$')
  295. let l:tempfile = expand('%:p:h') . '/' . localtime() . expand('%:t')
  296. try
  297. call writefile(l:buf, l:tempfile)
  298. catch /^Vim\%((\a\+)\)\=:E482/
  299. return {}
  300. endtry
  301. let l:escaped_tempfile = shellescape(l:tempfile)
  302. let l:command = g:clang_exec . " -cc1 -fsyntax-only"
  303. \ . " -fno-caret-diagnostics -fdiagnostics-print-source-range-info"
  304. \ . " -code-completion-at=" . l:escaped_tempfile . ":"
  305. \ . line('.') . ":" . b:col . " " . l:escaped_tempfile
  306. \ . " " . b:clang_parameters . " " . b:clang_user_options
  307. let l:clang_output = split(system(l:command), "\n")
  308. call delete(l:tempfile)
  309. call s:ClangQuickFix(l:clang_output, l:tempfile)
  310. if v:shell_error
  311. return {}
  312. endif
  313. if l:clang_output == []
  314. return {}
  315. endif
  316. let l:res = []
  317. for l:line in l:clang_output
  318. if l:line[:11] == 'COMPLETION: ' && b:should_overload != 1
  319. let l:value = l:line[12:]
  320. if l:value =~ 'Pattern'
  321. if g:clang_snippets != 1
  322. continue
  323. endif
  324. let l:value = l:value[10:]
  325. endif
  326. if l:value !~ '^' . a:base
  327. continue
  328. endif
  329. let l:colonidx = stridx(l:value, " : ")
  330. if l:colonidx == -1
  331. let l:wabbr = s:DemangleProto(l:value)
  332. let l:word = l:value
  333. let l:proto = l:value
  334. else
  335. let l:word = l:value[:l:colonidx - 1]
  336. " WTF is that?
  337. if l:word =~ '(Hidden)'
  338. let l:word = l:word[:-10]
  339. endif
  340. let l:wabbr = l:word
  341. let l:proto = l:value[l:colonidx + 3:]
  342. endif
  343. let l:kind = s:GetKind(l:proto)
  344. if g:clang_snippets == 1
  345. let l:word = substitute(l:proto, '\[#[^#]*#\]', "", "g")
  346. else
  347. let l:word = l:wabbr
  348. endif
  349. let l:proto = s:DemangleProto(l:proto)
  350. elseif l:line[:9] == 'OVERLOAD: ' && b:should_overload == 1
  351. \ && g:clang_snippets == 1
  352. let l:value = l:line[10:]
  353. if match(l:value, '<#') == -1
  354. continue
  355. endif
  356. " TODO: handle optionnal parameters correctly.
  357. let l:word = substitute(l:value, '.*<#', '<#', "g")
  358. let l:word = substitute(l:word, '#>.*', '#>', "g")
  359. let l:wabbr = substitute(l:word, '<#\([^#]*\)#>', '\1', "g")
  360. let l:proto = s:DemangleProto(l:value)
  361. let l:kind = ""
  362. else
  363. continue
  364. endif
  365. let l:item = {
  366. \ "word": l:word,
  367. \ "abbr": l:wabbr,
  368. \ "menu": l:proto,
  369. \ "info": l:proto,
  370. \ "dup": 1,
  371. \ "kind": l:kind }
  372. call add(l:res, l:item)
  373. endfor
  374. if g:clang_snippets == 1
  375. augroup ClangComplete
  376. au CursorMovedI <buffer> call BeginSnips()
  377. augroup end
  378. endif
  379. return l:res
  380. endif
  381. endfunction
  382. function ShouldComplete()
  383. if (getline(".") =~ '#\s*\(include\|import\)')
  384. return 0
  385. else
  386. return match(synIDattr(synID(line("."), col(".") - 1, 1), "name"),
  387. \'\C\<cComment\|\<cCppString\|\<cString\|\<cNumber') == -1
  388. endfunction
  389. function LaunchCompletion()
  390. if ShouldComplete()
  391. return "\<C-X>\<C-U>"
  392. else
  393. return ""
  394. endif
  395. endfunction
  396. function CompleteDot()
  397. return '.' . LaunchCompletion()
  398. endfunction
  399. function CompleteArrow()
  400. if getline('.')[col('.') - 2] != '-'
  401. return '>'
  402. endif
  403. return '>' . LaunchCompletion()
  404. endfunction
  405. function CompleteColon()
  406. if getline('.')[col('.') - 2] != ':'
  407. return ':'
  408. endif
  409. return ':' . LaunchCompletion()
  410. endfunction
  411. function UpdateSnips()
  412. let l:line = getline(".")
  413. let l:pattern = '<#[^#]*#>'
  414. if match(l:line, l:pattern) == -1
  415. return ""
  416. endif
  417. " Is it possible to use f/t/F/T on multiple chars?
  418. "return "\<esc>f<va>\<C-G>"
  419. return "\<esc>/<#\<CR>v/#>\<CR>l\<C-G>"
  420. endfunction
  421. function BeginSnips()
  422. if pumvisible() != 0
  423. return ""
  424. endif
  425. augroup ClangComplete
  426. au! CursorMovedI <buffer>
  427. augroup end
  428. " Do we need to launch UpdateSnippets()?
  429. let l:line = getline(".")
  430. let l:pattern = '<#[^#]*#>'
  431. if match(l:line, l:pattern) == -1
  432. return ""
  433. endif
  434. call feedkeys("\<esc>^\<tab>")
  435. return ""
  436. endfunction
  437. " May be used in a mapping to update the quickfix window.
  438. function g:ClangUpdateQuickFix()
  439. call s:DoPeriodicQuickFix()
  440. return ""
  441. endfunction