Awesome
Початок роботи з Lua в Neovim
Інші переклади
- :uk: English version
- :cn: Chinese version
- :es: Spanish version
- :brazil: Portuguese version
- :jp: Japanese version
- :ru: Russian version
Зміст
Початок
Інтеграція Lua в якості першокласної мови всередині Neovim перетворює її в одну із найважливіших особливостей. Тим не менш, кількість учбових матеріалів по розробці плагінів на Lua значно менше ніж аналогічних матеріалів на Vimscript. Цей довідник спроба надати базову інформацію для початку.
Довідник написаний з припущенням, що ви використовуєте що найменш 0.5 версію Neovim.
Вивчення Lua
Якщо ви ще не знайомі з цією мовою програмування, є велике різноманіття ресурсів щоб почати:
- Learn X in Y minutes page about Lua надасть вам швидкий огляд основи мови
- Цей гайд також добрий ресурс для швидкого початку
- Якщо вам більше подобаються відео, 1-hour tutorial on the language
- Потрібно більше інтерактивних прикладів? Спробуй the LuaScript tutorial
- Lua-users wiki великий обсяг корисної інформації по Lua
- Official reference manual for Lua може дати тобі найкомплексний огляд мови (Є Vimdoc плагін для читання всередині Neovim: milisims/nvim-luaref)
Слід зазначити що Lua дуже проста мова програмування. Її легко вчити, особливо якщо ви маєте досвід зі схожими мовами - наприклад Javscript. Ви можете знати Lua більше ніж вважаєте!
Інтегрована версія Lua у Neovim LuaJIT 2.1.0, що підтримує сумісність з Lua 5.1.
Існуючі туторіали по написанню Lua в Neovim
Декілька туторіалів вже написані щоб допомогти розроблювати Lua плагіни для Neovim. Деякі з них трохи допомогли написати цей довідник. Велика вдячність авторам.
- teukka.tech - From init.vim to init.lua
- dev.to - How to write neovim plugins in Lua
- dev.to - How to make UI for neovim plugins in Lua
- ms-jpq - Neovim Async Tutorial
- oroques.dev - Neovim 0.5 features and the switch to init.lua
- Building A Vim Statusline from Scratch - jdhao's blog
- Configuring Neovim using Lua
- Devlog | Everything you need to know to configure neovim using lua
Допоміжні плагіни
- Vimpeccable - Допоміжний плагін для написання
.vimrc
на Lua - plenary.nvim - Плагін, щоб не писати всі функції Lua двічі
- popup.nvim - Імплементація Popup API з vim
- nvim_utils - Допоміжні утіліти
- nvim-luadev - REPL/дебаг консоль для розробки Lua плагінів
- nvim-luapad - Інтерактивна пісочниця для вбудованого Lua
- nlua.nvim - Lua розробка для Neovim
- BetterLua.vim - Краще підсвічування синтаксису Lua у Vim/NeoVim
Куди класти Lua файли
init.lua
Neovim підтримує здатність використовувати конфіги написані на Lua - init.lua
, окрім звичного init.vim
init.lua
звісно опціональний формат для конфігурації. Підтримкаinit.vim
не зникла, та може використовуватись разом з Lua-конфігом. Також поки що не всі функції редактора підтримуються через Lua.
Дивіться також:
Модулі
Lua модулі можна знайти всередині lua/
директорії в вашому 'runtimepath'
(для більшості юзерів це буде ~/.config/nvim/lua
на *nix системах та ~/AppData/Local/nvim/lua
на Windows. За допомогою функції require()
ви можете імпортити модулі з ціеї директорії.
Давайте розглянемо структуру директорій:
📂 ~/.config/nvim
├── 📁 after
├── 📁 ftplugin
├── 📂 lua
│ ├── 🌑 myluamodule.lua
│ └── 📂 other_modules
│ ├── 🌑 anothermodule.lua
│ └── 🌑 init.lua
├── 📁 pack
├── 📁 plugin
├── 📁 syntax
└── 🇻 init.vim
Наступний код завантажить myluamodule.lua
:
require('myluamodule')
Можете не вказувати розширення .lua
.
Схожим чином завантаження other_modules/anothermodule.lua
виглядає так:
require('other_modules.anothermodule')
або
require('other_modules/anothermodule')
Розділителі шляху можуть бути крапкою .
або слешем /
.
Якщо ви намагаєтесь завантажити не існуючий модуль, це призведе до помилки та відміни виконання скрипта.
pcall()
функція може бути використана для запобігання помилок.
local ok, _ = pcall(require, 'module_with_error')
if not ok then
-- не заванжувати
end
Дивіться також:
Поради
Деякі плагіни можуть мати однакові імена файлів у lua/
директорії. Це може призвести до конфлікту простору імен.
Якщо два різних плагіни мають файл lua/main.lua
, а потім завантажуються шляхом require('main')
, то який з них буде використаний?
Тому буде гарною ідеєю покласти кожен main.lua
файл у діректорію з назвою плагіну: lua/plugin_name/main.lua
Файли рантайму
Як і Vimscript файли, Lua файли можуть бути завантажені автоматично зі спеціальної директорії runtimepath
. На даний час підтримуються наступні назви директорій:
colors/
compiler/
ftplugin/
ftdetect/
indent/
plugin/
syntax/
У рантайм директорії, усі
*.vim
файли мають бути визвані після*.lua
файлів.
Дивіться також:
Поради
Поки файли рантайму не базуються на системі модулів Lua, два плагіни можуть мати файл plugin/main.lua
без проблем.
Використання Lua у Vimscript
:lua
Ця команда виконує шматок Lua коду.
:lua require('myluamodule')
За допомогою heredoc синтаксису можливо виконувати багаторядкові скріпти:
echo "Here's a bigger chunk of Lua code"
lua << EOF
local mod = require('mymodule')
local tbl = {1, 2, 3}
for k, v in ipairs(tbl) do
mod.method(v)
end
print(tbl)
EOF
Кожна
lua
команда має свою область бачення та змінні оголошені з ключовим словомlocal
не будуть доступні за межами команди. Приклад:
:lua local foo = 1
:lua print(foo)
" виведе 'nil' замість '1'
Lua функція
print()
поводиться схоже до:echomsg
команди. Вихідні дані будуть збережені у історіі повідомлень.
Дивіться також:
:luado
Ця команда виконує шматок Lua коду який діє на діапазон рядків у поточному буфері. Якщо рядки не зазначені, то для цілого буферу. Будь-який рядок який повернувся з блоку, використовується для визначення того, чим слід замінити кожен рядок.
Наступна команда замінила би кожен рядок у поточному буфері текстом hello world
:
:luado return 'hello world'
Дві неявні змінні line
та linenr
також доступні. line
– це текст рядка, а linenr
– номер. Наступна команда зробить кожен рядок у верхньому регістрі, якщо номер рядка ділиться на 2 без залишку:
:luado if linenr % 2 == 0 then return line:upper() end
Дивіться також:
Підключення Lua файлів
Neovim надає 3 Ex команди щоб підключити Lua файли:
:luafile
:source
:runtime
:luafile
та :source
дуже схожі:
:luafile ~/foo/bar/baz/myluafile.lua
:luafile %
:source ~/foo/bar/baz/myluafile.lua
:source %
:source
також підтримує діапазони, котрі будуть корисні для виконання частини скрипту:
:1,10source
Команда runtime
дещо відрізняється: вона використовує 'runtimepath'
опцію для визначення директорії звідки будуть підключені файли. Більшe деталей: :help :runtime
.
Дивіться також:
Різниця між підключенням та викликом require():
Можливо вам цікаво у чому різниця та чи варто віддавати перевагу одному способу над іншим. Вони мають різні випадки використання:
require()
:- вбудована функція Lua. Дозволяє користуватися перевагами модульної системи Lua
- шукає модулі у директорії
lua/
у вашому'runtimepath'
- дозволяє слідкувати за тим які модулі були завантажені та запобігає повторному парсингу та виконанню скрипта. Якщо ви зміните код у вже завантаженому модулі та спробуєте завантажити його через
require()
ще раз, то зміни не застосуються.
:luafile
,:source
,:runtime
:- Ex команди. Не підтримують модулі
- повторно виконують раніше виконані скрипти
:luafile
та:source
приймають шлях до файлу як абсолютний так і відносний до робочої директорії поточного вікна:runtime
використовує'runtimepath'
опцію щоб знайти файли
luaeval()
Ця вбудована Vimscript функція виконує Lua вираження та повертає результат. Типи данних Lua автоматично конвертуються у Vimscript типи (і навпаки).
" You can store the result in a variable
let variable = luaeval('1 + 1')
echo variable
" 2
let concat = luaeval('"Lua".." is ".."awesome"')
echo concat
" 'Lua is awesome'
" List-like tables are converted to Vim lists
let list = luaeval('{1, 2, 3, 4}')
echo list[0]
" 1
echo list[1]
" 2
" Note that unlike Lua tables, Vim lists are 0-indexed
" Dict-like tables are converted to Vim dictionaries
let dict = luaeval('{foo = "bar", baz = "qux"}')
echo dict.foo
" 'bar'
" Same thing for booleans and nil
echo luaeval('true')
" v:true
echo luaeval('nil')
" v:null
" You can create Vimscript aliases for Lua functions
let LuaMathPow = luaeval('math.pow')
echo LuaMathPow(2, 2)
" 4
let LuaModuleFunction = luaeval('require("mymodule").myfunction')
call LuaModuleFunction()
" It is also possible to pass Lua functions as values to Vim functions
lua X = function(k, v) return string.format("%s:%s", k, v) end
luaeval()
приймає опціональний другий аргумент який дозволяє передавати дані до вираження. Потім у вас буде доступ до данних через магічну глобальну змінну _A
:
echo luaeval('_A[1] + _A[2]', [1, 1])
" 2
echo luaeval('string.format("Lua is %s", _A)', 'awesome')
" 'Lua is awesome'
Дивіться також:
v:lua
Це глобальна Vim змінна дозволяє вам викликати Lua функції у глобальному просторі імен (_G
) напряму з Vimscript. І знову типи данних Vim будуть сконвертовані у Lua типи і навпаки.
call v:lua.print('Hello from Lua!')
" 'Hello from Lua!'
let scream = v:lua.string.rep('A', 10)
echo scream
" 'AAAAAAAAAA'
" How about a nice statusline?
lua << EOF
" How about a nice statusline?
lua << EOF
function _G.statusline()
local filepath = '%f'
local align_section = '%='
local percentage_through_file = '%p%%'
return string.format(
'%s%s%s',
filepath,
align_section,
percentage_through_file
)
end
EOF
set statusline=%!v:lua.statusline()
" Also works in expression mappings
lua << EOF
function _G.check_back_space()
local col = vim.api.nvim_win_get_cursor(0)[2]
return (col == 0 or vim.api.nvim_get_current_line():sub(col, col):match('%s')) and true
end
EOF
inoremap <silent> <expr> <Tab>
\ pumvisible() ? "\<C-N>" :
\ v:lua.check_back_space() ? "\<Tab>" :
\ completion#trigger_completion()
" Call a function from a Lua module by using single quotes and omitting parentheses:
call v:lua.require'module'.foo()
Дивіться також:
Застереження
Ця змінна може бути використана тільки для виклику функцій. Наступний код завжди буде викидувати помилку:
" Aliasing functions doesn't work
let LuaPrint = v:lua.print
" Accessing dictionaries doesn't work
echo v:lua.some_global_dict['key']
" Using a function as a value doesn't work
echo map([1, 2, 3], v:lua.global_callback)
Поради
Ви можете увімкнути підсвічення синтаксису Lua всередині .vim
файлів. Додайте let g:vimsyn_embed = 'l'
до вашого файлу конфігурації. Більше інформаціЇ :help g:vimsyn_embed
.
Простір імен vim
Neovim має глобальну змінну vim
яка є вхідною точкою до API з Lua коду. Це надає користувачам розширену стандартну бібліотеку функцій, а також різноманітні підмодулі.
Деякі важливі функції та модулі:
vim.inspect
: трансформує Lua обʼєкти до людино-зрозумілий вигляду (корисно для інспектування Lua таблиць)vim.regex
: використовуйте регулярні вираження vim із Luavim.api
: модуль який надає доступ до API функцій (також використовується віддаленими плагінами)vim.ui
: UI функціїvim.loop
: модуль для доступу до event-loop (використовує LibUV)vim.lsp
: модуль що контролює вбудований LSP клієнтvim.treesitter
: модуль для доступу до функціоналу бібліотеки tree-sitter
Цей список не є вичерпним. Якщо ви бажаєте знати що доступно через vim
змінну, :help lua-stdlib
та :help lua-vim
. Додатково, ви можете використовувати команду :lua print(vim.inspect(vim))
щоб отримати повний список модулів. API функції теж документовані :help api-global
.
Поради
Писати print(vim.inspect(x))
кожен раз коли треба проінспектувати вміст обʼєкта може бути нудним процесом. Можливо, варто мати глобальну обгортку десь у файлі конфігурації (у Neovim 0.7.9+, ця функція вбудована, більше інформації :help vim.pretty_print()
):
function _G.put(...)
local objects = {}
for i = 1, select('#', ...) do
local v = select(i, ...)
table.insert(objects, vim.inspect(v))
end
print(table.concat(objects, '\n'))
return ...
end
You can then inspect the contents of an object very quickly in your code or from the command-line: Тепер ви можете інспектувати вміст обʼєкту швидко у вашому коді чи через командний рядок:
put({1, 2, 3})
:lua put(vim.loop)
Додатково ви можете використовувати :lua
команду для красиво вивести вираження додавши до нього префікс =
(Neovim 0.7+)
:lua =vim.loop