Vim
neovim
VimDay 18

Neovimがどういうプロジェクトなのかまとめ

はじめに

今年からVimからNeovimに移行してもうすぐ一年になろうとしています。
Neovimを使いだしたきっかけは、暗黒美夢王がリリースしている数々の良プラグインがNeovimでしか使えなかったからでした。
逆に言えばそれ以外具体的にNeovimがVimとどう違うのか、何がすぐれているのか全く知りませんでした。
VimConf2017で刺激を受けた私は、その膨大なインプットのなかでもっと私が使っているエディタについて知る必要があると考えました。
本稿ではNeovimとVimの違いを調べ、互いにどのような影響を与えているのかを述べます。

注意

私は中学生レベルの英語能力なので解釈に誤りがあることが多々あると思います。
もし誤りを見つけたのであれば教えていただければ幸いです。

Neovim Introduction

そもそもNeovimとはなにかREADMEを読んでみてもピンと来なくて、他になにかいいものはないか地味に探し回りました。
結果Neovim wikiが最も詳しかったので、ザクッと翻訳してみました。
以下翻訳...

vimは強力なテキストエディタであり、巨大なコミュニティとともに成長し続けています。
このエディタは開発から20年たった今でも、拡張や改善を続けており、主にVimscriptやサポートされたスクリプト言語を使用しています。

Motivation

20年以上に渡る歴史の中で、Vimはおよそ30万に渡るコード行を蓄積しており、
理解できる人材の少ないC89のコードがあるため、これ理解するためには混乱に立ち向かう勇気が要ります。
また、Vimの巨大なコードベースのメンテナーはBram Moolenaar氏ただ一人となっており、
一度新たなコードをマージしてしまえば、彼が責任を負う必要があるため、パッチを受け入れることに対して慎重です。

これらの問題は、機能追加やバグフィックスが取り込まれることへの足かせとなっており、
Vimはプラグインエコシステムの開発スピードに追いつけていません。

Solution

Neovimは以下の目標を達成するためにVimのソースコードを積極的にリファクタリングします。

  • メンテナンスを簡略化し、機能追加やバグフィックスの速度を改善する
  • 複数開発者で作業を分割する
  • コアのソースコード変更をせずにモダンなUIの構築を可能にする
  • コプロセッサ上で動作する新しいプラグインのアーキテクチャにより拡張性を向上する
  • エディタでサポートされていないプログラミング言語でプラグインは記述できるようにする

これらのゴールを達成することによりに新たな開発者がコミュニティに参加し、結果としてすべてのユーザのエディタが改善されます。
重要なこととして、このプロジェクトによりVimのフルスクラッチやVimをIDEへ変化させる事はないということを強調します。
実装の変更がVimの編集モデルやVimscript全般に影響をおよぼすことはありません。
Vimscriptで記述されたほとんどのプラグインは正常に動作します。

Neovim Features

Neovimの思想はなんとなくわかってきました。
ではNeovimの開発が始まって約3年(NeovimのフォークはVim 7.4.160時点)を経て、どのような機能が実装されているのかを具体的に見ていきます。
とりあえずNeovim上で :help nvim-features してみると目次が出てきます。
全部見ていくとキリがないので今回ご紹介するのは以下の通り

  • API
  • Job control
  • Remote plugins
  • Embedded terminal

API

Nvim exposes a powerful API that can be used by plugins and external processes
via RPC, Lua and VimL (eval-api).

nvimはプラグインや外部プロセスからRPC、Lua、VimL経由で使用するための強力なAPIを公開しています。

いきなりですが正直このあたりは、開発者でないとどういう恩恵を得ているのかわかりませんでした!
そのうち自分でもプラグインを作成してみたときに具体的な話をしようと思いますので今はざっくり理解でお茶を濁します。

Neovimの関連プロジェクトはいろいろあるのですが、
特に印象的なのはNeovimのGUIとしてatomやvscodeで有名なElectronで表現しているOniNyaoVimでした。

VimはCUIベースの無骨なエディタの代表格でもあるのですが、
他のモダンのリッチな外観を羨ましいと思うVimmerも一定数いらっしゃるのでは無いでしょうか。

かくいう私もVimからatomやvscodeなどのモダンなエディタに何回か浮気したこともあり、

  • カーソルをfunctionの上に置くと勝手にdoc引用
  • かっこいいポップアップでプロジェクト内をgrep
  • なめらかなトランジションのある操作フィードバック
  • インタラクティブに反応するmarkdown preview

とかモダンなGUI表現が欲しくなるといったことがあります。
VimにそういうGUI表現が付けばもはや最強(絶対にありえん)と思っていたらまさにそれをやっちゃってました。

Onifeaturesやら
NyaoVimui-plugin-examples
を一度ご覧ください。綺麗なUIしてるだろ...Vimなんだぜ...これ...

これらがどうやってやっているんだと思い。調べてみるとNyaoVim作者さんの記事を発見したので、
『Web Components と Electron でつくる Neovim フロントエンドの未来』を見てみました。

読んでみた私のざっくりした理解だと、
Neovimをバックエンドとして起動し、ElectronでUIを構築。ユーザ操作を受け取ったElectron側が入出力の情報をNeovimと通信する。
通信の際には、Electron側のnode.jsクライアント(後述するRemote plugin)からmsgpack-RPCで通信してNeovimのAPIを叩く。ということなのかなと。

こうしてモダンなUI上でVimが稼働している例を見ているとNeovimのfeatureである

  • コアのソースコード変更をせずにモダンなUIの構築を可能にする
  • コプロセッサ上で動作する新しいプラグインのアーキテクチャにより拡張性を向上する

の一例といえるでしょうか。
軽く試したところ、VimなのにUIがモダンというとてもつなく可能性を秘めてるなと思いました。
幾つかNeovim本体とは違う挙動をする部分もあり、本格導入には至りませんでしたが、
ターミナルにこもってるタイプのプログラマの私でもmarkdown編集時のサブエディタとして使用するなど、
有用な点が既に幾つかあり、今後も動向をウォッチしていこうと思います。

job-control

Job control is a way to perform multitasking in Nvim,
so scripts can spawn and control multiple processes without blocking the current Nvim instance.

ジョブコントロールはNvim上でマルチタスキングを実行する方法です。
幾つかのスクリプトは複数プロセスの発行管理することができ、Nvim のインスタンスをブロックしません

私がVimを使い始めた当初(2013年くらいだったと記憶している)、プログラミングに必要そうなプラグインを幾つか入れて動作させてみたところ、
不意にバックグラウンドで動作が走って画面が固まることがありました。

例えばsyntasticなど。
これはファイル保存をした際に、自動的にlinterを実行し、処理結果をVim上QuickFixで表示するプラグインですが、
linterが処理を完了するまで画面が固まり、なぜ動かんのじゃとイラついたことがあります。

その一方でneocompletevim-quickrunを使っている際は、普通に非同期処理でVimをブロックしないようにできていたので、
この差は何なのだろうと思っていました。
要するにvimproc.vimを裏側で利用していた事を全然理解していなかったわけですが...

というわけNeovimではこれまでvimproc.vimなど外部プラグインに依存する必要性があった非同期実行機能が本体側で存在するわけです。

この非同期処理を活用したプラグインは今では沢山あり、今私が常用しているだけでも以下のようなものがあり、便利に使わせていただいています。

しかし、このjob機能はVim本家において実装(Neovimのjobと全く同じものではない)されており、
具体的にVimとNeovimでjob機能がどのように違うのか、そもそもVim本体に取り込まれるまでにどのような経緯があり、
それにNeovimがどのような影響をもたらしたのか、興味深いところではありますが、それはまたおいおい調べます。

Remote plugins

Extensibility is a primary goal of Nvim.
Any programming language may be used to extend Nvim without changes to Nvim itself.
This is achieved with remote plugins, coprocesses that have a direct communication channel (via |RPC|) with the Nvim process.

拡張性はNvimの最優先の目標です。
どのようなプログラミング言語でもNvim自体を変更することなく、Nvimを拡張することができます。
これはリモートプラグインやコプロセスがチャネルによりNvimプロセスと直接通信することで実現します。

Vimを常用している人であれば、よりVimを便利にするためのプラグインを書いてみたいと思っている人も多いハズ。
しかし、Vimプラグインを作成するためにはVimscriptを覚える必要があります。
VimscriptはもともとVimの設定を記載するためのスクリプトという経緯(らしい)から、
個人的には最近のモダンな言語と比較すると冗長でわかりづらく見えます。

  • エディタでサポートされていないプログラミング言語でプラグインは記述できるようにする

リモートプラグインは上記の Neovim feature を実現するための機能であります。
なので普段仕事で使用しているような言語であれば、ノウハウがあるし、書いてみてもいいかも...
と思っているVimmerも安心というわけですね。

それで具体的にどうやっているんだろと軽く調べてみました。登場人物は以下の通りで

  • Neovimプロセス
  • Neovimクライアント
  • リモートプラグイン

Neovimプロセスとの通信プロトコルがRPCでその通信で使用されるフォーマットがmagpackなのね。という雑な理解

Neovimプロセス <=== msgpack-RPC ===> Neovimクライアント <=== ライブラリ/実装 ===> リモートプラグイン

msgpack-RPCについては以下の資料が詳しそう。
whats-messagepack

Neovim APIのクライアントは各言語ごとにある。
api-clients

ただ欠点として

  • Neovim APIのクライアントが適切に動作する環境を用意する必要があるなど依存関係が多いため、導入に際してハードルが高い(少なくとも私はdeniteとdeopleteが安定動作するまで結構かかった)
  • リモートプラグインはNeovimの独自機構だからvimでは使えないNeovim専用プラグインとなる(プラグイン実装者が頑張ればなんとかなる?)

ちなみにNeovimのリモートプラグインとして作成されているdeniteやdeopleteがなぜVim8で使えるんだおかしいだろと思って調べてみたら、
vim-hug-Neovim-rpcというリモートプラグインがNeovimに対して送信した内容を
パースしてvimと通信するというライブラリがdeniteやdeopleteのRequirementsに記載されていました。

作ってないやつが何偉そうに解説してんだといわれそうだからそのうち作ろう。そのうち...

Embedded terminal

Nvim embeds a VT220/xterm terminal emulator based on libvterm. The terminal is
presented as a special buffer type, asynchronously updated from the virtual
terminal as data is received from the program connected to it.

NvimにはlibvtermベースのVT220/xtermターミナルエミュレータが組込されています。
このターミナルは特殊なバッファタイプとなっており、仮想ターミナルに接続されたプログラムからデータ受信をすると非同期、このバッファが更新されます。

Neovimにはターミナルエミュレータがあるから:terminalで実行できるよって話なのですが、
実はtmuxユーザの私はあまり魅力的に写ってなかったんですよね。tmuxでペイン分ければいいじゃんくらいに思ってました。
けどよくよく調べてみると、確かに幾つかの利点があることがわかりました。

  • ヤンクペーストがNeovim上ですべて完結する(tmuxのvisual modeはそのあたりちょっと弱い気がする)
  • そもそもGVimユーザからしてみるとターミナルを直叩けなかった(文字描画はGVimのが速いから確かに魅力)
  • その他プラグインからターミナルを制御することで色々できることが増える(らしい)

ちなみにターミナル機能はVim8に追いても実装されているため、Neovim独自ではなくなっている

その他 Neovim Features

上記に上げたのが比較的大きなFeatureなんですが、その他にも以下のような変更がいろいろあります。
これも1つずつ見ていくのはだいぶ辛いので、個人的に印象的なものを少し抜粋します。

  • Options:
    • 'cpoptions' flags: |cpo-_|
    • 'guicursor' works in the terminal
    • 'inccommand' shows interactive results for |:substitute|-like commands
    • 'scrollback'
    • 'statusline' supports unlimited alignment sections
    • 'tabline' %@Func@foo%X can call any function on mouse-click
    • 'winhighlight' window-local highlights
  • Variables:
    • v:event
    • v:exiting
    • v:progpath: is always absolute ("full")
    • v:windowid: is always available (for use by external UIs)
  • Commands:
    • checkhealth
    • drop: is available on all platforms
    • Man: is available by default, with many improvements such as completion
    • tchdir: tab-local |current-directory|
  • Functions:
    • dictwatcheradd(): notifies a callback whenever a |Dict| is modified
    • dictwatcherdel()
    • menu_get()
    • msgpackdump(): msgpackparse() provide msgpack de/serialization
  • Events:
    • DirChanged
    • TabNewEntered
    • TermClose
    • TermOpen
    • TextYankPost
  • Highlight groups:
    • hl-QuickFixLine
    • hl-Substitute
    • hl-TermCursor
    • hl-TermCursorNC
    • hl-Whitespace: highlights 'listchars' whitespace

inccommand

inccommandは今回調べた結果初めて知ったオプションなのですが、
試しに導入してみたところ、3日でこれ便利だわってなってvimrcに入れた。

一言で言うと、文字列置換をインタラクティブに表示するオプション。
動画を見れば一発でわかります。

Neovim: incremental substitution ('inccommand')

設定は以下の通り

set inccommand=split

この動きを実現するvim-overというプラグインがあり、inccommandとほぼ同様の動作を行えるのですが、オプション設定のみでできる点や:%sをそのまま使えるのがいい感じです。

TextYankPost

vim-highlightedyankというヤンクした範囲をインタラクティブに表示するというプラグインがあるのですが、
Vimであればyankキーバインドをvim-highlightedyankの用意したプラグインコマンドと差し替える必要があるのですが、
Neovimであれば TextYankPost というAutoCommandによりインストール時点で無設定で使用することができます。

vim diff

上記で有名どこの違いはザラッとおさらいできたかなと思いますので次は、もうちょい地味めの違いについて。
:help vim_diffを見てみましよう。

nvim-configuration

NeovimとVimでは設定ファイルの位置が変わっていて、これはXDG Base Directory Specificationに準拠した結果とのこと。
詳しくは今後調べる予定。
設定ファイルの位置が.vimrcじゃなくて$XDG_CONFIG_HOME/nvim/init.vimに変わってたのはビビった。

nvim-default

NeovimとVimではオプションのデフォルトが異なります。
確かに、Vimのオプションにはこりゃデフォルト有効でよくね?っていうオプションとかが結構あって、
Neovimではそのあたりのからみや設定ファイルのディレクトリが変更になった影響などもあり、
デフォルトオプションが変わっています。

詳細は次の通り

  • Syntax highlighting is enabled by default
  • ":filetype plugin indent on" is enabled by default
  • 'autoindent' is set by default
  • 'autoread' is set by default
  • 'backspace' defaults to "indent,eol,start"
  • 'backupdir' defaults to .,~/.local/share/nvim/backup (xdg)
  • 'belloff' defaults to "all"
  • 'complete' doesn't include "i"
  • 'directory' defaults to ~/.local/share/nvim/swap// (xdg), auto-created
  • 'display' defaults to "lastline"
  • 'formatoptions' defaults to "tcqj"
  • 'history' defaults to 10000 (the maximum)
  • 'hlsearch' is set by default
  • 'incsearch' is set by default
  • 'langnoremap' is enabled by default
  • 'langremap' is disabled by default
  • 'laststatus' defaults to 2 (statusline is always shown)
  • 'listchars' defaults to "tab:> ,trail:-,nbsp:+"
  • 'nocompatible' is always set
  • 'nrformats' defaults to "bin,hex"
  • 'ruler' is set by default
  • 'sessionoptions' doesn't include "options"
  • 'showcmd' is set by default
  • 'smarttab' is set by default
  • 'tabpagemax' defaults to 50
  • 'tags' defaults to "./tags;,tags"
  • 'ttyfast' is always set
  • 'undodir' defaults to ~/.local/share/nvim/undo (xdg), auto-created
  • 'viminfo' includes "!"
  • 'wildmenu' is set by default

autoindent, hlsearch, incsearch, listchars, wildmenu辺りは私も必ずONにするのでもうデフォルトでよくね?とは思う。

NeovimがVimに与えた影響とは

Vimの作者であるBram Moolenaar氏はVim の作者 Bram Moolenaar への10の質問(10 Questions with Vim’s creator, Bram Moolenaar - Binpress の翻訳)
にてNeovimに対して聞かれた際に以下のコメントをしています。

Q. Neovim の開発はおよそ 8 ヶ月に渡って続いています。Neovim の勢いについてどのようにお考えですか?

A.
さあ。最終目標に対していくつかの基本的な選択は奇妙で不適切に見えます。
例えばあるシステムのサポートを切ったり後方互換性を維持していないことなどです。そのようなことをしなくてもうまく実装できたのではないでしょうか。
彼らは Vim の古いコードが嫌いでそれを “正しく” しようとしているように感じます。古いコードが醜いことには同意です。
しかし新しく実装するには膨大な手間がかかるでしょう。これは Elvis に起こったことによく似ています: Elvis は再実装により改善を行いました。
それは Vim が多くの機能拡張を行っている間も行われ続け、とても長い時間が費やされました。
最終的には Elvis は非常に多くのユーザーを失ってしまいました。再実装された Elvis は良いコードで書かれているかもしれませんが、ユーザーはそのことに気付きません。

Neovim から生じた良いものが Vim にも還元できたらと願っています。
より良いプラグインサポートや改善された GUI、Vim の他のアプリケーションへの埋め込みを望まない人はいませんよね?
大きな問題は、それをどのようにきちんと実装するかです。

Neovim Introductionを見た際にもなるほどと思いましたが、真逆の事を言っているこの回答に関してもなるほどと思います。
結局のところVimユーザに対してどのような価値を届けられるかなのかと。
リファクタリングした結果開発効率が早くなるのが先か、リファクタリングしている最中に機能拡張されるのが先か
どちらも正しいことを言っていてこの回答は結果が全てなのかなと思います。

私はVimやNeovimのpatchを書いているわけでも無いので推測でしか無いのですが、
NeovimがVimに与えた影響はけして小さなものでは無いのではと思いました。
Neovimが先んじて実装したjobやterminalといった機能は上記の通りVimにも還元されていますし、
同じようにVimのpatchはNeovim側においても取り込まれています。

雑感

この記事作成を通して、今までおぼろげであったNeovimの外観がやっとつかめてきました。
まず思ったのが、NeovimはVimに対してとてつもないリスペクトをしているということでした。
一度でもVimのマニュアルを眺めたことがあれば、
あれほどシンプルなエディタの中によくぞあの膨大な拡張性を持たせたものだと、感心したことがあると思います。

Neovimはそれらの拡張性を、下位互換性を保持したまま(すべてができているわけでは無いと思うが)リファクタリングし、
更には新機能追加までやってのけたのです。強力なVimに対するリスペクトなくしてできることではないでしょう。
私はこれまでNeovimを使っていて、vimで構築してきたvimrcをNeovimに合わせて苦労して修正したこともなく、
だからこそ今でもNeovimをメインのエディタに据えています。
(当然クリップボード設定など独自のアーキテクチャになっている部分については対応いますが)

この記事を作成する当初では、 Vim8がリリースされ、job/terminalなどがマージされたことや、
暗黒美夢王のnvim専用プラグインもVim8対応が実施されたことなどもあり、
現状ではNeovimを使用する利点はあまりないのではと思っていました。
しかし依然NeovimはRemotePluginやinccommand等の独自オプションや関数群などVimとの違いを見せています。

こうした現状を改めて見てみると、NeovimとVimのどっちが優れているということを論じるのはあまり意味は無いのではないかと思えてきます。
この先これらのテキストエディタがどのようになっていくかは誰にもわからない。だからこそ、これからの行く末が非常に楽しみです。