0

Is there a way to turn the "region bounded by two specific marks" into a text object similar to aw or ap?

I've been messing around with vi and found it convenient to use a and b. and define a region and to define commands that act on the region bounded by a and b.

It would be nice to be able to expose a text object for this purpose in vim.


I have the following bindings in vi for emulating something kind of like visual mode. I have some macros that set a and b to the boundaries of an interesting region. And another set of macros for manipulating the region bounded by a and b.

It's nice to be able to pick whether I want a linewise selection or not after setting the marks.

^A and ^P are literal control characters, in this case they set the mark a to the beginning of the buffer or paragraph and b to the end.

map ^A 1GmaGmb
map ^P }mb{jmak}

And other macros that do stuff to the region like indent it and dedent it.

map \. mp'a>'b`p
map \, mp'a<'b`p
2

Here is an example for a custom text object working with the marks a and b.

" char-wise 
onoremap am :<c-u>execute 'normal `av`b'<cr>
xnoremap am :<c-u>execute 'normal `av`b'<cr>
" line-wise
onoremap aM :<c-u>execute 'normal `aV`b'<cr>
xnoremap aM :<c-u>execute 'normal `aV`b'<cr>

To copy the characters between the marks a and b hit yam. To copy the lines between a and b hit yaM. You can use it with other commands., like =am to format the lines between marks a and b.

See :help :map-modes and :help omap-info.

Notes:

  • This moves the cursor. After it hit Ctrl-O twice to get back to your initial cursor position.
  • This changes the visual selection. If you used yam and hit gv afterwards, the visual selection is mark a to b.

If you want to work with any mark, you could try this:

onoremap <silent> am :<c-u>execute 'normal `'. nr2char(getchar()) . 'v`' . nr2char(getchar())<cr>
xnoremap <silent> am :<c-u>execute 'normal `'. nr2char(getchar()) . 'v`' . nr2char(getchar())<cr>
onoremap <silent> aM :<c-u>execute 'normal `'. nr2char(getchar()) . 'V`' . nr2char(getchar())<cr>
xnoremap <silent> aM :<c-u>execute 'normal `'. nr2char(getchar()) . 'V`' . nr2char(getchar())<cr>

Now you need to give the marks. Like yamab copies the range of the marks a and b, while yamxy uses the marks x and y. If you only want the line of the mark a copied, you could use yaMaa.

Be careful with upper case marks here. Upper case marks might change the buffer. Assuming mark A is in another buffer, the command yamAb will switch to the buffer of the mark A and will copy to mark b in that buffer.

1

I would use something like the following. There might be a lot of corner cases, but it's a start:

for s:nr1 in range(97, 122)
    for s:nr2 in range(97, 122)
        let s:char1 = nr2char(s:nr1)
        let s:char2 = nr2char(s:nr2)
        execute 'xnoremap i'.s:char1.s:char2.' :<C-u>normal! `'.s:char1.'v`'.s:char2.'<CR>'
        execute 'onoremap i'.s:char1.s:char2.' :normal vi'.s:char1.s:char2.'<CR>'
    endfor
endfor

I used range(97, 122) to get a list of numbers from 97 to 122 (see :h range() and these represent the ASCII values of characters a to z which could be converted to characters with :h nr2char(). So, if you want uppercase letter marks, you can edit the list range yourself.

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.