標籤:

YouCompleteMe 配合 UltiSnips 補全 C/C++ 函數參數

一直在 Vim 上用 YouCompleteMe 進行 C/C++ 自動補全,一個大的缺陷是不能進行函數參數的補全。後來在 GitHub 上搜索到了這個 issue 中的一個評論,解決了一部分問題,然而仍有一些問題:

1. 在有些時候選中了結果,但並不希望進行函數參數補全,比如輸入 C++ 的 I/O manipulator 的時候。因為 std::endl 之類的 I/O manipulator 實際上是個函數,但 std::ios_base 的 operator<< 是接受了一個函數指針作為參數,因此使用的時候只需 std::cout << "xxx" << std::endl; 不需要寫 std::endl 的參數。而此時如果你用了這個方法,選中了補全結果後再輸入任何鍵它都會進行參數列表的展開。

2. 在選中補全結果的時候必須使用 Ctrl-Y 進行 snippet 展開。如果直接輸入左括弧,則會出現多輸入一個括弧的現象。

3. 無法正確處理參數是函數指針的函數。

經過我一番摸索,改進了這個方法,並在那個 issue 下面貼了改進後的代碼。也在這裡貼一下:

function! s:onCompleteDone() let abbr = v:completed_item.abbr let startIdx = stridx(abbr,"(") let endIdx = strridx(abbr,")") if endIdx - startIdx > 1 let argsStr = strpart(abbr, startIdx+1, endIdx - startIdx -1) "let argsList = split(argsStr, ",") let argsList = [] let arg = let countParen = 0 for i in range(strlen(argsStr)) if argsStr[i] == , && countParen == 0 call add(argsList, arg) let arg = elseif argsStr[i] == ( let countParen += 1 let arg = arg . argsStr[i] elseif argsStr[i] == ) let countParen -= 1 let arg = arg . argsStr[i] else let arg = arg . argsStr[i] endif endfor if arg != && countParen == 0 call add(argsList, arg) endif else let argsList = [] endif let snippet = ( let c = 1 for i in argsList if c > 1 let snippet = snippet . ", " endif " strip space let arg = substitute(i, ^s*(.{-})s*$, 1, ) let snippet = snippet . ${ . c . ":" . arg . } let c += 1 endfor let snippet = snippet . ) . "$0" return UltiSnips#Anon(snippet)endfunctionautocmd VimEnter * imap <expr> ( pumvisible() && exists(v:completed_item) && !empty(v:completed_item) && v:completed_item.word != && v:completed_item.kind == f ? "<C-R>=<SID>onCompleteDone()<CR>" : "<Plug>delimitMate("

使用改進後的方法後,輸入左括弧時才會進行函數參數的補全,並且部分解決了函數參數是函數指針的問題。不過由於這個問題我並不是依靠 Clang 的語義分析來解決的,而是直接進行左右括弧的匹配,一定會存在仍不能解決的情況。不過大部分情況應該都沒問題了。另外我使用了 delimitMate,上面這個配置也兼容 delimitMate。

另外再提供兩個 Vim 的實用配置。

用 Tab 鍵進行 delimitMate 的游標跳轉(也就是說,輸入左括弧後使用 Tab 鍵就可跳轉到 delimitMate 生成的右括弧的右邊,而無需 <S-TAB>),且不破壞 UltiSnips 的 Tab 鍵展開,同時禁用 delimitMate 自帶的 <S-TAB>:

autocmd VimEnter * imap <silent> <expr> <TAB> delimitMate#ShouldJump() ? delimitMate#JumpAny() : "<C-r>=UltiSnips#ExpandSnippetOrJump()<CR>"autocmd VimEnter * inoremap <S-TAB> <S-TAB>

讓補全下拉菜單支持用回車鍵選擇補全結果,同時不破壞 endwise 的回車鍵:

autocmd VimEnter * imap <expr> <CR> pumvisible() ? (exists(v:completed_item) && !empty(v:completed_item) && v:completed_item.word != && v:completed_item.kind == f) ? "<C-R>=<SID>onCompleteDone()<CR>" : "<C-y>" : "<Plug>delimitMateCR<Plug>DiscretionaryEnd"

好了,這樣我的 Vim 終於可以像 IDE 一樣愉快地進行補全了。


推薦閱讀:

如何通過vim快捷鍵瀏覽知乎及其他網站 ?
Vim - 配置IDE一般的python環境
Vim 在插入模式下怎麼用 hjkl 控制方向?
Vim 對特定行處理常用方法(三):奇偶行分離(及寄存器入門)
Vim 對特定行處理常用方法(四):刪除、壓縮重複行

TAG:Vim |