Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

Contents

소개

NeoVim과 CoC를 이용해서 개발 환경을 만들기로 했다. Flutter개발 환경을 vi 기반으로 해보려고 하다가 결국 문서를 정리하게 됐다.

개발 환경

  • 운영체제 : 우분투리눅스 19.04
  • 사용언어 : Go, Python, Dart, Java

NeoVim 설치

software-properties-common은 apt 레포지토리 관리를 위한 툴들을 제공한다. 이 툴을 이용해서 배포판을 관리를 쉽게 할 수 있다.
# sudo apt-get install software-properties-common

neovim을 설치한다.
# sudo add-apt-repository ppa:neovim-ppa/stable
# sudo apt-get update
# sudo apt-get install neovim

neovim은 파이선모듈을 요구한다. 파이선 관련 패키지를 설치하자.
# sudo apt-get install python-dev python-pip python3-dev python3-pip

nvim으로 neovim을 실행 할 수 있다.

NeoVim 설정파일의 위치는 ~/.config/nvim/init.vim이다. 설정내용은 vim과 차이가 없다.

coc.nvim

자동완성(auto completion)을 위해서 coc.nvim을 설치하기로 했다. coc(Conquer of Completion)은 vim 8.0이상 neovim 0.3.1이상에서 사용 할 수 있는 intellisense 엔진이다. LSP(Language Server Protocol)를 완전히 지원한다.

LSP

LSP는 마이크로소프트사의 Visual Studio Code를 위해서 만든 프로토콜이다. VS Code는 다양한 언어를 지원하는 IDE이로, 언어마다 코드완성, 다양한 편집기능, 함수로의 이동, 코드검사등을 만들어야 했다. 이는 IDE에서 프로그래밍 언어의 스캐너, 파서, 형식 검사기 등등의 도메인 모델을 개발해야 한다는 얘기다. 이것은 매우 복잡한 작업으로 IDE를 확장하는데 큰 걸림돌이다. 이 문제를 해결하기 위해서 서버 & 클라이언트 기반의 언어 서버환경을 만들었다.

 LSP의 장점

각 언어를 위한 언어서버를 IDE에서 분리하는 방식이다. IDE는 언어서버에 요청을 하면, 자동완성, 함수 이동, 기타 메시지등을 제공을 한다. IDE는 언어 도메인에 대한 복잡하고 세부적인 구현을 하지 않고도, 언어를 통합 할 수 있다. IDE와 언어서버는 JSON RPC를 이용해서 통신한다.

 LSP 작동원리

coc.nvim 설치

nodejs를 설치한다.
# curl -sL install-node.now.sh/lts | bash

yarn을 설치한다. Yarn은 자바스크립트의 새로운 패키지 매니저다. npm에 비해서 보다 안정적이고 보안성이 뛰어나다고 한다.
# curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
# echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
# sudo apt-get update && sudo apt-get install yarn

vim-plug를 설치한다. vim-plug는 vim을 위한 플러그인 관리자다. vim과 neovim 모두 사용 할 수 있다.
# curl -fLo ~/.local/share/nvim/site/autoload/plug.vim --create-dirs \
    https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
init.vim에 설치할 플러그인을 설정하면 된다.
call plug#begin('~/.config/nvim/plugged')
" Use release branch
Plug 'neoclide/coc.nvim', {'branch': 'release'}
" Or latest tag
Plug 'neoclide/coc.nvim', {'tag': '*', 'branch': 'release'}
" Or build from source code by use yarn: https://yarnpkg.com
Plug 'neoclide/coc.nvim', {'do': 'yarn install --frozen-lockfile'}
call plug#end()
플러그인들은 plug#begin에 설정한 ~/.config/nvim/plugged 에 저장된다.

파일을 수정한다음 PlugInstall명령을 실행하자. nvim을 실행 한다음 :PlugInstall명령을 실행해도된다. 플러그인을 다운로드하고 (필요할 경우)빌드해서 설치하는 것을 확인 할 수 있다.
# nvim +PlugInstall
테스트 삼아서 NerdTree플러그인을 설치해봤다. Go 자동완성과 GoDoc 같은 유틸리티를 이용하기 위해서 vim-go, gocode 플러그인도 설치했다.
call plug#begin()
Plug 'scrooloose/nerdtree'
Plug 'neoclide/coc.nvim', {'branch': 'release'}
Plug 'neoclide/coc.nvim', {'tag': '*', 'branch': 'release'}
Plug 'neoclide/coc.nvim', {'do': 'yarn install --frozen-lockfile'}
Plug 'fatih/vim-go', { 'tag': '*'  }
Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim'  }
call plug#end()
새로 추가한 플러그인만 찾아서 잘 설치해준다. 지금까지 pathogen을 사용했는데, vim-plug로 갈아타야 겠다.

개발 환경 만들기

Coc로 언어 개발 환경을 만들기로 했다. nvim을 실행해서 :CocInstall coc-json명령을 실행했다. CocInstall은 Coc 확장을 설치하는 명령이다.

:CocConfig명령을 입력하면, CoC 설정파일을 오픈한다. 설정파일의 위치는 ./config/nvim/coc-settings.json이다. 설정파일은 json 형식을 따른다. 설정 방법은 아래와 같다.
{
  "languageserver": {
    "golang": {
      "command": "gopls",
      "rootPatterns": ["go.mod", ".vim/", ".git/", ".hg/"],
      "filetypes": ["go"]
    },
    "ccls": {
      "command": "ccls",
      "filetypes": ["c", "cpp", "objc", "objcpp"],
      "rootPatterns": [".ccls", "compile_commands.json", ".vim/", ".git/", ".hg/"],
      "initializationOptions": {
         "cache": {
           "directory": "/tmp/ccls"
         }
       }
    }
  }
}
langguageserver각 언어별 언어서버를 설정하면 된다. 많은 주요 언어들이 언어서버를 개발해서 배포하고 있다.

Go 환경
Go는 gopls라는 언어서버를 제공한다.
"languageserver": {
  "golang": {
    "command": "gopls",
    "rootPatterns": ["go.mod", ".vim/", ".git/", ".hg/"],
    "filetypes": ["go"]
  }
}
설정파일을 수정하고 :CocInstall coc-go명령을 수행한다. go 파일을 만들어보자. 제대로 작동하기 위해서는 gopls가 설치돼야 하는데, coc-go가 자동으로 설치해준다. 30초 정도면 설치가 끝난다. 잘 작동하는지 테스트...

Python 환경
설정내용은 아래와 같다.
"python": {
   "command": "python",
   "args": [
     "-mpyls",
     "-vv",
     "--log-file",
     "/tmp/lsp_python.log"
   ],
   "trace.server": "verbose",
   "filetypes": [
     "python"
   ],
   "settings": {
     "pyls": {
       "enable": true,
       "trace": {
         "server": "verbose"
       },
       "commandPath": "",
       "configurationSources": [
         "pycodestyle"
       ],
       "plugins": {
         "jedi_completion": {
           "enabled": true
         },
         "jedi_hover": {
           "enabled": true
         },
         "jedi_references": {
           "enabled": true
         },
         "jedi_signature_help": {
           "enabled": true
         },
         "jedi_symbols": {
           "enabled": true,
           "all_scopes": true
         },
         "mccabe": {
           "enabled": true,
           "threshold": 15
         },
         "preload": {
           "enabled": true
         },
         "pycodestyle": {
           "enabled": true
         },
         "pydocstyle": {
           "enabled": false,
           "match": "(?!test_).*\\.py",
           "matchDir": "[^\\.].*"
         },
         "pyflakes": {
           "enabled": true
         },
         "rope_completion": {
           "enabled": true
         },
         "yapf": {
           "enabled": true
        }
      }
    }
  }
}
":CocInstall coc-python", ":CocInstall coc-pypl" 를 설치한다. python 코딩테스트..

Dart 환경
Dart는 dart_language_server라는 언어서버를 제공한다. 이 녀석을 설치했다.
# pub global activate dart_language_server
Resolving dependencies... (4.2s)
+ analysis_server_lib 0.1.7
+ args 1.5.2
+ async 2.2.0
+ collection 1.14.11
+ dart_language_server 0.1.14
+ json_rpc_2 2.1.0
+ logging 0.11.3+2
+ meta 1.1.7
......
Activated dart_language_server 0.1.14.
Coc 설정파일에 아래 내용을 추가했다.
"dart": {
  "command": "dart_language_server",
  "args": [],
  "filetypes": ["dart"],
  "initializationOptions": {},
  "settings": {
    "dart": {
      "validation": {},
      "completion": {}
    }
  }
}
그리고 아래의 Dart plugin을 추가했다.
Plug 'dart-lang/dart-vim-plugin'

Java 환경
OpenJDK 11을 설치했다.
# sudo apt-get install openjdk-11-jdk
nvim에서 ":CocInstall coc-java"를 수행한다.

Java 언어서버는 eclipse.org에서 제공하는 jdt.ls를 사용한다. CocInstall coc-java명령을 실행하면 다운로드와 빌드를 수행하는데, 상당히 많은 시간이 걸린다. 나는 한 30분 정도 걸렸던 거 같은데, 참고하자.

init.vim 설정

사용 중인 init.vim 설정
set ts=4  " if you use terminator
" Plugins will be downloaded under the specified directory.
 
" List ends here. Plugins become visible to Vim after this call.
set autoindent
set cindent
set smartindent
set tabstop=4
set shiftwidth=4
set title
set wrap
set linebreak
set showmatch                            
set mouse=r
set laststatus=2
let g:go_def_mode='gopls'
let g:go_info_mode='gopls'       

inoremap <silent><expr> <TAB>   
    ¦ \ pumvisible() ?  "<C-n>" :
    ¦ \ coc#expandableOrJumpable() ? coc#rpc#request('doKeymap', ['snippets-expand-jump','']) :
    ¦ \ <SID>check_back_space() ? "\<TAB>" :
    ¦ \ coc#refresh()                         
function! s:check_back_space() abort
  let col = col('.') - 1
  return !col || getline('.')[col - 1]  =~# '\s'
endfunction

let g:coc_snippet_next = '<tab>'

"let g:ycm_keep_logfiles = 1
"let g:ycm_log_level = 'debug'
"/usr/share/nvim/runtime/colors
let g:promptline_theme = 'dracula'
let g:lightline = {
    \ 'colorscheme' : 'dracula',
    \ }
"let $NVIM_TUI_ENABLE_TRUE_COLOR=1
"set termguicolors
au BufReadPost *
\ if line("'\"") > 0 && line("'\"") <= line("$") |
\ exe "norm g`\"" |
\ endif
" Specify a directory for plugins
" " - For Neovim: ~/.local/share/nvim/plugged
" " - Avoid using standard Vim directory names like 'plugin'
 call plug#begin('~/.config/nvim/plugged')
"
" " Make sure you use single quotes
"
"" Shorthand notation; fetches https://github.com/junegunn/vim-easy-align
Plug 'neoclide/coc.nvim', {'do': 'yarn install --frozen-lockfile'}
Plug 'dracula/vim'
Plug 'junegunn/vim-easy-align'
" Any valid git URL is allowed
Plug 'https://github.com/junegunn/vim-github-dashboard.git'
"
" " Multiple Plug commands can be written in a single line using | separators
 Plug 'SirVer/ultisnips' | Plug 'honza/vim-snippets'
"
"
"" On-demand loading
Plug 'scrooloose/nerdtree', { 'on':  'NERDTreeToggle'  }
 
Plug 'tpope/vim-fireplace', { 'for': 'clojure'  }
 
" Using a non-master branch
Plug 'rdnetto/YCM-Generator', { 'branch': 'stable'  }
" Plug 'Valloric/YouCompleteMe'
"
" " Using a tagged release; wildcard allowed (requires git 1.9.2 or above)
Plug 'fatih/vim-go', { 'tag': '*'  }
"
"" Plugin options
Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim'  }
" Plugin outside ~/.vim/plugged with post-update hook
Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all'  }
 
" Unmanaged plugin (manually installed and updated)
Plug '~/my-prototype-plugin'
Plug 'itchyny/lightline.vim'
 
Plug 'airblade/vim-gitgutter' "추가,삭제,변경내역
Plug 'tpope/vim-fugitive' "깃 연동
 
Plug 'scrooloose/syntastic' "문법 체크
Plug 'Lokaltog/vim-easymotion' "커서이동
 
Plug 'scrooloose/nerdcommenter' "주석
Plug 'edkolev/promptline.vim' "쉘프롬프트
 
Plug 'vim-scripts/vcscommand.vim' "소스 버전 컨트롤
"먼지
Plug 'majutsushi/tagbar'
 
Plug 'xuhdev/SingleCompile'
 
Plug 'terryma/vim-multiple-cursors'
"위까지 모름
Plug 'jiangmiao/auto-pairs' "짝맞추기
Plug 'vim-scripts/taglist.vim'
 
Plug 'Yggdroot/indentLine' "들여쓰기 세로줄 라인
Plug 'mhinz/vim-signify' "버전 관리 파일 상태표시
 
Plug 'pangloss/vim-simplefold' "코드접기
Plug 'peterrincker/vim-argumentative' "함수 인자 위치변경
 
Plug 'tommcdo/vim-lion' "라인정렬
Plug 'dyng/ctrlsf.vim' "여러 파일 동시에 수정

Plug 'dart-lang/dart-vim-plugin'
 
Plug 'schickling/vim-bufonly' "현재 버퍼뺴고 모든 버퍼 삭제
Plug 'sjl/gundo.vim' "수정 되돌리기 트리
 
Plug 'andrewradev/sideways.vim' "단어 좌우 이동 ,로 분리된거
Plug 'octol/vim-cpp-enhanced-highlight'
 
Plug 'iamcco/markdown-preview.nvim'
 
"
call plug#end()
"key map setting
nmap <F3> :NERDTreeToggle<CR>
noremap <C-h> <C-w>h
noremap <C-j> <C-w>j
noremap <C-k> <C-w>k
noremap <C-l> <C-w>l
 
inoremap <silent><expr> <cr> pumvisible() ? coc#_select_confirm() : 
    ¦   ¦   ¦   ¦   ¦   ¦   ¦   ¦   ¦   ¦  \"\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"
let g:cpp_class_scope_highlight = 1
let g:cpp_member_variable_highlight = 1
let g:cpp_class_decl_highlight = 1
let g:cpp_experimental_template_highlight = 1
let g:cpp_concepts_highlight = 1
 
"change parameter position
nnoremap <c-a> :SidewaysLeft<cr>
nnoremap <c-s> :SidewaysRight<cr>
 
nmap<F10> :SingleCompileRun<cr> "F10누르면 컴파일후 실행, 누르기전에 저장해야함
nmap<F9> :SingleCompile<cr>  "F9누르면 컴파일 
 
let g:Tlist_Use_Right_Window = 1
nnoremap <silent> <F4> :TlistToggle<Cr>

정리

  • 앞으로 개발환경은 Coc 만 믿고가야겠다.
  • Python 같은 경우에는 PyLint 기반으로 코드의 문제점도 확인해 준다. 그냥 문제점만 확인해 주는게 아니고 pep8 스타일로 점검해 준다. 메시지가 너무 많아서, 사소한 것들을 예외처리해서 사용하고 있다. 코딩 스타일 잡을 때 도움이 될 것 같다.
  • 코드 intellisense 엔진이 서버로 분리되면서, vim 기반 환경도 크게 좋아질 것 같다. 이전에는 YCM같은 녀석이 있긴 했는데, 품질이 별로였다.