随分前にこんなスクリプトを書いていたんですけど…lintを通すとあちこちに変なところも見つかるし、vim弄り倒してる間に色々と覚えたテクニックもあるのでそろそろ書き直してあげてもいいのかな…ということで、ちょっくら弄ってみました。
括弧閉じにスクリプトを使うのは間違っているのか?
closer.vim
scriptencoding utf-8
function! Closer() abort
let bracketList = {}
let bracketList['open'] = filter(split(&matchpairs, '[:,]'), 'v:key % 2 == 0')
let bracketList['close'] = filter(split(&matchpairs, '[:,]'), 'v:key % 2 == 1')
let isFound = 0
let pos = getcurpos()
while search('['.escape(substitute(&matchpairs, '[:,]', '', 'g'), '\.*^$]~/').'"'''.']', 'bW') > 0
let foundBracket = matchstr(getline('.'), '.', col('.')-1)
if foundBracket ==# ''''
call searchpair('''','','''','bW')
elseif foundBracket ==# '"'
call searchpair('[^\\]/zs"','','"','bW')
else
let idx = match(bracketList.close, foundBracket)
if idx > -1
call searchpair(
\ escape(bracketList.open[idx], '/.*^$]~\'),
\ '',
\ escape(bracketList.close[idx], '/.*^$]~\'),
\ 'bW')
else
let isFound = 1
break
endif
endif
endwhile
call setpos('.', pos)
return isFound ? bracketList.close[match(bracketList.open, foundBracket)] : ''
endfunction
function! s:closeFromNormal(isBang) abort
let @c = Closer()
if @c !=# '' && a:isBang ==# '!'
execute 'normal "cPl'
else
echo @c !=# '' ? 'Bracket : '.@c : 'No bracket to close.'
endif
endfunction
command! -bang Closer call s:closeFromNormal('<bang>')
inoremap <expr> <Plug>(closer_main) Closer()
当スクリプトムラあり!
改めてこのスクリプトの馴れ初めなんですが、「マルチバイトの括弧を変換を介さずに入力する」のが目的で、業界でよく使われるlexima的な補完を目指していないものです。というわけで打ち込んだ括弧を先行して閉じるのではなく、多少不細工でもいいからカーソル位置より前にある括弧を探して、それを補完するようになっています。実際にやっていることは…
- 認識したい括弧をさかのぼって探す。
- 見つかった括弧の方向を確認する。
- 閉じる側の括弧なら対応する括弧に飛んで更にさかのぼる
- 閉じていなければ元の場所に戻って対応する括弧を挿入する
という…なんで強調する必要があるんですかねえ?
気軽に「対応する括弧に飛んで」…と書いてありますが、残念ながら対応している括弧が実際に対応を意図した括弧かどうかは全く判断していません。具体的にこのスクリプト自身で想定されるケースを試すと正規表現やそのエスケープのために使われる]
や、対応する括弧に開閉の方向がない'"
などによって正常な動作をしません…が、そもそもの意図がスクリプトの為ではないので、括弧の対応レベルが浅い平文では問題なく対応する括弧が入手出来るので問題ありません。そもそも一番書きたいスクリプトがvimscriptの自分にとっては、ダブルクォートの問題が山のように積み重なっているので…うn