我的Neovim

STJ

我的 Neovim

この記事は SenseTime Japan TECH Blog アドベントカレンダー 2021 の 5日目の記事です。

20XX年、世界はエディタ大戦の炎に包まれた! Atom, SublimeText といった名将が覇を競ったテキストエディタ戦国時代は VSCode の一人勝ちで終結しつつあった。 しかし、 Vim は死滅していなかった!

2021 年現在、ソフトウェア開発者は JetBrains をはじめとする IDE や VSCode 等の高機能なテキストエディタを使うことができます。 そのような状況においても、世界中にターミナルひきこもりは存在し、私はその一人です。

私の開発環境は主に3つの要素からなります。 コマンドラインシェルは zsh を使用し、常時 tmux を立ち上げ、Neovim でコードを書いています。 本稿では、主に私の Neovim について書き、少しだけ tmux と zsh の話もします。

基本的な設定の方針は、どこでも同じようにコードを書けるようにすることです。 私の主な開発マシンは仕事でもプライベートでも Ubuntu のデスクトップマシンですが、 MacBook でも Docker コンテナでもまったく同じようにコードを書けるようにしています。 そのため、以下で述べる設定はすべて dotfiles というレポジトリで管理しており、 新しい環境を作るときにはコマンド一発 ./setup.sh ですべて導入されるようになっています。

おい、VSCodeもいいけどNeovim使えよ[1]

私が Neovim をメインエディタとして使い始めたのは院生の頃です。 それから、およそ4年くらい秘伝の設定ファイル[2]を継ぎ足して使い続けてきました。 私の横の席の先輩も、その横の先輩も Vimmer だったので、自然と(?)私も (Neo)Vim を使うようになりました。[3] (Neo)Vim は ssh 先のリモート環境でも起動可能であるため、 リモートの GPU サーバの中で実験コードを書くことが多い私にとっては良い選択肢でした。[4] それまでは、Vi キーバインドで PyCharm, SublimeText, Atom などを使用していましたが、 起動が遅い、動作が重いなどの理由で使うのをやめてしまいました。[5]

以降、単に Vim といった場合は Neovim を指します。 また、ここで紹介しているプラグイン・設定の一部は古いものもあります(継ぎ足しているのでご勘弁ください)。 なお、私の環境の Neovim のバージョンは 0.4.3 です。[6]

Fancy Appearance

開発環境において最も重要なものは見た目です。 デフォルトでは、中央に編集画面、左に NerdTree (高機能ファイルエクスプローラ)を配置しています(図1)。

図1: Fancy Appearance

このあたりの見た目に関する設定は、 nvim/cfgs/general.vim に書いています。

NerdTree

左にある高機能なファイルエクスプローラです。 vim-devicons というプラグインにより、 ファイルタイプ毎のアイコンを表示できるようにし、 nerdtree-git-plugin により、 git管理下にあるファイルの変更状況を表示させています。

最近は coc-explorer が流行っているみたいなので、今後試してみたいと思います。

Airline

下部にあるステータスバーです。デフォルトのステータスバーに比べ、かなり Cool です。 モード、ブランチ、カレントバッファのファイル名、現在位置などが表示されています。

Tagbar

CTags と連携し、アウトラインを表示する窓です。右側に配置し、トグル表示できるようにしています。 Python や C++はもちろん、 Markdown でも使うことができます。

Tagbar は強力ですが、 CTags を実行し、タグを生成しなければ使えません。 自動的に CTags を実行するために、 vim-tags を使用しています。

アウトライン表示としては、最近では cocと連携が可能な vista が流行っているらしいです。 今後試してみたいと思います。

Onehalf

カラースキームは Onehalf Dark を使用しています。 特にカラースキームの好みはなかったはずなので、たぶん適当に決めたのでしょう。 他には、 Solarized Dark などが好きです。

Font

Source Code Pro を使用しています。これはサイズを含め gnome-terminal, alacritty などで設定しています。 かつては Neovim 用は init.vim 、zsh 用は .zshrc にと個別に設定していたのですが、 ターミナルの設定に統一したかったため、やめてしまいました。

プラグイン管理

プラグインの管理には vim-plug を使っています。 シンプルで使いやすいです。 他には NeoBundle などがあります[7]が、結局 vim-plug しか使ったことがありません。 使いたいプラグインを nvim/plug.vim に書いておき、:PlugInstall するだけでインストールできるため、 とても使い勝手が良いです(図2)。

図2: PlugInstall を実行した様子。既にプラグインがインストールされた状態だと面白みがない

IDEっぽい機能(自動補完・定義・参照・リネーム・リント・フォーマット)

暗黒の力を手に入れた Vim はもはや IDE です。 Vim の IDE 化については好みが分かれるところですが、私はプラグインを貪り続け、 IDE 化を推し進めてきました。 Vim だって補完できますけど!

前述のファンシーな見た目に関する設定は以前からほとんど変えていませんが、 この IDE 的な機能については、その設定、プラグインをこの1年ほどで大きく変えました。

Vim の補完などを司るプラグインは、この数年で大きく変化しました。 Language Server Protocol (LSP) という概念の登場・浸透により、 これまで開発されてきた各言語のためのエディタ専用の各種機能のプラグインは一つの LSP にまとめ上げられ、 そのエディタ用の LSP クライアントから LSP を使うといった構造に変化しました。 この仕組みは、 VSCode の誕生とともに発展してきたため、 VSCode が発展する限り、 Neovim でもその恩恵に預かることができるわけです。ありがたいですね。[8]

CoC[9] は Vim8/Neovim 向けの LSP クライアントの一つで、 私はこれを使っています。 他には vim-lsp などがありますが、使ったことがありません。 CoC は事前に Nodejs をインストールする必要があるため少し導入が面倒ですが、 使い勝手はなかなか良いと思います。

ここでは、私が主に使用するPython, C++について、その設定を紹介します。

Python

Pythonの自動補完は Jedi に頼っています。 以前は、暗黒美夢王謹製の deoplete, deolete-jedi を使用していました。 CoC への移行時に coc-python [10] を経由し、 現在は coc-jedi を使っています。

coc-jedi を導入することにより、自動補完、定義、参照、リネームなどの機能を使えるようになります。 さらに、各関数のドキュメントをフローティングウインドウに表示することもできます。 これだけで、かなり IDE のような感じになってきました。

さて、あと必要なものは、リンタ・フォーマッタです。 これらは coc-jedi に含まれないため、別途インストールする必要があります。 リンタ・フォーマッタについては、以前は syntastic や ale を使っていました。 cocへの移行時に coc-python を経由し、現在は diagnostic-languageserver の CoC 拡張である coc-diagnostic を使用しています。 diagnostic-languageserver は数多くの言語に対応し、 Python についても主要なリンタ・フォーマッタに対応しています。 私は中でも、 flake8, black, isort を読み込んでいます (正直なところ、isortはあまり使っていませんが)。

現在の環境では設定項目の数も減り、動作もそこそこ速いため概ね満足しています。 不満としては、選択範囲をフォーマットする機能 (formatSelected) が diagnostic-languageserver ではサポートされていない点があります。 ファイル全体をフォーマットすることは可能ですが、自分の変更と関係ない場所はあまり弄りたくありません。 この機能が今後のアップデートにおいて実装される可能性は低そう[12]なので、 積極的に貢献を行っていきたいところです。

C++

C++ 用には ccls を使用しています[13]。 CoC の ccls 拡張は存在しないため、自分で ccls をインストールする必要があります。 私が参加している C++ のプロジェクトでは開発用の Docker image を使用しているため、 その image を魔改造したオレオレ image を作成し、そのコンテナ内で NeoVim を使っています。 以下は、オレオレ image 作成用の Dockerfile 中の ccls をインストールする部分です。

RUN git clone --depth=1 --recursive https://github.com/MaskRay/ccls /tmp/ccls && \
    cd /tmp/ccls && \
    wget -c http://releases.llvm.org/8.0.0/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz && \
    tar xf clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz && \
    cmake -H. -BRelease -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=$PWD/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04 && \
    sudo cmake --build Release --target install -j${JOBS:-1}

その他便利なプラグイン・設定

blamer

VSCode の git-lens と同等の機能で、カーソル行、選択行について誰が編集を行ったかわかるようになっています。 こちらでは、blamer: 糾弾者とより直接的な名前になっています。 一週間前の自分は他人であると、よく実感できます(図3)。

図3: 犯人は…私です。

コマンドを他のターミナルに送る

上述した C++ 用の開発環境では、ホストの tmux のウィンドウを水平分割し、 上下のペインでコンテナにログインしています。 上のペインではシェルのセッション、下のペインではNeovimを起動しています。 このとき、下でコードを編集したあとにビルドを行うためには、 上に一度移動してコマンドを叩かなくてはなりません。しかし、これは面倒です。 そこで、下ペインのVimから上ペインにコマンドを送れるようにしました。 これは ttyecho を使うことで実現しています。

ttyecho は他のターミナルにコマンドやデータを送ることのできるソフトです。 以下のように Neovim の設定ファイル[2] に記述することで、 直接別のペインにビルドコマンドを送ることができるようになります。

function! BuildFooBar()
    call system('sudo ttyecho -n /dev/pts/1 "build command"')
endfunction

command! BuildFooBar :call BuildFooBar()

上記の設定で怪しいのは、 送り先のターミナルが /dev/pts/1 に固定されているところです。 少し気持ち悪い気もしますが、 コンテナから見えるターミナルはホストのtmuxにおける上ペインと下ペインの2つだけで、 先に起動するのが上ペインになるようになっているため、 大抵の場合上ペインのターミナルが /dev/pts/1 になります。 したがって、これで上ペインにコマンドを送ることができます (本当はもう少しマシな方法で指定したいところです)。

もちろん、ビルドコマンドを QuickFix で実行する方法もありますが、 大量の出力が流れると Vim の動作が重くなってしまうため、 上記の方法をとることにしました。

Markdown エディタとしての Neovim

(Neo)vim はもちろん Markdown エディタとして使うことができます。 ここでは、Markdown ビューアとして、 MarkdownPreview.nvim を使用しています(図4)。

図4: MarkdownPreview を実行した様子。これは Mac での結果ですが、 Ubuntu でも同様に表示されます。

ZSH

zshはカスタマイズ性の高いコマンドラインシェルです。 fish[14] 等の他のコマンドラインシェルと比較すると、 POSIX との互換性が高いので、 bash からの移行がしやすいです。 また、プラグインも充実しているため、プラグインジャンキーにとっては楽しいシェルです。

ここでも、私が特に気に入っているプラグイン・設定を紹介します。 また、下記で紹介するプラグインは zplug で管理しています。

pure

見た目の設定です。

Qiitaの名記事「お前らのターミナルはダサい」 に触発され、行き着いた先です。 まさに、 Simple is best. であり、ここに落ち着いてからはずっと変えていません(図5)。

図5: シンプルなプロンプト pure

enhancd

ディレクトリ間の移動を行う cd コマンドの拡張です。

ディレクトリの正確なパスは大抵の場合覚えていないため、自動補完がほしいです。 bash でも自動補完はある程度効きますが、それほど強力ではありませんでした。 このプラグインでは、不正確なパスでも過去のログから絞り込んで勝手に修正してくれるといった機能を 使うことができます。

詳細は作者による日本語の解説 ターミナルのディレクトリ移動を高速化する をご参照ください。

Tmux

tmux in tmux

私は tmux をネストする禁忌を日常的に侵しています。 本来、これは避けるべきですが、コンテナで開発するときには便利です。 私の場合、ホスト側は常に tmux を起動していますので、もちろんコンテナも tmux の中で起動します。 そして、そのコンテナの中で tmux を使いたいときがよくあります。

このとき問題となるのは、prefix が競合することくらいですが、 ホスト側の prefix を C-a にしておくと、コンテナ内の tmux (C-b) と競合しません。 もともとtmuxのprefixは C-a にする宗派なので、特に抵抗はありませんでした。 C-b は指が遠く、押しにくいのです。

tpm: tmux plugin manager

Vim, zsh と同様、 tmux でも各種プラグインを使用できます。 tpm を使うと、それらのプラグインを楽管理することができます。

数あるプラグインの中でも、 tmux resurrecttmux continuum が便利だと感じています。[15] 私は常に tmux のセッションを複数立ち上げ、いくつものウインドウ、ペインを開いているため、 PC の再起動などでtmuxのセッションを落としてしまうと、 どこに何を開いていたかわからなくなってしまいます。 tmux resurrect を使うと、 tmux のセッションを保存・復元することができるため、 安心して再起動を行うことができます。 また、セッションの保存を自動的に行ってくれるのが tmux continuum です。 これにより、突然のシャットダウンにも対応できるようになるため、 特に停電の発生が多かった常総旧社屋では重宝しました。

この記事を書くために、改めて自分の設定ファイルを見返し、調査を行ったところ、 既にメンテナンスされていないプロジェクトや、 似たような機能を持つ新しいプラグインの開発が進んでいることを知りました。 年末年始はこれで遊び尽くせそうです。

最後に、ここまでの話は SenseTime の技術と全く関係がありません。すみませんでした。 来年もターミナルにひきこもりたいと思います。

(おまけ)常総 AI・自動運転パーク

なぜブログの最後の方には本題と全く関係のない宣伝が入るのか、私気になります。

センスタイムジャパンにはいくつかの拠点がありますが、 その中でもっともホットな拠点が茨城県常総市にある AI・自動運転パークです。 私は、もっともホットな研究開発に参加するため、今年の春に常総に転勤してきました。[16]

AI・自動運転パークは、もとは自動車学校の跡地であり、それを整備してテストコースとして利用してきました。 付随するオフィスも自動車学校の廃校舎であったため、かなり古い建物です。 夏は虫と戯れられ、冬は換気が良いといった、素敵な環境で過ごしてきましたが、 今年10月に新社屋が竣工し、とても快適な環境で研究開発に取り組むことができるようになりました。

この節では、匠の技によって生まれ変わった常総の様子を紹介します。

ハーマンミラーの、あの有名椅子

新社屋に移ってもっとも良くなったと感じているものは、椅子です。 旧社屋で使用していた椅子もそれほど悪くありませんでしたが、 新社屋で導入されたH社のアーロンチェアと比べてしまうと…。 アーロンチェアはカスタマイズ性が高く、おしりも腰も痛くなることがなくなったため、 とても快適に仕事をすることができています(図6)[17]

図6: 椅子

広いバルコニー

私を含めた常総常駐者の居室は2階にあり、そこから広いバルコニーに出ることができます。 バルコニーからは広大な関東平野を望むことができ、午後のひととき、 コーヒーを飲みながら外の風に当たるのも気分転換としてはなかなか良いものです(図7)。

図7: バルコニーから見える風景

運動不足も解消できる階段

エントランスに入るとすぐ二階へつながる階段が現れます。オープンな作りが現代的です。 前述の通り、私達の居室は二階にありますので、一日に数回この階段を昇降します。 これにより、運動不足が解消できているような気がします。 また、たまに懸垂している人を見ることもできます(図8)。

図8: 階段で懸垂する人


1. センスタイムジャパンはエディハラのない健全な職場です。各々好きなエディタ・IDEを使っています(私の部署ではほとんどの人がVSCodeを使っているようです)。
2. Neovim なので vimrc ではなく .config/nvim/init.vim が出発点。どのように表記するのが正解でしょうか?
3. なお、現在では彼らも VSCode に改宗した模様。
4. 現在でも在宅勤務や出張先からオフィスの PC の接続してコードを書くので、非常に助かっています。
5. PyCharm には、リモートインタプリタという機能があり、ローカルで書いたコードを自動的にリモートに同期し、そのまま実行することができました。 一方で、 Deep Learning の学習は長い時間がかかるため、この機能はあまり適しておらず、結局すべてサーバで行ったほうが早いのではと思いました。 現在では、同じような機能が VSCode にもありますし、自動同期だけなら Mutagen を使うのが良いと思います。 また、実験管理ツールを使うことにより、この問題はある程度スマートに解決できるような気もします。
6. 先日 v0.6.0 がリリースされたのに…。 そろそろ令和に適応したいです。
7. おい、NeoBundle もいいけど vim-plug 使えよ
8. LSP についてはまったく詳しくないのでここの記述には誤りが含まれるかもしれません。language server protocolについて (前編) が参考になりました。
9. CoC は Conquer of Completion の略です。自動補完以外もできますが。
10. coc-python では補完機能のみ Jedi を使用していました。現在はアーカイブされた[11]ため、 coc-jedi に移行しました。
11. coc-pythonがArchive入りしていることに気がついたのでcoc-jediに変えてみた
12. https://github.com/iamcco/diagnostic-languageserver/issues/57#issuecomment-844722652
13. NeoVimでC/C++を書くときはcoc.nvim + cclsが良さげ
14. 一時期 fish を使っていた時期がありましたが、コマンドが違いすぎてストレスがたまり、 zsh へと移行しました。ですが、お洒落で高機能なコマンドラインシェルを教えてました。
15. tmuxメモ : Tmux Resurrectとtmux-continuumで環境の保存/復元 - もた日記
16. もとは東京オフィス所属でした。青山のおいしい店が恋しく感じられます。 特に、「きいろ」という和食の店、「天馬」というカレーの店がお気に入りでした。 青山にお越しの際はぜひ行ってみてください。
17. 逆に自宅の椅子はだいぶヘタっているため、在宅勤務をするとおしりが痛くなります。

投稿者プロフィール

Mi
Mi
先進運転技術部リサーチャー、常総AI・自動運転パーク在籍。
趣味はここにかけないものが多い。