Don't Repeat Yourself

Don't Repeat Yourself (DRY) is a principle of software development aimed at reducing repetition of all kinds. -- wikipedia

IntelliJ IdeaにIdeaVimを入れてNeovimとほぼ同じ動作をさせる

JavaScalaで仕事をしていたころは毎日使っていたIntelliJですが、いつの間にかRustで仕事するようになってまったく開かなくなりました。最近はNeovimで仕事をしており、そちらの方がもはや慣れています。

ところが最近Kotlinを使う必要が出てきたので、Neovimでいつも通りセットアップしたところ、kotlin-language-serverがあまり安定的に動作してくれませんでした[*1]。たとえばリネームをかけるとエラーを吐いて死ぬので、パッチを投げるというような状況です(執筆時点ではまだマージされていません泣)。

github.com

他にも不具合を見つけていたり、そもそも定義ジャンプ(Go To Definition)が私の環境では動作していないように見えるなど、結構不具合がまだ多めです。可能な限りパッチは投げたいと思って調査していますが、喫緊必要なのでIntelliJを使わざるを得ない、というわけです。

プライベートでは今後もNeovimを使い続けるであろう手前、IntelliJのショートカットキーとの頭の切り替えはたぶん難しいです。そういうわけで、IdeaVimをなんとかハックして、Neovimの環境に近い状況にできないかと考えました。

IdeaVim

IdeaVimはVimの設定ないしはキーバインドを反映できるJetBrains製品向けのプラグインです。

github.com

初期設定で.vimrcを読み込んでくれますが、.ideavimrcというファイルがあればそちらを優先的に読み込んでくれます。.ideavimrcを用意してJetBrains向けの設定を書いておきつつ、.vimrcをsourceして手元の設定も読み込ませておく、という使い方が無難そうかなと思っています。

Action List

エディタ内での操作は基本的にはIdeaVimの初期設定でなんとか間に合います。が、たとえばIntelliJのターミナルを開きたいとか、左にくっついてるファイルファインダーを呼びたいとか、そういった操作は初期設定だけだとIntelliJのデフォルトになってしまいます。Neovimを使用していた際は、たとえば<leader> thとか、<leader> eとかで開いていたので、これと同じキーバインドを割り当てたいわけです。

調べてみると、IntelliJには「Action List」と呼ばれる設定が存在することを知りました。たとえばここに載っているリストを見ていくと、ターミナルのオープンはActivateTerminalToolWindow、ファイルファインダーのオープンはActivateProjectToolWindowといった具合にです。これに対して、Vimでのキーバインドを設定すると、オリジナルのキーバインドIntelliJ上のアクションを呼び出すことができます。

あるいは、IdeaVimに「Track Action Ids」という機能があるので、これを利用してもAction Listを知ることができます。

オンにすると、ショートカットキーで何かを動作させると右下にアクションIDが表示される。

設定してみた

いわゆるリーダーキーは「space」に割り当てています。下記のように設定してみました。

let mapleader = " "

" Key mapping
nmap gi <Action>(GotoImplementation)
nmap gr <Action>(FindUsages)
nmap <leader>fa <Action>(GotoAction)
nmap <leader>ff <Action>(SearchEverywhere)
nmap <leader>fw <Action>(FindInPath)
nmap <leader>c <Action>(CloseContent)
nmap <leader>bc <Action>(CloseAllEditorsButActive)
nmap <leader>bC <Action>(CloseAllEditors)
nmap <leader>e <Action>(ActivateProjectToolWindow)
nmap <leader>la <Action>(ShowIntentionActions)
nmap <leader>ls <Action>(ActivateStructureToolWindow)
nmap <leader>lr <Action>(RenameElement)
nmap <leader>o <Action>(EditorEscape)
nmap <leader>th <Action>(ActivateTerminalToolWindow)
nmap <leader>q <Action>(HideAllWindows)
nmap <leader>/ <Action>(CommentByLineComment)
nmap [b <Action>(PreviousTab)
nmap ]b <Action>(NextTab)
nmap u <Action>($Undo)
nmap <C-l> <Action>(NextSplitter)
nmap <C-h> <Action>(PrevSplitter)

github.com

困った点としては、ファイルファインダーを表示できる<leader> eやターミナルを表示できる<leader> thは、キーバインドをしてしまうとトグル形式にはならないようで、Neovimでは一度キーバインドを押してもう一度押すと開いて閉じるわけですが、IntelliJは2度目は認識してくれず、開いたまま閉じませんでした。これは結構困るので、<leader> qで開いているツールウィンドウを全部閉じるように設定してみました。

一旦普段開発で利用しているものはだいたい設定できたと思います。当然ですがtelescopeはないし、lazygitはターミナルをわざわざ開いて起動する必要ありです。

まとめ

とりあえずほとんど同じキーバインドで動かせるようになったので、エディタの切り替え時に混乱することも少なくなりそうです💛

ところで、kotlin-language-serverに代わる何かを実装したくなってきました。ScalaのMetalsくらいを目標にちまちまがんばりたいかもしれません。

*1:Kotlinは公式がLanguage Serverを用意していません。JetBrains製で、IntelliJなどの自社製品を使ってもらうのが一番いいはずなので、戦略上理解はできますが、できれば使用するエディタはあまり縛っては欲しくないですね。