Quantcast
Channel: Vimタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 5608

(続)Vimでマクロを使わず、縦横の連番を作成する(文脈に合わせてステップ数を変える)

$
0
0

前の投稿の続きです。

マクロを使わず、Vimで簡単に縦の連番を作成する

連番作成の方法をいくつか紹介します。

縦方向と横方向に連番を作成する

前回は縦方向の連番だけでしたが、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]
vimrc
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 を書き直す必要があります。

面倒なので、その都度、文脈から読み取って欲しいですよね。

ステップ数を文脈から読み取って連番作成

デモ

renban_demo4opt.gif

(注)上のデモでは移動にmarkを使っています。

Excelのオートフィル機能(セルの右下の角をドラッグするやつ)のように、
連番を開始する最初の2つの数を読み取り、その差分だけ足していくように
してみました。この場合、小数の連番もできます。

カーソルは 2番目の数字に置いておきます。

## 3の倍数
0, 3, 0, 0, 0, 0, 0, 0 ,0 ...
^  ^       |
           v 
0, 3, 6, 9, 12, 15, 18, 21 ,24 ...
vimrc
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)を使っています。これは、numbermodelと同じ書式にして返します。例えば、 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
vimrc
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>

最後に

カーソルの位置をどこに置くかや、どのレジスタを使うかなど、連番の作成方法は個人の好みが分かれそうなので、プラグイン化はせず、方法論を述べるだけにします。

好きにカスタマイズしてみてください。


Viewing all articles
Browse latest Browse all 5608

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>