🔧

自分のinit.vimを解説してみる(Windows対応とかカラースキームの自動ダウンロードとかプラグインマネージャの自動インストールとか)

2022/05/21に公開

自分のinit.vimを解説してみました。
(途中で.vimrcの内容も解説しています。)

最近Vim始めたけどどういう設定したらいいのかわからないという人や、最近プラグインをインストールしてみて快適になったけど代わりに初期セットアップが少しめんどうになったから自動化したいと考えてる人、MacでもWindowsでも設定ファイル使いまわせるようにしたい人、などなどの参考になったらいいなぁと思ってます。(あと未来の自分がなんでこんなコードになってるのかわからなくなったとき用に...)

説明とかいいからコード読みたいって人へ

設定ファイルそのまま置いておきます。

init.vim
init.vim
" vimの設定を読み込む
if has('win64')
    source ~\_vimrc
else
    source ~/.vimrc
endif


" XDG Base Directory Specification
let s:config_home = has('win64') ? $LOCALAPPDATA : empty($XDG_CONFIG_HOME) ? $HOME . '/.config' : $XDG_CONFIG_HOME


" vim-plugがインストールされていなかったらインストールする
let s:plug_path = has('win64') ? $LOCALAPPDATA . '\nvim-data\site\autoload\plug.vim' : $HOME . '/.local/share/nvim/site/autoload/plug.vim'
if !filereadable(s:plug_path)
    call g:DownloadIfNotFileReadable(s:plug_path, 'https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim')

    " ついでにプラグインもインストールする
    if has('win64')
        " Windowsの場合
        source ~/AppData/Local/nvim/plugin_settings/vim-plug_setting.vim
    else
        " Windows以外の場合
        if empty($XDG_CONFIG_HOME)
            source ~/.config/nvim/plugin_settings/vim-plug_setting.vim
        else
            source $XDG_CONFIG_HOME/nvim/plugin_settings/vim-plug_setting.vim
        endif
    endif
    PlugInstall
endif


" ここからneovim向けの設定
" カラースキームの設定
" gruvboxがローカルに無かったらダウンロードする
call g:DownloadIfNotFileReadable(has('win64') ? s:config_home . '\nvim\colors\gruvbox.vim' : s:config_home . '/nvim/colors/gruvbox.vim', 'https://raw.githubusercontent.com/morhetz/gruvbox/master/colors/gruvbox.vim')
" カラースキームをgruvboxに指定する
colorscheme gruvbox


" コマンドラインを2行にする
set cmdheight=2


" 開いたファイルがあるディレクトリに自動でcdする
set autochdir


" undo履歴を保持し続ける
if has('persistent_undo')
    set undodir=~/.vimundo
    set undofile
endif


" python3のパスを指定
if has('win64')
    if !empty(system('where /Q python && echo has'))
        let g:python3_host_prog = s:config_home . '\Programs\Python\Python37-32\python.exe'
    endif
else
    let g:python3_host_prog = system("bash -c 'echo -n $(which python3)'")
endif


" filetypeの追加
autocmd BufNewFile,BufRead *.fish setfiletype fish
autocmd BufNewFile,BufRead *.vim setfiletype vim
autocmd BufNewFile,BufRead *.swift setfiletype swift
autocmd BufNewFile,BufRead *.kt setfiletype kotlin


" プラグインの設定
" vim-plugの設定を読み込む
if has('win64')
    source ~/AppData/Local/nvim/plugin_settings/vim-plug_setting.vim
elseif !empty($XDG_CONFIG_HOME)
    source $XDG_CONFIG_HOME/nvim/plugin_settings/vim-plug_setting.vim
else
    source ~/.config/nvim/plugin_settings/vim-plug_setting.vim
endif

" vim-indent-guidesの設定を読み込む
if has('win64')
    source ~/AppData/Local/nvim/plugin_settings/vim-indent-guides_setting.vim
elseif !empty($XDG_CONFIG_HOME)
    source $XDG_CONFIG_HOME/nvim/plugin_settings/vim-indent-guides_setting.vim
else
    source ~/.config/nvim/plugin_settings/vim-indent-guides_setting.vim
endif

" asyncompleteの設定を読み込む
if has('win64')
    source ~/AppData/Local/nvim/plugin_settings/asyncomplete_setting.vim
elseif !empty($XDG_CONFIG_HOME)
    source $XDG_CONFIG_HOME/nvim/plugin_settings/asyncomplete_setting.vim
else
    source ~/.config/nvim/plugin_settings/asyncomplete_setting.vim
endif

" aleの設定を読み込む
if has('win64')
    source ~/AppData/Local/nvim/plugin_settings/ale_setting.vim
elseif !empty($XDG_CONFIG_HOME)
    source $XDG_CONFIG_HOME/nvim/plugin_settings/ale_setting.vim
else
    source ~/.config/nvim/plugin_settings/ale_setting.vim
endif

" vim-lspの設定を読み込む
if has('win64')
    source ~/AppData/Local/nvim/plugin_settings/vim-lsp_setting.vim
elseif !empty($XDG_CONFIG_HOME)
    source $XDG_CONFIG_HOME/nvim/plugin_settings/vim-lsp_setting.vim
else
    source ~/.config/nvim/plugin_settings/vim-lsp_setting.vim
endif

" vim-closetagの設定を読み込む
if has('win64')
    source ~/AppData/Local/nvim/plugin_settings/vim-closetag_setting.vim
elseif !empty($XDG_CONFIG_HOME)
    source $XDG_CONFIG_HOME/nvim/plugin_settings/vim-closetag_setting.vim
else
    source ~/.config/nvim/plugin_settings/vim-closetag_setting.vim
endif

" Neosnippetの設定を読み込む
if has('win64')
    source ~/AppData/Local/nvim/plugin_settings/neosnippet_setting.vim
elseif !empty($XDG_CONFIG_HOME)
    source $XDG_CONFIG_HOME/nvim/plugin_settings/neosnippet_setting.vim
else
    source ~/.config/nvim/plugin_settings/neosnippet_setting.vim
endif


" myDE向けの設定を読み込む
if !empty($container_name)
    if !empty($XDG_CONFIG_HOME)
        source $SDG_CONFIG_HOME/nvim/plugin_settings/settings_for_myde.vim
    else
        source ~/.config/nvim/plugin_settings/settings_for_myde.vim
    endif
endif


" キーバインド
tnoremap <silent> <ESC> <C-\><C-n>
nnoremap <silent> <C-t><C-m> :split<CR> <C-w>j :terminal<CR> :resize 6<CR> i
.vimrc
.vimrc
" 文字コードの設定
" バッファ内での文字コードの指定
set encoding=utf-8
" fileencodingの設定を適用できないとき用の対策
set modifiable
" 書き込むときのデフォルトの文字コードの指定
set fileencoding=utf-8
" 読み込むときの文字コードの指定(左の方が優先度が高い)
set fileencodings=utf-8,cp932,sjis,euc-jp


" 改行コードの設定
" 左の方が優先度が高い
if has('win64')
    " Windowsのときは<CR+LF>を最優先する
    set fileformats=dos,unix,mac
else
    " それ以外のときは<CR>を最優先する
    set fileformats=unix,dos,mac
endif


" 行番号を表示する
set number


" シンタックスハイライトをオンにする
syntax enable 


" カラースキームの設定
" ファイルが無かったらダウンロードする関数
function! g:DownloadIfNotFileReadable(file_path, remote_url) abort
    if filereadable(a:file_path)
        " ファイルがローカルに存在していたら何もしない
        return
    endif
    " OS別のcurlコマンド
    let l:curl_command = has('win64') ? 'curl.exe' : 'curl'
    " ファイルをダウンロードする
    let l:message = system(l:curl_command . ' -Lo ' . a:file_path . ' --create-dirs ' . a:remote_url)
    if l:message  !~# '.*% Total.*% Received.*% Xferd.*'
        " ダウンロードに失敗したらエラーメッセージを表示する
        echo 'error: ' . l:message
    endif
endfunction

" neovimから読み込まれていたときは処理しない
if !has('nvim')
    " gruvboxがなかったらダウンロードする
    call g:DownloadIfNotFileReadable(has('win64') ? $HOME . '\vimfiles\colors\gruvbox.vim' : $HOME . '/.vim/colors/gruvbox.vim', 'https://raw.githubusercontent.com/morhetz/gruvbox/master/colors/gruvbox.vim')

    " カラースキームをgruvboxに指定する
    colorscheme gruvbox
endif

" ライトテーマを使う
set background=light


"インデントの設定
" インデントにスペースを使う
set expandtab

" Tab文字の長さの設定
set tabstop=4

" 一つのインデントのスペースの数
set shiftwidth=4

" よくわからないけどとりあえず書いておく。
" backspaceしたときに一度に消されるスペースの数らしい
set softtabstop=4  

" 改行したときにインデントしてくれる
set autoindent

" インデントがスマートになる(らしい)
set smartindent


" 全角文字をちゃんと表示する
set ambiwidth=double


" 折り返したときの設定
" 折り返したときにインデントする
set breakindent
" 折り返したときの追加のインデントの深さを指定する
set breakindentopt=shift:0


" 行末にセミコロンを挿入する
function! InsertEndSemicolon() abort
    " カーソルの現在位置を取得する
    let s:currentPosition = getpos('.')

    " 行末にセミコロンがなかったら挿入する
    if !(getline('.') =~ ';$')
        execute ':normal A;'
    endif

    " カーソル位置を戻す
    call setpos('.', s:currentPosition)
endfunction

" ノーマルモードでセミコロンを押すとInsertEndSemicolon()が呼ばれるようにする
nnoremap <silent> ; :call InsertEndSemicolon()<CR>
" インサートモードでセミコロン2度押しでその場にセミコロンを入力できるようにする
inoremap <silent> ;; ;
" インサートモードでセミコロン押してからエスケープキーを押すと行末にセミコロンを挿入してからノーマルモードに戻る
inoremap <silent> ;<ESC> <ESC>:call InsertEndSemicolon()<CR>


" 言語ごとのインデントの設定
filetype indent on
autocmd FileType java setlocal shiftwidth=2 softtabstop=2
autocmd FileType typescriptreact setlocal shiftwidth=2 softtabstop=2


" ローカルのvimの設定を読み込む(set columnsの上書きとかをする)
" ファイルがあるときだけ読み込む
if filereadable($HOME . '/.vimrc_local')
    source ~/.vimrc_local
endif
vim-plug_setting.vim
vim-plug_setting.vim
" vim-plugの設定
call plug#begin(stdpath('data') . '/plugged')
    " <C--><C-->でコメントインアウトを切替できるようにするプラグイン
    Plug 'tomtom/tcomment_vim'

    " ノーマルモードで cs"' ってやったら"を'に置換してくれる
    Plug 'tpope/vim-surround'

    " インデントを可視化してくれるやつ
    Plug 'nathanaelkane/vim-indent-guides'

    " 自動で閉じ括弧とか入力してくれるやつ
    Plug 'cohama/lexima.vim'

    " 自動でHTMLの閉じタグを入力してくれるプラグイン
    Plug 'alvan/vim-closetag'

    " lintしてくれるやつ
    Plug 'w0rp/ale'

    " 補完に使うプラグインたち
    Plug 'prabirshrestha/async.vim'
    Plug 'prabirshrestha/vim-lsp'
    Plug 'mattn/vim-lsp-settings'
    Plug 'prabirshrestha/asyncomplete.vim'
    Plug 'prabirshrestha/asyncomplete-lsp.vim'
    Plug 'Shougo/neosnippet.vim'
    Plug 'Shougo/neosnippet-snippets'
    Plug 'prabirshrestha/asyncomplete-neosnippet.vim'
call plug#end()
vim-indent-guides_setting.vim
vim-indent-guides_setting.vim
let g:indent_guides_enable_on_vim_startup = 1
let g:indent_guides_start_level= 1
let g:indent_guides_guide_size = 0
autocmd VimEnter,Colorscheme * :hi IndentGuidesOdd	ctermbg=220
autocmd VimEnter,Colorscheme * :hi IndentGuidesEven	ctermbg=214
asyncomplete_setting.vim
asyncomplete_setting.vim
set completeopt+=preview

autocmd! CompleteDone * if pumvisible() == 0 | pclose | endif
ale_setting.vim
ale_setting.vim
" let g:ale_sign_column_always = 1
set signcolumn=yes
highlight SignColumn ctermbg=15
highlight SignColumn ctermfg=15
highlight SignColumn guisp=15
highlight SignColumn guifg=15
highlight SignColumn guibg=15
let g:ale_sign_error = '☠️'
let g:ale_sign_warning = '⚠️'
vim-lsp_setting.vim
vim-lsp_setting.vim
" ノーマルモードで"def"と入力したら変数や関数の定義元にジャンプするようにする
nnoremap <silent> def :LspDefinition<CR>

" "neosnippet"からの補完情報を取得するようにする
call asyncomplete#register_source(asyncomplete#sources#neosnippet#get_source_options({
    \ 'name': 'neosnippet',
    \ 'whitelist': ['*'],
    \ 'completor': function('asyncomplete#sources#neosnippet#completor'),
    \ }))
vim-closetag_setting.vim
vim-closetag_setting.vim
" filenames like *.xml, *.html, *.xhtml, ...
" These are the file extensions where this plugin is enabled.
"
let g:closetag_filenames = '*.html,*.xhtml,*.phtml'

" filenames like *.xml, *.xhtml, ...
" This will make the list of non-closing tags self-closing in the specified files.
"
let g:closetag_xhtml_filenames = '*.xhtml,*.jsx'

" filetypes like xml, html, xhtml, ...
" These are the file types where this plugin is enabled.
"
let g:closetag_filetypes = 'html,xhtml,phtml,typescriptreact'

" filetypes like xml, xhtml, ...
" This will make the list of non-closing tags self-closing in the specified files.
"
let g:closetag_xhtml_filetypes = 'xhtml,jsx'

" integer value [0|1]
" This will make the list of non-closing tags case-sensitive (e.g. `<Link>` will be closed while `<link>` won't.)
"
let g:closetag_emptyTags_caseSensitive = 1

" dict
" Disables auto-close if not in a "valid" region (based on filetype)
"
let g:closetag_regions = {
    \ 'typescript.tsx': 'jsxRegion,tsxRegion',
    \ 'javascript.jsx': 'jsxRegion',
    \ 'typescriptreact': 'jsxRegion,tsxRegion',
    \ 'javascriptreact': 'jsxRegion',
    \ }

" Shortcut for closing tags, default is '>'
"
let g:closetag_shortcut = '>'

" Add > at current position without closing the current tag, default is ''
"
let g:closetag_close_shortcut = '<leader>>'
neosnippet_setting.vim
neosnippet_setting.vim
" Ctrl + kで補完候補のコードを展開したりできるようにする
imap <C-k> <Plug>(neosnippet_expand_or_jump)
smap <C-k> <Plug>(neosnippet_expand_or_jump)
xmap <C-k> <Plug>(neosnippet_expand_target)
if has('conceal')
    set conceallevel=0 concealcursor=niv
endif
settings_for_myde.vim
settings_for_myde.vim
" dockerコンテナ内向けの設定
" うまくいかなかったときの保険として、'name'の直の最初に'myde-'をつける
" この3つ以外は、パスが通ってるとこにコマンド配置しておけば勝手にいいかんじにしてくれる(vim-lsp-settingが)
if executable('/home/myde/omnisharp-lsp/run')
  au User lsp_setup call lsp#register_server({
        \ 'name': 'myde-omnisharp-lsp',
        \ 'cmd': {server_info->['/home/myde/omnisharp-lsp/run', '-lsp']},
        \ 'whitelist': ['cs']
        \ })
endif

if executable('/home/myde/eclipse-jdt-ls/eclipse-jdt-ls')
  au User lsp_setup call lsp#register_server({
        \ 'name': 'myde-eclipse-jdt-ls',
        \ 'cmd': {server_info->['/home/myde/eclipse-jdt-ls/eclipse-jdt-ls']},
        \ 'whitelist': ['java']
        \ })
endif

if executable('/home/myde/myCommands/kotlin-language-server')
  au User lsp_setup call lsp#register_server({
        \ 'name': 'myde-kotlin-language-server',
        \ 'cmd': {server_info->['/home/myde/myCommands/kotlin-language-server']},
        \ 'whitelist': ['kotlin']
        \ })
endif

Vimの設定を読み込む

VimとNeovimの両方に適用したい設定は.vimrcに書くようにしているので読み込みます。

init.vim
if has('win64')
    source ~\_vimrc
else
    source ~/.vimrc
endif

Windowsとそれ以外とでファイル名が異なるのでif文で分岐しています。
(フォルダの区切りはWindowsでもそれ以外でも"/"で大丈夫だったような気がしますが一応分けてます。)

いきなりではありますが、ここからしばらくは.vimrcに記述している内容になります。

文字コードの設定

.vimrc
set encoding=utf-8

バッファ内での文字コードの設定だそうです。過去のぼくがそうコメントでそう言ってました。
ちなみにぼくはまだVimのバッファについてよくわかってないです(´・ω・`)

fileencodingの設定を適用できないとき用の対策

.vimrc
set modifiable

次で出てくるfileencodingというやつの設定を適用できないとき用の対策だそうです。過去のぼくがコメントで(ry
先にfileencodingのことを書いたほうがわかりやすいような気がしますがぼくの.vimrcにはこの順番で書いてあるので、なにか理由が有るのか無いのかわかりませんがこの順番で書いています。

書き込むときのデフォルトの文字コードの指定

.vimrc
set fileencoding=utf-8

ファイルに書き込むときの文字コードをUTF-8に指定しています。
調べ直してみたら開いたファイルの文字コードがなんであれUTF-8で保存されるようになるっぽいので設定しないほうがいいのかも?
でも現状困ってないからよし(よくない)
※不都合に出会ったらこの記事共々修正します。

読み込むときの文字コードの指定

.vimrc
set fileencodings=utf-8,cp932,sjis,euc-jp

ファイルを開くときにどの文字コードとして読み込むかを指定しています。
左のものから順に試してみて、問題なく開けたらその文字コードで開かれます。

指定してある文字コードのどれにもマッチしなかった場合は"encoding"に指定してある文字コードで読み込まれるそうです。[1]

改行コードの設定

.vimrc
if has('win64')
    " Windowsのときは<CR+LF>を最優先する
    set fileformats=dos,unix,mac
else
    " それ以外のときは<CR>を最優先する
    set fileformats=unix,dos,mac
endif

ファイルを開くときに、どの改行コードで開くかを指定しています。
Windowsの場合とそれ以外とで優先順位を変えています。
"fileencodings"と同じく、左にあるものから順に照らし合わせ、問題がなかったものが使われます。
新規ファイルの場合は一番左のものが使われます。

行番号を表示する

.vimrc
set number

画面左端に行番号を表示するようにします。

シンタックスハイライトをオンにする

.vimrc
syntax enable

文字が色分けされて表示されるようになります。(なぜかここだけZennのシンタックスハイライトが効いていませんが、コメント行や、if・elseなどの色が変わってたりするやつがそれです。)

ファイルが無かったらダウンロードする関数を定義

.vimrc
function! g:DownloadIfNotFileReadable(file_path, remote_url) abort
    if filereadable(a:file_path)
        " ファイルがローカルに存在していたら何もしない
        return
    endif
    " OS別のcurlコマンド
    let l:curl_command = has('win64') ? 'curl.exe' : 'curl'
    " ファイルをダウンロードする
    let l:message = system(l:curl_command . ' -Lo ' . a:file_path . ' --create-dirs ' . a:remote_url)
    if l:message  !~# '.*% Total.*% Received.*% Xferd.*'
        " ダウンロードに失敗したらエラーメッセージを表示する
        echo 'error: ' . l:message
    endif
endfunction

長いので分割します

.vimrc
function! g:DownloadIfNotFileReadable(file_path, remote_url) abort

"DownloadIfNotFileReadable"という名前で関数を定義しています。
"g:"というのはスコープを指定するもので、今回の場合はこのファイルの中でも外でも読み込まれてさえいればどこからでも呼び出せる、というスコープになります。
(init.vimからも使いたいのでこのスコープにしています。)

引数にはダウンロード先のパス、ダウンロードしたいファイルのURLを受け取るようにしています。

末尾に付いてる"abort"というのは、この関数内で何かエラーが発生したらその場で関数を抜けるようにする、というものです。
(ただし、このあと使うシェルコマンドの実行のときはそのまま継続されます。あくまでvimscriptの処理が失敗した場合、のようです。)


.vimrc
if filereadable(a:file_path)
    " ファイルがローカルに存在していたら何もしない
    return
endif

引数で渡されたパスにファイルが存在するか確認して、もしファイルがもう既に存在したら何もせずに関数を終了します。


.vimrc
" OS別のcurlコマンド
let l:curl_command = has('win64') ? 'curl.exe' : 'curl'

Windowsの場合、"curl"という文字列はcurlではない別のプログラムのエイリアスになっていることがあって、"curl.exe"まで打たないとちゃんとcurlを実行してくれないのでそのための対策です。[2]


.vimrc
let l:message = system(l:curl_command . ' -Lo ' . a:file_path . ' --create-dirs ' . a:remote_url)

curlコマンドを実行します。
エラーメッセージの出力に使いたいので、実行結果の出力を変数に保管しておきます。


.vimrc
if l:message  !~# '.*% Total.*% Received.*% Xferd.*'
    " ダウンロードに失敗したらエラーメッセージを表示する
    echo 'error: ' . l:message
endif

curlコマンドがもしエラーを吐いていたらエラーメッセージとして出力します。
条件分岐は正規表現を使って無理やり実現しています(´・ω・`)


カラースキームの設定(Vim版)

.vimrc
" neovimから読み込まれていたときは処理しない
if !has('nvim')
    " gruvboxがなかったらダウンロードする
    call g:DownloadIfNotFileReadable(has('win64') ? $HOME . '\vimfiles\colors\gruvbox.vim' : $HOME . '/.vim/colors/gruvbox.vim', 'https://raw.githubusercontent.com/morhetz/gruvbox/master/colors/gruvbox.vim')

    " カラースキームをgruvboxに指定する
    colorscheme gruvbox
endif

" ライトテーマを使う
set background=light

長いので分割

.vimrc
if !has('nvim')

VimとNeovimではカラースキームの保存先が異なるので、Vimのときだけ実行するようにしています。


.vimrc
call g:DownloadIfNotFileReadable(has('win64') ? $HOME . '\vimfiles\colors\gruvbox.vim' : $HOME . '/.vim/colors/gruvbox.vim', 'https://raw.githubusercontent.com/morhetz/gruvbox/master/colors/gruvbox.vim')

先ほど定義した関数を使って、gruvboxがなかった場合はダウンロードして取ってくるようにしています。


.vimrc
colorscheme gruvbox

カラースキームにgruvboxを指定しています。
Neovimの場合はダウンロード先が違い、この時点ではファイルが用意できていないので"if has('nvim')"のスコープ内にしています。


.vimrc
set background=light

ダークテーマかライトテーマかを選べます。ここではライトテーマにするよう指定しています。


インデントにスペースを使う

.vimrc
set expandtab

インデントするときに半角スペースを使うように指定しています。

Tab文字の長さの設定

.vimrc
set tabstop=4

Tab文字1つの長さを指定しています。
この場合は半角スペース4つ分になります。

インデント1つあたりのスペースの数

.vimrc
set shiftwidth=4

"set expandtab"してある状態でTabキーを押したときに入力される半角スペースの数です。

1度にまとめて削除されるスペースの数

.vimrc
set softtabstop=4

BACKSPACEキーを1回押すたびにまとめて削除される半角スペースの数を指定しています。

改行したときに自動でインデントしてくれるようにする

.vimrc
set autoindent

これを書いておくと改行したときに自動でインデントされるようになります。

オートインデントをスマートにする

.vimrc
set smartindent

"{"や"}"によってインデントの深さを変えてくれたりするようになります。

全角文字をちゃんと表示する

.vimrc
set ambiwidth=double

これを設定しないと絵文字がちゃんと表示されなかったりします。

折り返したときにインデントする

.vimrc
set breakindent

1行が長くて画面に収まりきらないときに折り返して表示してくれるようになります。

折り返したときの追加のインデントの深さを指定する

.vimrc
set breakindentopt=shift:0

1行が長すぎて折り返し表示したときに行の先頭をどこに合わせるかを設定しています。
この場合は元の行の先頭に合わせてくれるようになります。

行末にセミコロンを挿入する関数を定義

.vimrc
function! InsertEndSemicolon() abort
    " カーソルの現在位置を取得する
    let s:currentPosition = getpos('.')

    " 行末にセミコロンがなかったら挿入する
    if !(getline('.') =~ ';$')
        execute ':normal A;'
    endif

    " カーソル位置を戻す
    call setpos('.', s:currentPosition)
endfunction

コメント行に書いてある通りです。
(というかvim scriptでこんなちゃんとした処理書くことをあんまりしていないのでコメントに書いてある以上のことは今のぼくにはわからないです。。。)

定義した関数をキーショートカットで呼び出せるようにする

.vimrc
" ノーマルモードでセミコロンを押すとInsertEndSemicolon()が呼ばれるようにする
nnoremap <silent> ; :call InsertEndSemicolon()<CR>
" インサートモードでセミコロン2度押しでその場にセミコロンを入力できるようにする
inoremap <silent> ;; ;
" インサートモードでセミコロン押してからエスケープキーを押すと行末にセミコロンを挿入してからノーマルモードに戻る
inoremap <silent> ;<ESC> <ESC>:call InsertEndSemicolon()<CR>

やりたいこととしてはコメント行に書いてある通りです。

"nnoremap"から始まる行はノーマルモードのときに有効なキーショートカット、"inoremap"から始まる行はインサートモードのときに有効なキーショートカットを定義しています。

"<silent>"と書いておくと、キーショートカットを押したときにもなにも表示しないで関数を呼び出してくれます。

"<silent>"の後ろの";"や";;"、";<ESC>"が登録したいキーショートカットです。ここに書いた順番でキーを押すとキーショートカットが実行されます。
("<ESC>"というのはescキーのことです、ノーマルモードに戻るときに連打するやつです。)

";"や";;"、";<ESC>"の後ろに書いてあるのが、実際に実行したい内容です。キーショートカットを実行すると、ここに書いてあることがそっくりそのまま実際に入力されます。
("<CR>"というのはEnterキーを押す、という意味です。)

例えば

nnoremap <silent> ; :call InsertEndSemicolon()<CR>

の場合、ノーマルモードで";"と入力すると、":call InsertEndSemicolon()"と入力してからEnterキーを押す、というところまでやってくれます。

言語ごとのインデントの設定

.vimrc
filetype indent on

開いたファイルの拡張子によってインデントの設定を変える、という機能を有効化しています。


.vimrc
autocmd FileType java setlocal shiftwidth=2 softtabstop=2
autocmd FileType typescriptreact setlocal shiftwidth=2 softtabstop=2

JavaもしくはReactフレームワークを使っているときのTypeScriptファイルを開いた場合はshiftwidthsofttabstopの値をそれぞれ2で上書きするようにしています。

端末固有の設定を読み込む

.vimrc
if filereadable($HOME . '/.vimrc_local')
    source ~/.vimrc_local
endif

環境によって値を変えたい、というような設定項目があった場合にホームディレクトリにある".vimrc_local"というファイル内に記述すれば自動で読み込んで設定を上書きしてくれるようにするための記述です。
例えばPCの画面サイズによって値を変えたい、みたいな設定があったら".vimrc_local"に記述するようにしています。
(たしか画面端の余白の大きさを設定するのに使っていたような気がします。現在は使っていないのであんまり覚えてないです。。。)


.vimrcに記述している内容はここまでになります。ここからは再びinit.vimに記述している内容に戻ります。

XDG Base Directory Specification

Windowsの場合と$XDG_CONFIG_HOMEという環境変数が設定されている場合と、それ以外の場合でカラースキームのダウンロード先などが異なるのであらかじめパスを取得しておきます。

init.vim
let s:config_home = has('win64') ? $LOCALAPPDATA : empty($XDG_CONFIG_HOME) ? $HOME . '/.config' : $XDG_CONFIG_HOME

三項演算子を2重で使ってそれぞれの場合に対応しています。

vim-plugがインストールされていなかったらインストールする

vim-plug本体と使いたいプラグインをインストールします。

init.vim
let s:plug_path = has('win64') ? $LOCALAPPDATA . '\nvim-data\site\autoload\plug.vim' : $HOME . '/.local/share/nvim/site/autoload/plug.vim'
if !filereadable(s:plug_path)
    call g:DownloadIfNotFileReadable(s:plug_path, 'https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim')

    " ついでにプラグインもインストールする
    if has('win64')
        " Windowsの場合
        source ~/AppData/Local/nvim/plugin_settings/vim-plug_setting.vim
    else
        " Windows以外の場合
        if empty($XDG_CONFIG_HOME)
            source ~/.config/nvim/plugin_settings/vim-plug_setting.vim
        else
            source $XDG_CONFIG_HOME/nvim/plugin_settings/vim-plug_setting.vim
        endif
    endif
    PlugInstall
endif

これも長いので分割します

init.vim
let s:plug_path = has('win64') ? $LOCALAPPDATA . '\nvim-data\site\autoload\plug.vim' : $HOME . '/.local/share/nvim/site/autoload/plug.vim'

Windowsの場合とそれ以外とでvim-plugのインストール先が異なるので、実行している環境に合わせたパスをあらかじめ変数に入れています。


init.vim
if !filereadable(s:plug_path)

インストールされていないときだけ実行すればいいので、vim-plugがインストールされているかどうかで分岐しています。


init.vim
call g:DownloadIfNotFileReadable(s:plug_path, 'https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim')

.vimrcで定義した関数を使ってvim-plugをダウンロードしています。


init.vim
" ついでにプラグインもインストールする
if has('win64')
    " Windowsの場合
    source ~/AppData/Local/nvim/plugin_settings/vim-plug_setting.vim
else
    " Windows以外の場合
    if empty($XDG_CONFIG_HOME)
        source ~/.config/nvim/plugin_settings/vim-plug_setting.vim
    else
        source $XDG_CONFIG_HOME/nvim/plugin_settings/vim-plug_setting.vim
    endif
endif

プラグインをインストールするための前準備として、インストールしたいプラグインの情報をまとめて記述しておいたファイルを読み込んでいます。
vim-plug_setting.vimの内容については後ほど紹介します。


init.vim
PlugInstall

プラグインをインストールします。


カラースキームの設定(Neovim版)

init.vim
call g:DownloadIfNotFileReadable(has('win64') ? s:config_home . '\nvim\colors\gruvbox.vim' : s:config_home . '/nvim/colors/gruvbox.vim', 'https://raw.githubusercontent.com/morhetz/gruvbox/master/colors/gruvbox.vim')

.vimrcで定義した関数を使って、ファイルがなかった場合はダウンロードするようにしています。


init.vim
colorscheme gruvbox

カラースキームに"gruvbox"を指定します。


コマンドラインを2行にする

init.vim
set cmdheight=2

echoコマンドを使ったときに結果が出力されたりする場所の行数を2行にしています。

ファイルを開いたときに、自動でそのファイル置かれているフォルダに移動する

init.vim
set autochdir

これを設定しておくと、ファイルを開いたときに自動でそのファイルが置かれているフォルダに移動してくれるようになります。
例えば、"nvim hoge/fuga.py"のようにしてファイルを開いていたとしても":!python3 fuga.py"で実行できます。

※補足:Vim・NeovimのExモードでは、"!"から始めることでシェルコマンドが実行できます。print文を使って変数の中身を確認したい、というような場合などにとても重宝します。

(細かい話をすると、Vim内でのカレントディレクトリが開いたファイルを置いているディレクトリに設定されます。そのため、Vimを閉じたらいつの間にかディレクトリが変わっていた、というようなことは起こらないです。)

undo履歴を保持し続けるようにする

init.vim
if has('persistent_undo')
    set undodir=~/.vimundo
    set undofile
endif

Vimを閉じてもundoの履歴を保持し続けるようにしています。

python3のパスを指定

init.vim
if has('win64')
    if !empty(system('where /Q python && echo has'))
        let g:python3_host_prog = s:config_home . '\Programs\Python\Python37-32\python.exe'
    endif
else
    let g:python3_host_prog = system("bash -c 'echo -n $(which python3)'")
endif

Neovimのプラグインの中にはPythonを使うものもあるので、Pythonがインストールされているパスを教えています。

Windowsの場合は、Pythonがインストールされていたら決め打ちでパスを指定しています。
かなりの力技な気がするのでもっとスマートなやりかたをご存知でしたら教えてくださると嬉しいですm(_ _)m

Windows以外の場合、純粋に

echo -n $(which python3)

としてしまうと、使っているシェルによっては動かなかったりするので明示的にbashで実行するようにしています。

また

which python3

だけにすると改行が含まれてしまい、Neovimがうまくパスを認識できないためechoコマンドを使って改行なしで出力するようにしています。

(これを書いていて気づいたのですが、最近のMacに標準でインストールされているbashは最新ではなくなったみたいなので、zshがインストールされていたらそっちを優先して使う、などのようにしておいたほうがいいかもしれないですね。。。)

拡張子ごとにfiletypeを指定する

init.vim
autocmd BufNewFile,BufRead *.fish setfiletype fish
autocmd BufNewFile,BufRead *.vim setfiletype vim
autocmd BufNewFile,BufRead *.swift setfiletype swift
autocmd BufNewFile,BufRead *.kt setfiletype kotlin

拡張子を見てファイルタイプをセットするようにしています。
ほとんどの場合Vimが勝手に認識してくれるのですが、たまにうまくファイルタイプを認識できていない拡張子に遭遇するので、そういった場合はこのようにして追加でファイルタイプを指定します。
("filetype"が設定されていないとコード補完系のプラグインが動かなかったりするのでちゃんと指定するようにしています。)

プラグインごとの追加の設定を読み込む

init.vim
" vim-plugの設定を読み込む
if has('win64')
    source ~/AppData/Local/nvim/plugin_settings/vim-plug_setting.vim
elseif !empty($XDG_CONFIG_HOME)
    source $XDG_CONFIG_HOME/nvim/plugin_settings/vim-plug_setting.vim
else
    source ~/.config/nvim/plugin_settings/vim-plug_setting.vim
endif

" vim-indent-guidesの設定を読み込む
if has('win64')
    source ~/AppData/Local/nvim/plugin_settings/vim-indent-guides_setting.vim
elseif !empty($XDG_CONFIG_HOME)
    source $XDG_CONFIG_HOME/nvim/plugin_settings/vim-indent-guides_setting.vim
else
    source ~/.config/nvim/plugin_settings/vim-indent-guides_setting.vim
endif

" asyncompleteの設定を読み込む
if has('win64')
    source ~/AppData/Local/nvim/plugin_settings/asyncomplete_setting.vim
elseif !empty($XDG_CONFIG_HOME)
    source $XDG_CONFIG_HOME/nvim/plugin_settings/asyncomplete_setting.vim
else
    source ~/.config/nvim/plugin_settings/asyncomplete_setting.vim
endif

" aleの設定を読み込む
if has('win64')
    source ~/AppData/Local/nvim/plugin_settings/ale_setting.vim
elseif !empty($XDG_CONFIG_HOME)
    source $XDG_CONFIG_HOME/nvim/plugin_settings/ale_setting.vim
else
    source ~/.config/nvim/plugin_settings/ale_setting.vim
endif

" vim-lspの設定を読み込む
if has('win64')
    source ~/AppData/Local/nvim/plugin_settings/vim-lsp_setting.vim
elseif !empty($XDG_CONFIG_HOME)
    source $XDG_CONFIG_HOME/nvim/plugin_settings/vim-lsp_setting.vim
else
    source ~/.config/nvim/plugin_settings/vim-lsp_setting.vim
endif

" vim-closetagの設定を読み込む
if has('win64')
    source ~/AppData/Local/nvim/plugin_settings/vim-closetag_setting.vim
elseif !empty($XDG_CONFIG_HOME)
    source $XDG_CONFIG_HOME/nvim/plugin_settings/vim-closetag_setting.vim
else
    source ~/.config/nvim/plugin_settings/vim-closetag_setting.vim
endif

" Neosnippetの設定を読み込む
if has('win64')
    source ~/AppData/Local/nvim/plugin_settings/neosnippet_setting.vim
elseif !empty($XDG_CONFIG_HOME)
    source $XDG_CONFIG_HOME/nvim/plugin_settings/neosnippet_setting.vim
else
    source ~/.config/nvim/plugin_settings/neosnippet_setting.vim
endif

プラグイン固有の設定は別ファイルに分けて書くようにしているので、それらのファイルを読み込むようにしています。

それぞれのファイルの中身は以下の通りです

vim-plug固有の設定

vim-plug_setting.vim
call plug#begin(stdpath('data') . '/plugged')
    " <C--><C-->でコメントインアウトを切替できるようにするプラグイン
    Plug 'tomtom/tcomment_vim'

    " ノーマルモードで cs"' ってやったら"を'に置換してくれる
    Plug 'tpope/vim-surround'

    " インデントを可視化してくれるやつ
    Plug 'nathanaelkane/vim-indent-guides'

    " 自動で閉じ括弧とか入力してくれるやつ
    Plug 'cohama/lexima.vim'

    " 自動でHTMLの閉じタグを入力してくれるプラグイン
    Plug 'alvan/vim-closetag'

    " lintしてくれるやつ
    Plug 'w0rp/ale'

    " 補完に使うプラグインたち
    Plug 'prabirshrestha/async.vim'
    Plug 'prabirshrestha/vim-lsp'
    Plug 'mattn/vim-lsp-settings'
    Plug 'prabirshrestha/asyncomplete.vim'
    Plug 'prabirshrestha/asyncomplete-lsp.vim'
    Plug 'Shougo/neosnippet.vim'
    Plug 'Shougo/neosnippet-snippets'
    Plug 'prabirshrestha/asyncomplete-neosnippet.vim'
call plug#end()

vim-plugでは

call plug#begin(stdpath('data') . '/plugged')

から

call plug#end()

の間に使いたいプラグインの情報を書いておくと、自動でプラグインを有効化してくれます。
また、Exモードで

:PlugInstall

と実行すると、プラグインのインストールも行ってくれます。

"Plug"のあとの文字列は、Githubの'ユーザー名/リポジトリ名'になっています。
例えば"hoge"という方が"fuga"という名前のリポジトリで公開してくださっているプラグインを使いたい場合は、

Plug 'hoge/fuga'

のように記述します。

Indent Guides固有の設定

vim-indent-guides_setting.vim
" Indent Guidesを有効化する
let g:indent_guides_enable_on_vim_startup = 1

" いくつめのインデントから色をつけるかの指定
let g:indent_guides_start_level= 1

" インデント1つあたりの、色をつけるスペースの数
let g:indent_guides_guide_size = 0

" インデントにつける色の設定
autocmd VimEnter,Colorscheme * :hi IndentGuidesOdd	ctermbg=220
autocmd VimEnter,Colorscheme * :hi IndentGuidesEven	ctermbg=214

Indent Guidesはインデントを可視化してくれるプラグインです。
設定している内容に関してはコメント行に書いてある通りです。
(現在Indent Guidesはメンテナンスが終了しているようです。移行先探さないと・・・。)

asyncomplete.vim固有の設定

asyncomplete_setting.vim
set completeopt+=preview

autocmd! CompleteDone * if pumvisible() == 0 | pclose | endif

asyncomplete.vimはコード補完をしてくれるプラグインです。
たしか補完候補をプレビューウィンドウで表示するためにこの設定を書いた、というかどこからかコピペしてきたような気がしますがコメントも残っていないので真相は不明です・・・。

ALE (Asynchronous Lint Engine)固有の設定

ale_setting.vim
" let g:ale_sign_column_always = 1
set signcolumn=yes
highlight SignColumn ctermbg=15
highlight SignColumn ctermfg=15
highlight SignColumn guisp=15
highlight SignColumn guifg=15
highlight SignColumn guibg=15
let g:ale_sign_error = '☠️'
let g:ale_sign_warning = '⚠️'

ALEは、ソースコードを読んでエラーや警告があった場合には表示してくれるプラグインです。

警告やエラーがある行に表示する絵文字を設定しています。
また、恐らく多分きっとエラーや警告の絵文字を表示する列の背景色とかも指定しています。(コメントが残っていないのではっきりとはわからないです。コメントはちゃんと残しましょう。)

vim-lsp固有の設定

vim-lsp_setting.vim
" ノーマルモードで"def"と入力したら変数や関数の定義元にジャンプするようにする
nnoremap <silent> def :LspDefinition<CR>

" "neosnippet"からの補完情報を取得するようにする
call asyncomplete#register_source(asyncomplete#sources#neosnippet#get_source_options({
    \ 'name': 'neosnippet',
    \ 'whitelist': ['*'],
    \ 'completor': function('asyncomplete#sources#neosnippet#completor'),
    \ }))

vim-lspは、VimでLanguage Server Protocolを扱えるようにするためのプラグインです。
asyncomplete.vimなどのコード補完プラグインと組み合わせて使います。

設定内容に関してはコメント行に書いてある通りです。

closetag.vim固有の設定

vim-closetag_setting.vim
" filenames like *.xml, *.html, *.xhtml, ...
" These are the file extensions where this plugin is enabled.
"
let g:closetag_filenames = '*.html,*.xhtml,*.phtml'

" filenames like *.xml, *.xhtml, ...
" This will make the list of non-closing tags self-closing in the specified files.
"
let g:closetag_xhtml_filenames = '*.xhtml,*.jsx'

" filetypes like xml, html, xhtml, ...
" These are the file types where this plugin is enabled.
"
let g:closetag_filetypes = 'html,xhtml,phtml,typescriptreact'

" filetypes like xml, xhtml, ...
" This will make the list of non-closing tags self-closing in the specified files.
"
let g:closetag_xhtml_filetypes = 'xhtml,jsx'

" integer value [0|1]
" This will make the list of non-closing tags case-sensitive (e.g. `<Link>` will be closed while `<link>` won't.)
"
let g:closetag_emptyTags_caseSensitive = 1

" dict
" Disables auto-close if not in a "valid" region (based on filetype)
"
let g:closetag_regions = {
    \ 'typescript.tsx': 'jsxRegion,tsxRegion',
    \ 'javascript.jsx': 'jsxRegion',
    \ 'typescriptreact': 'jsxRegion,tsxRegion',
    \ 'javascriptreact': 'jsxRegion',
    \ }

" Shortcut for closing tags, default is '>'
"
let g:closetag_shortcut = '>'

" Add > at current position without closing the current tag, default is ''
"
let g:closetag_close_shortcut = '<leader>>'

closetag.vimは、HTML形式のタグを自動で閉じてくれるプラグインです。

公式のREADMEに書いてあった内容にプラスして、ファイルタイプが"typescriptreact"だった場合にも有効化されるようにしています。

Neosnippet固有の設定

init.vim
imap <C-k> <Plug>(neosnippet_expand_or_jump)
smap <C-k> <Plug>(neosnippet_expand_or_jump)
xmap <C-k> <Plug>(neosnippet_expand_target)
if has('conceal')
    set conceallevel=0 concealcursor=niv
endif

Neosnippetは簡単なコード補完を行ってくれるプラグインです。

このように設定しておくと、選択した補完候補をCtrl + kで展開してくれたりするようにしています。
(公式のREADMEに書いてある内容のコピペです。)

個別の設定はしていないけど使っているプラグインたちの紹介

tcomment

Ctrlキーを押しながら"-"2度押しでカーソルがある行をコメントアウト・コメントインしてくれます。

surround.vim

カーソルがある行内での置換を楽にしてくれるプラグイン。
ノーマルモードで例えば"csab"と入力すると、カーソル行内の"a"を"b"に置換してくれます。
ダブルクオートをシングルクオートに置換したい、という場合などに重宝します。

lexima.vim

括弧やクオートを自動で閉じてくれるプラグインです。
もはや居てくれないと困る存在です。

async.vim

asyncomplete.vimの動作に必要なプラグインです。

vim-lsp-settings

vim-lspの設定をコマンド1つでやってくれるようになるプラグインです。
(これが無かったらvim-lsp扱えていませんでした。。。)

asyncomplete-lsp.vim

asyncomplete.vimvim-lspの仲介をしてくれるプラグインです。

Neosnippet-snippets

Neosnippetにスニペットの情報を提供するプラグインです。

asyncomplete-neosnippet.vim

asyncomplete.vimNeosnippetの仲介をしてくれるプラグインです。

自作Dockerコンテナ用の設定を読み込む

プラグインがインストールされた状態のNeovimをDockerコンテナとして用意することで初期セットアップの手間を省こうとしていた頃の名残です。
現在は使っていないのですが、もしかしたら将来dockerfileを書くときに役立つかもしれない・・・と思ったので残してあります。
(せっかくGit管理しているので普通だったら消したほうがいいとは思うのですがそうすると、過去のコミットにこの情報が載っている、ということ自体を忘れそうなので消さずに残しています・・・。)

init.vim
if !empty($container_name)
    if !empty($XDG_CONFIG_HOME)
        source $SDG_CONFIG_HOME/nvim/plugin_settings/settings_for_myde.vim
    else
        source ~/.config/nvim/plugin_settings/settings_for_myde.vim
    endif
endif



"settings_for_myde.vim"の中身はこんな感じです。

settings_for_myde.vim
" dockerコンテナ内向けの設定
" うまくいかなかったときの保険として、'name'の直の最初に'myde-'をつける
" この3つ以外は、パスが通ってるとこにコマンド配置しておけば勝手にいいかんじにしてくれる(vim-lsp-settingが)
if executable('/home/myde/omnisharp-lsp/run')
  au User lsp_setup call lsp#register_server({
        \ 'name': 'myde-omnisharp-lsp',
        \ 'cmd': {server_info->['/home/myde/omnisharp-lsp/run', '-lsp']},
        \ 'whitelist': ['cs']
        \ })
endif

if executable('/home/myde/eclipse-jdt-ls/eclipse-jdt-ls')
  au User lsp_setup call lsp#register_server({
        \ 'name': 'myde-eclipse-jdt-ls',
        \ 'cmd': {server_info->['/home/myde/eclipse-jdt-ls/eclipse-jdt-ls']},
        \ 'whitelist': ['java']
        \ })
endif

if executable('/home/myde/myCommands/kotlin-language-server')
  au User lsp_setup call lsp#register_server({
        \ 'name': 'myde-kotlin-language-server',
        \ 'cmd': {server_info->['/home/myde/myCommands/kotlin-language-server']},
        \ 'whitelist': ['kotlin']
        \ })
endif

キーショートカットの追加(Neovim版)

init.vim
tnoremap <silent> <ESC> <C-\><C-n>

Neovimでターミナルを開いているときにもescキーでインサートモードからノーマルモードに移るようにしています。

init.vim
nnoremap <silent> <C-t><C-m> :split<CR> <C-w>j :terminal<CR> :resize 6<CR> i

ノーマルモードのときに、Ctrlキーを押しながら"tm"と入力することで画面を水平分割してからターミナルを起動するようにしています。

あとがき

自分が使っているinit.vimをWindows対応にしたので、いい機会だと思ってこの記事を書き始めました。
が、思っていたよりもかなりボリューミーになって驚いています。いつの間にか結構な量を設定してたんですね。。。

記事の内容に関しては、なるべく正確な情報になるよう改めて調べ直しながら書いてはいますが、誤りなどがありましたらコメントで教えてくださるとうれしいです。

脚注
  1. 参考文献:https://vim-jp.org/vimdoc-ja/options.html#'fileencodings' ↩︎

  2. 参考文献:https://ascii.jp/elem/000/004/021/4021036/ ↩︎

Discussion