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

ドットコマンドでもカーソル位置を保持したい

$
0
0

VimConf2016の延長線で出来たネタ - derisの日記をよみました。また、 Vim で operator 実行時にカーソルを移動させないような operator をつくった - Secret Garden(Instrumental)というのも読んだことがあります。とても、便利ですね。

オペレータによっては「カーソルを保持」の意味について考える必要があるので、デフォルトの挙動に納得してはいます。 (例えば行の中ほどで d0したときにカーソルの絶対位置が保持されても変ですよね?) ただ、いくつかのオペレータでカーソル位置を保持したいというのは、私も思ったことがあります。その時ぶつかった問題がありまして、ドットコマンドの時の場合です。

最初から見ていくために簡単なオペレータを作ってみましょう。特に難しいことはしないので、いろいろ省略していたり、拡張性は考えられていません。なお、 vim-operator-userを使うともっと簡単です。

function! MyOperatorNo1(motionwise) abort
  let cursor = getcurpos()
  echohl MyOperatorNo1Green
  echom'このおれさまのオペレータがせかいでいちばん!つよいってことなんだよ!'
  echohl NONEcall setpos('.', cursor)endfunctionfunction! MyOperatorNo1Pre() abort
  let&operatorfunc='MyOperatorNo1'return''endfunctionhighlight default MyOperatorNo1Green ctermfg=2 guifg=#008800noremap<expr><SID>(MyOperatorNo1Pre) MyOperatorNo1Pre()nnoremap<silent><script> M <SID>(MyOperatorNo1Pre)g@
xnoremap <silent><script> M <SID>(MyOperatorNo1Pre)g@
onoremap <silent> M g@

できました。実行するとメッセージを表示するだけのオペレータです。色付きです、すごいですね。オペレータ関数の先頭でカーソル位置を保存して最後にカーソルを戻しています。

MyOperatorNo1-Demo1

おや?カーソル位置が長い長いチャンピオン○ードの先頭に戻ってしまいました。これは実はオペレータ関数 MyOperatorNo1()が呼ばれた時にはすでに Vim がテキストオブジェクト対象範囲の先頭へカーソルを動かした後であるためです。

MyOperatorNo1-CursorPosition

ではカーソル位置は事前に取得しておきましょう。

function! MyOperatorNo1(motionwise) abort
  echohl MyOperatorNo1Green
  echom'このおれさまのオペレータがせかいでいちばん!つよいってことなんだよ!'
  echohl NONEcall setpos('.',s:cursor)endfunctionfunction! MyOperatorNo1Pre() abort
  lets:cursor = getcurpos()let&operatorfunc='MyOperatorNo1'return''endfunctionhighlight default MyOperatorNo1Green ctermfg=2 guifg=#008800noremap<expr><SID>(MyOperatorNo1Pre) MyOperatorNo1Pre()nnoremap<silent><script> M <SID>(MyOperatorNo1Pre)g@
xnoremap <silent><script> M <SID>(MyOperatorNo1Pre)g@
onoremap <silent> M g@

MyOperatorNo1-Demo2

カーソル位置を保持に成功しました。しかし、ドットリピートをしてみると?

MyOperatorNo1-Demo3

カーソルが最初にオペレータを実行した位置に戻ってしまいますね。 MyOperatorNo1Pre()はドットリピートの時実行されないためです。困りましたね。


さて、今となっては明らかですが、要するにドットコマンドを押した瞬間のカーソル位置を取得する方法がオペレータ関数にないのです。そこで、ドットコマンドの前に実行されるオートコマンドを追加するプラグインを書きました。

vim-event-DotCommandPre

これを使用していると、ユーザー定義オートコマンドイベント DotCommandPreが使えます。また、いくつかのドットコマンドを実行する直前の情報を g:DotCommandPreに保存します。カーソル位置や画面の状態もこれに含まれるので、これを使ってみましょう。

function! MyOperatorNo1(motionwise) abort
  echohl MyOperatorNo1Green
  echom'このおれさまのオペレータがせかいでいちばん!つよいってことなんだよ!'
  echohl NONEifs:dotcommand
    " dot repeatif exists('g:DotCommandPre')call winrestview(g:DotCommandPre.view)endifelse    " first actioncall winrestview(s:view)endiflets:dotcommand =1endfunctionfunction! MyOperatorNo1Pre() abort
  lets:dotcommand =0lets:view= winsaveview()let&operatorfunc='MyOperatorNo1'return''endfunctionhighlight default MyOperatorNo1Green ctermfg=2 guifg=#008800noremap<expr><SID>(MyOperatorNo1Pre) MyOperatorNo1Pre()nnoremap<silent><script> M <SID>(MyOperatorNo1Pre)g@
xnoremap <silent><script> M <SID>(MyOperatorNo1Pre)g@
onoremap <silent> M g@
lets:dotcommand =1

MyOperatorNo1-Demo4

ドットコマンドでもカーソル位置を保持できるようになりました。s:dotcommandは最初の実行の時だけ 0 で、ドットリピートの時は常に 1 です。カーソル位置の復元に winsaveview()winrestview()を使うようにしています。とても便利な関数です。

気が付けばオートコマンドはいらなくなっていますが、まあ、いいでしょう。今回のこれはこういうものがあればいいなという提案で、私の作ったこれでなくてもいいので Vim のプロがもっといいものを作って普及させてくれないかなー、と思っています。


ところで、 vim-event-DotCommandPre を書いているときに気が付いたんですが、ユーザー定義イベントに対するオートコマンド定義が存在するかどうかを

if exists('#User#DotCommandPre')    " fooendif

で取得できるみたいですね。これって意図された挙動なんでしょうか?

echo exists('#User#DotCommandPre')" 0autocmdUser DotCommandPre echo 'before dot!'
echo exists('#User#DotCommandPre')" 1

Viewing all articles
Browse latest Browse all 5608

Trending Articles



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