6

Suppose I’ve selected some text in visual mode. How do I insert a certain piece of text at the beginning of the selection and a different piece of text at the end?

(I’m only concerned with “normal” visual mode, not visual line mode or visual block mode.)

3 Answers 3

9

Use c and <c-r> to replace the text and "surround" it. From visual mode:

c(<c-r>")

This will surround the text with ( and ).

Alternative: If you find yourself doing many "surrounding" like operations you may want to look into the surround.vim or vim-sandwich plugins.

For more help see:

:h v_c
:h i_CTRL-R
1
  • Typing the first command c(<c-r>") is so tedious that I usually add this to a macro: switch to the visual mode, select some words, start recording, type that command, go back to the normal mode, and stop recording. From the next time, you can just select words in the visual mode and invoke the macro.
    – Culip
    Commented Nov 12, 2022 at 12:56
2

This is hopefully a more comprehensive solution to the problem:

vnoremap <special> <silent> <Leader>(
\ @=<SID>StoreMode()<CR>:<C-U>call <SID>SurroundVisual('(', ')')<CR>
function s:StoreMode()
  let s:mode = mode()
  return ''
endfunction
function s:SurroundVisual(before, after)
  const l:block = s:mode ==# "\<C-V>" || s:mode ==# "\<C-S>"
  const l:view = winsaveview()
  try
    if !l:block
      keeppatterns lockmarks '<,'> substitute
      \ /\m\%V\_.*\%V\_./\=a:before .. submatch(0) .. a:after/e
    else
      silent keeppatterns '<,'> substitute
      \ /\m\%V.*\%V./\=a:before .. submatch(0) .. a:after/e
    endif
  finally
    call winrestview(l:view)
  endtry
endfunction

Explanation:

  • :vnoremap defines a mapping to wrap the selected text in parentheses.
  • <special> overrides any < flag in 'cpoptions'.
  • s:StoreMode() captures the current visual or selection mode. It must be run with @= because the mode always returns to normal after :.
  • The value the expression after @= evaluates to is treated as keys to be entered in visual or selection mode. s:StoreMode() returns an empty string to indicate nothing should be done. It could return an Ex-command (e.g., :<C-U>call ...), but that command would not be hidden by <silent> and would stay in command-line history.
  • s:SurroundVisual() does the actual work.
  • l:block is true if visual or selection is blockwise, in which case the selected part of each line is individually surrounded by parentheses.
  • winsaveview() and winrestview() return the cursor to its original position after substitution. This reduces user surprise and is merely cosmetic.
  • :keeppatterns, :lockmarks, and the search patterns are explained below.
  • \m makes sure special characters are interpreted correctly in the pattern.
  • \= allows arbitrary expressions to be used as replacements, which is required here because variables appear in the replacements.
  • submatch(0) is the same as & in regular replacements.
  • s///e ignores search failures when 'selection' is exclusive and an empty selection is made. Nothing is done because there is no easy way to detect an empty selection.
  • :silent removes the message when the selection spans three or more lines.

Original answer

The caveat in @bdesham's answer is relatively straightforward to fix. Here is the full command, which works for all three visual modes:

:lockmarks keeppatterns '<,'>s/\%V\_.*\%V\_./[[&]]/

Explanation:

  • :lockmarks ensures '< and '> stay put during the substitution.
  • :keeppatterns prevents the pattern from being added to the search history, which can become annoying fast when 'hlsearch' is used.
  • \_. matches any single character or end-of-line, effectively turning the pattern multiline.
0

Suppose you want to wrap the selected text with [[ and ]]. Make your selection and then run this command:

:'<,'>s/\%V.*\%V./[[&]]/

Note that if you type : in visual mode then the '<,'> is inserted for you, so you only need to type :s to get the :'<,'>s at the beginning of the command.

What’s going on here?

\%V is a special matching atom. For whatever reason, the docs say, “To make sure the whole pattern is inside the Visual area put it at the start and just before the end of the pattern” (emphasis added). That’s why there’s an extra . in the pattern. Here’s what each atom in the pattern means:

  • \%V: match the beginning of the visual selection.
  • .*: match everything from the beginning of the selection up to, but not including, the final character.
  • \%V: match “just before the end” of the selection.
  • .: match the final character in the selection.

Despite these complexities, we can use the convenient & in the replacement string to mean “the entire string that was selected.”

Caveat

This will not work as expected if your selection spans multiple lines: each line will end up with its own [[ and ]] (or whatever strings you’re using). I tried to replace the . with \_. in the pattern but that just made things weird—text was inserted well outside of the visual selection.

Improvements to my answer would be greatly appreciated!

1
  • 1
    The "whatever reason" part makes sense if we think of the delimiting marks '< and '> as pointing to just before the character. For example, if we select bcd in abcde, the marks are at a|bc|de. (We can check that this is true by selecting a single character; '< and '> point to the same place.) If \%V is to be inside the visual area, it can only be at a|b|c|de. Commented Feb 22, 2023 at 1:32

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.