La integración de Lua como lenguaje de primera clase dentro de Neovim se perfila como una de sus características más destacadas. Sin embargo, la cantidad de material didáctico para aprender a escribir plugins en Lua no es tan grande como la que se puede encontrar para escribirlos en Vimscript. Esta guia es un intento de proveer información básica a las personas para que puedan empezar.
Esta guía asume que tú estás usando, al menos, la versión 0.5
de Neovim.
Si tú no estas familiarizado con este lenguaje, hay un varios recursos para empezar:
- Aprende X en Y minutes, una pagina sobre Lua debería darte una vision general de los conceptos básicos.
- Esta guia tambien es un buen recurso para empezar rápidamente
- Si aprender mediante vídeos es más de tu agrado, Derek Banas tiene un tutorial de 1-hora sobre el lenguaje
- ¿Deseas algo más interactivo con ejemplos ejecutables? intenta The LuaScript tutorial
- La lua-users wiki está llena de información de todo tipo referente a tópicos de Lua.
- La official reference manual for Lua debería darte el tourt más amigable sobre este lenguaje (existe como Vicdoc plugin si quiere leerlo desde la comodidad de tu editor: wsdjeg/luarefvim)
También hay que destacar que Lua es un lenguaje muy limpio y sencillo. Es fácil de aprender, especialmente si tienes experiencia con lenguajes de scripting similares como JavaScript. Es posible que ya conozcas Lua, mucho más de lo que crees!
Nota: La versión de Lua que Neovim incorpora es LuaJIT 2.1.0, que se mantiene la compatibilidad con Lua 5.1
Ya se han escrito algunos tutoriales para ayudar a la gente a escribir plugins en Lua. Algunos de ellos han ayudado muchísimo a la hora de escribir esta guía. Muchas gracias a sus autores.
- 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
Plugins complementarios
- Vimpeccable - Plugin to help write your .vimrc in Lua
- plenary.nvim - All the lua functions I don't want to write twice
- popup.nvim - An implementation of the Popup API from vim in Neovim
- nvim_utils
- nvim-luadev - REPL/debug console for nvim lua plugins
- nvim-luapad - Interactive real time neovim scratchpad for embedded lua engine
- nlua.nvim - Lua Development for Neovim
- BetterLua.vim - Better Lua syntax highlighting in Vim/NeoVim
Neovim permite la carga de un archivo init.lua
para la configuración, en lugar del habitual init.vim
.
Nota: init.lua
es, por supuesto, completamente opcional. El soporte para init.vim
no va a desaparecer
y sigue siendo una opción válida para la configuración. Ten en cuenta que algunas características
no están 100% soportadas por Lua todavía.
También ve:
:help config
Los módulos de Lua se encuentran dentro de la carpeta Lua/
en tu 'runtimepath'
(para la mayoria
de usuarios, esto significa ~/.config/nvim/lua
en sistemas *nix y ~/AppData/Local/nvim/lua
en Windows).
Los paquetes globales package.path
y package.cpath
se ajustan automáticamente para incluir los archivos
Lua en esta carpeta. Esto significa que pueda que debas hacer un require()
en estos archivos como
módulos de Lua.
Tomemos como ejemplo esta estructura de carpetas:
📂 ~/.config/nvim
├── 📁 after
├── 📁 ftplugin
├── 📂 lua
│ ├── 🌑 myluamodule.lua
│ └── 📂 other_modules
│ ├── 🌑 anothermodule.lua
│ └── 🌑 init.lua
├── 📁 pack
├── 📁 plugin
├── 📁 syntax
└── 🇻 init.vim
El siguiente código de Lua cargará myluamodule.lua
:
require('myluamodule')
Date cuenta la ausencia de la extension .lua
.
Así mismo, cargar other_modules/anothermodule.lua
se hace así:
require('other_modules.anothermodule')
-- or
require('other_modules/anothermodule')
Los separadores de ruta se indican con un punto .
o con un slash /
.
Una carpeta que contiene un archivo init.lua
puede ser requerida directamente, sin tener que especificar
el nombre del archivo.
require('other_modules') -- loads other_modules/init.lua
Requerir un módulo inexistente o un módulo que contenga errores de sintaxis aborta el script que se está
ejecutando. Se puede utilizar pcall()
para evitar errores.
local ok, _ = pcall(require, 'module_with_error')
if not ok then
-- not loaded
end
También ve:
:help lua-require
Varios plugins de Lua pueden tener nombres de archivo idénticos en su carpeta lua/
. Esto podría
conducir a conflictos de espacio de nombres.
Si dos plugins diferentes tienen un archivo lua/main.lua
, entonces hacer require('main')
es incierto:
¿qué archivo queremos obtener?
Una buena idea seria diferenciar los nombres de tu configuración o plugin con una carpeta
de nivel superior, así: lua/nombre_del_plugin/main.lua
Al igual que los archivos Vimscript, los archivos Lua pueden cargarse automáticamente desde carpetas
especiales en tu runtimepath
. Actualmente, se admiten las siguientes carpetas:
colors/
compiler/
ftplugin/
ftdetect/
indent/
plugin/
syntax/
Nota: en un directorio de ejecución, todos los archivos *.vim
se proceden o se originan antes que
los archivos *.lua
.
También ve:
:help 'runtimepath'
:help load-plugins
Dado que los archivos en tiempo de ejecución no se basan en el sistema de módulos de Lua, dos
plugins pueden tener un archivo plugin/main.lua
sin que ello suponga un problema.
Este comando ejecutará un trozo de código Lua.
:lua require('myluamodule')
Multi-line scripts are possible using heredoc syntax: Scripts multilinea son posibles usando la sintaxis de 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
Nota: cada comando :lua
tiene su propio alcance y las variables declaradas con la palabra clave local
no son accesibles fuera del comando. Esto no funcionará:
:lua local foo = 1
:lua print(foo)
" prints 'nil' instead of '1'
Nota 2: la función print()
en Lua se comporta de forma similar al comando :echomsg
. Su salida se
guarda en el historial de mensajes y puede ser suprimida por el comando :silent
.
También ve:
:help :lua
:help :lua-heredoc
Este comando ejecuta un trozo de código Lua que actúa sobre un rango de líneas en el buffer actual.
Si no se especifica ningún rango, se utiliza todo el buffer. El string que se retornado
del fragmento
que se utiliza para determinar con qué debe reemplazarse cada línea.
El siguiente comando reemplazaría cada línea del buffer actual con el texto "hello world":
:luado return 'hello world'
También se proporcionan dos variables implícitas line
y linenr
. line
es el texto de la
línea sobre la que se está iterando mientras que linenr
es su número. El siguiente comando
hará que todas las líneas cuyo número sea divisible por 2 sean mayúsculas:
:luado if linenr % 2 == 0 then return line:upper() end
También ve:
:help :luado
:luafile
:source
:runtime
:luafile
y :source
son muy similares:
:luafile ~/foo/bar/baz/myluafile.lua
:luafile %
:source ~/foo/bar/baz/myluafile.lua
:source %
:source
tambien soporta rangos, que tambien pueden ser utiles para solo ejecutar una parte de
un script:
:1,10source
:runtime
es un poco diferente: utiliza la opción 'runtimepath'
para determinar
los archivos que se deben ejecutar. Ver :help :runtime
para más detalles
También ve:
:help :luafile
:help :source
:help :runtime
Puede que te preguntes cuál es la diferencia entre llamar a la función
require()
y ejecutar un archivo Lua y si deberías preferir una forma sobre la otra.
Tienen diferentes casos de uso:
require()
:- Es una función integrada en Lua. Te permite aprovechar el sistema de módulos de Lua.
- Busca módulos en las carpetas
lua/
de tu'runtimepath'
. - Mantiene un registro de los módulos que han sido cargados y evita que un script sea analizado
y ejecutado por segunda vez. Si cambias el archivo que contiene el código de un módulo e
intentas
require()
por segunda vez mientras Neovim se está ejecutando, el módulo no se actualizará.
:luafile
,:source
y:runtime
:- Son comandos Ex. Los módulos no están soportados.
- Ejecutan el contenido de un script independientemente de que se haya ejecutado antes.
:luafile
y:source
toman una ruta absoluta o relativa al directorio de trabajo de la ventana actual.:runtime
utiliza la opción'runtimepath'
para encontrar los archivos.
Los archivos ejecutados a través de :source
, :runtime
o automáticamente desde los directorios de
ejecución también aparecerán en :scriptnames
y --startuptime
.
Esta función ya incorporada de Vimscript evalúa un string de Lua y devuelve su valor. Los tipos de datos de Lua se convierten automáticamente en tipos de Vimscript (y viceversa).
" Puedes almacenar el valor de una variable
let variable = luaeval('1 + 1')
echo variable
" 2
let concat = luaeval('"Lua".." is ".."awesome"')
echo concat
" 'Lua is awesome'
" Listas que se parecen a tablas son convertias en listas Vim
let list = luaeval('{1, 2, 3, 4}')
echo list[0]
" 1
echo list[1]
" 2
" Nota que a diferencia de las tablas en Lua, las listas en Vim son @-indexadas
" Diccionarios como tablas son convertidos en dictionarios vim
let dict = luaeval('{foo = "bar", baz = "qux"}')
echo dict.foo
" 'bar'
" Lo mismo para boleanos y nil
echo luaeval('true')
" v:true
echo luaeval('nil')
" v:null
" Puedes crear Vimscript-alias para las funciones Lua
let LuaMathPow = luaeval('math.pow')
echo LuaMathPow(2, 2)
" 4
let LuaModuleFunction = luaeval('require("mymodule").myfunction')
call LuaModuleFunction()
" Tambien es posibla pasar una funcion Lua como valor de una función Vim
lua X = function(k, v) return string.format("%s:%s", k, v) end
echo map([1, 2, 3], luaeval("X"))
luaeval()
takes an optional second argument that allows you to pass data to the expression. You can then access that data from Lua using the magic global _A
:
echo luaeval('_A[1] + _A[2]', [1, 1])
" 2
echo luaeval('string.format("Lua is %s", _A)', 'awesome')
" 'Lua is awesome'
También ve:
:help luaeval()
Esta variable global de Vim te permita llamar funciones Lua en el espacio de nombres global
(_G
) directamente desde Vimscript. De nuevo, los tipos
de datos de Vim se convierten en tipos de Lua y viceversa.
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
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()
También ve:
:help v:lua
:help v:lua-call
Esta variable solo puede ser usada para llamar funciones. El siguiente código siempre arrojará error:
" 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)
Puedes tener un resaltado de sintaxis de Lua dentro de los archivos .vim poniendo
let g:vimsyn_embed = 'l'
en su archivo de configuración. Vea :help g:vimsyn_embed
para más
información sobre esta opción.
Neovim expone una variable global vim
que sirve como punto de entrada para interactuar con sus
APIs desde Lua. Ofrece a los usuarios una "biblioteca estándar" ampliada de funciones, así como
varios submódulos.
Algunas funciones y módulos notables son:
vim.inspect
: imprime esteticamente objetos Lua (útil para inspeccionar tablas).vim.regex
: utiliza regexes de Vim desde Lua.vim.api
: módulo que expone funciones de la API (la misma API utilizada por los plugins remotos).vim.loop
: módulo que expone la funcionalidad del event-loop de Neovim (usando LibUV).vim.lsp
: módulo que controla el cliente LSP incorporado.vim.treesitter
: módulo que expone la funcionalidad de la biblioteca tree-sitter.
Esta lista no es para nada extensa. Si desea saber más sobre lo que está disponible por
la variable vim
, :help lua-stdlib
y :help lua-vim
son el camino a seguir. Alternativamente,
puede hacer :lua print(vim.inspect(vim))
para obtener una lista de cada módulo.
Escribir print(vim.inspect(x))
cada vez que quieras inspeccionar el contenido de un objeto puede
resultar muy molesto. Puede ser que valga la pena tener una función envolvente global en algún
lugar de su configuración:
function _G.dump(...)
local objects = vim.tbl_map(vim.inspect, {...})
print(unpack(objects))
return ...
end
Así, podrá inspeccionar el contenido de un objeto rápidamente en tu código o desde la línea de comandos:
dump({1, 2, 3})
:lua dump(vim.loop)
Además, puedes encontrar que las funciones integradas de Lua son a veces deficientes comparadas
con las que encontrarías en otros lenguajes (por ejemplo os.clock()
sólo retorna un valor en
segundos, no en milisegundos). Asegúrate de mirar en Neovim stdlib (y vim.fn
, más adelante),
probablemente tenga lo que estás buscando.
Esta función evalúa una expresión string de Vimscript y devuelve su valor. Los tipos de datos de Vimscript se convierten automáticamente en tipos de Lua (y viceversa).
Es el equivalente en Lua de la función luaeval()
en Vimscript
-- Data types are converted correctly
print(vim.api.nvim_eval('1 + 1')) -- 2
print(vim.inspect(vim.api.nvim_eval('[1, 2, 3]'))) -- { 1, 2, 3 }
print(vim.inspect(vim.api.nvim_eval('{"foo": "bar", "baz": "qux"}'))) -- { baz = "qux", foo = "bar" }
print(vim.api.nvim_eval('v:true')) -- true
print(vim.api.nvim_eval('v:null')) -- nil
TODO: Es posible para vim.api.nvim_eval()
retornar una funcref
?
A diferencia de luaeval()
, vim.api.nvim_eval()
no proporciona una variable implícita _A
para pasar datos a la expresión.
Esta función evalúa un trozo de código Vimscript. Toma una cadena que contiene el código fuente a ejecutar y un booleano para determinar si la salida del código debe ser retornada por la función (puede almacenar la salida en una variable, por ejemplo).
local result = vim.api.nvim_exec(
[[
let mytext = 'hello world'
function! MyFunction(text)
echo a:text
endfunction
call MyFunction(mytext)
]],
true)
print(result) -- 'hello world'
TODO: La documentación dice que el alcance del script (s:
) está soportado,
pero ejecutar este snippet con el alcance de un script arrojá un error. ¿Por qué?
Esta funcion ejecuta un comando ex. Toma el string que contiene el comando que se debe ejecutar.
vim.api.nvim_command('new')
vim.api.nvim_command('wincmd H')
vim.api.nvim_command('set nonumber')
vim.api.nvim_command('%s/foo/bar/g')
Alias for vim.api.nvim_exec()
. Only the command argument is needed, output
is always set to false
.
Alias para vim.api.nvim_exec()
. Solo el argumento comando es necesitado, output
estará siempre
predeterminado en false
.
vim.cmd('buffers')
vim.cmd([[
let g:multiline =<< EOF
foo
bar
baz
EOF
]])
Cuando quieras pasar cadenas de texto (String) en estas funciones, puedes escapar usando las barras invertidad:
vim.cmd('%s/\\Vfoo/bar/g')
Literale strings son más fáciles de usar ya que no requieren escapar caracteres:
vim.cmd([[%s/\Vfoo/bar/g]])
Esta función de la API le permite escapar de los códigos de la terminal y de los códigos de las teclas de Vim.
Es posible que te hayas encontrado con mapeos como este:
inoremap <expr> <Tab> pumvisible() ? "\<C-n>" : "\<Tab>"
Intentar hacer lo mismo en Lua puede resultar en todoun reto. Puedes estar tentado de hacerlo así:
function _G.smart_tab()
return vim.fn.pumvisible() == 1 and [[\<C-n>]] or [[\<Tab>]]
end
vim.api.nvim_set_keymap('i', '<Tab>', 'v:lua.smart_tab()', {expr = true, noremap = true})
sólo para descubrir que el mapeo inserta \<Tab>
y \<C-n>
.
La posibilidad de escapar de los códigos de las teclas es en realidad una característica de Vimscript.
además de las secuencias de escape habituales como \r
, \42
o \x10
que son comunes a muchos
lenguajes de programación, las expr-quotes
de Vimscript (strings rodeadas de comillas dobles)
te permiten escapar de la representación legible para el ser humano de los códigos de tecla
de Vim.
Lua no tiene esa función incorporada. Afortunadamente, Neovim tiene una función API para escapar
los códigos de los terminales y los códigos de las teclas: nvim_replace_termcodes()
.
print(vim.api.nvim_replace_termcodes('<Tab>', true, true, true))
Esto es un poco verboso. Crear un envoltorio reutilizable puede ayudar:
-- La función se llama `t` para `termcodes`.
-- No es necesario llamarla así, pero me parece que es conveniente para la
función local t(str)
-- Ajusta los argumentos booleanos según sea necesario
return vim.api.nvim_replace_termcodes(str, true, true, true)
fin
print(t'<Tab>')
Volviendo a nuestro ejemplo anterior, ahora debería funcionar como se esperaba:
local function t(str)
return vim.api.nvim_replace_termcodes(str, true, true, true)
end
function _G.smart_tab()
return vim.fn.pumvisible() == 1 and t'<C-n>' or t'<Tab>'
end
vim.api.nvim_set_keymap('i', '<Tab>', 'v:lua.smart_tab()', {expr = true, noremap = true})
Mira también:
:help keycodes
:help expr-quote
:help nvim_replace_termcodes()
Neovim proporciona un conjunto de funciones API para establecer una opción u obtener su valor actual:
- Opciones globales:
vim.api.nvim_set_option()
vim.api.nvim_get_option()
- Opciones de buffer-local:
vim.api.nvim_buf_set_option()
vim.api.nvim_buf_get_option()
- Opciones de ventana-local:
vim.api.nvim_win_set_option()
vim.api.nvim_win_get_option()
Toman un string que contiene el nombre de la opción a establecer/obtener, así como el valor que desea establecer.
Las opciones booleanas (como (no)number
) deben establecerse como true
o false
:
vim.api.nvim_set_option('smarttab', false)
print(vim.api.nvim_get_option('smarttab')) -- false
Como es lógico, las opciones de cadena de texto tienen que establecerse en un string:
vim.api.nvim_set_option('selection', 'exclusive')
print(vim.api.nvim_get_option('selection')) -- 'exclusive'
Las opciones numéricas aceptan un número:
vim.api.nvim_set_option('updatetime', 3000)
print(vim.api.nvim_get_option('updatetime')) -- 3000
Las opciones "Buffer-local" y "window-local" también necesitan un número de buffer o de ventana (el uso de "0" establecerá/obtendrá la opción para el buffer/ventana actual):
vim.api.nvim_win_set_option(0, 'number', true)
vim.api.nvim_buf_set_option(10, 'shiftwidth', 4)
print(vim.api.nvim_win_get_option(0, 'number')) -- true
print(vim.api.nvim_buf_get_option(10, 'shiftwidth')) -- 4
Las variables internas se pueden manipular de forma más intuitiva utilizando estos meta-accesos:
vim.g.{name}
: variables globalesvim.b.{name}
: variables de buffervim.w.{name}
: variables de ventanavim.t.{name}
: variables de tab-pagevim.v.{name}
: Vim variables predefinidas de Vimvim.env.{name}
: variables de ambiente (entorno)
vim.g.some_global_variable = {
key1 = 'value',
key2 = 300
}
print(vim.inspect(vim.g.some_global_variable)) -- { key1 = "value", key2 = 300 }
Algunos nombres de variables pueden contener caracteres que no pueden ser utilizados como
identificadores en Lua. Aún así puedes manipular estas variables utilizando esta
sintaxis: vim.g['mi#variable']
.
Para eliminar una de estas variables, simplemente asigna nil
a la misma:
vim.g.some_global_variable = nil
También ve:
:help lua-vim-variables
A diferencia de las opciones meta-accesos, no se puede especificar un número para las variables de ámbito buffer/ventana/página de pestañas.
Además, no puedes añadir/actualizar/borrar claves de un diccionario almacenado en una de estas variables. Por ejemplo, este fragmento de código Vimscript no funciona como se esperaba:
let g:variable = {}
lua vim.g.variable.key = 'a'
echo g:variable
" {}
Puedes utilizar una variable temporal como solución:
let g:variable = {}
lua << EOF
local tmp = vim.g.variable
tmp.key = 'a'
vim.g.variable = tmp
EOF
echo g:variable
" {'key': 'a'}
Este es un problema conocido:
Se puede utilizar vim.fn
para llamar a una función de Vimscript. Los tipos de datos se
convierten de un lado a otro de Lua a Vimscript.
print(vim.fn.printf('Hello from %s', 'Lua'))
local reversed_list = vim.fn.reverse({ 'a', 'b', 'c' })
print(vim.inspect(reversed_list)) -- { "c", "b", "a" }
local function print_stdout(chan_id, data, name)
print(data[1])
end
vim.fn.jobstart('ls', { on_stdout = print_stdout })
Los hash (#
) no son caracteres válidos para los identificadores en Lua, por lo que las
funciones de autocarga tienen que ser llamadas con esta sintaxis:
vim.fn['my#autoload#function']()
La funcionalidad de vim.fn
es idéntica a la de vim.call
, pero permite una sintaxis
más parecida a la de Lua.
Se diferencia de vim.api.nvim_call_function
en que la conversión de objetos Vim/Lua es
automática: vim.api.nvim_call_function
retorna una tabla para números de punto flotante
y no acepta closures de Lua mientras que vim.fn
maneja estos tipos de forma transparente.
También ve:
:help vim.fn
Neovim tiene una extensa biblioteca de potentes funciones incorporadas que son muy útiles
para los plugins. Consulta :help vim-function
para una lista alfabética y :help function-list
para una lista de funciones agrupadas por temas.
Las funciones de la API de Neovim pueden utilizarse directamente a través de vim.api.{..}
.
Ver :help api
para más información.
Algunas funciones de Vim que deberían devolver un booleano devuelven 1
o 0
en su lugar.
Esto no es un problema en Vimscript, ya que 1
es verdadero y 0
falso, lo que permite
construcciones como éstas:
if has('nvim')
" do something...
endif
En Lua, sin embargo, sólo false
y nil
se consideran falsos (falsy), los números siempre se
evalúan como true
sin importar su valor. Tienes que comprobar si explícitamente se trata
de 1
o 0
:
if vim.fn.has('nvim') == 1 then
-- do something...
end
Neovim proporciona una lista de funciones de la API para establecer, obtener y eliminar mapeos:
- Mapeo global:
vim.api.nvim_set_keymap()
vim.api.nvim_get_keymap()
vim.api.nvim_del_keymap()
- Mapeo de buffer-local:
vim.api.nvim_buf_set_keymap()
vim.api.nvim_buf_get_keymap()
vim.api.nvim_buf_del_keymap()
Empecemos con vim.api.nvim_set_keymap()
y vim.api.nvim_buf_set_keymap()
.
El primer argumento que se pasa a la función es un string que contiene el nombre del modo para el que el mapeo tendrá efecto:
String value | Help page | Affected modes | Vimscript equivalent |
---|---|---|---|
'' (an empty string) |
mapmode-nvo |
Normal, Visual, Select, Operator-pending | :map |
'n' |
mapmode-n |
Normal | :nmap |
'v' |
mapmode-v |
Visual and Select | :vmap |
's' |
mapmode-s |
Select | :smap |
'x' |
mapmode-x |
Visual | :xmap |
'o' |
mapmode-o |
Operator-pending | :omap |
'!' |
mapmode-ic |
Insert and Command-line | :map! |
'i' |
mapmode-i |
Insert | :imap |
'l' |
mapmode-l |
Insert, Command-line, Lang-Arg | :lmap |
'c' |
mapmode-c |
Command-line | :cmap |
't' |
mapmode-t |
Terminal | :tmap |
El segundo argumento es una cadena (string) que contiene el lado izquierdo del mapeo (la clave o
conjunto de claves que activan el comando definido en el mapeo). Una cadena vacía equivale
a <Nop>
, que desactiva una clave.
El tercer argumento es un string que contiene el lado derecho del mapeo (el comando a ejecutar).
El argumento final es una tabla que contiene las opciones booleanas para el mapeo como se define
en :help :map-arguments
(incluyendo noremap
y excluyendo buffer
).
Los mapeos locales del buffer también toman un número de buffer como su primer argumento
(0
establece el mapeo para el buffer actual).
vim.api.nvim_set_keymap('n', '<Leader><Space>', ':set hlsearch!<CR>', { noremap = true, silent = true })
-- :nnoremap <silent> <Leader><Space> :set hlsearch<CR>
vim.api.nvim_set_keymap('n', '<Leader>tegf', [[<Cmd>lua require('telescope.builtin').git_files()<CR>]], { noremap = true, silent = true })
-- :nnoremap <silent> <Leader>tegf <Cmd>lua require('telescope.builtin').git_files()<CR>
vim.api.nvim_buf_set_keymap(0, '', 'cc', 'line(".") == 1 ? "cc" : "ggcc"', { noremap = true, expr = true })
-- :noremap <buffer> <expr> cc line('.') == 1 ? 'cc' : 'ggcc'
vim.api.nvim_get_keymap()
toma un string que contiene el nombre corto del modo para el que
se quiere la lista de mapeos (ver tabla anterior). El valor retornado es una tabla que contiene
todas las asignaciones globales para el modo.
print(vim.inspect(vim.api.nvim_get_keymap('n')))
-- :verbose nmap
vim.api.nvim_buf_get_keymap()
takes an additional buffer number as its first argument (0
will get mapppings for the current bufffer)
print(vim.inspect(vim.api.nvim_buf_get_keymap(0, 'i')))
-- :verbose imap <buffer>
vim.api.nvim_del_keymap()
takes a mode and the left-hand side of a mapping.
vim.api.nvim_del_keymap('n', '<Leader><Space>')
-- :nunmap <Leader><Space>
Again, vim.api.nvim_buf_del_keymap()
, takes a buffer number as its first argument, with 0
representing the current buffer.
vim.api.nvim_buf_del_keymap(0, 'i', '<Tab>')
-- :iunmap <buffer> <Tab>
Actualmente no existe una interfaz para crear comandos de usuario en Lua. Sin embargo, se está planeando hacer lo siguiente:
Por los momentos, probablemente sea mejor crear comandos en Vimscript.
Augroups y autocommands no tienen interfaces aun pero se está trabajando en ello:
Mientras tanto, puedes crear autocomandos en Vimscript o utilizar esta envoltura de norcalli/nvim_utils
La API de sintaxis es todavía un trabajo en progreso. Aquí hay un par de indicaciones:
- Issue #9876
- tjdevries/colorbuddy.vim, a library for creating colorschemes in Lua
:help lua-treesitter
En Lua, la función require()
almacena en caché los módulos. Esto es bueno para el rendimiento,
pero puede hacer que el trabajo con los plugins sea un poco engorroso porque los módulos no se
actualizan en las siguientes llamadas a require()
.
Si quieres refrescar la caché de un módulo en particular, tienes que modificar la tabla
global package.loaded
:
package.loaded['modname'] = nil
require('modname') -- loads an updated version of module 'modname'
El plugin nvim-lua/plenary.nvim tiene una función personalizada que hace esto por ti.
No puedes interactuar directamente con la referencia a un objeto Vim desde Lua o un objeto Lua
desde Vimscript.
Por ejemplo, la función map()
de Vimscript modifica una variable en su lugar:
let s:list = [1, 2, 3]
let s:newlist = map(s:list, {_, v -> v * 2})
echo s:list
" [2, 4, 6]
echo s:newlist
" [2, 4, 6]
Usando esta función de Lua se crea una copia en su lugar:
local tbl = {1, 2, 3}
local newtbl = vim.fn.map(tbl, function(_, v) return v * 2 end)
print(vim.inspect(tbl)) -- { 1, 2, 3 }
print(vim.inspect(newtbl)) -- { 2, 4, 6 }
Esto afecta principalmente a funciones y tablas:
Las tablas de Lua que son una mezcla entre una Lista y un Diccionario no pueden ser convertidas:
print(vim.fn.count({1, 1, number = 1}, 1))
-- E5100: Cannot convert given lua table: table should either have a sequence of positive integer keys or contain only string keys
Mientras que puedes llamar a funciones de Vim en Lua con vim.fn
, no puedes mantener
referencias a ellas. Esto puede causar comportamientos extraños:
local FugitiveHead = vim.fn.funcref('FugitiveHead')
print(FugitiveHead) -- vim.NIL
vim.cmd("let g:test_dict = {'test_lambda': {-> 1}}")
print(vim.g.test_dict.test_lambda) -- nil
print(vim.inspect(vim.g.test_dict)) -- {}
Pasar funciones de Lua a funciones de Vim está bien, almacenarlas en variables de Vim no lo es:
-- Esto funciona:
vim.fn.jobstart({'ls'}, {
on_stdout = function(chan_id, data, name)
print(vim.inspect(data))
end
})
-- Esto no;
vim.g.test_dict = {test_lambda = function() return 1 end} -- Error: Cannot convert given lua type
Sin embargo, ten en cuenta que hacer lo mismo desde Vimscript con luaeval()
funciona:
let g:test_dict = {'test_lambda': luaeval('function() return 1 end')}
echo g:test_dict
" {'test_lambda': function('<lambda>4714')}
Un patrón común en los scripts de Vim es utilizar 1
o 0
en lugar de booleanos propiamente dichos.
De hecho, Vim no tuvo un tipo booleano separado hasta la versión 7.4.1154.
Los booleanos de Lua se convierten en booleanos reales en Vimscript, no en números:
lua vim.g.lua_true = true
echo g:lua_true
" v:true
lua vim.g.lua_false = false
echo g:lua_false
" v:false
Si utilizas linters y/o servidores de lenguaje para obtener diagnósticos y autocompletar los proyectos Lua, es posible que tengas que configurar ajustes específicos de Neovim para ellos. Aquí tienes algunas configuraciones recomendadas para las herramientas más populares:
Puedes hacer que luacheck reconozca el global vim
poniendo esta configuración en ~/.luacheckrc
(o $XDG_CONFIG_HOME/luacheck/.luacheckrc
):
globals = {
"vim",
}
El servidor de idiomas Alloyed/lua-lsp utiliza luacheck
para proporcionar linting y leer el mismo archivo.
Para más información de como configurar luacheck
, por favor ir a la
documentación
El repositorio de nvim-lspconfig contiene instrucciones para configurar sumneko/lua-language-server (el ejemplo utiliza el cliente LSP incorporado pero la configuración debería ser idéntica para otras implementaciones de clientes LSP).
Para más información de como configurar sumneko/lua-language-server mira "Setting without VSCode"
La fuente completa de rafcamlet/coc-nvim-lua para coc.nvim provee elementos para la completación de Neovim stdlib .
Puedes depurar el código Lua que se ejecuta en una instancia separada de Neovim con jbyuki/one-small-step-for-vimkind
El plugin utiliza el Debug Adapter Protocol. La conexión a un adaptador de depuración requiere un cliente DAP como mfussenegger/nvim-dap o puremourning/vimspector.
wbthomason/packer.nvim soporta paquetes Luarocks. Las intrucciones de como establecer esto están en el README
vim.loop
es el módulo que expone la API de LibUV. Algunos recursos:
- Documentación oficial de LibUV
- Documentación de Luv](https://github.com/luvit/luv/blob/master/docs.md)
- Teukka.tech - Uso de LibUV en Neovim](https://teukka.tech/posts/2020-01-07-vimloop/)
También ve:
:help vim.loop
vim.lsp
es el módulo que controla el cliente LSP incorporado. El repositorio neovim/nvim-lspconfig contiene configuraciones por defecto para servidores de idiomas populares.
El comportamiento del cliente puede configurarse mediante "lsp-handlers". Para más información:
:help lsp-handler
- neovim/neovim#12655
- Cómo migrar desde diagnostic-nvim](nvim-lua/diagnostic-nvim#73 (comment))
También puedes echar un vistazo a los plugins construidos alrededor del cliente LSP:
También ve:
:help lsp
vim.treesitter
es el módulo que controla la integración de la biblioteca Tree-sitter en Neovim.
Si quieres saber más sobre Tree-sitter, puede interesarte esta presentación (38:37).
La organización nvim-treesitter alberga varios plugins que toman ventaja de la biblioteca.
También ve:
:help lua-treesitter
Una de las ventajas de utilizar Lua es que no hay que escribir código. Hay una multitud de transpiladores disponibles para el lenguaje.
Probablemente uno de los transpiladores más conocidos para Lua. Añade un montón de características convenientes como clases, comprensión de listas o funciones literales. El plugin svermeulen/nvim-moonmaker permite escribir plugins y configuraciones de Neovim directamente en Moonscript.
Un lisp que compila en Lua. Puedes escribir configuraciones y plugins para Neovim en Fennel con el plugin Olical/aniseed. Además, el plugin Olical/conjure proporciona un entorno de desarrollo interactivo que soporta Fennel (entre otros lenguajes).
Otros proyectos interesantes: