✌️

nvimとVSCodeを共存させて使い分けするためのオレオレ設定

2022/05/29に公開約10,400字

こんにちは。
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を使用しています。

https://github.com/wez/wezterm

yutkat さんがzennでも紹介されておりましたので、そちらを見て導入しました。

https://zenn.dev/yutakatay/articles/wezterm-intro

私は主にtmuxを利用しますので、正直 alacrittyでもよかったのですが、何も考えずに brew でインストールした時に、日本語のインライン入力ができなかったので、諦めました。

正直 weztermで iTerm2 より体感速度が上昇したので、十分かなぁなんて思っています。

nvim

nvim の version 0.7.0を利用しています。

https://github.com/neovim/neovim

以下、nvimをvimと表現します。また、私は vimnvim の aliasとして登録しておりますので、 vim というコマンドで nvim を起動しております。

vimとtmuxの統合

まずはターミナルで tmuxを起動します。

その次にお気に入りのペインに分割するためのシェルの関数を呼び出します。

私は小さい画面では mini_ide 、大きい画面では ide と呼び出す関数を使い分けたりしています。

そして tmuxで分割した画面の移動は基本的に ctrl を押しながら

  • h で左方向
  • l で右方向
  • k で上方向
  • j で下方向

に移動できるようにキーを設定しております。

tmuxのペイン移動

また、vimで分割した画面についても同様のキーで移動できるようにしております。

vimのペイン移動

この辺りの設定はvim-tmux-navigator という vimの拡張機能を利用して vimで分割したペイン、tmuxで分割したペイン双方の移動も同様に ctrl キーで移動できるようにしています。

https://github.com/christoomey/vim-tmux-navigator

vim<->tmuxペイン移動

tmuxの設定ファイルを以下のように記述しています。

.tmux.conf
# 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 を押しながらlh を押下することで、タブを切り替える用に移動できるように設定しております。

以下gifでは l h としか表示されておりませんが、実際は cmd を押しながら lhを押下しています

vimのタブ移動

Ctrl+p で表示された画面(ファジーファインダー)は別途プラグインの際にご紹介します。

vimの設定ファイル
" バッファの移動をメタキーで
nnoremap <silent> <M-h> :bprev<CR>
nnoremap <silent> <M-l> :bnext<CR>
nnoremap <silent> <M-w> :bd<CR>

このままですと Optionキーを押下して lh でバッファの切り替えとなってしまうため、後述する Karabiner-Elements というアプリでターミナル系のアプリケーションを開いている時のキーバインドを変更しています。

キーバインドの設定

Karabiner-Elements

私はMacを利用しており、キーバインドの変更などはKarabiner-Elementsを利用しています。

https://karabiner-elements.pqrs.org/

以下のように、 Iterm2alacritty , 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 というプラグインを利用しています。

https://github.com/Shougo/dein.vim

dein自体の導入方法などは上記公式や別途記事をご参照ください。

ファジーファインダー

telescope.vim というプラグインを利用しています。

VSCodeでいう Cmd+p でファイルを検索するような機能をイメージしてもらえれば良いと思います。

以下のようにファイル検索は Ctrl+p をマッピングしております。

コーディング風景

さらに grep のように特定の文字列が含まれたファイルなどを探している場合には、<Ctrl+g> で検索できるようにしております。

コーディング風景

このプラグインのすごいところは、ファイルのプレビューも表示してくれるという点です。(上記例では作業スペースが狭くて見えておりませんが・・・・)

以下のような形でプレビューをみることができます。

コーディング風景

この点に関して、VSCodeの Cmd+p よりとても気に入っています。

またこの Meta + PMeta + 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でいう以下画像のような一括置換の機能になります。

VSCodeの置換

これに関しては上記でご紹介した telescope.vim とvim-qfreplaceというプラグインを利用しています。

https://github.com/thinca/vim-qfreplace/blob/master/doc/qfreplace.jax

例えば以下のように test という文字列が含まれた複数のファイルについて、 dest という文字列に置き換える場合のオペレーションは以下のような形になります。

全置換

結構手数が多いですが

  • telescope.vim でファイル横断で test という文字列を検索
  • ctrl+atest という文字列が見つかった部分を vimのQuickfixに送る
  • vim-qfreplace の機能で QuickFixに対して置換を行う
    • 上記では test という文字列を検索
    • %s//dest/g というコマンドで一括置換を行っています
    • %s/test/dest/g と同じことを行っています。

[参考]

vimgrep と QuickFixについて

https://qiita.com/yuku_t/items/0c1aff03949cb1b8fe6b

もちろんファイル単位で置換するか選択することもできますし、 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 というプラグインをファイラーとして利用しております。

https://github.com/Shougo/defx.nvim
  • 新規でファイルを追加する時
  • ファイル・ディレクトリをコピーする時
  • ファイル・ディレクトリの名前を変更する時
  • 俯瞰でプロジェクトの構造をみたい時

などに利用しています。

ctrl+n でファイラーが開くようにしています。

ファイラーの基本操作

defxはかなり自由にカスタマイズすることができるようですが、私は以下のように設定を入れております。

  • N で新規ファイルを作成
  • M で新規ディレクトリを作成
  • c でコピー
  • p でペースト

defxの全設定はこちら。

https://github.com/bun913/dotfiles/blob/main/config/nvim/rc/defx.rc.vim

なお、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製なのでどの環境でも動かしやすくてとても好き)

https://github.com/jesseduffield/lazygit

vimから lazygit を呼び出せるように 以下プラグインを利用しております。

https://github.com/kdheepak/lazygit.nvim

以下のように設定していて、 <Space>gg でlazygitが起動するようになっております。

nnoremap <silent> <leader>gg :LazyGit<CR>

lazygit

その他

今回は開発に当たっての操作感という趣旨ですので詳細は割愛いたしますが、以下のようなプラグインを入れており、もはや必須です。

LSP(言語ごとのインテリセンスやフォーマッターなど)

  • vim-lsp
  • vim-lsp-settings

mattnさんのプラグインは本当に神。

https://zenn.dev/mattn/articles/b83f9d94202914

言語ごとに色をつけたり、インデントを調整したりは以下のプラグインを利用しております。

  • nvim-treesitter

https://github.com/nvim-treesitter/nvim-treesitter

https://zenn.dev/duglaser/articles/c02d6a937a48df

https://zenn.dev/monaqa/articles/2021-12-22-vim-nvim-treesitter-highlight

最後に

ライトvimmerですが、 vimはいいぞ・・・

今回ご紹介した設定は こちらのリポジトリで管理しておりますので、必要に応じて参考にしていただければと思います。

https://github.com/bun913/dotfiles
GitHubで編集を提案

Discussion

ログインするとコメントできます