Skip to content
This repository has been archived by the owner on Dec 19, 2022. It is now read-only.

Mention vim.api.nvim_call_function #6

Closed
clason opened this issue Aug 19, 2020 · 18 comments · Fixed by #49 or #56
Closed

Mention vim.api.nvim_call_function #6

clason opened this issue Aug 19, 2020 · 18 comments · Fixed by #49 or #56

Comments

@clason
Copy link

clason commented Aug 19, 2020

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?

@nanotee
Copy link
Owner

nanotee commented Aug 20, 2020

Yeah, it's not clear to me if there are any advantages to using it over vim.call() or vim.fn.x() most of the time. The weirdness around type conversion makes it less ergonomic to use.

One interesting tidbit from the documentation is vim.api.nvim_call_dict_function(), which AFAIK isn't supported by either vim.call() or vim.fn.x(). Fairly niche use case, but still good to mention.

@matu3ba
Copy link
Contributor

matu3ba commented Feb 7, 2021

:help vim.call

vim.call({func}, {...})					*vim.call()*
        Invokes |vim-function| or |user-function| {func} with arguments {...}.
        See also |vim.fn|.
        Equivalent to: 
            vim.fn[func]({...})

It is the same. Closing?

@matu3ba
Copy link
Contributor

matu3ba commented Feb 14, 2021

@nanotee If possible, one shall always use vim.cmd or vim.api (because they dont need to be parsed or something). However, you cant call user functions with that. Should I add this to the vim.fn description?

@nanotee
Copy link
Owner

nanotee commented Feb 14, 2021

From what I gathered this is really context dependent, and even then I'd be surprised if the difference was noticeable in most cases. I don't know if this is all that useful to mention in a "getting started" guide

@clason
Copy link
Author

clason commented Feb 14, 2021

I forgot that I originally opened this issue 🤦

The difference between vim.call and vim.fn (the latter is just syntactic sugar on top of the former for a more "Lua look") is already well explained in the guide; there's nothing that should be added or changed there. (In particular, "vim.fn does the exact same thing as vim.call" is correct and useful information; it's really just a matter of aesthetics.)

It might be useful to mention that if the function is part of the nvim API (i.e., available through vim.api.*), calling that directly rather than through vim.fn.* is in general preferable as this avoids a roundtrip. (I don't think it will be a noticeable performance difference, but the additional wrapping may make a difference for things like scheduling.)

vim.cmd is another kettle of fish entirely and a red herring in this context.

@matu3ba
Copy link
Contributor

matu3ba commented Feb 15, 2021

@clason

It might be useful to mention that if the function is part of the nvim API (i.e., available through vim.api.), calling that directly rather than through vim.fn. is in general preferable as this avoids a roundtrip. (I don't think it will be a noticeable performance difference, but the additional wrapping may make a difference for things like scheduling.)

This belongs to the advanced guide, as it is a performance optimisation in hot code paths. I am not sure either, if it makes a difference in jitted code imho.

vim.cmd is another kettle of fish entirely and a red herring in this context.

I dont understand. vim.api.xxx is most performant, as the other wrap and parse the types. The help page is quite unprecise on what is happening though. So this belongs to the advanced guide in #43 .

@clason
Copy link
Author

clason commented Feb 15, 2021

This belongs to the advanced guide, as it is a performance optimisation in hot code paths. I am not sure either, if it makes a difference in jitted code imho.

I disagree; it's important to know how the different methods relate (e.g., that vim.fn and vim.call are exactly the same but vim.api is different under the hood). And as I wrote, the point is not performance but actual behavior (i.e., some things work with one but not the other). You don't need to go into details and make a table or so, but it's something I think beginners should be aware of.

vim.cmd is another kettle of fish entirely and a red herring in this context.

I dont understand. vim.api.xxx is most performant, as the other wrap and parse the types. The help page is quite unprecise on what is happening though. So this belongs to the advanced guide in #43 .

The point is that vim.cmd allows executing arbitrary vimscript (as vimscript), while vim.api only allows calling neovim API functions from Lua code. Very different use cases -- there are things you can't do with vim.api (such as setting autocommands).

@matu3ba
Copy link
Contributor

matu3ba commented Feb 15, 2021

@clason Thats true. You can not use internal functions from vim.api. Do you have a text suggestion to add in calling-vimscript-functions ?

@clason
Copy link
Author

clason commented Feb 15, 2021

I'd suggest to add the following short notes to https://github.com/nanotee/nvim-lua-guide#vimfnfunction :

  1. Before the link to the help, add a sentence that vim.fn is functionally identical to vim.call but allows a more Lua-like syntax.
  2. In Tips, add a second bullet point that API functions from :h api are also available directly through vim.api; e.g., vim.fn.nvim_list_uis() and vim.api.nvim_list_uis() both work, and that in general the latter is preferable.

@matu3ba
Copy link
Contributor

matu3ba commented Feb 16, 2021

@clason After short discussion with TJ Devries (one core dev of neovim), we removed 1. in 16423fe as it might get deprecated.

The PR should address 2.

@clason
Copy link
Author

clason commented Feb 16, 2021

@clason After short discussion with TJ Devries (one core dev of neovim), we removed 1. in 16423fe as it might get deprecated.

No, that's not going to happen; vim.fn is implemented on top of vim.call -- if the latter goes away, so does the former. The point is that people should be discouraged from using vim.call in favor of vim.fn, and this is exactly something that should be in this guide.

Also, vim.api.X may in fact not be the optimal way if vim.X also exists. So the rule of thumb should be

vim.X >> vim.api.X >> vim.fn.X

(assuming these exist; i.e., you'd only use a later one if the previous one doesn't exist).

@gegoune
Copy link

gegoune commented Feb 16, 2021

@clason thats very concise and informative way to put it. It should be added to the guide I think.

@matu3ba
Copy link
Contributor

matu3ba commented Feb 20, 2021

@clason Good enough like this? I would prefer to explain internal functions into the advanced guide, ie for plugin creation.

@clason
Copy link
Author

clason commented Feb 20, 2021

Yes, that's good.

nanotee pushed a commit that referenced this issue Feb 22, 2021
also hint why `vim.call` exists to prevent usage

fixes #6
@nanotee nanotee reopened this Feb 22, 2021
@nanotee
Copy link
Owner

nanotee commented Feb 22, 2021

I have finally merged #49, apologies for the delay.

Looking at the backtrace for nvim_list_uis() with vim.fn vs vim.api in gdb, it does seem like the vim.fn version is doing some extra work, so that's good to know.

I believe the original issue was about nvim_call_function() vs vim.call/vim.fn specifically, which is a different matter entirely. I have yet to encounter a situation where I'd use nvim_call_function() over vim.fn, so I don't know if I should mention it here

@clason
Copy link
Author

clason commented Feb 22, 2021

from :h vim.fn (which just calls vim.call):

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.

So I don't know where you'd use that, either -- maybe if you want to do some sort of transformation of vimscript code? That wouldn't be anything for a beginner's guide, though 😆

It's been a hot minute since I opened the issue, so it's hard to recall what precisely was the point of it... I think my original motivation was that there were too many ways of calling vimscript from lua, and I wanted to have a quick overview over the differences (or lack thereof, as it turned out later) between them.

I guess "don't worry about vim.api.nvim_call_function, you always want to use the higher-level vim.fn instead (unless you know what you're doing -- but in that case you wouldn't be reading this guide!" would be an adequate answer.

As I wrote above, I think it's good to mention all possible ways, even if only to quickly discount some of them -- "don't worry about that" is very useful information ;)

@matu3ba
Copy link
Contributor

matu3ba commented Mar 10, 2021

I believe the original issue was about nvim_call_function() vs vim.call/vim.fn specifically, which is a different matter entirely. I have yet to encounter a situation where I'd use nvim_call_function() over vim.fn, so I don't know if I should mention it here

It would be great, if you could examplify "what extra work" you mean or close the issue.

@nanotee
Copy link
Owner

nanotee commented Apr 4, 2021

It would be great, if you could examplify "what extra work" you mean or close the issue.

In my tests this resulted in 2 extra function calls for vim.fn vs using vim.api directly when printing the backtrace, but now that using API functions via vim.fn is no longer allowed, this is kinda moot

I have addressed nvim_call_function in #56, hopefully it's sufficiently detailed. Thanks everyone for your input, I genuinely appreciate the help!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
4 participants