1

I want to turn

one two three four

into

'one' 'two' 'three' 'four'

(I have surround.vim)

I can do: ysiW', then do W. repeatedly until I've surrounded all words in the line

But I would like to repeat the movement (W) and repetition (.) multiple times without needing to manually type that (imagine much longer lines).

I tried

:4norm ysiW'W

but of course that fails. I tried pressing 4 then typing :norm ysiW'W, but that only runs it once (after deleting the automatically inserted .,.+3 range).

How do I do this without having to record a macro?

  • 2
    Does :s/\w\+/'&'/g solve your problem? If not, please give a more representative example. – Quasímodo Sep 24 at 18:04
  • 1
    @Quasímodo imagine the line f g one two three four and you want it f g "one" "two" "th... – theonlygusti Sep 24 at 20:41
  • @theonlygusti :s/\w\{2,}/"&"/g will only replace on words with at least two characters, so it won't match f or g. – filbranden Sep 25 at 3:10
  • 1
    @filbranden how about f g f g f g and you want f g f "g" "f" "g". Yes you can continue making the substitution more complex, but I'd just love to learn about a way to repeat commands. Looks like it doesn't exist. (I guess you can 3@:) – theonlygusti Sep 25 at 8:31
2

With a macro:

0qqysiW'W

and then 3@q.


The issue is that :normal with a range is line-wise; that is, it repeats the normal command on each line in the range.

You could do

normal ysiW'<C-r>=repeat('W.', 3)<CR>

But at that point you're doing more work than the original, I think.


Quasímodo points out in the comments that

substitute/\w\+/'&'/g

is a little more natural, and I agree.

| improve this answer | |
2

Using a macro or a :s command is probably the easiest way to get this task done...

But if you'd like to explore an alternative, then the gn command is a good fit. This commands selects the next search match in a visual selection. You can use it as a text object, so you can have your ys command act on the gn block, which you can then simply repeat with a ., without the need for a motion to get to the next match.

The problem with using the gn command here is that we need a fairly complex regular expression, since it needs to only match the text before the modification, but not match it after it. If the search pattern still matches the text after the modification, the gn command will apply the modification again to the current block, rather than skip to the next one.

So let's come up with a search that will match sequences of non-whitespace characters, as long as they don't start with a single quote.

This search pattern should work:

/\v(^|\s@<=)[^' \t]\S+

If you have 'hlsearch' set, you'll see Vim highlighting each of the individual words, one, two, three, four. If you surround any of them with ', it will stop being highlighted, which means it will no longer match.

This regex is fairly complex. I'm using "verymagic" mode to avoid having to backquote many metacharacters. Then I'm matching a start of line ^ or a space, but I'm using a zero-width match-behind /\@<= to match it without including it into the pattern. This is to ensure we only match beginning of words. Then I'm using [^' \t] to match a character other than the single quote, a space or a tab. So this is the start of a word (sequence of non-whitespace characters) that doesn't start with a single quote.

Having performed this search, we're ready to use the gn magic. Set your cursor somewhere in the first word you want to change, in this case one, and then use:

ysgn'

This will surround the one in single quotes: 'one'. If you have 'hlsearch' enabled, you'll see that 'one' is no longer highlighted, since it's not a match anymore.

At this point, you can repeat the ysgn' command with a single .. Since the word under the cursor is not a match, Vim will jump forward to the next match and act on it, so you'll get 'one' 'two'. Repeat it one more time with . and you'll get 'three', and finally one last . for 'four'. Done!

So this is probably overkill for this specific case, but when you're doing operations that modify the term enough that it's easy to match it before the change and not match it after the change, this technique is super useful. One great example is switching identifiers from snake_case to CamelCase or vice-versa.

| improve this answer | |
1

Once again, I suggest to work with https://github.com/mg979/vim-visual-multi

I would have clicked each word with the mouse then <c-n> When all selected type S' The S' surrounds it with apostrophe.

You can also select with keyboard, by doing \\/\< which searches beginnings of words. Type <c-n> until all are selected, then esc and sw (selects word) .

| improve this answer | |

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.