Luaで最小構成のNeoVimのプラグインを作る

目次

  1. 背景
  2. 作るもの
  3. プロジェクト構成
  4. luaフォルダ
    1. lua/simple-nvim-plugin.lua
    2. lua/simple-nvim-pluginフォルダ
    3. lua/run.lua
  5. 実行
  6. NeoVimでプラグインを読み込む
  7. プラグインにキー・コマンドを割り当てる
  8. Luaを覚える
  9. Vim固有のLua
    1. vim.api
    2. vim.fn
    3. vim.cmd
    4. Lua API
  10. もっと詳しく知りたい人は…
  11. まとめ
  12. 関連記事
  13. PR

背景

こんにちは。 かりんとうマニア(@karintozuki)です。

最近NeoVimを触っています。
色々なプラグインでカスタマイズできるのが魅力ですが、自分でプラグインを作れたら楽しいと思います。

今回は最小構成で作るNeoVimプラグインを紹介します。
この記事で紹介しているソースを見たい人はGitHubを見てみてください。
https://github.com/karintomania/simple-nvim-plugin

作るもの

とりあえずHello worldするプラグインを作ってみます。

作業環境は以下の通りです。

  • Mac OS Monteray
  • NeoVim v0.8.1

プロジェクト構成

プロジェクトのフォルダ構成は以下の通りです。

1
2
3
4
5
6
7
/simple-nvim-plugin
└── lua
   ├── simple-nvim-plugin
   │   └── testModule.lua
   ├── simple-nvim-plugin.lua
   └── run.lua

細かく説明していきます。

luaフォルダ

lua/simple-nvim-plugin.lua

このファイルがプラグインのメインのファイルになります。
本当に最小限の構成にするならこのファイルだけで完結します。

ただ、実際のプラグイン作成では次の章で解説するモジュール読み込みを使って
ファイルを分割するのがおすすめです。

ファイルの中身はこんな感じです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-- テーブルを宣言する
local M = {}

-- モジュールの読み込み
local testModule = require('simple-nvim-plugin/testModule')

-- 関数宣言
function M.test()
print('hello world');
end

-- モジュールの関数を呼び出す
function M.callModule(str)
testModule.test(str)
end

-- テーブルを返す
return M

Mというテーブルを作り、そこに関数を生やしていって最後にMをリターンしています。慣例的にMという変数名を使うようです。おそらくModuleのことだと思います。
テーブルはLuaのデータ構造でJSでいうところのオブジェクトです。

lua/simple-nvim-pluginフォルダ

このフォルダにはモジュールを切っていきます。
testModuleという引数をオウム返しするだけのモジュールを作ってみました。
ファイルの中身はこんな感じです。

1
2
3
4
5
6
7
8
9
10
11
local M = {}

function M.test(str)
if str ~= '' then
print(str)
else
print('input string!')
end
end

return M

大体メインのファイルと同じ流れで、
テーブル宣言→関数宣言→リターン
という流れです。

モジュールをわざわざ/simple-nvim-pluginというフォルダを一階層増やして作っているのは他のプラグインに同名のモジュールがあった際のエラーを無くすためです。

フォルダ名を/simple-nvim-pluginとすることでtestという名前のモジュールを読み込む際、
requrie(’simple-nvim-plugin/test)となるので他のプラグインがtestというモジュール名を持っていても大丈夫です。

名前空間みたいな感じです。

lua/run.lua

このファイルは必須ではないですが、開発中にプラグインをデバッグするためのファイルです。

1
2
3
4
5
6
7
8
9
10
11
-- キャッシュされたモジュールを削除
package.loaded['simple-nvim-plugin'] = nil
package.loaded['simple-nvim-plugin/testModule'] = nil

-- モジュールを呼び出す
local simple = require('simple-nvim-plugin')

-- 関数呼び出し
simple.test()
simple.callModule('test')
simple.callModule('')

ファイルの先頭でキャッシュの削除をしています。

Luaは読み込んだモジュールをキャッシュするので、それを削除しないとモジュール内のコードの変更が毎回反映されないため、このようにしています。

関数呼び出しのところでテストしたい関数を呼びます。

assert()関数などを使うことでユニットテストを書くことも可能です。

実行

開発中はrun.luaファイルをNeoVimから実行することでデバッグを行います。
そのためのコマンドは以下になります。

1
:luafile run.lua

また、luaをさくっと実行するには以下のコマンドが使えます。

1
:lua print('hello')

NeoVimでプラグインを読み込む

無事にプラグインができたら、それをNeoVimで読み込んでみましょう。
init.vimに設定を書き込んでいきます。

Plugを使っていればローカルのプラグインは以下のように読み込めます。
パスはDocuments配下にプロジェクトがあることを想定しているので、適宜書き換えてください。

1
2
Plug '~/Documents/simple-nvim-plugin'

もちろんGitHubにソースをアップロードしているなら普通のプラグインのようにインストールできます。

プラグインにキー・コマンドを割り当てる

プラグインを読み込んだだけでは何もしないので、
キーマップやコマンド割当をinit.vimに追記します。

1
2
3
nnoremap <F9> <Cmd>lua require('simple-nvim-plugin').test()<CR>
command! -nargs=? Simple lua require('simple-nvim-plugin').callModule(<q-args>)

一行目はキーマップの割当でF9を押すとtest()関数を実行するようにしています。

また二行目では:Simple xxxというExコマンドでcallModule()関数を実行できるようにしています。

引数ありのコマンドの参考にしてみてください。

Luaを覚える

最後にLuaを使ってプラグインを書く際に役立ったLuaという言語についての情報を書いてきます。

基本的な文法を覚えるには以下のリンクがおすすめです。
とりあえずソースの部分をざっくり読むだけでも大体の雰囲気がつかめると思います。

https://learnxinyminutes.com/docs/lua/

Notes/Lua_Quick_Guide.ipynb at main · medwatt/Notes

Vim固有のLua

NeoVim上でLuaを動かす際に役立つ書き方を紹介します。
vim変数を使ってapiやvimの関数、コマンドにアクセスすることが可能です。

vim.api

vim.api.xxxx と書くことでNeoVimのAPIが使えます。

1
2
3
:lua print(vim.api.nvim_get_current_buf())
// 現在のバッファハンドルが表示される

上記のコードは現在のバッファのハンドル(IDみたいなもの)を取得できます。
バッファ上で何か操作したいときに使えます。

どのようなAPIがあるかは:help APIを実行してみてください。

vim.fn

[vim.fn.xxx](http://vim.fn.xxx) と書くことでVimscriptの関数が使えます。
例えばstrlen関数を実行したい場合は以下のようになります。

1
2
3
4
5
:lua print(vim.fn.strlen('abc'))

// 実行結果
3

こちらもVimscriptにたくさん関数があるので、それらを使いたいときに良いです。

Luaがシンプルな言語なこともあって、やりたいことがLuaですっきりとできない際にVimscript標準の関数に助けられることもあります。

関数の一覧がhelp function-listコマンドで見ることができます。

vim.cmd

Exコマンドを実行できます。
例えば新しいバッファを開く:enewコマンドを実行したい場合は以下のようになります。

1
2
3
:lua vim.cmd('enew')
// 新しいバッファが開く

ちなみに普通のキーストロークは:normalコマンドを使うことで実現できます。

1
2
3
:lua vim.cmd('normal j')
// カーソルが一行下に移動する

Lua API

vim.api / vim.fn / vim.cmdはNeoVim、またはVimscriptの仕組みをLuaから使うためのものでした。
それ以外のvim.* はLuaの標準ライブラリとしてのVimモジュールとして定義されています。

例としてはvim.pretty_print()があり、 print() ではちゃんと表示できないテーブルをデバッグしたいときに使えます。

1
2
3
4
5
6
7
8
9
10
:lua vim.pretty_print({x=1})
//実行結果
{
x = 1
}

//ただのプリントではうまくデバッグできない
:lua print({x=1})
//実行結果
table: 0x0102b11260

APIの一覧はこちらからどうぞ。

https://neovim.io/doc/user/lua.html#lua-stdlib

もっと詳しく知りたい人は…

もっと詳しく知りたい人のために勉強になるリンクを紹介します。

https://github.com/nanotee/nvim-lua-guide

NeoVimのLuaガイドです。NeoVimでLuaを動かすために必要な知識が一通り書いてあります。
全部読むというよりリファレンスとして使うのが良いかもしれません。

How to Write a Neovim Plugin with Lua

Linodeが出しているプラグインの作り方です。
実際にプラグインを作理ながら解説してくれているので実践的な内容になっています。

https://github.com/karintomania/nvim-ai-chat

手前味噌ですが、私が開発したプラグインです。
実際のプラグインを見て勉強したい人は開いて見ください。
大して複雑なことをしていないので笑、簡単に読めると思います。

OpenAIのAPIにVimから直接質問できるプラグインで、そこそこ便利なので興味があれば使ってみてください。

まとめ

いかがでしょうか。

Vimのプラグイン開発やLuaの情報は日本語のものがまだ少ないのでこのブログでも少しずつ取り上げて行けたら良いなと思っています。
プラグイン開発のハードルが少しでも下がってコミュニティが盛り上がって欲しいです。

それじゃ今日はこの辺で。

関連記事

こちらの記事もおすすめです。

Mac Vimを導入してみた

PR

あなたの会社はあなたの技術を評価してくれていますか?
技術力を高めようと頑張っているのに、
技術が評価されないような会社にいたらそれは良い環境なのでしょうか?
エンジニアとして技術を高めたいのなら環境を選ぶことも大事です。

レバテックキャリア
エンジニアとして働いていて実務経験があるなら、
求人数の充実具合からレバテックキャリアがおすすめです。
IT転職ではデファクト・スタンダードですね。
▼レバテック キャリア 登録はこちら▼


Tech Clips
Tech Clipsは年収500万以上&自社サービスを持った会社に特化した求人サイトです。
首都圏限定になってはしまいますが、
収入を増やしたい、自社サービスを持った企業への転職をしたい人におすすめです。

▼Tech Clips 登録はこちら▼