neovim icon indicating copy to clipboard operation
neovim copied to clipboard

Lua stdlib feature parity against Vimscript

Open lewis6991 opened this issue 3 years ago • 15 comments

Tracking issue to collect the current status of Lua stdlib against Vimscript.

As all functions listed here are accessible in Lua via vim.fn.*, the goal is not to achieve 100% parity, however the typval conversion may have perf concerns and/or functional issues for specific functions so a Lua equivalent may be desirable. Also some vimscript functions aren't particularly relevant in the Lua domain.

I've filled the table to the best of my current knowledge. I there are any mistakes/disagreements/omissions then please comment.

Total 412
Equivalent exists :white_check_mark: 155
Not required :heavy_minus_sign: 50
Wanted :grey_exclamation: 5
Uncategorised 204
Functions
Math functions (29/29 :white_check_mark:)
Vimscript function Lua equivalent / Notes Equiv exists(:white_check_mark:)/wanted(:grey_exclamation:) /not required(:heavy_minus_sign:)
abs math.abs :white_check_mark:
acos math.acos :white_check_mark:
and bit.band :white_check_mark: *1
asin math.asin :white_check_mark:
atan2 math.atan2 :white_check_mark:
atan math.atan :white_check_mark:
ceil math.ceil :white_check_mark:
cos math.cos :white_check_mark:
cosh math.cosh :white_check_mark:
exp math.exp :white_check_mark:
float2nr math.floor(x+0.5) :white_check_mark:
floor math.floor :white_check_mark:
fmod math.fmod :white_check_mark:
invert bit.bnot :white_check_mark: *1
log10 math.log10 :white_check_mark:
log math.log :white_check_mark:
max math.max :white_check_mark:
min math.min :white_check_mark:
or bit.bor :white_check_mark: *1
pow math.pow :white_check_mark:
rand math.random, vim.loop.random :white_check_mark:
round math.floor(x+0.5) :white_check_mark:
sin math.sin :white_check_mark:
sinh math.sinh :white_check_mark:
sqrt math.sqrt :white_check_mark:
srand math.randomseed :white_check_mark:
tan math.tan :white_check_mark:
tanh math.tanh :white_check_mark:
xor bit.bxor :white_check_mark: *1

*1: Not yet available with PUC Lua (#21222)

Vimscript function Lua equivalent / Notes Equiv exists(:white_check_mark:)/wanted(:grey_exclamation:) /not required(:heavy_minus_sign:)
add table.insert :white_check_mark:
api_info vim.api.nvim_get_api_info :white_check_mark:
append vim.api.nvim_buf_set_lines :white_check_mark:
appendbufline vim.api.nvim_buf_set_lines :white_check_mark:
argc
argidx
arglistid
argv
assert_beeps :heavy_minus_sign:
assert_equal :heavy_minus_sign:
assert_equalfile :heavy_minus_sign:
assert_exception :heavy_minus_sign:
assert_fails :heavy_minus_sign:
assert_false assert(not value) :heavy_minus_sign:
assert_inrange :heavy_minus_sign:
assert_match :heavy_minus_sign:
assert_nobeep :heavy_minus_sign:
assert_notequal :heavy_minus_sign:
assert_notmatch :heavy_minus_sign:
assert_report :heavy_minus_sign:
assert_true assert :heavy_minus_sign:
browse Does nothing in Neovim :heavy_minus_sign:
browsedir Does nothing in Neovim :heavy_minus_sign:
bufadd (to create bufs) vim.api.nvim_create_buf :white_check_mark:
bufexists vim.api.nvim_buf_is_valid :white_check_mark:
buflisted
bufload
bufloaded vim.api.nvim_buf_is_loaded :white_check_mark:
bufname vim.api.nvim_buf_get_name :white_check_mark:
bufnr
bufnr() vim.api.nvim_get_current_buf :white_check_mark:
bufwinid Easy to implement (see below) :heavy_minus_sign:
bufwinnr
byte2line
byteidx
byteidxcomp
call NA (typval) :heavy_minus_sign:
chanclose
changenr
chansend vim.api.nvim_chan_send :white_check_mark:
char2nr string.byte *only works with ASCII :white_check_mark:
charcol
charidx
chdir vim.api.nvim_set_current_dir :white_check_mark:
cindent :heavy_minus_sign:
clearmatches
col
complete_add
complete_check
complete_info
complete
confirm vim.ui.select :white_check_mark:
copy {table.unpack(obj)} :white_check_mark:
count
ctxget(0) vim.api.nvim_get_context :white_check_mark:
ctxget
ctxpop
ctxpush
ctxset
ctxsize
cursor vim.api.nvim_win_set_cursor :white_check_mark:
debugbreak
deepcopy vim.deepcopy :white_check_mark:
delete
deletebufline vim.api.nvim_buf_set_lines :white_check_mark:
dictwatcheradd __index metamethod + autocmd :heavy_minus_sign:
dictwatcherdel __index metamethod + autocmd :heavy_minus_sign:
did_filetype
diff_filler
diff_hlID Obscure :heavy_minus_sign:
digraph_get{,list}
digraph_set{,list}
empty vim.tbl_isempty :white_check_mark:
environ vim.loop.uv.os_environ :white_check_mark:
escape
eval loadstring :white_check_mark:
eventhandler :heavy_minus_sign:?
executable
execute vim.api.nvim_exec, vim.cmd :white_check_mark:
exepath :white_check_mark:
exists
expand
expandcmd
extend vim.list_extend, vim.tbl_extend :white_check_mark:
feedkeys vim.api.nvim_feedkeys :white_check_mark:
filereadable vim.loop.fs_stat :white_check_mark:
filewritable vim.loop.fs_stat :white_check_mark:
filter vim.tbl_filter :white_check_mark:
finddir
findfile vim.fs.find :white_check_mark:
flatten vim.tbl_flatten
fnameescape
fnamemodify
foldclosed
foldclosedend
foldlevel
foldtext
foldtextresult
foreground
fullcommand
funcref NA (typval) :heavy_minus_sign:
function NA (typval) :heavy_minus_sign:
garbagecollect :heavy_minus_sign:
get lua <table>.<key_or_idx> or <default> :white_check_mark:
getbufinfo vim.api.nvim_list_bufs + vim.api.nvim_buf_* :white_check_mark:
getbufline
getbufvar vim.api.nvim_buf_get_var, vim.b[idx][name] :white_check_mark:
getchangelist
getchar
getcharmod
getcharpos
getcharsearch
getcharstr
getcmdline
getcmdpos
getcmdtype
getcmdwintype :grey_exclamation: (#10735)
getcompletion
getcurpos
getcursorcharpos
getcwd vim.loop.cwd *does not work for window/tab local :white_check_mark:
getenv vim.loop.os_getenv, os.getenv :white_check_mark:
getfontname
getfperm vim.loop.fs_stat(...).mode :white_check_mark:
getfsize vim.loop.fs_stat(...).size :white_check_mark:
getftime vim.loop.fs_stat(...).mtime :white_check_mark:
getftype vim.loop.fs_stat(...).type :white_check_mark:
getjumplist
getline vim.api.nvim_buf_get_lines :white_check_mark:
getloclist
getmarklist vim.api.nvim_get_mark :white_check_mark:
getmatches
getmousepos
getpid vim.loop.os_getpid :white_check_mark:
getpos('.') vim.api.nvim_win_get_cursor
getpos
getqflist
getreg
getreginfo
getregtype
gettabinfo
gettabvar vim.api.nvim_tab_get_var, vim.t[idx][name] :white_check_mark:
gettabwinvar vim.api.nvim_win_get_var, vim.w[idx][name] :white_check_mark:
gettagstack
getwininfo Partially with #15248 :grey_exclamation:
getwinpos
getwinposx
getwinposy
getwinvar vim.api.nvim_win_get_var, vim.w[idx][name] :white_check_mark:
glob2regpat
glob
globpath
has_key <table>[key] ~= nil :white_check_mark:
has
haslocaldir
hasmapto
histadd
histdel
histget
histnr
hlID vim.api.nvim_get_hl_id_by_name :white_check_mark:
hlexists vim.api.nvim_get_hl_* :white_check_mark:
hostname vim.loop.os_gethostname() :white_check_mark:
iconv vim.iconv :white_check_mark:
id NA (typval) :heavy_minus_sign:
indent
index
input vim.ui.input :white_check_mark:
inputlist vim.ui.select :white_check_mark:
inputrestore
inputsave
inputsecret
insert
interrupt No args or return so no typval conversion :heavy_minus_sign:
isdirectory vim.loop.fs_stat(...).type == 'directory' :white_check_mark:
isinf Easy to implement, see below :heavy_minus_sign:
islocked :heavy_minus_sign:
isnan Easy to implement, see below :heavy_minus_sign:
items pairs :white_check_mark:
jobpid vim.loop.process_get_pid :white_check_mark:
jobresize
jobstart vim.loop.spawn :white_check_mark:
jobstop vim.loop.shutdown and vim.loop.close :white_check_mark:
jobwait
join table.concat :white_check_mark:
json_decode vim.json.decode :white_check_mark:
json_encode vim.json.encode :white_check_mark:
keys vim.tbl_keys :white_check_mark:
len Lua # operator, #vim.tbl_keys() :white_check_mark:
libcall, libcallnr package.loadlib, ffi.load :white_check_mark:
line2byte
line
lispindent
list2str vim.inspect, table.concat :white_check_mark:
localtime vim.loop.gettimeofday :white_check_mark:
luaeval loadstring :white_check_mark:
map vim.tbl_map :white_check_mark:
maparg
mapcheck
match vim.regex with regex:match_str :white_check_mark:
matchadd
matchaddpos Extmarks :white_check_mark:
matcharg
matchdelete
matchend
matchfuzzy
matchfuzzypos
matchlist
matchstr
matchstrpos
menu_get
mkdir vim.loop.fs_mkdir :white_check_mark:
mode vim.api.nvim_get_mode :white_check_mark:
msgpackdump vim.mpack.encode :white_check_mark:
msgpackparse vim.mpack.decode :white_check_mark:
nextnonblank
nr2char string.char *only works with ASCII :white_check_mark:
pathshorten
perleval
prevnonblank
printf print(string.format(...)) :white_check_mark:
prompt_getprompt
prompt_setcallback
prompt_setinterrupt
prompt_setprompt
pum_getpos
pumvisible
py3eval
pyeval
pyxeval
range Various ways depending on context :white_check_mark:
readdir
readfile vim.loop.fs_read :white_check_mark:
reduce
reg_executing
reg_recorded
reg_recording
reltime[,float,str] vim.loop.hrtime, vim.loop.gettimeofday :white_check_mark:
remove os.remove :white_check_mark:
rename vim.loop.fs_rename, os.rename :white_check_mark:
repeat string.rep :white_check_mark:
resolve vim.loop.fs_readlink :white_check_mark:
reverse Easy to implement, see below :heavy_minus_sign:
rpcnotify vim.rpcnotify :white_check_mark:
rpcrequest vim.rpcrequest :white_check_mark:
rpcstart
rubyeval
screenattr
screenchar
screenchars
screencol
screenpos
screenrow
screenstring
search
searchcount
searchdecl
searchpair
searchpairpos
searchpos
serverlist Covered by vim.loop.*? :heavy_minus_sign:
serverstart Covered by vim.loop.*? :heavy_minus_sign:
serverstop Covered by vim.loop.*? :heavy_minus_sign:
setbufline vim.api.nvim_buf_set_lines :white_check_mark:
setbufvar vim.api.nvim_buf_set_var, vim.b[idx][name] :white_check_mark:
setcharpos
setcharsearch
setcmdpos
setcursorcharpos
setenv vim.loop.os_setenv :white_check_mark:
setfperm vim.loop.fs_chmod :white_check_mark:
setline vim.api.nvim_buf_set_lines :white_check_mark:
setloclist
setmatches
setpos
setqflist
setreg
settabvar vim.api.nvim_tabpage_set_var, vim.t[idx][name] :white_check_mark:
settabwinvar
settagstack
setwinvar vim.api.nvim_win_set_var, vim.w[idx][name] :white_check_mark:
sha256 Typval conversion is costly :grey_exclamation:
shellescape
shiftwidth
sign_define extmarks :heavy_minus_sign:
sign_getdefined extmarks :heavy_minus_sign:
sign_getplaced extmarks :heavy_minus_sign:
sign_jump extmarks :heavy_minus_sign:
sign_place extmarks :heavy_minus_sign:
sign_placelist extmarks :heavy_minus_sign:
sign_undefine extmarks :heavy_minus_sign:
sign_unplace extmarks :heavy_minus_sign:
sign_unplacelist extmarks :heavy_minus_sign:
simplify
sockconnect Should be covered in vim.loop.* :heavy_minus_sign:
sort table.sort :white_check_mark:
soundfold
spellbadword vim.spell.check :white_check_mark:
spellsuggest
split vim.split :white_check_mark:
stdioopen
stdpath
str2float tonumber :white_check_mark:
str2list vim.inspect, table.concat :white_check_mark:
str2nr tonumber :white_check_mark:
strcharpart string.sub :white_check_mark:
strchars string.len :white_check_mark:
strdisplaywidth
strftime date.format :white_check_mark:
strgetchar string.sub :white_check_mark:
stridx
string tostring :white_check_mark:
strlen string.len or Lua # operator :white_check_mark:
strpart
strptime
strridx string.find :white_check_mark:
strtrans
strwidth vim.api.nvim_strwidth :white_check_mark:
submatch
substitute vim.gsub (Lua patterns instead of regex) :white_check_mark:
swapinfo
swapname
synID
synIDattr
synIDtrans
synconcealed
synstack
system vim.loop.spawn :white_check_mark:
systemlist vim.loop.spawn :white_check_mark:
tabpagebuflist
tabpagenr vim.api.nvim_get_current_tabpage :white_check_mark:
tabpagewinnr
tagfiles
taglist
tempname vim.loop.fs_mktemp, os.tmpname :white_check_mark:
termopen
test_garbagecollect_now :heavy_minus_sign:
test_write_list_log :heavy_minus_sign:
timer_info vim.loop.timer_{get_repeat,get_due_in} :white_check_mark:
timer_pause
timer_start vim.loop.timer_start :white_check_mark:
timer_stop vim.loop.timer_stop :white_check_mark:
timer_stopall No args or return so no typval conversion :heavy_minus_sign:
tolower string.lower :white_check_mark:
toupper string.upper :white_check_mark:
tr
trim vim.trim :white_check_mark:
trunc
type NA (typval) :heavy_minus_sign:
undofile
undotree
uniq :grey_exclamation:
values vim.tbl_values :white_check_mark:
virtcol
visualmode
wait vim.wait :white_check_mark:
wildmenumode
win_execute vim.api.nvim_win_call :white_check_mark:
win_findbuf
win_getid
win_getid() vim.api.nvim_get_current_win :white_check_mark:
win_gettype
win_gotoid vim.api.nvim_set_current_win :white_check_mark:
win_id2tabwin
win_id2win
win_move_separator vim.api.nvim_win_set_width :heavy_minus_sign:
win_move_statusline
win_screenpos vim.api.nvim_win_get_position :white_check_mark:
win_splitmove
winbufnr vim.api.nvim_win_get_buf :white_check_mark:
wincol
windowsversion
winheight vim.api.nvim_win_get_height :white_check_mark:
winlayout
winline
winnr('$') ('#') ...
winnr() vim.api.nvim_win_get_number :white_check_mark:
winrestcmd
winrestview
winsaveview
winwidth vim.api.nvim_win_get_width :white_check_mark:
wordcount Typval conv is negligible :heavy_minus_sign:
writefile vim.loop.fs_write :white_check_mark:
Commands
Vimscript command Lua equivalent / Notes Equiv exists(:white_check_mark:)/wanted(:grey_exclamation:) /not required(:heavy_minus_sign:)
abbrev (and variants) :grey_exclamation:
augroup vim.api.nvim_create_augroup :white_check_mark:
autocmd vim.api.nvim_create_autocmd :white_check_mark:
colorscheme
highlight vim.api.nvim_set_hl :white_check_mark:
set (and variants) vim.api.nvim_set_option_value :white_check_mark:
sign Extmarks API :heavy_minus_sign:
map vim.keymap :white_check_mark:
command vim.api.nvim_create_user_command :white_check_mark:
Lua equivalent snippets

bufwinid()

NOTE: This works for all tabpages, unlike vim.fn.bufwinid() which only works for the current tabpage.

Expand
local function bufwinid(buf)
  for _, w in ipairs(vim.api.nvim_list_wins()) do
    if vim.api.nvim_win_get_buf(w) == buf then
      return w
    end
  end
  return -1
end

reverse()

Expand
function reverse(t)
  local n = #t
  local i = 1
  for i = 1, n do
    t[i],t[n] = t[n],t[i]

    n = n - 1
  end
end

isinf()

Expand
local function isinf(value)
  return value == math.huge and 1 or value == -math.huge and -1 or 0
end

isnan()

Expand
local function isnan(value)
  return value ~= value
end

Other Notes:

  • Does typconv conversion carry a heavy cost compared to the rest of the function execution? It may if the function can input large objects for it arguments or return values, or if the function is expected to be run in hot loops.
  • Vimscript doesn't have proper booleans, so any vimscript function which should return boolean woud probably benefit from a Lua equivalent.

lewis6991 avatar May 03 '22 15:05 lewis6991

qq on bit.*: is that available when running neovim with puc lua?

fsouza avatar May 03 '22 15:05 fsouza

qq on bit.*: is that available when running neovim with puc lua?

https://github.com/neovim/neovim/issues/11352

clason avatar May 03 '22 15:05 clason

Strictly speaking, nvim_win_get_cursor() cannot get curswant.

zeertzjq avatar May 03 '22 15:05 zeertzjq

Strictly speaking, nvim_win_get_cursor() cannot get curswant.

Fixed. Added pos('.') which maps to nvim_win_get_cursor

lewis6991 avatar May 03 '22 15:05 lewis6991

vim.loop.exepath is not equivalent to Vim script's exepath. The former returns the full path to the current executable (i.e. nvim) while the latter takes an argument and returns the path to that, if it exists.

Also vim.input should be vim.ui.input.

gpanders avatar May 03 '22 15:05 gpanders

vim.loop.exepath is not equivalent to Vim script's exepath. The former returns the full path to the current executable (i.e. nvim) while the latter takes an argument and returns the path to that, if it exists.

Also vim.input should be vim.ui.input.

Done. Feel free to edit directly (I think you have permissions).

lewis6991 avatar May 03 '22 15:05 lewis6991

Is the table in the OP missing cnoreabbrev that is available in vimscript?

I have the following in my init.lua wrapped inside a vim.cmd[[]].

" Replace :w with :up
cnoreabbrev <expr> w getcmdtype() == ":" && getcmdline() == 'w' ? 'up' : 'w'

cnoreabbrev <expr> h getcmdtype() == ":" && getcmdline() == 'h' ? 'tab help' : 'h'
cnoreabbrev <expr> help getcmdtype() == ":" && getcmdline() == 'help' ? 'tab help' : 'help'
cnoreabbrev <expr> helpgrep getcmdtype() == ":" && getcmdline() == 'helpgrep' ? 'tab helpgrep' : 'helpgrep'
cnoreabbrev <expr> Man getcmdtype() == ":" && getcmdline() == 'Man' ? 'tab Man' : 'Man'

krishnakumarg1984 avatar May 18 '22 10:05 krishnakumarg1984

cnoreabbrev is a command, not a function and isn't included in vim.fn.*.

However, your point is valid that there is no Lua analogue for these kinds of Vimscript commands.

lewis6991 avatar May 18 '22 10:05 lewis6991

string.byte() and string.char() only work with ASCII. nvim_open_term is completely different from termopen().

zeertzjq avatar Dec 26 '22 09:12 zeertzjq

I don't think stdpath and nvim_get_runtime_file are really equivalents. stdpath is used to get the root of one of the standard paths (cache, config, etc.) while nvim_get_runtime_file is used to search for files under any of the runtime paths. For example, it is impossible to get the path to cache with nvim_get_runtime_file.

musjj avatar Dec 30 '22 11:12 musjj

vim.loop.cwd() cannot be used as a direct replacement for vim.fn.getcwd(), as it does not support arguments as vim.fn.getcwd() does, specifically being able to retrieve the cwd local to a window or tab.

andrewferrier avatar Jan 16 '23 21:01 andrewferrier

bufadd is not completely equivalent to vim.api.nvim_create_buf , bufadd() to can be use to create a named buffers or get the bufnr of a named buffer (let g:foo_nr = bufadd('./foo.lua')) while nvim_create_buf can only create unnamed/scratch buffers, whats even worse is that :badd and bufadd() don't even have the same behavior, :badd will list the unloaded buffer while bufadd() wont

mike325 avatar Jan 18 '23 15:01 mike325

Please avoid comments about "incomplete equivalence of A to B". 100% fidelity is not the point of this tracking issue, we are trying to figure out API surface area. The details of one-to-one equivalence will be a task to address per-function, after we have a general idea of the major gaps.

justinmk avatar Jan 23 '23 11:01 justinmk

Do we have a Lua API to turn off syntax for a specific buffer?

vim.bo[bufnr].syntax = ''

Please do not clutter the issue tracker with user questions. There are other forums for this.

lewis6991 avatar Jan 31 '23 10:01 lewis6991

Please avoid comments about "incomplete equivalence of A to B". 100% fidelity is not the point of this tracking issue, we are trying to figure out API surface area. The details of one-to-one equivalence will be a task to address per-function, after we have a general idea of the major gaps.

FWIW, I don't mind people reporting differences between the reported mappings above. Even though 100% fidelity isn't a goal, these differences are useful to know about (or some of them are). Not to mention some of the equivalents I listed were completely wrong.

I've updated the OP and hidden all the comments.

lewis6991 avatar Jan 31 '23 10:01 lewis6991

@lewis6991 Do we have the Lua equivalent of :checktime {filename}?

nyngwang avatar Apr 08 '23 20:04 nyngwang

Do we have tabdo in Lua?

nyngwang avatar May 04 '23 18:05 nyngwang

Do we have tabdo in Lua?

There's nvim_buf_call() and nvim_win_call() but no nvim_tabpage_call(), you can still use a for loop with nvim_list_tabpages() to iterate over tabs and then use nvim_tabpage_list_wins() to perform any action on the local windows

mike325 avatar May 05 '23 07:05 mike325

There is not a complete feature parity with printf function. string.format does not support positional arguments, whereas printf does.

Also see this stackoverflow question.

alexmozaidze avatar Jan 24 '24 10:01 alexmozaidze