11

I want to insert text like this:

similar text wordA similar text ...
similar text wordB similar text ...
similar text wordC similar text ...

I know I can paste the text many times then use the substitution to change single word for each line, however I still find this method very inconvenient.

Is there a way to save a word or a word list into a "variable" then use this variable to generate similar text ?


EDIT I made something unclear in the original question, in fact I'm trying to make some very alike function definitions, so wordA wordB... may appear many times in each clone of text and the text may contain many lines. the pattern is exactly like this:

similar_text1 wordA similar_text2 wordA similar_text3 ...
similar_text1 wordB similar_text2 wordB similar_text3 ...
similar_text1 wordC similar_text2 wordC similar_text3 ...

(text may contain new lines, and wordA,wordB... may exist many times)


2nd EDIT The "similar_text" parts maybe different each time I do such job, so the solution for this job is better to be reusable. Because I'm trying to make very alike function definitions, or a string to function mapping table.

After reading answers and some practice, I find that it is more convenient to think this job as some kind of substitution, because separate "part1","part2"..."partN" which may contain newlines is hard to write, so write a template first:

similar_text1 $WORD similar_text2 $WORD similar_text3 ...
(text may contain <ENTER>, make sure $WORD not exist in "similar_text" parts )

Then trying to put clones of the text but replacing $WORD with a list of words is more straight thinking.

So it seems my problem changed to be "how to clone a text block many times, but each time by substituting a keyword in it with a word from a list?"

1
  • That is why registers are used for. You can yank (copy) text to a register and then use that register. Macros can be used to do similar as well as repetitive tasks.
    – SibiCoder
    Commented Jul 7, 2016 at 13:15

3 Answers 3

7

You can use the following function:

function! GenerateLines()
    let firstpart="similar text part 1"
    let secondpart="similar text part 2"

    let words=["wordA","wordB","wordC","wordD","wordE"]

    for word in words
        let line = firstpart . word . secondpart
        call append(line('.')+1, line)
        normal j
    endfor
endfunction

In the variables firstpart and secondpart put the text to repeate before and after the words and in the list words put the words which should change.

Then the loop will create the content of the line for each word of the list, insert the line in the buffer and go to this new line.

You can then simply call the function: call GenerateLines().


Edit To make it easier to manipulate you can also pass the different parts as arguments, the function should look like this:

function! GenerateLines(firstpart, secondpart, words)
    for word in a:words
        call append(line('.')+1, a:firstpart . word . a:secondpart)
        normal j
    endfor
endfunction

And you can call it like this:

call GenerateLines("first part", "second part", ["wordA","wordB","wordC","wordD","wordE"])
3
  • You could avoid the loop with call map(a:words, 'a:firstpart . v:val . a:secondpart') and call append(line('.'), a:words).
    – Antony
    Commented Jul 7, 2016 at 21:52
  • Since I often make such kind of edit with very long "firstpart" and "secondpart", even "thirdpart", and these parts may contain newlines, it seems to be too long to type that much in command mode, so maybe it's more convinient to yank text in to registers, then use functions to generate text from registers? Commented Jul 7, 2016 at 23:50
  • I edited the question, it changes a lot, sorry for the inconvenience. Commented Jul 8, 2016 at 1:05
5

My strategy in such cases will vary, but it often follows the following steps:

  1. Write the unique word(s), e.g.

    wordA
    wordB
    ...
    wordK
    
  2. Either

    a) do a search and replace, e.g.

    :%s/\w\+/similar text \0 similar text .../
    

    b) or use visual block mode, see :h blockwise-visual. In particular, I woul select the words and use I to insert similar text in front, or A to append similar text after.

I would argue that this strategy makes sense when you do this kind of editing task relatively seldom. However, if you find you need to do this often, then you should follow @statox's suggestion with a function that expands the list of words.

3
  • I think the solution depends on where the word list is coming from. If it's from a file, then :read followed by your substitution makes a lot of sense.
    – Antony
    Commented Jul 7, 2016 at 21:42
  • your solution is easy to do and solve the problem I asked in the original question easily. However there are some mistakes in my original post, I made 2 edits and it changes a lot, thank you for the answer, sorry for my changing and bad English :) Commented Jul 8, 2016 at 0:52
  • @social_loser you can adapt this solution by using "\r" to stand for a newline in the replacement part of the substitution
    – frangio
    Commented Jul 9, 2016 at 2:42
2

I'll argue that this is a job for the visual block (Ctrl+v). First I would write all distinct words:

wordA
wordB
wordC
wordD
wordE

Starting with the cursor on the first w I would then perform:

<c-v>5j$yA <esc>pA <esc>p

I can control how many columns I need by pressing A <esc>p as many times I want. Let's assume that I needed three columns just like the command above shows. Now we have this:

wordA wordA wordA
wordB wordB wordB
wordC wordC wordC
wordD wordD wordD
wordE wordE wordE

And the cursor is at the the beginning of the 3rd wordA in the first line. Next I would block select the column of whitespace between the columns going backward and adding the text in between. From the current cursor location:

h<c-v>5jI similar text 3<esc>

This results in (the % marks the cursor position):

wordA wordA%similar text 3 wordA
wordB wordB similar text 3 wordB
wordC wordC similar text 3 wordC
wordD wordD similar text 3 wordD
wordE wordE similar text 3 wordE

Repeat for next column:

bh<c-v>5jI similar text 2<esc>

And the last/first (the one on the left side) column:

b<c-v>5jIsimilar text 1 <esc>

Finally resulting in:

similar text 1 wordA similar text 2 wordA similar text 3 wordA
similar text 1 wordB similar text 2 wordB similar text 3 wordB
similar text 1 wordC similar text 2 wordC similar text 3 wordC
similar text 1 wordD similar text 2 wordD similar text 3 wordD
similar text 1 wordE similar text 2 wordE similar text 3 wordE

Update: added a screencast

enter image description here


This appears to be a lot of typing but it is actually very easy to get used to. It provides you the freedom to change the number of words, number of columns and even spacing between the columns.

The drawback is that you need to know the number of distinct words you're using. I have a 5 hardcoded in all commands above because it is the number of rows the commands are operating on.

Debugging note: all commands start and end in normal mode, if you find yourself in visual or insert mode at the end of a command you're doing something wrong.

1
  • vim-visual-multi may be easier (you don't have to keep typing <c-v>5j all the time)
    – user202729
    Commented Sep 23, 2021 at 13:06

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.