Phrixus
Phrixus

Reputation: 1219

Vim normal command using variable value

I need to do the following:

I started by setting the value 100 to temp using let temp=100.

I then figure out the following ex command that I can apply to a line: :execute "normal! ". temp. "^A" this will take temp and increase the number by temp for the current line.

Unfortunately, this will not work when I visually select a range of lines and then hit : to apply a command to all the lines selected.

How can I achieve the same but for a range of lines?

Here is an example:

1
2
3
4
5

Should become

101
102
103
104
105

Then I will update the temp to let temp=temp + 100 and repeat for the next block and so on.

Thanks!

Upvotes: 3

Views: 818

Answers (3)

Bob Qi
Bob Qi

Reputation: 11

I think we can use global command to do the work, it accepts the range.

:'<,'>g/./execute "normal! ". temp. "^A"

Upvotes: 0

romainl
romainl

Reputation: 196556

To answer your question directly, :help :execute is what is blocking you for two reasons:

  • :execute doesn't accept a range,
  • :execute is not necessary to begin with.

The following command does the job without :execute:

:[range]normal! <C-r>=temp<CR><C-v><C-a><CR>

Breakdown:

  • [range] would be '<,'> after a visual selection.
  • :help :normal executes the given macro in normal mode.
  • :help c_ctrl-r inserts the content of the given register in the command line.
  • :help "= is the expression register, which returns an evaluated expression.
  • temp is the expression to evaluate, so <C-r>=temp<CR> inserts the content of the variable temp.
  • <C-v><C-a> inserts a literal ^A.
  • <CR> executes the command.

But that's a lot to type so a simple mapping seems more appropriate in this case:

xnoremap <expr> <key> temp . '<C-a>'

Breakdown:

  • :help :xnoremap creates a visual mode mapping.
  • :help <expr> makes it an expression mapping, where the actual RHS is evaluated at runtime.
  • <key> is what key you want to press.
  • temp . '<C-a>' is your expression, which concatenates the current value of temp with <C-a> to obtain 100<C-a>, 200<C-a>, etc..

Usage:

  1. Set temp to the desired value:

    :let temp = 100
    
  2. Select some lines:

    v<motion>
    
  3. Increment the first number of each line:

    <key>
    
  4. Change the value of temp:

    :let temp += 100
    
  5. Move to next block and select some lines:

    <motion>
    v<motion>
    
  6. Increment the first number of each line:

    <key>
    

However, the manual way would go like this:

v<motion>    " visually select the desired lines
100<C-a>     " increment the first number on each line by 100

then:

<motion>
v<motion>
200<C-a>     " increment the first number on each line by 200

and so on… so I'm not sure what's be the benefit of introducing variables, :normal, etc., here.

Upvotes: 5

Phrixus
Phrixus

Reputation: 1219

I just found a workaround, if no one knows a better way.

I recorded a macro b to run execute "normal! " . temp . "^A" on the current line. Then recorded another macro a that will go and visually select all the lines in the group interested and run :'<,'>norm @b this will apply that operation on every line, then before ending the macro @a, I also set let temp=temp+100.

Upvotes: 2

Related Questions