clurin.vim = cycle.vim + switch.vim + α?

  • 8
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

はじめに

これまで、switch.vim を使っていると、順方向にしかいけないことと, ctrl-A+ に割り振りたいことなどに不満がでてきたので、clurin.vim を作ってみた。

提供する機能により、SunMonTue → ... のような切り替えができます。本プラグインの特徴は以下の通りです

  • 正規表現での指定が可能 (switch.vim 相当の機能はだいたいできる.)
  • 順方向だけでなく逆方向にもたどれる (cycle.vim 相当のことができる)
  • n 個先にも飛べる
  • カーソル直下の文字にマッチするものがなかった場合の処理を設定可能
  • ファイルタイプ毎の設定が容易. (好みの問題かも)

switch.vim からの現時点での機能落ちは以下だと思います. 他にもあったらごめんなさい。

  • separate mappings (あまり必要性が...)
  • nested dict definitions (実現できないわけではない)

使い方

.vimrc に以下のように書いてください。

nmap + <Plug>(clurin-next)
nmap - <Plug>(clurin-prev)
vmap + <Plug>(clurin-next)
vmap - <Plug>(clurin-prev)

あとは、
1. Sun という文字列の上にカーソルを置いて,
2. + を押すと, Mon になり,
3. 2+ を押すと, Wed になり,
4. - を押すと, Tue になります.

カスタマイズ

設定はグローバル変数 g:clurin の定義により行います。g:clurinfiletype をキーとする辞書です。 switch.vim では「次にいくにはどう変換するか」を定義しますが、clurin.vim では「自分になるにはどう変換するか」を定義します。以下に例を示します。

function! g:CountUp(str, cnt, def) abort
  " a:str: マッチした文字列
  " a:cnt: clurin-next なら正の値, clurin-prev なら負の値
  " a:def: マッチした定義
  return str2nr(a:str) + a:cnt
endfunction

function! g:CtrlAX(cnt) abort 
    if a:cnt >= 0
        execute 'normal!' a:cnt . "\<C-A>"
    else
        execute 'normal!' (-a:cnt) . "\<C-X>"
    endif
endfunction

let g:clurin = {
\    '-': {
\        'def': [
\            ['on', 'off'],
\            [
\                {'pattern': '\<true\>', 'replace': 'true'},
\                {'pattern': '\<false\>', 'replace': 'false'}
\            ], 
\        ]  
\    }, 
\    'c': {
\        'def': [
\            ['&&', '||'],
\            [
\                {'pattern': '\(\k\+\)\.', 'replace': '\1.'},
\                {'pattern': '\(\k\+\)->', 'replace': '\1->'}
\            ]  
\        ], 
\        'nomatch': function('g:CtrlAX')
\    }, 
\    'vim': {
\        'def': [
\            [
\                {'pattern': '''\(\k\+\)''', 'replace': '''\1'''},
\                {'pattern': '"\(\k\+\)"', 'replace': '"\1"'}
\            ],
\            [
\                {'pattern': '\(-\?\d\+\)', 'replace': function('g:CountUp')}
\            ]
\        ]  
\    }  
\}  

詳細をみていくと、

let g:clurin = {
\    '-': {

- はデフォルトの設定を表します。通常すべてのファイルタイプで使用されます。
cft=c の場合専用の設定になります。

\        'def': [
\            ['on', 'off'],

def の値はリストで、そこに切り替えるグループを定義します。この場合、onoff を切り替える定義になっています。

\            [
\                {'pattern': '\<true\>', 'replace': 'true'},
\                {'pattern': '\<false\>', 'replace': 'false'}
\            ], 
....
\            [
\                {'pattern': '\(\k\+\)\.', 'replace': '\1.'},
\                {'pattern': '\(\k\+\)->', 'replace': '\1->'}
\            ]  

正規表現を使いたい場合には、文字列ではなく辞書を使用します。pattern にマッチしたもの, replace に自分に変換するときのルールを書きます. 上のグループは truefalse のグループを表しています.

\        'nomatch': function('g:CtrlAX')

マッチするものがなかった場合の処理を Funcref で渡すことができます. この設定の場合, g:CtrlAX を呼び出して、Ctrl-A, Ctrl-X 相当を実現しています。

\            [
\                {'pattern': '\(-\?\d\+\)', 'replace': function('g:CountUp')}
\            ]

(だいぶルール違反な気もしていますが)変換ルールに Funcref を渡すことも可能です。
うまく定義してやれば, vim-speeddating と共存みたいなこともできると思いますし, switch.vimNested dict definitions も実現できるはずです。

その他、サイクリックにしないとか、デフォルトの設定を利用しないとかも指定できます。詳細は近いうち更新されるであろうヘルプを参照してください。

マッチ判定ルール

カーソル下の文字列が複数のパターンにマッチする場合には以下の優先順位によりどのパターンを利用するかを判定します.
1. 開始位置がカーソル位置に近いほう
2. 対象文字列が短いほう
3. 定義された位置がはやいほう

どちらの場合もマッチしなかったほうがカーソル位置を移動すればマッチ対象にできるので、このルールを使用するようにしました。

定義は以下の順序で探索します.
1. g:clurin[&filetype]
2. g:clurin['-']
3. clurin.vim デフォルトの &filetype 用定義
4. clurin.vim デフォルト定義 (-)

おわりに

何か問題・リクエスト等あれば issues によろしくお願いします.