27

If I have a couple of lines of vimscript that I want to test before putting in my vimrc, is it possible to visually select, then source them?

I have tried

  • make visual selection v, then motions to make selection etc, then
  • :'<,'>so

But vim gives an error

no range allowed

also

:'<,'>w | so

gives

Use ! to write partial buffer

How can I source visually selected vimscript?

8 Answers 8

22

On the command line hit <C-u> then type @* and enter.

<C-u> will delete the visual range '<,'> that has been automatically inserted if some text is visually selected.

:@* will execute the content of the * register which contains whatever is visually selected at the moment.

Even if the text that is selected / highlighted is in your web browser (firefox, chrome, ...), it will work, the * register will allow you to access it directly in vim.


If your text is selected in a vim buffer, another solution is to copy it in a named register, for example the a register by typing "ay.

Then, on the command line, type : <C-r>a and enter.
Ctrl + r will give you a sort of prompt represented by the character " (for more information read :h c_Ctrl-r).
At this new prompt, if you give the name of a register, its content will be inserted on the command line, where you'll be able to edit it before execution.


Besides if you copy some text without specifying a register, it will be accessible from the unnamed register ".

So suppose you've got a line of vimscript in a file and want to source it quickly, you can copy it with Y, then source it on the command line with : :<C-r>" and enter.

Finally, if you use this last solution, there's just a problem you must be aware of.
If you see a strange character on the command line like this ^M, it's the caret notation of the carriage return. You should delete it, otherwise you'll probably get an error.

In summary, you :

  1. copy your line of vimscript with Y
  2. switch to command mode with :
  3. hit <C-r> to enter the registers prompt
  4. type " to call the unnamed register
  5. hit backspace to delete the ^M character
  6. and hit enter.
4
  • 1
    Sweet! :D works nicely - I had never come across the @* before, thanks! Commented Nov 13, 2015 at 11:12
  • I'm glad it helped you. I've edited the answer to propose another solution.
    – saginaw
    Commented Nov 13, 2015 at 11:20
  • 1
    nice one, I tried your suggestion about using : <C-r>a it actually worked fine too :) I guess vim read the ^M's as line returns - and executed each one as its own individual line / command? Commented Nov 13, 2015 at 11:54
  • That's a good question, honestly I don't know. Usually if I don't delete this character I've got errors. I'm glad it worked for you without errors. Maybe someone with more knowledge on the subject will explain to us how vim treats that kind of special characters.
    – saginaw
    Commented Nov 13, 2015 at 12:06
6

During development of vimscript code, having to source the whole file where it resides just to try out a snippet is sub-optimal. In my experience, there are always a bunch of variants of the code we're trying to make work that we don't want to throw out (yet), but we need to keep commenting it out (or in) so that the :so % doesn't trip up all over the place.

What is really wanted is not so much sourcing the file, but rather executing some snippet. Here is a function and a mapping that allow us to execute only the vimscript that is visually highlighted:

function! ExecHighlighted () range

        " Grab the highlighted text: save the contents of an arbitrary
        " register, yank the highlighted text to it, copy the register
        " contents to a local variable, and restore the register
        " contents.
    let l:saved_a = @a
    silent! normal! gv"ay
    let l:text = @a
    let @a = l:saved_a

        " Concatenate continuation lines, else for some reason it
        " fails to work.
    let l:text = substitute(l:text, '\n\s*\\\s*', ' ', 'g')

        " This previous version is bugged (just goes to
        " show I didn't use it much with code that has
        " continuation lines).
   " let l:text = substitute(l:text, '\n\s*\\\\', ' ', 'g')

        " Execute the grabbed text.
    exec l:text

endfunction

    " Have a Visual-mode-only mapping to invoke the function.
xnoremap <f9> :call ExecHighlighted()<cr>

So, visually select some text, press F9, and it will be executed.

The range argument ensures that the function is called only once even if the highlighted text has more than one line.

0
5

More directly to the question than my other answer, you could define a mapping to like this (nnoremap for no selection, just execute the current line; vnoremap for when visual mode is active):

nnoremap <F2> :execute getline(".")<CR>
vnoremap <F2> :<C-u>for line in getline("'<", "'>") \| execute line \| endfor<CR>

In both cases, the "current line" is :executed, so in the visual version you're :executeing one line after another through the visual selection.

As stated in another answer, the <C-u> deletes the range specification that pops up in the command line when : is typed. The | characters need to be escaped because otherwise they're interpreted as the end of the vnoremap command.

This could also be turned into a function to get rid of some of the escape gymnastics but it doesn't seem like a huge win to me at this time. It would basically be the vnoremap expanded into multiple lines and called with a range or not depending on the [nv] mapping mode.

1
  • Oh, the visual mode mapping executes entire lines so if you're block- or character-wise it'll ignore the start and end points within the lines. Commented Jun 6, 2016 at 18:09
3

I actually do it a bit differently, I just make the edits in my vimrc and re-source it every time I write it. (I have a cascading set of vimrc files so this allows me to change any one of them.)

if $MYVIMRC != ''
    autocmd BufWritePost    *vimrc      source $MYVIMRC
endif
2

Note: Please use the other solution I posted instead of this kind of broken one.

Original post:

I have this mapping in my .vimrc to do precisely what you ask:

:vmap <f9> y:exec substitute(@", '\n\s*\\', ' ', 'g')<cr>

So, visually select some text, press F9, and it will be vim-compiled.

It has one important shortcoming: it fails if the vimscript to compile contains comments. Still, I find it extremely useful and have been using it regularly for years.

Explanation:

So how does this work?:

:vmap <f9> y:exec substitute(@", '\n\s*\\', ' ', 'g')<cr>

In visual mode, pressing F9 will yank (y) the highlighted text, which will go into the unnamed register ("), and then execute (:exec … <cr>) the code resulting from the 'substitute' expression, which will operate on the contents of the unnamed register (@"), replacing all ('g') instances of text matching the regex \n\s*\\ with a single space (' '). The regex matches a newline followed by zero or more instances of whitespace, followed by a backslash.

Now, what is that replacement about? The idea was that I wanted to be able to highlight for execution something like these three lines for example:

echo "foo " .
 \\ "bar " .
 \\ "baz."

So I wanted the result of the substitution to be:

echo "foo " .  "bar " .  "baz."

The problem is, that regex is wrong: it should have been \n\s*\\\\, to match two consecutive backslashes.

Why did the mapping work? Well, it didn't really, but it appears that I never really used it with continuation lines, so it was fine. And now, I'm not sure why simply fixing the regex isn't sufficient instead of that ExecHighlighted() function in my other post. I think I had found another problem, which I wanted to document here (which is why I hadn't filled in the explanation yet), but now I forgot.

5
  • 1
    Welcome to Vi and Vim! You might want to expand on your post to explain what it does and also why you use the substitute() with the regex on it. You might also want to consider using :xnoremap, which is more specific in that it only triggers in Visual mode (not Select mode) and it doesn't recursively use any other mappings you might have (so doesn't get interference from those.)
    – filbranden
    Commented Mar 4, 2021 at 18:35
  • 1
    Thanks @guntbert for the improved formatting. I'll Learn more about formatting… and be more careful in the future.
    – lucs
    Commented Mar 5, 2021 at 13:05
  • 2
    Thanks @filbranden for the suggested improvements. Reminded of my solution's main shortcoming (which in practical terms didn't really bother me before), I worked on it again and found something that works, for which I will post a new answer. I will also edit this answer to deprecate it and to explain how it works and why it fails.
    – lucs
    Commented Mar 5, 2021 at 13:07
  • You forgot the explanation...
    – bew
    Commented Apr 16, 2021 at 10:43
  • @bew Right, thanks for the reminder.
    – lucs
    Commented Apr 19, 2021 at 3:40
1

say you have a line in a file like:

echo "hello world"

you would make a visual selection of that text then type:

:'<,'>w! /tmp/x.vim | so /tmp/x.vim

and it will run it.

you can also give yourself a mapping, for example in gx, so you would select some text and then type gx and it will run it. like this:

vmap gx :w! /tmp/x.vim<CR>:so /tmp/x.vim<CR>

enter the above in your .vimrc file.

1

I just have this in my .vimrc to source anything that I have yanked to the unnamed buffer:

" source any yanked block of text
nnoremap <silent> <LEADER>sy :@"<CR>
1

In Vim 9, you can source either all the lines in a buffer or some selected lines in a buffer by simply using the :source command without any arguments. The help topic source-range has more information about this.

Your Answer

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

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