🎑

NeovimのためのLua入門 init.lua編

2020/11/03に公開

2022-07-12 追記

かなり情報が古くなっていたので更新しました。
lua-introにもあるnanotee/nvim-lua-guideも参照してください。
日本語訳もあります。

はじめに

今回はlua.txtからLuaで設定を書くために必要なものを紹介します。
NVIM v0.8.0-dev+604-gd8360e903を使用しています。

どうやって読み込むのか

.config/nvim/init.vimの代わりに.config/nvim/init.luaを置くと、設定ファイルとして読み込まれます。
init.viminit.luaが同時に存在するとエラーになります。

プラグインとして使用したい場合もruntimepath内でplugin/hoge.luaのように配置し、今までの.vimファイルを置き換えれば自動で読み込んでくれます。
(Vim scriptを置き換える目的ならもうsetup()は必須ではありません。)

モジュールを配置したい場合は、runtimepath内のluaディレクトリ内にファイルを置くと、Luaのrequireから読み込めるようになります。

-- lua/hi.lua
return 'hi'
--

:lua print(require('hi"))
"hi

lua/hello/init.luaのようにディレクトリを作るとディレクトリ名でrequireできます。

-- lua/hello/init.lua
return 'hello'
--

:lua print(require('hello')) 
"hello

LuaからVim script

昔からあるNvim API(:h api.txt)を使用する方法と、前回でてきたlua-stdlibを使用する方法があります。
どちらもvimモジュールからアクセスできますが、ヘルプを探す時はapi.txtlua.txtで別れているので注意が必要です。
また、api.txtのサンプルコードはVim scriptなので読み変える必要があります。

lua-stdlibの方は当然Lua用なのでAPIよりは使いやすくなっています。

関数

vim.fnでvim側の関数を使用できます。

-- vim組込み
vim.fn.abs(-10)

-- autoload関数の場合
vim.fn['dein#update']()
-- API
print( tostring( vim.api.nvim_get_current_line() ) )

vim.apiはテーブルなので少しタイプ数を減らせます。

local api = vim.api
api.nvim_buf_line_count(0)

EXコマンド

文字列がEXコマンドとして実行されます。
何でもできる最終手段です。

vim.cmd('echo 1234')

変数

vim.g.loaded_netrw = 1の用に変数に直接アクセスできます。
スコープはg,b,w,t,vと、Vim scriptと同様です。

環境変数はenvを使います。

print( vim.env.TERM )

オプション

通常は:set相当のことができるvim.optを使用します。
:setlocal:setglobalに対応するvim.opt_localvim.opt_globalもあります。
詳細は:h lua-vim-optを参照してください。

vim.opt.number = true
vim.opt.background = 'light'
vim.opt.wildignore = { '*.o', '*.a', '__pycache__' }

:set +=などの操作はテーブルをいじる方法と関数を使う方法があります。

vim.opt.wildignore = vim.opt.wildignore + { "*.pyc", "node_modules" }
vim.opt.wildignore:append { "*.pyc", "node_modules" }

ショートハンドとしてvim.ovim.bovim.wovim.goといった変数もあります。

    lua            command      global_value       local_value
vim.o           :set                set                set
vim.bo/vim.wo   :setlocal            -                 set
vim.go          :setglobal          set                 -

マッピング

nvim_set_keymap()vim.keymap.set()を使います。
詳細は:h lua-keymapを参照してください。

vim.keymap.set()はおもしろい関数で通常の:mapとは動作が異なります。
デフォルトでremapが無効(noremap)になっていて、<Plug>が含まれていると自動で有効(map)になります。
Luaの関数をそのままマッピングすることもできるので基本的にこちらを使うことになると思います。

-- 等価
vim.api.nvim_set_keymap( 'n', 'j', 'gj', {noremap = true} )
vim.keymap.set( 'n', 'j', 'gj' )

-- 関数
vim.keymap.set('n', 'lhs', function() print("real lua function") end)
vim.keymap.set('n', 'asdf', require('jkl').my_fun)

-- 複数のモード
vim.keymap.set({'n', 'v'}, '<leader>lr', vim.lsp.buf.references, { buffer=true })

-- <Plug>
vim.keymap.set('n', '[%', '<Plug>(MatchitNormalMultiBackward)')

autocmd

nvim_create_augroup()nvim_create_autocmd()を使います。

nvim_create_augroup()のオプションのclearがデフォルトでオンになっていて、よくある:autocmd!で多重登録を防ぐというのが不要になっています。
詳細は:h api-autocmdを参照してください。

-- 従来
vim.cmd('augroup lua')
vim.cmd('autocmd!')
vim.cmd('autocmd InsertEnter * echo "insert enter"')
vim.cmd('augroup END')

-- 上と同じ
vim.api.nvim_create_augroup( 'lua', {} )
vim.api.nvim_create_autocmd( 'insertenter', {
  group = 'lua',
  callback = function() print( 'insert enter') end
})

-- Vim scriptも渡せます
vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
  pattern = {"*.c", "*.h"},
  command = "echo 'Entering a C or C++ file'",
})

ユーザー定義コマンド

nvim_create_user_command()を使います。バッファを指定できるnvim_buf_create_user_command()というのもあります。
詳細は:h api-commandを参照してください。

-- 等価
vim.cmd('command Hi echo "Hi!"')
vim.api.nvim_create_user_command( 'Hi', function() print( 'Hi!' ) end, {} )

発火するにはvim.api.nvim_exec_autocmds()を使います。

vim.api.nvim_exec_autocmds( 'InsertEnter', {} )

Vim scriptからLua

ヒアドキュメント :lua-heredoc

Vim script内にLuaをそのまま埋め込むことができます。
ヒアドキュメント内のローカル変数はその中でしか使えません。

function! CurrentLineInfo()
lua << EOF
local linenr = vim.api.nvim_win_get_cursor(0)[1]
local curline = vim.api.nvim_buf_get_lines(0, linenr, linenr + 1, false)[1]
print(string.format("Current line [%d] has %d bytes",linenr, #curline))
EOF
endfunction

v:lua

Luaのグローバルな関数を呼ぶことができます。

lua << EOF
function Hello()
  print("Hello")
end
EOF

call v:lua.Hello()
"Hello

Vim scriptの関数を設定するところにも使えるようです。

vim.api.nvim_buf_set_option(0, 'omnifunc', 'v:lua.mymod.omnifunc')

v:luaは関数を呼ぶことしかできず、式では使用できません。

let g:Myvar = v:lua.myfunc        " Error
call SomeFunc(v:lua.mycallback)   " Error
let g:foo = v:lua                 " Error
let g:foo = v:['lua']             " Error

おわりに

よく使いそうな物の紹介は以上です。
各機能の細かいところまでは書いていませんが、Luaから設定できるものが揃ってきた印象があります。

参考

Discussion