开发者

Search and Replace with incremented values in Vim

开发者 https://www.devze.com 2023-02-10 02:55 出处:网络
Lets say I wrote a simple CSS rule like this: .star_10 { background: url(stars.png) no-repeat 0 0; } And I need 10, so I copied it 9 times.

Lets say I wrote a simple CSS rule like this:

.star_10 {
  background: url(stars.png) no-repeat 0 0;
}

And I need 10, so I copied it 9 times.

.star_10 {
  background: url(stars.png) no-repeat 0 0;
}
.star_10 {
  background: url(stars.png) no-repeat 0 0;
}
.star_10 {
  background: url(stars.png) no-repea开发者_C百科t 0 0;
}
.star_10 {
  background: url(stars.png) no-repeat 0 0;
}
.star_10 {
  background: url(stars.png) no-repeat 0 0;
}

etc.

Now I want to change the star_10 and 0 0 with incremented values so it looks like this:

.star_10 {
  background: url(stars.png) no-repeat 0 0;
}
.star_9 {
  background: url(stars.png) no-repeat 0 -18px;
}
.star_8 {
  background: url(stars.png) no-repeat 0 -36px;
}
.star_7 {
  background: url(stars.png) no-repeat 0 -54px;
}

and so on...

So how can I search/replace every instance, do a calculation and write it?


You can do it easily with a macro. Lets say you have only this:

.star_10 {
  background: url(stars.png) no-repeat 0 0;
}

Place your cursor over the first dot (in .star10) and type the following in normal mode:

qa3yy3jp^Xjt;18^Xk0q

Explaining:

  1. qa will start a macro recording in register "a".
  2. 3yy will yank (copy) the following 3 lines.
  3. 3j will place the cursor 3 lines down.
  4. p will paste the past yanked text.
  5. ^X (ctrl+x) will decrement the star class number.
  6. j will place your cursor one line down.
  7. t; will place your cursor before the next ; in the current line.
  8. 18^X will decrement the y coordinate of backround by 18;
  9. k will put the cursor one line up,
  10. 0 will put the cursor at the beggining of the line.
  11. q will finish the macro recording.

After that, you may have something like this.

.star_10 {
  background: url(stars.png) no-repeat 0 0;
}

.star_9 {
  background: url(stars.png) no-repeat 0 -18;
}

That's it. Just place your cursor at the dot on .star_9 and press 8@a to execute the macro recorded in register a eight more times.


You can use the s/pattern/replace construct with the \= symbol in order to evaluate a function or expression, as shown:

Decrementing the .star_10:

let g:decr = 11

fu! Decr() 
  let g:decr = g:decr - 1
  return g:decr
endfu

:%s/.star_10/\=".star_" . Decr()/

Similarly, you'd do

let g:decr_18 = 18
fu! Decr18() 
  let g:decr_18 = g:decr_18 - 18
  return g:decr_18
endfu

and then replace the 0 0; with a

:%s/no-repeat 0 0;/\="no-repeat 0 " . Decr18() . "px"/

For both functions, a (global) variable is declared that is manipulated within the functions and returned. The functin itself is called for every line matching the pattern. the pattern is substituted with the expression following the \=.


You could probably write a function for this, but I usually do something simpler. I use a key macro involving the ^A and ^X keys (increment and decrement a number). Use qa to start recording, do your stuff, using those keys with count modifiers (e.g 18^X), and just do say, 10@ afterwards.


I needed this solution but also with leading zero support. My use of the original question is more like Example 1 than what Example 2 (this was the original example).

I created a function called SearchIncr() (search, replace, and increment).

There are four required arguments: target_string, replacement_string, start_value, increment_value. There are also two optional arguments: leading_zeros, position

leading_zeros is a positive integer value, and position is 'before' or 'after'. See example 2.


Example 1

:'<,'>call SearchIncr("0 0","new",1,5,8)

If the file looks like this:

what 0 0 post
areu 0 0 post
doin 0 0 post

You will then get:

what new00000001 post
areu new00000006 post
doin new00000011 post

Example 2

:'<,'>call SearchIncr("0;","px;",-18,-18,0,'after')

If the file looks like this:

.star_10 {
  background: url(stars.png) no-repeat 0 0;
}
.star_10 {
  background: url(stars.png) no-repeat 0 0;
}
.star_10 {
  background: url(stars.png) no-repeat 0 0;
}

You will then get:

.star_10 {
  background: url(stars.png) no-repeat 0 -18px;
}
.star_10 {
  background: url(stars.png) no-repeat 0 -36px;
}
.star_10 {
  background: url(stars.png) no-repeat 0 -54px;
}

The vimscript is here:

" place functions here
function! SearchIncr(a1,a2,a3,a4,...) 
  " patn, repl, start, increment
  let target = a:a1
  let repl = a:a2
  let startval = a:a3
  let increment = a:a4

  " 1st optional argument handles leading zeros
  " process optional leading zero
  if a:0 >= 1
    let leadingz = a:1
  else
    " default to 0
    let leadingz = 0
  endif

  " 2nd optional argument handles if target string goes before or after
  " default is before
  if a:0 == 2
    " ideally set to 'after'
    let repl_pos = a:2 
  else
    let repl_pos = 'before'
  endif

  " inits
  let l:incr = startval - increment
  let lnum = a:firstline

  " cycle though selection
  while lnum <= a:lastline

    " the FORMATTING string
    if repl_pos == 'before'
      let l:incrstring = '\=printf(''%s%0'. leadingz . 'd'',repl,l:incr)'
    elseif repl_pos == 'after'
      let l:incrstring = '\=printf(''%0'. leadingz . 'd%s'',l:incr,repl)'
    else
      "default to no leading zero and before placement
      let l:incrstring = '\=printf(''%s%0d'',repl,l:incr)'
    endif

    " only increment counter when target matches
    if match(getline(lnum),target) > 0
      let l:incr = l:incr + increment
    endif

    " the search and replace
    call setline(lnum,substitute(getline(lnum),target,
    \ l:incrstring,'g'))

    let lnum = lnum + 1
  endwhile
endfunction

Any comments to improve on the above would be greatly appreciated!


Just using pure vim resources, no scripting:

:let c=10 | g/_\zs\d\+\ze/ s//\=c/ | let c-=1 
:let c=0 | g/no-repeat 0 \zs\d\+/ s//\=c/ | let c-=18
0

精彩评论

暂无评论...
验证码 换一张
取 消