Slide 1

Slide 1 text

© DeNA Co., Ltd. 1 Neovim で今風のプラグインを 書く方法 delphinus 株式会社ディー・エヌ・エー

Slide 2

Slide 2 text

© DeNA Co., Ltd. 2 陣内 靖 Vim歴: 15 年以上? 最初の dotfiles のコミット(2012/1/28) の vimrc は 749 行。 DeNA ソリューション事業本部ゲームアライアンス事業部 プラットフォーム開発部アカウントユーザーサービスグ ループ @delphinus @delphinus35 [email protected] [email protected] © DeNA Co., Ltd. 自己紹介

Slide 3

Slide 3 text

© DeNA Co., Ltd. 3 今回の発表のテーマ ● 対象者 ○ Neovim 使う人(Vim でも可) ■ プラグイン開発経験があるとなお可。 ● この発表を聞くとこうなります。 ○ 「プラグイン」とは何か、分かる。 ○ Neovim + Lua でプラグインを書きたくなる。 ● こうはなりません。 ○ 流行りのプラグインが分かる。 ○ プラグイン管理が分かる。

Slide 4

Slide 4 text

© DeNA Co., Ltd. 4 目次 そもそもプラグインとは? Neovim 上の Lua 1 2 Lua プラグインのお作法 3 まとめ 4

Slide 5

Slide 5 text

© DeNA Co., Ltd. 5 そもそもプラグインとは?

Slide 6

Slide 6 text

© DeNA Co., Ltd. 6 プラグイン書いてますか?何を書いてますか? 1. ファジーファインダープラグイン ○ telescope.nvim, ddu.vim, fzf-preview.vim, …… 2. 自動補完プラグイン ○ nvim-cmp, ddc.vim, coc.nvim, …… 3. ファイルタイププラグイン ○ vim-go, vim-perl, vim-ruby, …… 4. UI 改善系 ○ nvim-notify, noice.nvim, lspsaga.nvim, scrollbar.nvim, …… ○ ↑ここが今一番元気! 1 その他、カラースキームや入力支援系 などなど。

Slide 7

Slide 7 text

© DeNA Co., Ltd. 7 そのプラグイン、本当に必要? Neovim / Vim は年々進化しています。プラグインの機能を取り込むことも…… ● incsearch.vim → :h 'incsearch' (ブログエントリ) ● vim-highlightedyank → :h vim.highlight.on_yank() (reddit) ● そもそもターミナル機能やジョブ機能だって昔はプラグインで実現してましたね。 ○ vimshell.vim, vimproc.vim, …… 1

Slide 8

Slide 8 text

© DeNA Co., Ltd. 8 そもそもプラグインとは? プラグインとは、「'runtimepath' に指定したディレクトリにあるスクリプトを指定したタ イミングで評価するもの」。 1 " /tmp/hoge ディレクトリにあるプラグインを追加 set runtimepath+=/tmp/hoge " すると、/tmp/hoge/plugin/hoge.lua のようなファイルが読み込まれます。

Slide 9

Slide 9 text

© DeNA Co., Ltd. 9 プラグインにはどんなファイルがある? 各ディレクトリにスクリプト(*.vim, *.lua)があるものとします。 ● /plugin : 自動的('runtimepath' に追加した時点)に評価されます。 ● /ftdetect : ↑に同じ。 ● /ftplugin : ファイル名と一致したファイルタイプのバッファーに入ると評価されます。 ● /syntax : ファイル名と一致したシンタックスのバッファーに入ると評価されます。 ● /colors : 「:colorscheme ファイル名」すると評価されます。 ● /lua : require("hoge") すると評価されます。 他にも色々ある! :h 'runtimepath' 1

Slide 10

Slide 10 text

© DeNA Co., Ltd. 10 プラグインにはどんなファイルがある? 各ディレクトリにスクリプト(*.vim, *.lua)があるものとします。 ● /plugin : 自動的('runtimepath' に追加した時点)に評価されます。 ● /ftdetect : ↑に同じ。 ● /ftplugin : ファイル名と一致したファイルタイプのバッファーに入ると評価されます。 ● /syntax : ファイル名と一致したシンタックスのバッファーに入ると評価されます。 ● /colors : 「:colorscheme ファイル名」すると評価されます。 ● /lua : require("hoge") すると評価されます。 他にも色々ある! :h 'runtimepath' 1 この3つ、もう要らないかも……

Slide 11

Slide 11 text

© DeNA Co., Ltd. 11 ファイルタイププラグインの需要が低下している件 全部 LSP と Tree-sitter のせい。 ● :make とか :compiler の設定 → Langauge Server ● ftdetect → すでに Neovim / Vim で対応しているもの多数。 ● syntax → Tree-sitter ファイルタイププラグインを入れる目的はだいたいこれら。ある程度メジャーな言語なら個 別にプラグインを用意する必要はありません(indent とかは要るかも)。 ● LSP → nvim-lspconfig, vim-lsp, coc.nvim など。 ● Tree-sitter → nvim-treesitter 1

Slide 12

Slide 12 text

© DeNA Co., Ltd. 12 プラグイン、いくつ入れてますか というわけで、入れてるプラグインは 適宜見直しましょう。 1

Slide 13

Slide 13 text

© DeNA Co., Ltd. 13 プラグイン、いくつ入れてますか というわけで、入れてるプラグインは 適宜見直しましょう。 1 僕は 163 個も 入れてるけどね!

Slide 14

Slide 14 text

© DeNA Co., Ltd. 14 Neovim 上の Lua

Slide 15

Slide 15 text

© DeNA Co., Ltd. 15 Neovim 上の Lua 2 Neovim 上の Lua は 5.1 相当(Lua JIT)。 Lua はみんな大好きだよね!!!!!(圧)

Slide 16

Slide 16 text

© DeNA Co., Ltd. 16 Lua のいいところ 2 速い!速さは正義! 右は単純プラグインなので実用的な例では ないですが、実際 Vimscript より めちゃめちゃ速いです。 (Vim9script より速い) https://github.com/delphinus/artify.nvim

Slide 17

Slide 17 text

© DeNA Co., Ltd. 17 Lua のイヤなところ 2 ● プロトタイプ型のオブジェクト指向言語なのが取っ付き辛い。 ○ クラスが無い。昔の JavaScript みたい。 ● 動的型付き言語、また、メタプログラミングし放題なため 汎用性の無い独自実装が生まれがち。 ● 言語コアのライブラリが貧弱で、外からモジュールを import する統一的な仕組みが無い。 ○ LuaRocks というのもあるにはあるが、Lua JIT では使えないモジュールも多い。 なんか愚痴ばっかりですが……とにかく速いので許して。

Slide 18

Slide 18 text

© DeNA Co., Ltd. 18 Neovim 上の Lua 開発体験(LSP & Tree-sitter) 2 ● Language Server (sumneko 氏の)が大変出来が良い。 ○ Type Annotation が強力。 ○ ただ、Formatting は StyLua を別に使った方がポータブル。 ● Neovim では 1st citizen Langauge として扱われているため、 Neovim コアのライブラリや Tree-sitter 対応が充実してます。 ● LSP & TS の具体的な設定方法などは、ここでは省きます(他の資料見てね)。

Slide 19

Slide 19 text

© DeNA Co., Ltd. 19 Neovim 上の Lua 開発体験(コアライブラリ) 2 ● Lua 自体のコアは大変貧弱なため、 便利関数やモジュールが Neovim コアに入ってます。 ● 全ての関数には vim というグローバル変数からアクセスします(:h lua-builtin)。 -- オプション設定 vim.opt.termguicolors = true vim.opt.runtimepath:append "/path/to/hoge_plugin" -- Vim 関数・コマンド print(vim.fn.bufname()) vim.cmd [[echo "Hello, world!"]]

Slide 20

Slide 20 text

© DeNA Co., Ltd. 20 コアライブラリ: vim.api 2 ● 元来、Neovim では Vimscript 以外の言語で自由にプラグインを 書けることが強みでした。(:h remote-plugin) その時別プロセスから Vim の機能にアクセスするために作られた仕組みが vim.api.nvim_…… の関数群です(:h eval-api)。 ● そのうち Neovim も方針変更して Lua JIT を組み込んでそれで直接プラグインを 書けるようになり、Vimscript でできることは全て Lua でやることになりました。 vim.api もそれに併せてどんどん拡張されています。 ○ Remote Plugin はその割りを食ってほとんど使われなくなっちゃった……

Slide 21

Slide 21 text

© DeNA Co., Ltd. 21 コアライブラリ: vim.api 2 -- bufname() と一緒 print(vim.api.nvim_buf_get_name()) -- ハイライト設定 vim.api.nvim_set_hl(0, "HogeRed", { fg = "#ff0000", bg = "#220000", bold = true }) -- Autocmd 設定 vim.api.nvim_create_autocmd("BufEnter", { pattern = "*.pl", callback = function() vim.notify "You’ve entered into a Perl buffer!" end, })

Slide 22

Slide 22 text

© DeNA Co., Ltd. 22 コアライブラリ: 便利関数たち 2 便利関数は全て $VIMRUNTIME/lua/vim に定義されています。 Neovim 上のターミナルから見やすいです。 ここに無いもの(vim.api や vim.regex とか)は C で書かれているので ドキュメントや Neovim のソースを見てね。 ls $VIMRUNTIME/lua/vim F.lua filetype/ keymap.lua treesitter.lua _editor.lua filetype.lua lsp/ ui.lua _init_packages.lua fs.lua lsp.lua uri.lua _inspector.lua health.lua secure.lua _meta.lua highlight.lua shared.lua diagnostic.lua inspect.lua treesitter/

Slide 23

Slide 23 text

© DeNA Co., Ltd. 23 コアライブラリ: 便利関数たち 2 vim.api の各関数は元の経緯から低レベルなものが多いです。 そのままでは使いにくいため便利なラッパーが作られています。 vim.opt --> Vim 自体のオプション vim.fn --> Vim 関数 vim.cmd --> Vim コマンド vim.keymap.set("n", "", function() print(123) end) --> キーマップ vim.notify "Hello, world!" --> :echo コマンドの代わり(後述) -- vim.fs シリーズ vim.fs.normalize "~/.config/nvim" vim.fs.find(".git", { path = vim.loop.cwd(), upward = true }) -- ↑これは https://github.com/delphinus/rtr.nvim で使っています。

Slide 24

Slide 24 text

© DeNA Co., Ltd. 24 コアライブラリ: vim.loop 2 Neovim はそのコアを libuv を使ったイベントループモデルで実装しています。 ● libuv is a multi-platform C library that provides support for asynchronous I/O based on event loops. libuv - Wikipedia Lua によるインターフェースは luv を使っており、そこには vim.loop からアクセス できます。機能としてはタイマー、ファイルシステムへのアクセス、TCP/UDP 通信、 コルーチンを使った非同期プログラミング、などなど。 要するに今風の言語なら使えるやつは大体使えます。 どんな機能があるかは :h luv を読んでください。

Slide 25

Slide 25 text

© DeNA Co., Ltd. 25 Lua プラグインのお作法

Slide 26

Slide 26 text

© DeNA Co., Ltd. 26 Lua プラグインっぽさ、とは何か Vimscript のプラグインと違い、多くの Lua プラグインには 以下のような特徴(というかベストプラクティス)が見られます。 1. Lua API をなるべく使おう。 2. 'runtimepath' に追加するだけでは評価されないようにしよう。 3. ドキュメントが無くて README を読む必要がある。 ○ この点だけはバッドプラクティスですが…… 3

Slide 27

Slide 27 text

© DeNA Co., Ltd. 27 1. Lua API をなるべく使おう。 ぶっちゃけ、vim.cmd [[ …… ]] の中に Vimscript が書けるのでそれで Lua 化は完了します。 まあそれだとかっこ悪いよね、ということで以下の点に気を付けましょう。 ● vim.fn.foo と vim.* で機能が被ったらなるべく後者を使う。 ○ 例えば vim.fn.timer_start ではなく vim.loop.new_timer にする、とか。 ● ファイルシステムやネットワークへのアクセスはコルーチンを使って非同期に。 ○ vim.fs.dir のソースとかが参考になります。 ● あとはドキュメントをとにかく読もう。 ○ :h lua.txt , :h luv ref.txt , :h api.txt 3

Slide 28

Slide 28 text

© DeNA Co., Ltd. 28 1. Lua API をなるべく使おう。 :echo するよりも、vim.notify を積極的に使いましょう。ユーザーは処理を上書きして メッセージの表示方法やフィルタリングを自由に定義できます(:h vim.notify()) nvim-notify などのプラグインを入れることでも手軽に拡張できます。 3 vim.cmd [[echo "debug hoge"]] -- これや print "debug hoge" -- こう書くより vim.notify "debug hoge" -- こっちがベター vim.notify = function(msg, level, options) if not msg:match "debug" or level >= vim.log.levels.INFO then print(msg) end end -- ↑一例: "debug" という文字列を含んだり INFO 未満のメッセージを省く

Slide 29

Slide 29 text

© DeNA Co., Ltd. 29 2. 'runtimepath' に追加するだけでは評価されないようにする Vimscript なプラグインでは plugin/hoge.vim のように、'runtimepath' に追加した時自動で 評価される場所にスクリプトを置くことが多いです。Lua なプラグインではこれが、 明示的に setup() 関数を呼ばないと動作しないようになっています。 ● こうなった理由はいまいち分かりません。流行みたいなものかも知れない。 利点としては以下のような点が考えられます。 ● 評価のタイミングがコントロールし易い(遅延読み込みとか)。 ● オプションを引数に与えるような自然な書き方ができる。 ● 他のみんながやってるので驚きを最小限にできる(これが一番大事かも)。 3

Slide 30

Slide 30 text

© DeNA Co., Ltd. 30 2. 'runtimepath' に追加するだけでは評価されないようにする。 -- 例えば以下のようなファイル構成にします。 /README.md /lua/hoge/init.lua -- lua/hoge/init.lua return { setup = function(opts) vim.validate(opts, ……) -- 実際の処理 end, } 3 -- 呼び出し側ではこうします。 { "fugafuga/hoge.nvim", config = function() require("hoge").setup { fugahoge = { "abc", "cde" }, } end, } -- ↑何らかのプラグインマネージャーを -- 使う想定のコードです。

Slide 31

Slide 31 text

© DeNA Co., Ltd. 31 3. ドキュメントが無くて README を読む必要がある ● これは正直やめて欲しい習慣。 ● ドキュメントはちゃんと書きましょう。でないと Neovim の中から参照しにくいです。 ○ 一応、lazy.nvim のようなプラグインマネージャーは README への参照を :h hogehoge で引けるようにしてくれます。 ● REDAME にはプラグインのスクリーンショットや代表的な使い方を書き、 オプションの詳細や追加情報はドキュメントに載せて欲しいです。 3

Slide 32

Slide 32 text

© DeNA Co., Ltd. 32 まとめ

Slide 33

Slide 33 text

© DeNA Co., Ltd. 33 4 まとめ ● プラグイン、どんどん入れましょう! ○ そして定期的に棚卸ししましょう。 ● Neovim 上の Lua、結構良いです。 ● せっかく Lua なら今風のプラグインにしましょう。 ● あと、ドキュメントは書きましょう!

Slide 34

Slide 34 text

© DeNA Co., Ltd. 34