neovim / pynvim

Python client and plugin host for Nvim
http://pynvim.readthedocs.io/en/latest/
Apache License 2.0
1.54k stars 120 forks source link

luaeval behavior: functions are serialized to `None` #550

Open wookayin opened 12 months ago

wookayin commented 12 months ago

:python3 vim.funcs.luaeval(...) calls vim.call("luaeval", ...), which then calls nvim_call_function('luaeval', [...]) via RPC, i.e. vim.request('nvim_call_function', 'luaeval', [...]).

Below tested with nvim 0.10.x nightly (as of 11/28/2023) and pynvim 0.5.0 nightly (and 0.4.3).

Primitives are OK:

:python3= vim.funcs.luaeval('1')
:echo nvim_call_function('luaeval', ['1'])
1

:python3= vim.funcs.luaeval('"foo"')
:echo nvim_call_function('luaeval', ['"foo"'])
foo

:python3= vim.funcs.luaeval('nil')
None
:echo nvim_call_function('luaeval', ['nil'])
v:null

:python3= vim.funcs.luaeval('{}')
:echo nvim_call_function('luaeval', ['{}'])
[]

:python3= vim.funcs.luaeval('{ dict = true }')
{'dict': True}
:echo nvim_call_function('luaeval', ['{ dict = true }'])
{'dict': v:true}

Functions are not:

:echo nvim_call_function('luaeval', ['function() end'])
<lambda>36075

" ❌ THIS IS BUGGY !!!!!!!!!
:python3= vim.funcs.luaeval('function() end')
None

" the proxy function is callable!
:call nvim_call_function('luaeval', ['print'])("foo")
foo

:lua vim.api.nvim_call_function('luaeval', { "print" })("foo")
foo

and hence modules:

" ❌ THIS IS BUGGY !!!!!!!!!
:python3= vim.funcs.luaeval('require("vim.ui")')
{'select': None, 'open': None, 'input': None}

:echo nvim_call_function('luaeval', ['vim.ui'])
{'select': function('<lambda>254'), 'open': function('<lambda>255'), 'input': function('<lambda>256')}

which should instead throw an error if conversion is not possible. Ideally it may return some "proxy" callable that delegates to the target lua function via RPC.

Also, the following seems to be neovim core's bug (or a lack of feature):

" ❌ should return a lambda function?
:echo nvim_call_function('eval', ['{ -> 3 }'])
v:null

" ❌ should return a function?
:python3= vim.call('eval', '{ -> 3 }')
None

:echo eval('{ -> 3 }')
function('<lambda>49485')
wookayin commented 12 months ago

Also for vim.exec_lua python API:

vim.lua.require("vim.F")

throws pynvim.api.common.NvimError: Cannot convert given lua type. It calls

vim.exec_lua("return require(...)", "vim.F")

which then calls via RPC:

rpcrequest('nvim_execute_lua', "return require(...)", ["vim.F"])

This RPC request cannot convert a lua function and raises errors as expected.

Note -- this nvim_execute_lua RPC API has a different execution mechanism than luaeval() which uses nvim_call_function RPC API, but very related.

justinmk commented 12 months ago

Returning functions ("funcrefs") over RPC is not supported. https://github.com/neovim/neovim/issues/1898

wookayin commented 12 months ago

Exactly. So on the pynvim side explicit errors should be raised as in nvim_exec_lua case, but I guess that some fixes would need to be done on the neovim core side rather than pynvim, because the crux is that RPC requests that would evaluate to "funcrefs", e.g.,

# python (using pynvim APIs)

vim.request('nvim_call_function', 'luaeval', [ "function() end" ])
vim.request('nvim_call_function', 'eval', [ "function('has')" ])

vim.funcs.eval("function('has')")

returns null (None) instead of errors?