nanotee / nvim-lua-guide Goto Github PK
View Code? Open in Web Editor NEWA guide to using Lua in Neovim
A guide to using Lua in Neovim
With packer.nvim
, many people might be tempted to switch from vim-plug
to the native package system. There are some gotchas with that, since packages are not available during the time init.vim
is read.
Things to mention, maybe (I'm sure there are others):
vim.cmd [[packadd! plugin]]
in the lua code.nvim/config/plugin
that packadd
s and require
s the lua code from .nvim/config/lua
using the pluginrelated: create and link a cheat sheet like this for newcomers.
Originally posted by @matu3ba in #7 (comment)
In neovim 0.5.1, when I execute the example in the vim.api.nvim_exec() section (by pasting that text into a lua script, which I then source), I get the error
"E5113: Error while calling lua chunk: tmp.lua:1: Vim(let):E461: Illegal variable name: s:mytext"
It seems that script-local variables (and also script-local functions) are not allowed from nvim_exec, so the "s:" prefixes should be omitted.
neovim/neovim#14661 (lua: autocmds take 2)
see also the not yet merged PR on the docs: https://github.com/neovim/neovim/pull/17545/files
Hi, I'm trying to setup my installer which installs my entire neovim setup on completely bare systems.
With vimplug, I had installed all my packages from this state with
nvim -c PlugInstall -c qa --headless
(of course there was a bootstrap for fetching vimplug in my init.vim
).
I've tried the same approach with packer, but while the boostrap script does successfully fetch packer itself, for some reason I do not seem to be able to get it to actually execute PackerInstall
, PackerSync
or anything else before it exits.
After a bit more investigation, I found that this seems to be happening because nvim
does not know to wait for PackerSync
before executing qa
and killing itself. A temporary (but not at all ideal) workaround is to do
nvim -c PackerSync -c 'sleep 5' -c qa --headless
Is there some way of forcing nvim to wait for packer to finish its sync before exiting?
Quite embarassingly I did not study how things are working in neovim, but its also not documented inside the neovim help page under :h lua
.
Probably we could link here.
For example how packer does it and this explanation why give hints.
This is relevant for plugin authors and if the user wants to spawn background threads for resource-demanding computations in lua.
neovim/neovim#13479 just got merged 🎉 with the following changes:
vim.o
(set global option) was renamed to vim.go
vim.o
was added that behaves (essentially) like set
(is expected to -- meaning there may be weird corner cases)vim.opt
was added to allow a more "luanatic" way of setting and getting (in particular, appending) options.The docs are quite good and explicit, so maybe it's not necessary to repeat much (or just copy the examples from the docs), but the change in vim.o
meaning should be reflected.
How to disable word/tag/buffer based completions?
Is there a way to check if a vim feature exists? In vimscript, I can use:
if exists('+termguicolors')
set termguicolors
end
Is there an equivalent in lua?
I'm using a 'filename', path=1, symbols = { unnamed = '' }
section in the following scenario
❯ tree ~/With\ Space/
/Users/simon.mandlik/With\ Space/
└── test.txt
0 directories, 1 file
If I cd
into the "With Space"
dir and open test.txt
, the following is shown in lualine:
:pwd
in neovim returns /Users/simon.mandlik/With Space
Since neovim/neovim@8402865, vim.cmd
is now an alias to vim.api.nvim_exec()
instead of vim.api.nvim_command()
. (This should allow vim.cmd
to work correctly with multi-line vimscript, too.)
...as of neovim/neovim#13119, so the section on packages can be simplified now. (It's still used for opt
packages, mind you.)
I find myself constantly referencing this guide. Thanks a lot it's super awesome. It would be amazing if it was merged into master.
Thanks! ❤️
Hi,
I'm struggeling a while now with this and tried serveral solutions. My goal is to use the edit command with the path of the currently edited file filled in:
nnoremap <leader>e :e <C-R>=expand("%:p:h") . "/" <CR>
vim.api.nvim_set_keymap('n', '<leader>e', '<cmd>e ???<cr>', {silent = true, noremap = true})
Using it directly just works as expected.
I tried to wrap <C-R>
in the termcodes
function and several other ways, but none of them worked. Maybe there is even a better way to solve this with lua. Any help is appreciated 🙏🏻
It should include these functions:
vim.api.nvim_set_hl_ns()
vim.api.nvim_set_hl()
vim.api.nvim_set_decoration_provider()
?Here's another GoodToKnow(tm): every :lua
statement has its own scope, which means that local
variables in one statement will not be available in subsequent statements -- tripping people up that want to debug a script by pasting it in line-by-line. (A similar but more subtle effect is setting a local
variable in a init.vim
heredoc syntax and trying to access it later from a different heredoc snippet or from the command line via :lua
.)
So
:lua local foo = 1
:lua print(foo)
will print nil
, not 1
.
A sentence pointing this out could go after the multiline heredoc example.
Converting your init.vim
to init.lua
is the new sport, it seems. One thing that I've seen people being bitten by repeatedly is converting something like
let g:foo = 1
to
vim.g.foo = true
which of course won't work since Lua true
gets converted to v:true
, which is not the same as 1
. (Same for false
and v:false
vs. 0
.)
I think it would be good to add a big, fat, caveat somewhere -- but where? The conversion itself is mentioned under "Accessing Lua from Vimscript", but it should probably be repeated on the converse side as well.
Assume the following snippet be in the init.lua
local opts = { noremap = true, silent = true }
local map = vim.api.nvim_set_keymap
map('nt', '<C-x>j', [[<cmd> lua print("not broken!")<CR>]], opts)
-- prints either nt or n
local add_cmd = vim.api.nvim_create_user_command
add_cmd('Printmode', function() print(vim.api.nvim_get_mode().mode) end, {})
This fails with E5113: Error while calling lua chunk: $USER/.config/nvim/lua/my_keymaps.lua:534: Shortname is too long: nt
,
even though nvim_get_mode()
provides us with a mode nt
.
I would expect that there is somewhere a lua function for the "visual mode in the terminal" such that one can do keybindings there, if there is an api function that tells us we are in a different mode.
see below: "mapping mode is just the first char of nvim_get_mode().mode" info is missing
This is just a drive by comment since we were chatting in gitter.
You can do something like this (which you can't do with v:lua
)
lua X = function(k, v) return string.format("%s:%s", k, v) end
echo map([1, 2, 3], luaeval("X"))
Do with this information as you wish ;)
It's hard to hard to find working examples of user commands (or even autocommands) that use ranges. It could be helpful to include them in the guide.
Is there a document for the "standard library" exposed via the global vim
object? I see some functions documented when I run :h lua-vim
, but quite a few of them are not. I'm trying to figure out what some functions (e.g. vim.api.nvim_list_wins
) do and what their call signatures are.
If it helps, I'm on version NVIM v0.6.0-dev+106-g545e05d2f
.
Now that neovim/neovim#14686 is merged, neovim sources Lua files from the usual config directories directly during startup (after all vim files are sourced in the corresponding directory).
Also :source foo.lua
is now a valid alternative to :luafile foo.lua
.
As a bonus, :<range>source
works on Lua code.
Reloading cached modules does not adjust the keymappings, ie
add_cmd('CRel', function()
local lua_dirs = vim.fn.glob("./lua/*", 0, 1)
for _, dir in ipairs(lua_dirs) do
dir = string.gsub(dir, "./lua/", "")
require("plenary.reload").reload_module(dir)
end
-- TODO keybindings are not reloaded
end, {})
does not work as expected. It would be nice to mention this and ideally explain what to use as workaround (for now).
especially the difference to vim.fn
/vim.call
:
vim.fn.{func}({...}) vim.fn
Invokes vim-function or user-function {func} with arguments {...}.
To call autoload functions, use the syntax:
vim.fn['some#function']({...})
Unlike vim.api.nvim_call_function this converts directly between Vim
objects and Lua objects. If the Vim function returns a float, it will
be represented directly as a Lua number. Empty lists and dictionaries
both are represented by an empty table.
Note: v:null values as part of the return value is represented as
vim.NIL special value
Note: vim.fn keys are generated lazily, thus pairs(vim.fn) only
enumerates functions that were called at least once.
so I think you'd always want to use vim.call
?
Hello,
I am having troubles loading modules from init.lua
.
For all my plugins I use vim-plug
. I recently decided to use completion-nvim
. Unfortunately, when loading the module:
local completion = require'completion'
I get:
E5113: Error while calling lua chunk: ~/.config/nvim/init.lua:6: module 'completion' not found:
no field package.preload['completion']
no file './completion.lua'
no file '/usr/share/luajit-2.0.5/completion.lua'
no file '/usr/local/share/lua/5.1/completion.lua'
no file '/usr/local/share/lua/5.1/completion/init.lua'
no file '/usr/share/lua/5.1/completion.lua'
no file '/usr/share/lua/5.1/completion/init.lua'
no file './completion.so'
no file '/usr/local/lib/lua/5.1/completion.so'
no file '/usr/lib/lua/5.1/completion.so'
no file '/usr/local/lib/lua/5.1/loadall.so'
I know the files modules are loaded via the paths contained in package.path
but since it is impossible (if I'm not mistaken) to have recursive patterns in lua, I don't wish to add every package/neovim extensions paths in package.path
.
Is there a more simple way ?
Thank you very much in advance for any help.
Hello,
First of all, thank you for this amazing guide!!! I am finally VimL free :-) and much of it is thanks to this guide :-)
One of the last issues I tackled was setting environment variables in init.lua
. It would be nice if the guide would mention that as well: in init.vim
it was as easy as (for example) let $GOFLAGS='-tags=development'
however I could not find any immediate/obvious way to achieve that in init.lua
short of using vim.cmd(...)
.
After some more digging, I finally found setenv()
so settled for vim.fn.setenv("GOFLAGS", "-tags=development")
which seems somewhat nicer.
Cheers!
Hello,
I hope opening an issue for this type of question is okay!
Is there a recommended lua replacement for vimscript's executable()
?
My use-case is that I share a config between several machines, not all of which have the same language servers.
Instead of e.g. if executable("bash-language-server")
, currently I'm using:
if os.execute("command -v bash-language-server") == 0 then
nvim_lsp.bashls.setup({
on_attach = default_lsp_on_attach,
})
end
Is there a recommended alternative?
Hey @nanotee thanks for this awesome repository. It is certainly helping me on moving all my plugins and dotfiles to lua right now in neovim. I was wondering if you have any plans to make it available on other languages. I recently started streaming and i am planning to make that guide available in portuguese (my main language). Is this something you are interested for making it available here as well?
Thanks in advance!
package.loaded['modname'] = nil
plenary
's reload function?packer
has support for them, so I want to at least mention that in the guidechecklist:
vim.call
and vim.fn
expose quite the same functionality, so remove the non-luaish and link to the according neovim :help
, pending core dev reply...vim.o.backup = false
for set
options (it also checks types etc), remove the othervim.api.nvim_set_keymap('i', '<Tab>', 'v:lua.smart_tab()', {expr = true, noremap = true})
such that one can use something like localfunction_setting_mmode(dictionary)
(dictionary) with variables definable as tab.somekey = "some value"
to minimize clutter => better than vimscript :)Do you prefer individual PRs for each issue?
EDIT: according to information in discussion, TBD:=to be discussed
link to pr and manual neovim/neovim#16752
which superseded the linked neovim/neovim#11613
section "Defining user commands"
Here is an example:
:'<,'>Commentary
The command I tried is:
vim.cmd("'<,'>Commentary")
but it doesn't work.
The section https://github.com/nanotee/nvim-lua-guide#caveats-2 (very helpfully!) points out that you can't directly assign to keys in, e.g., vim.w
dicts and refers to neovim/neovim#12544.
In fact, the comment neovim/neovim#12544 (comment) suggests a workaround, and it would be (even more) helpful to directly add this to the guide.
I ran into an issue where the following settings weren't getting applied:
vim.bo.shiftwidth = 4
vim.bo.softtabstop = 0
vim.bo.tabstop = 4
They're in the neovim help as buffer options so I couldn't figure out why it wasn't working.
I happened to see a video on converting VimScript config to lua and he mentioned that you had to also set those options globally. After setting them on vim.o
, they work.
I tried removing them from vim.bo
and just leaving the vim.o
settings but this didn't work either. It seems that they have to be set in both places to work.
I don't know why this is (would love to know) but it might be good to at least document it here in case anyone else has this problem.
Shells provide aliases to abbreviate typing for functionality. For this access to the command arguments (not the one neovim was started with) from lua would be necessary.
An alternative would be linking or providing a description 1. how to pipe shell output to telescope/another lua program and 2. how to sync pwd
of the shell and lwd
or gwd
(:lcd and :cd folder) of neovim.
Does this description make sense to you?
debug.debug()
has special handling in Neovim and could be pretty useful to debug stuff.
Hey I get the following error. I tried a lot already but I can't fix it maybe someone has an idea?
used_by is deprecated, please use 'filetype_to_parsername'
Fehler beim Ausführen von "/Users/user/.config/nvim/after/plugin/lspkind.rc.lua":
E5113: Error while calling lua chunk: ...cal/share/nvim/plugged/lspkind-nvim/lua/lspkind/init.lua:123: Vim(echoerr):DEPRECATED replaced by mode option.
stack traceback:
[C]: in function 'nvim_command'
...cal/share/nvim/plugged/lspkind-nvim/lua/lspkind/init.lua:123: in function 'init'
/Users/user/.config/nvim/after/plugin/lspkind.rc.lua:4: in main chunk
Thanks in advanced
First of all, wonderful write-up! It really fills a hole in the current documentation (and pretty much closes my neovim issue about the lack of such a high-level overview: neovim/neovim#12369)
One question I still have is about the practical difference between :lua require'foo'
and :luafile PATH_TO_FOO.LUA
. Is it just about the explicit path for the latter and the machinery that prevents loading twice for the former? (Assuming that foo.lua
is a single lua script, nothing fancy involving modules.)
Perhaps changing it to the archive.org link would be preferred?
The one and only luajit wiki and luajit not yet implemented.
Performance tips: wiki + wait for status information, computing performance guide recommended by scilua author
Debugging: one step for vimkind
luarocks => [wbthomason/packer.nvim] has now support luarocks packages => add firejail profile for luarocks done, but I want to test it further before upstreaming. Currently I have weird folder quirks.
:checkhealth integration => check core and treesitter implementation
Explaining what is most performant and why (vim.api) and what happens under the hood ie for vim.call
,vim.fn
etc. (blocked by missing documentation upstream) see #6
https://www.reddit.com/r/neovim/comments/oeevcy/what_is_the_difference_between_vimapi_and_vimfn/h45ujiz?utm_source=share&utm_medium=web2x&context=3
"What is the difference between vim.api
and vim.fn
functions?"
"you can no longer use api functions from vim.fn which used to be the case
vim.api is nvim specific functions and it goes from lua -> C while vim.fn is viml functions which goes from lua -> viml -> C
there's more overhead for using vim.fn than vim.api"
Some internal functions (categories?) and how to find more etc
Plugin guidelines:
plugin
folder makes profiling annoying/kinda breaks it): nvim-lua/wishlist#15 (comment)local function a = bla()
vs local a = function()
. Related function M.bla()
for a table.I copied some content to the plugin template project and will delete this issue, once the stuff is documented there properly.
Neovim just merged neovim/neovim#13875, which makes calling vim.fn.foo
an error if vim.api.foo
exists. This makes for an easier recommendation in the corresponding section :)
Hi, I checked the doc but it seems like there is no option to get specific vim variable from lua. Is it possible?
:help lua-vim-options
shows
*lua-vim-options*
*lua-vim-opt*
*lua-vim-set*
*lua-vim-optlocal*
*lua-vim-setlocal*
and they are undocumented (dead links).
At least for me I have this issue that the filetype is set after everything is loaded, which makes vim.bo.filetype
unusable to conditionally set options. see this issue.
Using the metaaccessor filetype
does not work, but it works with vimscript
:
if ( vim.bo.filetype == "cpp" ) --first branch never gets executed
vim.bo.shiftwidth = 4; --visual mode >,<-key: number of spaces for indendation
else
vim.bo.shiftwidth = 2;
end
if ( vim.bo.filetype == "cpp" ) then --first branch never gets executed
vim.bo.tabstop = 4; --Tab key: number of spaces for indendation
else
vim.bo.tabstop = 2;
end
But
if expand('%:e') == 'tex'
"command! Buildauto :call Latexmklualatex()
command! Buildauto :call Latexmkpdflatex()
command! Pdf :call jobstart(["okular", "build/" . expand("%:t:r") . ".pdf"])
endif
just works. I did not find yet lua methods to replace expand
.
As someone trying to write my absolute first line of nvim Lua, I scanned the README hoping it'd tell me what to put in my init.lua
to just make something show up in :messages
and prove things were running.
(I assume the right way to do so is to perhaps execute echom
on vim
but yeah may be nice to explicitly call out.)
Perhaps related to #5
This is a very frequently asked question on Gitter, especially "how can I get lsp to recognize vim global"?
Until https://github.com/tjdevries/nlua.nvim is ready, it might be good to have an example config here to link to. Mine is the following (which is not uncontroversial, especially the disable
on the diagnostics):
require'lspconfig'.sumneko_lua.setup{
settings = {
Lua = {
runtime = {
version = "LuaJIT",
path = vim.split(package.path, ';'),
},
workspace = {
library = {
[vim.fn.expand("$VIMRUNTIME/lua")] = true,
[vim.fn.expand("$VIMRUNTIME/lua/vim/lsp")] = true,
},
},
diagnostics = {
globals = {"vim"},
disable = {"lowercase-global", "unused-function"},
},
completion = {
keywordSnippet = "Disable",
},
},
},
}
I couldn't find any information on neovim lua abbrev or noreabbrev in your guide.
The side from which one does the conversion matters when converting functions:
local function hello()
print('hello')
end
local Myfunc = vim.fn['function'](hello)
print(Myfunc)
-- vim.NIL
vim.g.Hello = hello
-- Error: Cannot convert given lua type
lua << EOF
function _G.hello()
print('hello')
end
EOF
let s:Myfunc = function(luaeval('_G.hello'))
echo s:Myfunc
" <lambda>1
let g:Hello = luaeval('_G.hello')
echo g:Hello
" <lambda>2
Bumped into this when I tried to pass a Lua callback to vim.fn['fzf#wrap']
from the Lua side
https://github.com/Koihik/LuaFormatter https://github.com/JohnnyMorganz/StyLua looks best, since it is simple.
For everything plugin development related or to simplify the guide, I would recommend linking to https://github.com/nvim-lua/nvim-lua-plugin-template and to plenary lua ("inofficial neovim luajit libstd").
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.