1

I’ve seen a couple of QAs lately where the OP’s main issue is a missing execute (they need string interpolation/function return values in commands).

I’ve voted to close one as a dupe of the other, but I was looking for a more canonical dupe and failed to find it. (I’m quite sure it exists...)

Does anybody know where it is?

Edit: My real issue was a lack of suitable resource to point users towards in these so-similar situations (thanks Martin for pointing that out). With that in mind, the real question is:

Is the example self-answer a strong resource to have? Can you provide feedback on it?

And I suspect that the proper answer is just to post it.

  • 2
    Maybe if we can't find it, the answer would be to make a proper self-answered question that we encourage to upvote ? – perelo Jul 23 at 21:22
  • It is a pretty old thing (sorry shouldn't be a self advertisement, but thats the one i remember). I think "Use a variable or return value in string or mapping" would be a good title for a proper self-answered question since every keyword is in it – Doktor OSwaldo Jul 24 at 13:42
  • @DoktorOSwaldo thats a good historical find. I may put together a self-answer for feedback here. – D. Ben Knoble Jul 24 at 13:44
  • @perelo care to review? – D. Ben Knoble Jul 26 at 0:49
  • @DoktorOSwaldo care to review? – D. Ben Knoble Jul 26 at 0:49
3

This is a suggestion for a canonical resource. Please review.

Question: how do I use a variable or return value in option, command, or mapping?

I have a variable foo with let foo='value', and a function F like

function F()
  return 'value'
endfunction

and I want to use them

  • in an option: :set option=foo, :set option=F(); or
  • in a command: :edit foo, :edit F(); or
  • in a mapping: :nnoremap f :foo<CR>, :nnoremap f :F()<CR>

How can I do this?

, , , ,

Answer

The reason your example attempts don't work is that in many locations text is simply seen as a literal string, rather than VimScript. So functions, variables, and the like don't work.

For example, if you do:

:let var='value'
:set option=var

Then Vim will simply set the value of option to the literal value var, since it doesn't recognize VimScript in :set. The same applies to :map and many other locations.

In general, most colon/Ex/: commands do not evaluate VimScript.

:execute

Not all is lost, since you can still build a string with :execute:

:let var='value'
:execute ':set option=' . var

The :execute command executes the given VimScript expression. In this case, we build a string using the variable, and what :execute sees is :set option=value.


You can use any Vim expression; some more examples:

" Function return value
:execute 'set option=' . Fun()

" You don't need to use string concatenation; all arguments are 
" concatenated automatically. A space is added.
:execute 'edit' var
:execute 'edit' Fun()

map expands special key sequences like <CR>. To get them in an execute, you need to prepend the < with a \: "\<CR>". You need to use double quotes, single quotes won't work:

:let key = 'h'
:execute 'nnoremap ' . key . ":echo 'hello'\<CR>"

Because you're building VimScript code in a VimScript string things tend to look rather ugly. C'est la vie. Depending on your personal tastes, printf() can make things a bit easier on the eyes:

:execute printf("nnoremap %s :echo 'hello'\<CR>", key)

Note: it's very common to abbreviate :execute to :exe.

See :help :execute for more information.

Expression mappings

Finally, you can use expression mappings to use a Vim expression for the entire right-hand side.

" Execute the contents of the current line as a Vim command.
:nnoremap <expr> f getline('.')

The biggest difference with :execute is that the expression is run when the key is pressed, and not when the command is defined. So every time you press f in the above example getline() gets the value of the current line. If we had used:

:execute ':nnoremap f' . getline('.')

Then it would get the value of the line once. So if you had been on the line gg it would be the same as typing:

:nnoremap f gg

See :help :map-<expr> for more information.

  • 2
    Small suggested edit: In order to do so, you must build a string containing the value and evaluate it with execute. Or directly evaluate the value with execute. It is a nice question/answer which should answer all these questions. Thank you. – Doktor OSwaldo Jul 26 at 7:05
  • 1
    Nice. First read of the beginning of the Q was unclear to me, maybe "I have a variable let foo='variable', and a function f ... and I want to use them". Add : in "set and :map". And maybe add "Note also that in contrast, variants of :map automatically expands key-notations such as <CR>" – perelo Jul 26 at 14:08
  • I made a large edit; it's intended as a proposal, so feel free to roll back/edit further/etc. I tried to provide some more realistic examples people can actually run and play around with. I also tried to introduce things a bit more gently, starting with one example and building on that. Overall, this should hopefully be a bit easier to understand, especially for people who aren't professional programmers. Still needs a bit more work, I think. The entire concept can be tricky grok as you're basically running code in code. Also, the question could probably be a bit less abstract. – Martin Tournoij Jul 29 at 19:21
  • @MartinTournoij thanks for the help, i do think that improves the flow some (though i dont see many non-programmers in vim :P ) – D. Ben Knoble Jul 29 at 21:25
  • @MartinTournoij Pretty clear indeed. Minor edits : missing spaces on the :execute 'nnoremap ... lines : ... . keys . "_:echo 'hello'... and ...nnoremap f_' . getline('.') since it is string-concatenation. – perelo Jul 30 at 21:43
  • @perelo actually there are some instances where I may remove the spaces and the dots: execute automatically inserts spaces between arguments. – D. Ben Knoble Jul 30 at 21:45
  • Maybe it's better to just leave that off. I didn't even know you could do that before reading this answer, and it just adds extra/potentially confusing information. – Martin Tournoij Aug 1 at 21:57
1

The general approach I prefer is to write a detailed explanation to one question, and then answer other questions with a brief answer to the stated question, and linking the more detailed/general explanation.

I'm not a huge fan of marking questions as duplicates of "canonical" questions. People come here because they don't know how to solve a problem, and while a detailed explanation of how :execute works may be helpful for some people, it may also not be clear to everyone how to apply the information from the detailed explanation to their problem.

Pointing people to another question is essentially just saying "RTFM". I think something along the lines of "here's the source of your mistake/confusion, here's how to fix it, RTFM if you need more information" is much more helpful and constructive. Not everyone is confused about the same aspects, and even if they are, it may not be obvious to the OP.

I've seen many questions being closed as duplicates of canonicals on Stack Overflow with the OP expressing their confusion (and sometimes anger) on how it solved their problem; they simply lacked the knowledge and/or experience to see the connections. I've seen it on this site too; one of my mistakes in the earlier days of this site was to close a bit too many questions as dupes of "canonicals".

I see "Canonical questions" as a Stack Overflow-ism, and is one (of several) reasons I don't like Stack Overflow, and why many perceive the site as so hostile. While we use the same software and network, we don't need to copy Stack Overflow in everything. I think not copying Stack Overflow is one the reasons in having our own Vi site, which I consider to be much more constructive, friendlier, and useful than Stack Overflow.

Additionally, sometimes a question may have more than one possible answer, a duplicate is supposed to be an exact duplicate; that is, every possible answer to question A should also be applicable to question B, and vice versa. In practice, it turns out this is rarer than you'd might think.


Personally, I think closing How to pass function return value to make command in VIM was a mistake; while I can see how it's essentially the same answer ("use :execute"), I had to carefully read everything to see how the answer on the dupe target (which is just a terse "Do the :execute inside your mapping instead"* with an example) applies to the OP's question. Perhaps even worse, most of the answer is not applicable to the question at all, which is rather confusing.

Aside from whether the question should have been closed as a duplicate of some generic "use :execute" answer in the first place, I think this is a very poor dupe target.

  • Thanks for the feedback Martin. Perhaps canonical dupe is a poor choice of words... i was looking at things like « How do I debug my vimrc » and thinking this would be a great resource to have. I can see how overclosing could lead to some discontent; indeed, the whole idea here was that I wasn’t satisfied by the dupe target for the question you mention, but I was sure there was a better one. I likely acted in haste for the linked question and am determined not to do so here (see our keymap discussion 🙃). All of this said, I will still post the self-answer as a resource to have... – D. Ben Knoble Jul 26 at 19:53
  • ...And try to point users toward it without over-closing. The nice thing about duplicates is increased visibility – D. Ben Knoble Jul 26 at 19:54
  • Oh yeah, it would of course be helpful to have a good resource on it @D.BenKnoble, and certainly some questions could/should be closed as a duplicate; just not all. My "answer" here is more of an extended comment. – Martin Tournoij Jul 27 at 18:04
  • While I agree with your statement, I think it is crucial to have these questions linked. Different answers on different instances of the same questions may all be valuable. Right now marking these questions as duplicate is the way to do that. But in the case you are describing it is valuable to answer "duplicate" questions. – Doktor OSwaldo Jul 29 at 9:45

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .