前の投稿の続きです。
連番作成の方法をいくつか紹介します。
縦方向と横方向に連番を作成する
前回は縦方向の連番だけでしたが、vim-textobj-numeralに新しいmotionを加えたので、横方向にも連番できるようになりました。
motionというのは j,k,w,$ のようなノーマルモードの移動コマンドのことです。
例
一番左の "0" にカーソルを移動して ))))))))))
または 10)
let array = [0,0,0,0,0,0,0,0,0,0,0]
|vlet array = [0,1,2,3,4,5,6,7,8,9,10]
NeoBundle 'kana/vim-textobj-user'
NeoBundle 'tkhren/vim-textobj-numeral'setnrformats-=octal
function! SequentialIncrement(motion, textobj, step)let inc_key =a:step >0 ? "\<C-a>" : "\<C-x>"let @z ='"zy' . a:textobj . a:motion . 'v' .
\ a:textobj . '"zp' . abs(a:step) . inc_key
return'@z'endfunction
nmap <expr>+ SequentialIncrement('j','ad',1)""縦方向の連番
nmap <expr>) SequentialIncrement('gNd','ad',1)""横方向の連番
"" `gNd` は次のテキストオブジェクト(この場合整数)に移動するmotion
ほとんどのケースではこれで十分かもしれません。
しかし、不満な点が一つあります。上の例では足す数 step
が +1 固定です。
例えば 3 の倍数の連番を作りたいとき、その都度 vimrc を書き直す必要があります。
面倒なので、その都度、文脈から読み取って欲しいですよね。
ステップ数を文脈から読み取って連番作成
デモ
(注)上のデモでは移動にmarkを使っています。
Excelのオートフィル機能(セルの右下の角をドラッグするやつ)のように、
連番を開始する最初の2つの数を読み取り、その差分だけ足していくように
してみました。この場合、小数の連番もできます。
カーソルは 2番目の数字に置いておきます。
例
## 3の倍数
0, 3, 0, 0, 0, 0, 0, 0 ,0 ...
^ ^ |
v
0, 3, 6, 9, 12, 15, 18, 21 ,24 ...
NeoBundle 'kana/vim-textobj-user'
NeoBundle 'tkhren/vim-textobj-numeral'setnrformats-=octal
function!s:SequentialIncrement(prev_motion, next_motion, textobj)let @z =a:prev_motion . 'h"xy' . a:textobj .
\ a:next_motion . '"zy' . a:textobj .
\ a:next_motion . '"_c' . a:textobj .
\ "\<C-r>=textobj#numeral#imitate_format(@z," .
\ " 2.0 * eval(@z) - eval(@x))\<CR>\<ESC>"return'@z'endfunction
nmap <script><expr>+<SID>SequentialIncrement('k','j','af')
nmap <script><expr>)<SID>SequentialIncrement('gPf','gNf','af')
少し補足すると、prev_motion
は一個前の数字に戻るmotionで、縦方向の場合はk
で、横の場合はgPf
です。next_motion
は次の数字に移動するmotionで、縦方向の場合はj
で、横の場合はgNf
です。
上の例ではmotionで移動しながら、@x
と @z
にそれぞれ最初の2つの数をコピーします。
差分を足した、@z + (@z - @x)
を3番目の数として置き換えています。
ただし、このままだとゼロフィルなどの書式が揃わないので、前の数字と同じ書式にするユーティリティ関数 textobj#numeral#imitate_format(model, number)
を使っています。これは、number
を model
と同じ書式にして返します。例えば、 model="+3.14"
, number=2.71828
なら、imitate_format()
の返り値は "+2.72"
になります。
a:prev_motion
の後のh
はとあるバグを防ぐためにとった苦肉の策です。ほとんどのケースでうまく動きますが、厳密にやりたい場合は osyo-manga/vim-operator-jump_side等を使うとよいです。どんなバグかはh
抜きで使っていると分かります。
ブレース展開で連番作成をする
すでにある数列を連番化するのは上の方法が簡単だと思いますが、
ゼロから数列を作りたい場合には少し面倒です。
横方向の連番の場合、シェルのブレース展開に近いものを作れば、
より速く、より簡単に連番を入力できます。
波カッコの中でC-y
すれば、中が展開されて連番になります。
正直、))))))))
とかやるより断然速いですね。
|
の左側は式なので{0|1:10}
とかすれば0,0,0,0,0,0,0...
が作れます。ループカウントは$i
に入ります
例
{start:end[:step[:delim]]}
{1:10} 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
{1:10:2} 1, 3, 5, 7, 9
{1:10:2:/} 1/3/5/7/9
{0|1:10} 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
{$i|1:10} 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
{$i%3|1:10} 1, 2, 0, 1, 2, 0, 1, 2, 0, 1
{1:3:0.5} 1, 1.5, 2.0, 2.5, 3.0
function! BraceExpand(brace)let _brace = substitute(a:brace,'^{\|}$','','g')let _mls = split(_brace,'|')if len(_mls)>1let _templ =get(_mls,0)let _range =get(_mls,1)elselet _templ ='$i'let _range =get(_mls,0,'')endififmatch(_range,'^[-+]\?[0-9.]\+\(:[-+]\?[0-9.]\+\)\{1,2}\(:.*\)\?$')>-1let _args = split(_range,':')let _start = eval(get(_args,0,0))let _end = eval(get(_args,1,0))let _step = eval(get(_args,2,1))let _delim =get(_args,3,', ')let _ret =''let _i = _start
while _i <= _end
let _e = eval(substitute(_templ,'\$i', string(_i),'g'))let _ret .= string(_e) . _delim
let _i += _step
endwhilelet _ret = substitute(_ret, _delim.'$','','g')return _ret
endifreturna:brace
endfunction
nmap <C-y>"zca}<C-r>=BraceExpand(@z)<CR><ESC>
最後に
カーソルの位置をどこに置くかや、どのレジスタを使うかなど、連番の作成方法は個人の好みが分かれそうなので、プラグイン化はせず、方法論を述べるだけにします。
好きにカスタマイズしてみてください。