2

I know I can evaluate a (visually) selected piece of vimscript by yanking it into a register and then executing the register with :@registerName.

Can this be done without clobbering a register e.g., by somehow executing the contents of a variable?

I've tried:

function! VimEval()
    let l:curQ = @" 
    yank
    let l:toEval = @"   
    let @" = l:curQ
    echo "TO EVAL:" . l:toEval
    echo "CURRENT:" . l:curQ
    l:toEval "<= THIS DOESN'T WORK
endfunction

But the last line of the function doesn't work like I expected it to.

Also, @" doesn't seem to work in the context of a function either:

function! SimpleVimEval()
    yank
    @"  "also doesn't work as expected
endfunction

I've tested the above with the following piece of vimscript in visual selection:

let inf = getbufinfo()
for key in inf
    echo key
endfor

Normal mode yank+":@" works, but neither '<,'>call SimpleVimEval() nor '<,'>call VimEval() does.

Can they be made to work?

  • Does this answer your question? How to use a variable in the expression of a 'normal' command? – Luc Hermitte Aug 9 at 11:36
  • @LucHermitte Thanks, but not really. It should run in exec mode but e.g., in the SimpleVimEval version, I'm getting undefine inf variable errors which I don't get with a plain y:@" in normal mode. – PSkocik Aug 9 at 11:47
  • why do you need a function instead of merely mapping y:@"? – Mass Aug 9 at 13:30
  • @Mass I'd like to do it without clobbering the register. I already have a function that does selection-eval via a temp buffer, but it seems there should be a simpler way. I'm also curious as to why not even my SimpleVimEval function works the way I'd expect it. – PSkocik Aug 9 at 14:45
  • What about @=somevimexpression then? – Luc Hermitte Aug 9 at 14:52
1

Your SimpleVimEval() function isn't working because when you call a function with a range Vim will just call it repeatedly for each line in the range.

In the case of the specific expression you're trying to evaluate, a for line on its own doesn't make sense, neither does an endfor, and the line inside the for will fail since the key variable won't have been set.

You can fix that by defining the function using the range modifier, in which case Vim will call it only once when called with a range. It will then pass the function a pair of extra arguments, a:firstline and a:lastline, to let the function handle the range by itself.

In practice, that means you need to take those into account when executing the yank command. In this case, you need to use execute to assemble a command as a string, using the values of those two arguments. (See :help :execute for more details if you're not familiar with it.)

This function definition will work for evaluating that for block when called with a range:

function! SimpleVimEval() range
  execute a:firstline "," a:lastline "yank"
  @"
endfunction

It turns out that the answer to your original question of how to execute a set of Ex commands without using registers is also :execute!

For example:

function! ArgVimEval(expr)
  execute a:expr
endfunction

And then you can call it with:

:call ArgVimEval("let inf = getbufinfo()\nfor key in inf\n    echo key\nendfor\n")

But it's much easier to call it with :call ArgVimEval(@") after yanking the visual selection if it's text from your buffer.


If you're trying to create a mapping to execute the current Visual selection, then I'd just use @" to execute it, but saving and restoring the original contents of the register around the operation.

Also, instead of a range function, I'd use the Normal mode gv command to select the last visual block, that way the command will still work with a blockwise or characterwise selection skipping some characters in some of the lines.

This implements what I just described:

function! ExecuteVisual()
  let save_reg = @"
  let save_regtype = getregtype('')
  try
    normal! gvy
    @"
  finally
    call setreg('', save_reg, save_regtype)
  endtry
endfunction
xnoremap <Leader>x :<C-U>call ExecuteVisual()<CR>
| improve this answer | |
  • 1
    Excellent answer, thanks! – PSkocik Aug 9 at 15:39
  • Before you posted the implementation, I did function! VimEval() range let l:curQ = @" execute a:firstline "," a:lastline "yank" let l:toEval = @" let @" = l:curQ execute l:toEval endfunction (I really don't care about applying it to sub-line selections, & I like it also works on the current line if it's unselected). Should I also be using setreg()? – PSkocik Aug 9 at 16:15
  • 1
    @PSkocik You need setreg for save and restore to preserve a copy of a visual block, that one behaves differently and needs to be restored with the proper options to be preserved correctly... – filbranden Aug 9 at 16:19

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.