nvimとVSCodeを共存させて使い分けするためのオレオレ設定
こんにちは。
nvimとVSCodeを行ったりきたりしているbunです。
私の場合どちらのエディターもライトユーザー感丸出しですが、vimとVSCodeを両方使っている方は意外といらっしゃるのではないかと思います。
私の場合は
- 基本的にコーディングやメモはnvimで行いたい(サクサク動くし、かっこいいから!!)
- ペアプロやVSCodeの拡張機能が利用したい時にはVSCodeを違和感なく利用できるようにしたい
といった感じで使い分けたいと考えています。
どっちの良いところも享受できれば嬉しいなと。
以前は VSCodeのnvim拡張をメインで使っていたのですが、どうしても怪しい挙動をすることも多く、nvimのさくさく感も味わいたいので、 基本ターミナルに住むようにして、必要に応じてGUIの世界に戻ろう
と決めたのが1週間前くらいの出来事です。
ただ VSCodeを使う時にもnvimを使う時にも似たような開発体験をするためには、各種プラグインの導入・キーバインドの設定などが必要であるため、1週間くらいかけて諸々抜本的なnvim環境の作成しました。
同僚「そんなことするくらいならVSCodeでいいじゃん・・・」
僕「こんなにかっこよく育ってくれたターミナルとnvimの前でよくそんなこと言えるな!!この子の前でもう一度言ってみろ!!」
同僚(クソデカため息)「そんなことするくらいならVSCodeでいいじゃん・・・」
というやりとりもあったとか・・・
概要・前提条件など
以下のような意図でnvim等の設定を行いました。
- 全てのキーバインドを2つのエディタで合わせるのではなく、タブの移動とかファイラーの操作などの感覚を似せて、違和感なく両方のエディターを使えるようにする
- 双方のエディターに便利な機能があるので、得意なことを得意な方にやってもらう
また、筆者は弱々vimmerですので、以下に記載する内容より良い方法・設定があると思いますのでご容赦ください。
【筆者の環境】
- macOS BigSur(AppleSiliconとIntelチップ両方で動作確認済み)
コーディングの基本道具・基本動作
基本的なvimの操作方法などはここでは割愛しております。
設定内容のご紹介や、実際にどのように動かすのかご紹介いたします。
基本道具
ターミナル
ターミナルとして Macに weztermを使用しています。
yutkat さんがzennでも紹介されておりましたので、そちらを見て導入しました。
私は主にtmuxを利用しますので、正直 alacrittyでもよかったのですが、何も考えずに brew
でインストールした時に、日本語のインライン入力ができなかったので、諦めました。
正直 weztermで iTerm2
より体感速度が上昇したので、十分かなぁなんて思っています。
nvim
nvim の version 0.7.0を利用しています。
以下、nvimをvimと表現します。また、私は vim
を nvim
の aliasとして登録しておりますので、 vim
というコマンドで nvim
を起動しております。
vimとtmuxの統合
まずはターミナルで tmuxを起動します。
その次にお気に入りのペインに分割するためのシェルの関数を呼び出します。
私は小さい画面では mini_ide
、大きい画面では ide
と呼び出す関数を使い分けたりしています。
そして tmuxで分割した画面の移動は基本的に ctrl
を押しながら
-
h
で左方向 -
l
で右方向 -
k
で上方向 -
j
で下方向
に移動できるようにキーを設定しております。
また、vimで分割した画面についても同様のキーで移動できるようにしております。
この辺りの設定はvim-tmux-navigator
という vimの拡張機能を利用して vimで分割したペイン、tmuxで分割したペイン双方の移動も同様に ctrl
キーで移動できるようにしています。
tmuxの設定ファイルを以下のように記述しています。
# Vim Tmux Navigator
is_zsh="ps -o state= -o comm= -t '#{pane_tty}' | grep -iqE 'Ss\\+\\s*-zsh$'"
is_vim="ps -o state= -o comm= -t '#{pane_tty}' | grep -iqE 'S\\+\\s*?g?(view|n?vim?x?)(diff)?$'"
is_fzf="ps -o state= -o comm= -t '#{pane_tty}' | grep -iqE 'S\\+\\s*fzf$'"
is_peco="ps -o state= -o comm= -t '#{pane_tty}' | grep -iqE 'S\\+\\s*peco$'"
bind -n C-h run "($is_zsh && tmux send-keys C-h) || ($is_vim && tmux send-keys C-h) || ($is_fzf && tmux send-keys C-h) || ($is_peco && tmux send-keys C-h) || tmux select-pane -L"
bind -n C-j run "($is_zsh && tmux send-keys C-j) || ($is_vim && tmux send-keys C-j) || ($is_fzf && tmux send-keys C-j) || ($is_peco && tmux send-keys C-j) || tmux select-pane -D"
bind -n C-k run "($is_zsh && tmux send-keys C-k) || ($is_vim && tmux send-keys C-k) || ($is_fzf && tmux send-keys C-k) || ($is_peco && tmux send-keys C-k) || tmux select-pane -U"
bind -n C-l if-shell "$is_vim" "send-keys C-l" "select-pane -R"
vim内でのバッファ(VSCodeのタブ)移動
私はVSCodeで cmd+l
で右のタブに、 cmd+h
で左のタブに移動するように設定しております。
// VSCodeのKeyBinding.jsonの設定
{
"key": "cmd+l",
"command": "workbench.action.nextEditor"
},
{
"key": "cmd+h",
"command": "workbench.action.previousEditor"
}
そのためvimで開いたバッファは cmd
を押しながらl
や h
を押下することで、タブを切り替える用に移動できるように設定しております。
以下gifでは l
h
としか表示されておりませんが、実際は cmd
を押しながら l
やh
を押下しています
※ Ctrl+p
で表示された画面(ファジーファインダー)は別途プラグインの際にご紹介します。
" バッファの移動をメタキーで
nnoremap <silent> <M-h> :bprev<CR>
nnoremap <silent> <M-l> :bnext<CR>
nnoremap <silent> <M-w> :bd<CR>
このままですと Optionキーを押下して l
、 h
でバッファの切り替えとなってしまうため、後述する Karabiner-Elements
というアプリでターミナル系のアプリケーションを開いている時のキーバインドを変更しています。
キーバインドの設定
Karabiner-Elements
私はMacを利用しており、キーバインドの変更などはKarabiner-Elementsを利用しています。
以下のように、 Iterm2
、 alacritty
, wezterm
を利用している際に、以下のようにキーバインドを変更しております。
-
cmd
を押しながらl
=>opt
を押しながらl
-
cmd
を押しながらh
=>opt
を押しながらh
設定のjson(~/.config/karabiner/karabiner.json)は以下のように設定しております。
{
"description": "Command-l to Option-l on iTerm2",
"manipulators": [
{
"type": "basic",
"from": {
"key_code": "l",
"modifiers": {
"mandatory": [
"left_command"
]
}
},
"to": [
{
"key_code": "l",
"modifiers": [
"left_option"
]
}
],
"conditions": [
{
"type": "frontmost_application_if",
"bundle_identifiers": [
"^com\\.googlecode\\.iterm2",
"^io\\.alacritty",
"^com\\.github\\.wez.wezterm"
]
}
]
}
]
}
// 同じような設定であるため hは省略
※ なお筆者は 左のoptキーをメタキーとして設定しています。その代わり全く使っていなかった右の cmdキーを左のoptionキーとして動作するように設定しております。
vimで利用しているプラグインについて
私がvimを使うにあたり利用しているプラグイン・設定しているショートカットなどをご紹介します。
なお、ここではプラグイン管理の導入方法などはご紹介しておりませんのでご了承ください。
プラグイン管理
dein.vim というプラグインを利用しています。
dein自体の導入方法などは上記公式や別途記事をご参照ください。
ファジーファインダー
telescope.vim というプラグインを利用しています。
VSCodeでいう Cmd+p
でファイルを検索するような機能をイメージしてもらえれば良いと思います。
以下のようにファイル検索は Ctrl+p
をマッピングしております。
さらに grep
のように特定の文字列が含まれたファイルなどを探している場合には、<Ctrl+g> で検索できるようにしております。
このプラグインのすごいところは、ファイルのプレビューも表示してくれるという点です。(上記例では作業スペースが狭くて見えておりませんが・・・・)
以下のような形でプレビューをみることができます。
この点に関して、VSCodeの Cmd+p
よりとても気に入っています。
またこの Meta + P
と Meta + G
と、プロジェクトルート(最初にvimで開いたディレクトリ)ではなく、現在バッファで開いているファイルのルートを起点に検索するためのキーバインドも入れております。
nnoremap <C-p> <cmd>Telescope find_files<cr>
nnoremap <C-g> <cmd>Telescope live_grep<cr>
" プロジェクトルートではなく現在開いているファイルを起点にファイル検索
nnoremap <M-p> <cmd>lua require('telescope.builtin').find_files( { cwd = vim.fn.expand('%:p:h') })<cr>
nnoremap <M-g> <cmd>lua require('telescope.builtin').live_grep( { cwd = vim.fn.expand('%:p:h') })<cr>
ファイル横断での置換
VSCodeでいう以下画像のような一括置換の機能になります。
これに関しては上記でご紹介した telescope.vim とvim-qfreplaceというプラグインを利用しています。
例えば以下のように test
という文字列が含まれた複数のファイルについて、 dest
という文字列に置き換える場合のオペレーションは以下のような形になります。
結構手数が多いですが
-
telescope.vim
でファイル横断でtest
という文字列を検索 -
ctrl+a
でtest
という文字列が見つかった部分を vimのQuickfixに送る -
vim-qfreplace
の機能で QuickFixに対して置換を行う- 上記では
test
という文字列を検索 -
%s//dest/g
というコマンドで一括置換を行っています -
%s/test/dest/g
と同じことを行っています。
- 上記では
[参考]
vimgrep と QuickFixについて
もちろんファイル単位で置換するか選択することもできますし、 1箇所ずつ置換するか確認できます。
今度は逆に dest
という文字列を test
に変換してみます
-
telescope.vim
でQuickFixに送るものを選択する場合tab
で選択してから、ctrl+q
で送ります - 今度は
%s//dest/gc
として1箇所ずつ確認してから置換を行っています
以下のような設定をしています。
telescope
lua <<EOF
require('telescope').setup{
defaults = {
mappings = {
i = {
# ctrl + a で検出された結果全てをquickfixに送る
["<C-a>"] = require('telescope.actions').send_to_qflist + require('telescope.actions').open_qflist,
# ctrl + qで選択した部分をquickfixに送る
["<C-q>"] = require('telescope.actions').send_selected_to_qflist + require('telescope.actions').open_qflist
}
}
}
}
EOF
qfreplace
nnoremap qf :Qfreplace<CR>
ファイラー
defx.nvim
というプラグインをファイラーとして利用しております。
- 新規でファイルを追加する時
- ファイル・ディレクトリをコピーする時
- ファイル・ディレクトリの名前を変更する時
- 俯瞰でプロジェクトの構造をみたい時
などに利用しています。
ctrl+n
でファイラーが開くようにしています。
defxはかなり自由にカスタマイズすることができるようですが、私は以下のように設定を入れております。
-
N
で新規ファイルを作成 -
M
で新規ディレクトリを作成 -
c
でコピー -
p
でペースト
defxの全設定はこちら。
なお、VSCodeのサイドバーに表示されるファイラーでも似たような操作ができるようにキーバインドを以下のように設定しております。
// 新規ファイル作成
{
"key": "shift+n",
"command": "explorer.newFile"
},
// 新規フォルダ作成
{
"key": "shift+m",
"command": "explorer.newFolder"
},
// dでファイルを削除
{
"key": "d",
"command": "deleteFile",
"when": "explorerViewletVisible && filesExplorerFocus && !explorerResourceReadonly && !inputFocus"
},
// rでファイルのリネーム
{
"key": "r",
"command": "renameFile",
"when": "explorerViewletVisible && filesExplorerFocus && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
},
{
"key": "enter",
"command": "-renameFile",
"when": "explorerViewletVisible && filesExplorerFocus && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
},
// vで分割して開く
{
"key": "v",
"command": "explorer.openToSide",
"when": "explorerViewletVisible && filesExplorerFocus && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
},
// ファイルのコピー
{
"key": "y",
"command": "filesExplorer.copy",
"when": "explorerViewletVisible && filesExplorerFocus && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
},
// ファイルのペースト
{
"key": "p",
"command": "filesExplorer.paste",
"when": "explorerViewletVisible && filesExplorerFocus && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
},
git操作
私はターミナルで動作する lazygit
を好んで使っています。
(Go製なのでどの環境でも動かしやすくてとても好き)
vimから lazygit
を呼び出せるように 以下プラグインを利用しております。
以下のように設定していて、 <Space>gg
でlazygitが起動するようになっております。
nnoremap <silent> <leader>gg :LazyGit<CR>
その他
今回は開発に当たっての操作感という趣旨ですので詳細は割愛いたしますが、以下のようなプラグインを入れており、もはや必須です。
LSP(言語ごとのインテリセンスやフォーマッターなど)
- vim-lsp
- vim-lsp-settings
mattnさんのプラグインは本当に神。
言語ごとに色をつけたり、インデントを調整したりは以下のプラグインを利用しております。
- nvim-treesitter
最後に
ライトvimmerですが、 vimはいいぞ・・・
今回ご紹介した設定は こちらのリポジトリで管理しておりますので、必要に応じて参考にしていただければと思います。
Discussion