4

I find the ci' (and similar commands) very useful, since I can execute it at the start of the line, and it will automatically change the first occurrence of text between single quotes ('...').

However, how can I change the N'h occurrence using this command?

I've tried <N>ci' and other ways that came to my mind, but none worked.

2 Answers 2

3

Plug-in wellle/targets.vim implements this feature, through an in' text-object (n for "next".) You can use it with a count, so you can use c3in' or 3cin' to change inside the third next single quoted string from the cursor.

See the README section for the next and last pair (and also the linked cheat sheet.)

This kind of mapping is made possible because Vim operator mappings can start and end in a point other than the cursor position. This is documented in :help omap-info:

To ignore the starting cursor position and select different text, you can have the omap start Visual mode to select the text to be operated upon. Example that operates on a function name in the current line:

onoremap <silent> F :<C-U>normal! 0f(hviw<CR>

The <C-U> is used to remove the range that Vim may insert. The Normal mode commands find the first ( character and select the first word before it. That usually is the function name.

The text objects from targets.vim implement lookups for the next and last (previous) pairs, but you could use similar mappings to lookup the Nth pair in the current line if you're interested in the Nth absolute one from the beginning of the line, rather than the next Nth one from the current position (or following the current pair.)

1

I'm afraid it is not possible (but we can make a nice mapping, skip to the end for that). See :h object-select for reference. Unlike other commands, such as i(, iW, etc., i' does not have an optional [count].

a"
a'
    "a quoted string".  Selects the text from the previous
    quote until the next quote.
i"   
i'
    Like a", a' and a`, but exclude the quotes.

To understand why, enter a line like this in Vim.

(u((ss|da)ssfda)) (hs) ad 'asd asd' jasi 'op pqq' a

Put the cursor on the | character.

  • di( deletes ss|da.
  • 2di( deletes (ss|da)ssada
  • 3di( deletes u((ss|da)ssfda)

So, the preceding number deletes "count" levels of parenthesis nesting.

Since opening and closing quotation marks are the same symbol, they cannot be nested and the [count] could not be applied in the same manner. Maybe that's why Vim developers did not implement [count]di', but only di'.

A built-in alternative is to use [count]f' to reach the quoted string you want to change and only then use ci'. You can make a mapping out of that,

:nnoremap <F8> :<C-u>execute "normal! " . v:count*2 . "f'ci'"<CR>

so that pressing 1<F8> changes asd asd and 2<F8> changes op pqq in our example above.

4
  • 1
    Actually, this can be implemented. Plug-in targets.vim implements in' (inside next single quoted string) that works exactly like that. See my answer.
    – filbranden
    Sep 9, 2020 at 1:41
  • 1
    @filbranden There seems to always be a plugin to get us covered, nice!
    – Quasímodo
    Sep 9, 2020 at 12:23
  • @Quasímodo - Can you explain :nnoremap <F8> :<C-u>execute "normal! " . v:count*2 . "f'ci'"<CR>? Why is <C-u> needed, and why use :normal! and not :normal?
    – Shuzheng
    Sep 12, 2020 at 9:25
  • @Shuzheng Please refer to the linked answer from which I have extracted that, they are quite well explained. And for normal!, see :h :normal.
    – Quasímodo
    Sep 12, 2020 at 10:26

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.