0

While scripting I could achieve the same result both through Ex commands and vimscript functions, for example:

var filename = "foo.txt"

exe "new" filename              # Ex command 
execute("new " .. filename)     # Vimscript

setlocal nobuflisted            # Ex command 
setbufvar('', "&buflisted", 0)  # Vimscript

...

which form is recommended to use?

7
  • 2
    This is going to be a difficult fit for Vi and Vim: we prefer questions whose answers can be judged objectively and are not (solely) based on opinion. See vi.stackexchange.com/help/dont-ask. I would like to find a way to make this question answerable, but I don’t see it offhand. Commented Jun 2, 2024 at 11:59
  • 2
    FWIW the major factor for me is “Ex until I can’t do it that way”; some APIs are function-only, including the ones that operate on arbitrary windows/buffers. This is just one opinion, though. Commented Jun 2, 2024 at 12:00
  • 1
    I took the freedom to slightly adapt your code to make the two alternative more equivalent or more compact. Feel free to revert my changes :-) Commented Jun 2, 2024 at 12:07
  • 3
    @D.BenKnoble that is the whole point that I was wondering while writing: how do I know a-priori if “it is just a matter of taste” or if there are any objective benefits in using one form over the other? In the Vim world you cannot really take anything for granted :) However, if the answer is objectively ”a matter of taste”, then let’s close the question. :) Commented Jun 2, 2024 at 12:11
  • This question is being discussed on meta Commented Jun 3, 2024 at 15:00

3 Answers 3

1

This is very much a matter of taste.

Very often the command syntax is more compact which makes that I prefer to use it.

The API is more rich and play well with variable which are good reasons to use that alternative.

2
  • 1
    I have the feeling that there are things in command that cannot be easily done with vimscript. For example, how to do e.g. :bw! 3 or normal! $xin vim script? Commented Jun 3, 2024 at 19:12
  • commands are also Vim script Commented Jul 11, 2024 at 14:18
3

As with any topic of sufficient complexity, the answer depends a lot on the context. Unfortunately, "when scripting" is a highly ambiguous statement which led to a lengthy discussion on meta. I'll try to dodge that controversial item by avoiding the term and providing my own context.

Let's first get the most obvious point in the question Ex command vs. function out of the way: For some Ex commands, there is no corresponding function. The :new in the question is a good example. In the absence of a function, use an Ex command. This works both ways as there are functions that don't mirror an Ex command.

Next, let's have a look on the inherent properties of Ex commands and functions:

property Ex commands functions
target the running Vim instance (:set, :edit) or the current buffer (:substitute, :global) the Vim instance or their arguments. Can change the buffer as a side effect
calling easy, you just need to press : and start typing Are relatively awkward to call. In legacy Vimscript, you need to go through one of the commands :call, :let, :if, etc. In Vim9 script, :call became optional, reducing the awkwardness somewhat.
arguments can have arguments OR operate on a range take arguments, which can include lines for a range
return value don't return anything, their output can be :redirected, however can return a value
user-definition commands can be user-defined, the replacement commands have to be chained with |s, decreasing readability Functions can be user-defined, one statement per line
autoload no such thing functions can be autoloaded
history very old, inherited from ex a relatively new feature, support has been and is still growing

To change the current buffer, an Ex command is usually beneficial because you can directly feed it a range whereas a function would first need another function to pick up a line from the buffer (e.g. getline('.')). Furthermore, Ex commands are the bread and butter of Vim. You literally can't quit the editor without the :q command. An experienced Vim user will know the most commonly used Ex commands.

To have a value returned to do follow-up calculations, a function is the obvious choice. In everyday editing, you can never touch a function and still get the most out of Vim.

When it comes to user-defined commands, the bar chaining results in poorer readability compared to user-defined functions.

A pattern we see a lot in the user interface of plugins are user-defined commands that call a user-defined (autoloaded) function. This gives the best of both worlds: an accessible command that leverages the power of functions under the hood.

Let's discuss the two examples in the question:

  • :execute vs. execute(): As we've seen, we cannot (easily) replace the :new command so let's stick with it. IMHO, both approaches are flawed. Romainl once demonstrated (insert link here) that :new expands environment variables, so I'd use the following well-known Ex commands:
    :let $filename = "foo.txt"
    :new $filename
    
  • How to avoid :execute: In general, "Ex commands" (not counting those :let, :call, :echo and other "script-like" commands) do not support expression evaluation. This is why we need to do :execute so often. Howevere, there is an exception - filename argument (the VimL parser knows it by special internal flag). So in place of it one can use not only cmdline-special (say, :e #42) but also environment variables (:lcd $HOME/.vim) and backtick-equal" expressions. For example,
" create new buffer in HOME directory
let filename = "foo.txt"
new `=$HOME .. "/" .. filename`
" save session
mksession! `=v:this_session`

To emphasize, this works for things like argadd, new, edit, cd etc. But not for :argdelete or :bwipeout. (If you're lost then open help; whenever it says {file} or {name} it works).

Also, for user commands the only special items are those in angle brackets. However, anything else may still get expanded on the next stage. So the following works:

command! -nargs=* Git !git -C %:p:h:S <args>

Here <args> gets expanded by Git command. And then by :!. Therefore, :Git diff HEAD~1 %:t becomes :!git -C '/path/to/foobar' diff HEAD~1 foobar.txt.

  • :setlocal vs. setbufvar(): I completely agree with cjs' answer. The :set family of commands is one of the first that new Vim users learn. Not using it borders on code obfuscation.

Let's conclude this question. When writing Vimscript on your own machine, for your own use, it does not matter. Take whatever gets the job done, and quickly.
(Most of the time, that will be an Ex command.)

This advice mostly holds true when collaborating with other people, with some additional considerations.

When contributing to an existing project, it's best to adapt to the coding style already used there. This is general advice for any software project.

When working on a plugin you want to release to other people — it doesn't matter whether this is one tiny .vim file or a huge project — it's usually best to use functions internally (for their scalability and controllable side effects). Make these functions autoloaded to minimize overhead. As a user interface, either provide user-defined commands or let the user define their own mappings.

4
  • 1
    :new expands environment variables Actually, any builtin command expands all cmdline-specials in place of file name arguments. This is also documented at :h cmdline-special. Though, maybe it's not so clear from the docs that $ is treated as cmdline-special too. Commented Jul 11, 2024 at 8:24
  • @Matt thank you for your comment. I couldn't find anything related in :help cmdline-special, the best I could find was :help expand-env and :help filename-backslash. I was only talking about :new because that's what's in the example. I'm a bit at loss on how to generalize it. Feel free to edit with a better explanation. Commented Jul 11, 2024 at 9:08
  • 1
    honestly, using execute() for :new is a misuse of the API. execute() is rather meant to be used as a redir replacement, not for executing random ex commands. Secondly, the whole question is a bit strange. Ex commands also count as vim script, since they are part of the language. So use them, unless you have a good reason to avoid them (usually, avoid side effects) Commented Jul 11, 2024 at 9:24
  • Well, not sure if succeeded but tried at least. Commented Jul 11, 2024 at 10:26
1

For what's given in the question, obviously the ex commands are the ones you should be using. setlocal nobuflisted is far more clear than setbufvar('', "&buflisted", 0); because it makes it obvious at a glance that it's a local setting, as well as being more succinct.

I do not find any context in the question for which I could point out disadvantages of setlocal nobuflisted. "When scripting" is quite vague.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.