Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to quickly select parenthesized text from outside of the parentheses in Vim?

Tags:

vim

I know that I can select the text inside single or double quotes by typing vi' or vi" in Normal mode. For example, with the cursor located at the letter H here:

Hello "World"

I can select the word World by issuing vi".

But this does not seem to work if World is parenthesized instead of being quoted:

Hello (World)

If my cursor is at H and I try to select World using vi), it does not work. It seems that the only way to do this is to actually move your cursor inside the parentheses first and only then issue vi).

Why does selecting text inside parentheses and quotes differ in this way?

like image 538
Bernhard Heijstek Avatar asked Oct 18 '11 07:10

Bernhard Heijstek


2 Answers

The difference between {a,i}×{(,),b} text-object commands and their ", ', ` counterparts primarily results from the difference in definitions of a block and a quoted string (see :help v_ab, :help v_aquote).

While the latter is the text from the previous quote character on the current line until the next one on that same line (escaped ones aside), the former is the text between the nth previous unmatched opening parenthesis and the matching closing one. Simply put, the command va( (without a count) is like [(v%—if there is no unmatched parenthesis before the cursor, both select nothing. However, the command va" scans the current line to find a matching pair of quotes, anyway.

The main reason for this difference in behavior, I suppose, is that quoted strings, in contrast to parenthesis, are assumed to be non-nested (at least in perspective of the built-in Vim text objects).

To select the text in the next parenthesis on the current line, one can use %vi( or %va(, depending on whether it is desirable to include the parentheses in the selection or not, respectively.

like image 162
ib. Avatar answered Nov 09 '22 04:11

ib.


There is a comment on Hacker News that points to a script supposedly solving the issue.

Untested, but it’s by Steve Losh, so it might be good.

--- EDIT ---

Here is a working link and here it is copied here for posterity:

" Motion for "next/last object". For example, "din(" would go to the next "()" pair
" and delete its contents.

onoremap an :<c-u>call <SID>NextTextObject('a', 'f')<cr>
xnoremap an :<c-u>call <SID>NextTextObject('a', 'f')<cr>
onoremap in :<c-u>call <SID>NextTextObject('i', 'f')<cr>
xnoremap in :<c-u>call <SID>NextTextObject('i', 'f')<cr>

onoremap al :<c-u>call <SID>NextTextObject('a', 'F')<cr>
xnoremap al :<c-u>call <SID>NextTextObject('a', 'F')<cr>
onoremap il :<c-u>call <SID>NextTextObject('i', 'F')<cr>
xnoremap il :<c-u>call <SID>NextTextObject('i', 'F')<cr>

function! s:NextTextObject(motion, dir)
  let c = nr2char(getchar())

  if c ==# "b"
      let c = "("
  elseif c ==# "B"
      let c = "{"
  elseif c ==# "d"
      let c = "["
  endif

  exe "normal! ".a:dir.c."v".a:motion.c
endfunction
like image 43
romainl Avatar answered Nov 09 '22 04:11

romainl