VimmerのVimmerによるVimmerのためのVSCode環境構築(Part1)

2020-07-08 (Wed)
Vimmerにとって快適な環境をVSCodeで目指してみる

政治、宗教、野球に並んでタブー視されている話題、エディター。
これまで数多くの血が流れてきた歴史がある。
私は普段、NeoVimにゴリゴリにプラグインを詰め込んで使っているVimmerである。

今回は最近やりはじめたVSCodeのVim化計画についてメモとして残しておく。
ボリュームがすごいことになりそうなので、おそらく数回に分けて記事にすると思う。

この記事の内容

  • なぜVSCodeの環境構築を始めたか?
  • VSCodeをVim化するための最低限の設定

なぜVSCodeの環境構築を始めたか?

この記事の冒頭でVimmerを名乗ったが、ぶっちゃけるとVim歴は結構浅く、本格的に使い始めてからまだ1年ちょっとくらいである。
しかし、既にVimの虜になっており、Vimが使えない環境でテキストを打つのが苦痛に感じるようになってしまった。
(仕事ではWordで文章を作成しなければならないこともあるが、それが一番の苦行…)

また、私はVimmerであると同時にUbuntu使いでもあり、自宅PCもUbuntuが入っている。
しかし、やはり仕事ではWindowsを使わなければならないことが多く、これまたストレス…
Ubuntuに関してはdotfilesで設定をすべて管理しており、エディター環境も含めてすべてすぐに再現できるようにしている。
だが、Windowsだと勝手が違いすぎて、なかなかこうはできない…
(WSL2とか使うといい感じにできたりするのかな?その辺は今度試してみたい。)

そして、最近人気のエディターとしてVSCodeがある。
非常に多機能であり、ターミナル操作もできることから様々な言語を統合的に扱えるIDE的な位置づけになりつつある。
そして何より、Windows、MacOS、Linuxと様々な環境で動かすことができる。

私も以前から一応VSCodeは使っていたが、やはり自分でチューニングしたNeoVimの操作感には勝てず、そこまで使う機会は多くなかった。
しかし、VSCodeにもVimのキーバインドを使えるようにするプラグインも存在し、複数の端末で設定を同期させる方法があることを知り、これをしっかりとチューニングして使いやすくできればLinux・Windowsの壁を超えて使えるエディターになるのではないか?と思った。
ちなみに複数の端末で設定を同期する方法は2通りあり、私は後者の方法を使うことにした。

ということで、Vimmerに使いやすい環境をVSCodeに構築することを目指してみる。
(なお、この記事はVSCodeに色々と試しながら構築した環境をVSCode Insidersに整理しつつ移植しながら作成している。)
設定項目についても一つひとつ説明していくので、「Vimを使ったことがあるけど、なんかしっくり来なかった」というような人もこれを機に使ってみてもらいたい。

VSCodeをVim化するための最低限の設定

では、早速はじめる。
今回の記事では、とりあえず必要最低限の設定を行うことを目標とする。
必要最低限とはとりあえずタイピングを行う上でイライラすることのないレベルの環境構築と定義しておく。
そのため、ファイル操作などをキーボードのみで行えるようにする設定などは次回以降構築していきたい。

拡張機能のインストール

まずはVSCodeに拡張機能をインストールしていく。
今回は最低限なので2つのみインストールする。

片方テーマじゃねぇか、ってツッコミを入れたそこのあなた、正解!
私はNeoVimでもこのテーマを使っているが、これを入れるだけで一気にかっこいいエディターになる。
形から入るの、大事。

ちなみに私はターミナルもこのIcebergのテーマにしている。
詳細はこちら

キーバインド設定

先ほどのVimプラグインを入れた時点で、Vimキーバインドが有効になるのだが、これだけでは残念ながら使い物にならない。
その理由は主に3つ。

  • VSCodeのデフォルトのキーバインドと被り、うまく働かないものがある
  • 一部のVimコマンドが不安定で、想定していない挙動をする
  • (そもそものVimの問題だが)デフォルトのVimコマンドでは使いにくいものがある

これらを調整するため、2つのファイルを編集する必要がある。

  • settings.json : VSCodeの各種設定ファイル
  • keybindings.json : キーバインドの設定

この2つのファイルの関係性が非常にわかりにくいのだが、VSCode自体やプラグインの設定はsettings.json、VSCode上でのキーバインドの設定はkeybindings.jsonで行う。
したがって、プラグインであるVimのキーバインドはsettings.jsonで設定する。
その際、同様のキーバインドがkeybindings.jsonに設定されていると、そちらが優先されてしまい、想定通りの動作を実現できない。

なお、これらのファイルは、Shift+Ctrl+pを押してコマンドパレットを開き、

  • settings.json : Preferences: Open Settings (JSON)
  • keybindings.json : Preferences: Open Keyboard Shortcuts (JSON)

を選択することでそれぞれ開くことができる。

各種項目の設定

まず、settings.jsonから設定していく。
ファイル全体はあとでまとめて載せるとして、一つひとつの設定が何をしているのか確認しながら説明していく。

エディター自体の設定

  • "editor.minimap.enabled": false

VSCodeのエディター部分右上に表示される、テキスト全体の縮小表示画面。
個人的には必要性を感じないのでOFF。

  • "editor.renderWhitespace": "all",

半角スペースを可視化する設定。
これによりタブや全角スペースが混入していないか確認しやすくなる。

  • "files.trimTrailingWhitespace": true,

不要なスペースを自動削除する設定。
便利なのでONにしておく。

Vimの設定

  • "vim.leader": "<space>",

VimのLeaderの設定。
Leaderは他のキーと組み合わせて様々なコマンドを実行できるようにするもの。
色々な流派があるが、私はスペースキーを割り当てる派。

  • "vim.useSystemClipboard": true,

これをONにすると、Vimでヤンク(コピーみたいなもの)した文字がクリップボードに保存されるようになる。
したがって、ブラウザなど他のソフトへ貼り付けられるようになる。
逆もまた然り。
便利なので私はON。

  • "vim.useCtrlKeys": true,

VimのキーバインドでCtrlを使えるようにする設定。
ONにしておく。

  • "vim.easymotion": true,

ラベルを使ってカーソル位置をジャンプする拡張。
NormalモードでLeaderを2回押すことで起動する。
その後、様々なモーションを選択できるが、一番使うのは「s」キーの一文字検索モーション。
Leader+Leader+s+(検索したい文字)でラベルが表示されるので、ジャンプしたい場所のキーを押すことでジャンプできる。

  • "vim.visualstar": true,

Visualモードで、*キーや#キーで選択中の単語を検索できるようにする。
便利なのでON。

  • "vim.hlsearch": true,

検索結果をハイライト表示するハイライト検索を有効にする設定。
検索結果がわかりやすくなるのでON。

Normalモードのキーバインド

JSON
{
    "before": [
        "u"
    ],
    "commands": [
        {
            "command": "undo"
        }
    ]
}

{
    "before": [
        "<C-r>"
    ],
    "commands": [
        {
            "command": "redo"
        }
    ]
}

uキーによるundoとCtrl+rによるredoの挙動がなぜかおかしいのでVSCodeの機能によるundoとredoへと書き換える。
(元々の設定だと、undoとredoの範囲が広く一回で相当前or後まで飛ばされてしまう)

JSON
        {
    "before": [
        "x"
    ],
    "after": [
        "\"",
        "_",
        "x"
    ]
}

{
    "before": [
        "s"
    ],
    "after": [
        "\"",
        "_",
        "s"
    ]
}

xキーとsキーの一文字削除、一文字修正したときにクリップボードに書き込まれないようにする設定。

JSON
{
    "before": [
        "j"
    ],
    "after": [
        "g",
        "j"
    ]
}

{
    "before": [
        "k"
    ],
    "after": [
        "g",
        "k"
    ]
}

jとkのカーソル移動を行が折り返されているときに表示通りに動けるようにする設定。

JSON
{
    "before": [
        "n"
    ],
    "after": [
        "n",
        "z",
        "z"
    ]
}

{
    "before": [
        "N"
    ],
    "after": [
        "N",
        "z",
        "z"
    ]
}

{
    "before": [
        "*"
    ],
    "after": [
        "*",
        "z",
        "z"
    ]
}

{
    "before": [
        "#"
    ],
    "after": [
        "#",
        "z",
        "z"
    ]
}

検索で移動するときに選択されている単語を画面中央に持ってくる設定。

JSON
{
  "before": ["<Leader>", "c"],
  "commands": [
    {
      "command": "editor.action.commentLine"
    }
  ]
}

Leader+cでその行をコメントアウトする設定。

JSON
{
  "before": ["<Leader>", "w"],
  "after": [],
  "commands": [
    {
      "command": "workbench.action.files.save",
      "args": []
    }
  ]
}

Leader+wでファイルを保存する設定。

JSON
{
  "before": ["<Leader>", "q"],
  "after": [],
  "commands": [
    {
      "command": "workbench.action.closeActiveEditor",
      "args": []
    }
  ]
}

Leader+qでファイルを閉じる設定。

JSON
{
  "before": ["<Leader>", "x"],
  "after": [],
  "commands": [
    {
      "command": "editor.action.formatDocument",
      "args": []
    }
  ]
}

Leader+xでファイルのフォーマット(整形)を行う設定。

JSON
{
    "before": [
        "<Leader>",
        "l"
    ],
    "after": [
        "$"
    ]
}

{
    "before": [
        "<Leader>",
        "h"
    ],
    "after": [
        "^"
    ]
}

Leader+lで行末にカーソル移動、Leader+hで行頭にカーソル移動する設定。
Vimの問題だが、デフォルトのキーは押しにくい位置にあるので、書き換えるととても快適になる。

JSON
{
  "before": ["<Leader>", "m"],
  "after": ["%"]
}

Leader+mで対応する括弧に移動する設定。
これもデフォルトのキーが押しにくい。

JSON
{
  "before": ["<Leader>", "z"],
  "commands": [":noh"]
}

Leader+zで検索のハイライトを消去する設定。
あると地味に便利。

Insertモードのキーバインド

JSON
{
  "before": ["j", "j"],
  "after": ["<Esc>"]
}

j+jでInsertモードを抜ける設定。
必須設定。
かなりの頻度で使用するのに、Escキーは押しにくすぎる。

JSON
{
  "before": [";", ";"],
  "commands": ["editor.action.triggerSuggest"]
}

;+;で補完ビューを表示する設定。
他の人が設定しているのを見て、便利そうだと思ってパクった。

Visualモードの設定

JSON
{
    "before": [
        "<Leader>",
        "l"
    ],
    "after": [
        "$"
    ]
}

{
    "before": [
        "<Leader>",
        "h"
    ],
    "after": [
        "^"
    ]
}

{
    "before": [
        "<Leader>",
        "m"
    ],
    "after": [
        "%"
    ]
}

Normalモードと同様に高速でカーソル移動できるようにするための設定。

JSON
{
    "before": [
        ">"
    ],
    "commands": [
        "editor.action.indentLines"
    ]
}

{
    "before": [
        "<"
    ],
    "commands": [
        "editor.action.outdentLines"
    ]
}

インデントの変更を連続で行えるようにする設定。
これもネットで見つけて追加してみた。
便利な気がする一方、手動でVisualモードに戻るのが面倒な気も…
Vimは.キーで繰り返し処理を行えるので、もしかしたらいらないかも。
少し使ってみて微妙に感じたらやめる。

テーマの設定

  • "workbench.colorTheme": "Iceberg",

Icebergをインストールしたら自動で入るはず。
かっちょいい。

便利に使うためにはまだまだ設定しないといけない項目はあるが、今回はここまでにしておく。
なんだかんだ結構長くなってしまった…

ということで今回設定した内容。

  • settings.json
JSON
{
  "editor.minimap.enabled": false,
  "editor.renderWhitespace": "all",
  "files.trimTrailingWhitespace": true,
  "vim.leader": "<space>",
  "vim.useSystemClipboard": true,
  "vim.useCtrlKeys": true,
  "vim.easymotion": true,
  "vim.visualstar": true,
  "vim.hlsearch": true,
  "vim.normalModeKeyBindingsNonRecursive": [
    {
      "before": ["u"],
      "commands": [
        {
          "command": "undo"
        }
      ]
    },
    {
      "before": ["<C-r>"],
      "commands": [
        {
          "command": "redo"
        }
      ]
    },
    {
      "before": ["x"],
      "after": ["\"", "_", "x"]
    },
    {
      "before": ["s"],
      "after": ["\"", "_", "s"]
    },
    {
      "before": ["j"],
      "after": ["g", "j"]
    },
    {
      "before": ["k"],
      "after": ["g", "k"]
    },
    {
      "before": ["n"],
      "after": ["n", "z", "z"]
    },
    {
      "before": ["N"],
      "after": ["N", "z", "z"]
    },
    {
      "before": ["*"],
      "after": ["*", "z", "z"]
    },
    {
      "before": ["#"],
      "after": ["#", "z", "z"]
    },
    {
      "before": ["<Enter>"],
      "commands": ["editor.action.insertLineAfter"]
    },
    {
      "before": ["<Leader>", "c"],
      "commands": [
        {
          "command": "editor.action.commentLine"
        }
      ]
    },
    {
      "before": ["<Leader>", "w"],
      "after": [],
      "commands": [
        {
          "command": "workbench.action.files.save",
          "args": []
        }
      ]
    },
    {
      "before": ["<Leader>", "q"],
      "after": [],
      "commands": [
        {
          "command": "workbench.action.closeActiveEditor",
          "args": []
        }
      ]
    },
    {
      "before": ["<Leader>", "x"],
      "after": [],
      "commands": [
        {
          "command": "editor.action.formatDocument",
          "args": []
        }
      ]
    },
    {
      "before": ["<Leader>", "l"],
      "after": ["$"]
    },
    {
      "before": ["<Leader>", "h"],
      "after": ["^"]
    },
    {
      "before": ["<Leader>", "m"],
      "after": ["%"]
    },
    {
      "before": ["<Leader>", "z"],
      "commands": [":noh"]
    }
  ],
  "vim.insertModeKeyBindingsNonRecursive": [
    {
      "before": ["j", "j"],
      "after": ["<Esc>"]
    },
    {
      "before": [";", ";"],
      "commands": ["editor.action.triggerSuggest"]
    }
  ],
  "vim.visualModeKeyBindingsNonRecursive": [
    {
      "before": ["<Leader>", "l"],
      "after": ["$"]
    },
    {
      "before": ["<Leader>", "h"],
      "after": ["^"]
    },
    {
      "before": ["<Leader>", "m"],
      "after": ["%"]
    },
    {
      "before": [">"],
      "commands": ["editor.action.indentLines"]
    },
    {
      "before": ["<"],
      "commands": ["editor.action.outdentLines"]
    }
  ],
  "workbench.colorTheme": "Iceberg"
}

まとめ

今回の設定で、とりあえず最低限エディターとしては使える状態になったと思う。
次回はkeybindings.jsonなどをいじって、より便利な環境に近づけていきたい。

Author Profile
liebe-magi

りーべ / liebe-magi

ものづくりが大好きな自称フルスタック(?)エンジニア。大学・大学院でコンピュータサイエンスを専攻し、現在は某企業の研究所所属。専門は組み合わせ最適化問題や機械学習など。主に使用している言語はPython、JavaScript (TypeScript)、Rust、Go。最近は競技プログラミングに興味を持ち、AtCoderのコンテスト (ABC) に毎週参加中 (現在緑)。趣味はマジック、漫画・アニメ、ゲーム(電源・電源問わず)。