본문 바로가기

C

C언어 개발 환경 구축 (4) - 자동완성, CoC 그리고 bear

기존에는 자동완성 도구로 YCM (YouCompleteMe)를 사용하였으나 설치 및 설정이 불편한 문제 때문에 CoC로 변경하여 사용하고 있다.

 

https://github.com/neoclide/coc.nvim

 

GitHub - neoclide/coc.nvim: Nodejs extension host for vim & neovim, load extensions like VSCode and host language servers.

Nodejs extension host for vim & neovim, load extensions like VSCode and host language servers. - GitHub - neoclide/coc.nvim: Nodejs extension host for vim & neovim, load extensions like VSC...

github.com

설치 및 사용은 기존 https://onurmark.tistory.com/3 에서 설명한 부분에서 몇가지 변경하여 설정한다.

 

call plug#begin('~/.vim/plugged')

Plug 'ctrlpvim/ctrlp.vim'
Plug 'SirVer/ultisnips'
Plug 'honza/vim-snippets'
Plug 'vim-airline/vim-airline'
Plug 'vim-airline/vim-airline-themes'
Plug 'airblade/vim-gitgutter'
Plug 'tpope/vim-fugitive'
" Plug 'Valloric/YouCompleteMe'                             <---- 제거
" Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' }      <---- 제거
Plug 'neoclide/coc.nvim', {'branch': 'release'}             <---- 추가
Plug 'flazz/vim-colorschemes'
Plug 'tomasr/molokai'

... 아래 설정 추가 ...

" Use <c-space> to trigger completion.
if has('nvim')
  inoremap <silent><expr> <c-space> coc#refresh()
else
  inoremap <silent><expr> <c-@> coc#refresh()
endif

" Make <CR> auto-select the first completion item and notify coc.nvim to
" format on enter, <cr> could be remapped by other vim plugin
inoremap <silent><expr> <cr> pumvisible() ? coc#_select_confirm()
  \: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"

" Use `[g` and `]g` to navigate diagnostics
" Use `:CocDiagnostics` to get all diagnostics of current buffer in location list.
nmap <silent> [g <Plug>(coc-diagnostic-prev)
nmap <silent> ]g <Plug>(coc-diagnostic-next)

" GoTo code navigation.
nmap <silent> gd <Plug>(coc-definition)
nmap <silent> gy <Plug>(coc-type-definition)
nmap <silent> gi <Plug>(coc-implementation)
nmap <silent> gr <Plug>(coc-references)

" Use K to show documentation in preview window.
nnoremap <silent> K :call <SID>show_documentation()<CR>

function! s:show_documentation()
  if (index(['vim','help'], &filetype) >= 0)
    execute 'h '.expand('<cword>')
  elseif (coc#rpc#ready())
    call CocActionAsync('doHover')
  else
    execute '!' . &keywordprg . " " . expand('<cword>')
  endif
endfunction

" Highlight the symbol and its references when holding the cursor.
autocmd CursorHold * silent call CocActionAsync('highlight')

" Symbol renaming.
nmap <leader>rn <Plug>(coc-rename)

" Formatting selected code.
xmap <leader>f  <Plug>(coc-format-selected)
nmap <leader>f  <Plug>(coc-format-selected)

augroup mygroup
  autocmd!
  " Setup formatexpr specified filetype(s).
  autocmd FileType typescript,json setl formatexpr=CocAction('formatSelected')
  " Update signature help on jump placeholder.
  autocmd User CocJumpPlaceholder call CocActionAsync('showSignatureHelp')
augroup end

" Applying codeAction to the selected region.
" Example: `<leader>aap` for current paragraph
xmap <leader>a  <Plug>(coc-codeaction-selected)
nmap <leader>a  <Plug>(coc-codeaction-selected)

" Remap keys for applying codeAction to the current buffer.
nmap <leader>ac  <Plug>(coc-codeaction)
" Apply AutoFix to problem on the current line.
nmap <leader>qf  <Plug>(coc-fix-current)

" Map function and class text objects
" NOTE: Requires 'textDocument.documentSymbol' support from the language server.
xmap if <Plug>(coc-funcobj-i)
omap if <Plug>(coc-funcobj-i)
xmap af <Plug>(coc-funcobj-a)
omap af <Plug>(coc-funcobj-a)
xmap ic <Plug>(coc-classobj-i)
omap ic <Plug>(coc-classobj-i)
xmap ac <Plug>(coc-classobj-a)
omap ac <Plug>(coc-classobj-a)

" Remap <C-f> and <C-b> for scroll float windows/popups.
if has('nvim-0.4.0') || has('patch-8.2.0750')
  nnoremap <silent><nowait><expr> <C-f> coc#float#has_scroll() ? coc#float#scroll(1) : "\<C-f>"
  nnoremap <silent><nowait><expr> <C-b> coc#float#has_scroll() ? coc#float#scroll(0) : "\<C-b>"
  inoremap <silent><nowait><expr> <C-f> coc#float#has_scroll() ? "\<c-r>=coc#float#scroll(1)\<cr>" : "\<Right>"
  inoremap <silent><nowait><expr> <C-b> coc#float#has_scroll() ? "\<c-r>=coc#float#scroll(0)\<cr>" : "\<Left>"
  vnoremap <silent><nowait><expr> <C-f> coc#float#has_scroll() ? coc#float#scroll(1) : "\<C-f>"
  vnoremap <silent><nowait><expr> <C-b> coc#float#has_scroll() ? coc#float#scroll(0) : "\<C-b>"
endif

" Use CTRL-S for selections ranges.
" Requires 'textDocument/selectionRange' support of language server.
nmap <silent> <C-s> <Plug>(coc-range-select)
xmap <silent> <C-s> <Plug>(coc-range-select)

" Add `:Format` command to format current buffer.
command! -nargs=0 Format :call CocAction('format')

" Add `:Fold` command to fold current buffer.
command! -nargs=? Fold :call     CocAction('fold', <f-args>)

" Add `:OR` command for organize imports of the current buffer.
command! -nargs=0 OR   :call     CocAction('runCommand', 'editor.action.organizeImport')

" Add (Neo)Vim's native statusline support.
" NOTE: Please see `:h coc-status` for integrations with external plugins that
" provide custom statusline: lightline.vim, vim-airline.
set statusline^=%{coc#status()}%{get(b:,'coc_current_function','')}

...

Plug 'neoclide/coc.nvim', {'branch': 'release'} 를 추가하여 vim-plug가 coc를 설치할 수 있도록 설정 한다. 그 아래는 단축키 및 coc의 설정인데 복잡해 보이지만 그리 어렵지 않다. 일단은 그대로 사용해 보고 자기 손에 맞게 수정하여 사용하도록 한다.

 

저장 후 vim의 명령줄에 :PlugInstall 을 입력하여 CoC를 설치한다.

 

CoC가 동작하기 위해서는 v12이상의 nodejs가 필요하다.  ubuntu의 apt에서는 현재 v10 버전을 배포하기 때문에 다른 방법을 이요앟여 설치를 한다.

 

 nvm은 nodejs를 버전별로 설치를 손쉽게 할 수 있도록 도와주는 패키지 매니저이다. 

https://github.com/nvm-sh/nvm#installing-and-updating

 

GitHub - nvm-sh/nvm: Node Version Manager - POSIX-compliant bash script to manage multiple active node.js versions

Node Version Manager - POSIX-compliant bash script to manage multiple active node.js versions - GitHub - nvm-sh/nvm: Node Version Manager - POSIX-compliant bash script to manage multiple active nod...

github.com

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash

위와 같이 설치 후 source ~/.bashrc를 하거나 재로그인 하여 nvm 명령을 사용할 수 있도록 하자.

nvm install node

위와 같이 입력하면 가장 최신버전의 nodejs를 설치하게 된다.

 

C 언어 분석을 위한 language server인 clangd를 설치한다.

sudo apt install clangd

vi를 실행하여 C언어 자동완성을 위한 CoC 플러그인을 다음과 같이 설치하자

:CocInstall coc-clangd

이렇게 하면 자동완성을 위한 기초 설정은 모두 완료되었다.

 

자동완성을 수행하기 위해서 bear 패키지를 설치한다. 이는 컴파일 과정을 분석해서 compile_commands.json 파일을 생성해준다. 컴파일에 필요한 명령어와 옵션을 자동으로 분석하여 생성해주면 CoC와 clangd가 컴파일 오류와 자동완성을 실시간으로 번역해 보여준다.

 

sudo apt install bear

 

자동완성이 정상적으로 수행되는지 알아보기 위해 다음 소스로 테스트를 수행해보자

$ git clone https://github.com/onurmark/amhello
$ autoreconf -i
$ ./configure

만일 autoreconf 과정에서 오류가 발생한다면 sudo apt install autoconf libtool make를 입력하여 필요한 도구들을 설치한다.

 

자 이제 bear가 make시에 컴파일 옵션을 분석하여 compile_commands.json을 출력하도록 한다.

bear make
ubuntu@vim:~/amhello$ bear make
make  all-recursive
make[1]: Entering directory '/home/ubuntu/amhello'
Making all in lib
make[2]: Entering directory '/home/ubuntu/amhello/lib'
/bin/bash ../libtool  --tag=CC   --mode=compile gcc -DHAVE_CONFIG_H -I. -I..     -g -O2 -MT greeting.lo -MD -MP -MF .deps/greeting.Tpo -c -o greeting.lo greeting.c
libtool: compile:  gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -MT greeting.lo -MD -MP -MF .deps/greeting.Tpo -c greeting.c  -fPIC -DPIC -o .libs/greeting.o
libtool: compile:  gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -MT greeting.lo -MD -MP -MF .deps/greeting.Tpo -c greeting.c -o greeting.o >/dev/null 2>&1
mv -f .deps/greeting.Tpo .deps/greeting.Plo
/bin/bash ../libtool  --tag=CC   --mode=link gcc  -g -O2 -version-info 0:0:0  -o libgreeting.la -rpath /usr/local/lib greeting.lo
libtool: link: gcc -shared  -fPIC -DPIC  .libs/greeting.o    -g -O2   -Wl,-soname -Wl,libgreeting.so.0 -o .libs/libgreeting.so.0.0.0
libtool: link: (cd ".libs" && rm -f "libgreeting.so.0" && ln -s "libgreeting.so.0.0.0" "libgreeting.so.0")
libtool: link: (cd ".libs" && rm -f "libgreeting.so" && ln -s "libgreeting.so.0.0.0" "libgreeting.so")
libtool: link: ar cr .libs/libgreeting.a  greeting.o
libtool: link: ranlib .libs/libgreeting.a
...

정상적으로 컴파일이 수행되고 같은 디렉토리에 compile_command.json이 생성되는 것을 확인 할 수 있다.

 

그럼 이제 vi를 입력하여 정상적으로 자동완성 및 syntax error가 출력이 되는지 확인해 보자

 

자동완성 및 syntax 에러 처리가 정상적으로 수행되는 것을 볼 수 있다.