0

In :help let-heredoc it explains that one can set a variable to a 'heredoc' where each line of the heredoc becomes an entry in a list as follows:

let text =<< trim END
   if ok
     echo 'done'
   endif
END
:echo text

The above example produces: ['if ok', ' echo ''done''', 'endif']

That's fine, but I would like to embed this list into an entry in a dictionary so that the dictionary variable contains multiple heredocs as values each with a key, as follows:

{'code1': ['if ok', '  echo ''done''', 'endif']}
{'code2': ['if something', '  echo something else', 'endif']}

How can I do this in Vim? Also, Neovim doesn't appear to have heredocs as nothing comes up in the help files. How can I achieve the same effect in Neovim to make this cross-editor compatible? (In Neovim, the first block of code evaluates to an E15: Invalid expression error.)


EDIT:

Just for some clarificaton, I'm imagining there must be some way to add lines to a dictionary like the following:

let testdictionary = {'code1': '    some code!', 'code2': 'some code again'}
echo testdictionary

to something along those lines:

let testdictionary = {'code1': << END
    some code!
END
\ , 'code2': 'some code again'}
echo testdictionary

...and for both to produce a dictionary that looks like: {'code1': ' some code!', 'code2': some code again''}

And that if there are more than one line in the heredoc, that it gets turned into a list: {'code1': [' some code!', 'line2', 'line3'], 'code2': some code again''}

Any clean solution would be helpful.

  • 1
    In Neovim, the first block of code evaluates to an E15: Invalid expression error. Update your Neovim binary. You need this patch: github.com/neovim/neovim/commit/… – user938271 May 13 at 2:07
  • As far as I know I have the latest copy of Neovim. For Neovim, what should I do to check which build# I have installed? That info isn't displayed on :version or with :checkhealth or anything else I have tried. – Incalculon May 13 at 2:44
2

Simply use :let to set each entry of the dictionary at a time.

let text = {}
let text['code1'] =<< trim END
   if ok
     echo 'done'
   endif
END
let text['code2'] =<< trim END
   if something
     echo something else
   endif
END
echo text

This produces the following output:

{'code1': ['if ok', '  echo ''done''', 'endif'],
 'code2': ['if something', '  echo something else', 'endif']}

The syntax is very specifically limited to a =<< special operator, you can't use << in more general contexts as you often can do in a shell... There are a few more commands that take heredocs in Vim/NeoVim, mainly the commands to insert blocks of extension languages (e.g. :python3 <<END or :lua <<END, etc.)

Regarding NeoVim, at least the online documentation for it suggests it's available, see :help :let-heredoc, but as of this writing only nightly builds of NeoVim have this feature, it will only be available on a stable version starting with NeoVim v0.5.0.

Regarding portability, the feature is very recent for both NeoVim and Vim. It was only introduced on Vim 8.1.1354 (May 2019) and was ported to NeoVim on this commit (September 2019).

So if you care about portability to even slightly older versions of Vim/NeoVim, you should probably avoid using this feature in plug-ins for now.

You can still use it to generate the equivalent List of strings, by defining a local variable with the heredoc notation and then producing the equivalent List block with :put =execute('echo text'), possibly followed by a formatting command to break it into a more readable multi-line statement.

| improve this answer | |
  • 1
    The :let-heredoc syntax was only introduced in Vim 8.1.1354 and NeoVim after that, so it's quite possible you need a very recent (nightly build?) of NeoVim for that to work... – filbranden May 13 at 2:25
  • 1
    "You can still use it to generate the ...multi-line statement." I went ahead and marked your answer as the best answer, thanks so much for your answer. I was wondering, for the last part of your answer, do you mind giving an example of this / illustrating how it would be done? – Incalculon May 13 at 3:04
  • 1
    @Incalculon Just try the :put =execute('echo text') I mentioned! It will generate the text I pasted under "output", only in a single long line, I broke that line manually... You could use that to create a :let command that will work with any version of Vim/NeoVim. – filbranden May 13 at 3:07
  • 1
    I believe I tried it: Did you mean that I could still define a local variable with the heredoc notation on a version of Vim/Neovim prior to the patch supporting Heredocs? I'm doing it in my version of Neovim but I suppose I don't quite understand how to use it, because it just outputs '{ }' after all the same errors. – Incalculon May 13 at 3:17
  • 1
    @Incalculon No, rather that you can define a local variable with heredoc on a version of Vim/NeoVim with the patch, and then use that execute() command to produce an equivalent List syntax that will work on a version of Vim/NeoVim prior to the patch! So the heredoc would help you write it at first, but then you convert it to syntax compatible with older versions... – filbranden May 13 at 3:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.