LoginSignup
15
4

More than 5 years have passed since last update.

マウスジェスチャー on Neovim

Last updated at Posted at 2018-12-15

マウスジェスチャーを可能にするNeovimのプラグインgesture.nvimを作成しました。
以下のようにジェスチャーの入力をキーにマッピングできます。

" Drag中はジェスチャーが入力される
nnoremap <silent> <LeftDrag> :<C-u>call gesture#draw()<CR>
" Drag終了時に登録されたジェスチャーとマッチしたら実行する
nnoremap <silent> <LeftRelease> :<C-u>call gesture#finish()<CR>

" ジェスチャー登録
" 上下 = G
call gesture#register().up().down().noremap('G')
" 下上 = gg
call gesture#register().down().up().noremap('gg')

コンセプト

あまり長いジェスチャーは覚えられない、かつ入力ミスをしやすいので、
上下左右の組み合わせ以外でもバリエーションを増やせるようにしました。

ジェスチャーの1画の長さを区別する

以下のようにジェスチャー長の最大・最小を1画ごとに設定できるため、
同じ方向でも長さによって違う動作をマッピングできます。

" 右に25文字以下移動 = tabnext
call gesture#register().right({'max_length' : 25}).noremap(":\<C-u>tabnext\<CR>")

" 右に26文字以上移動 = tablast
call gesture#register().right({'min_length' : 26}).noremap(":\<C-u>tablast\<CR>")

バッファローカルなジェスチャーにする

map系のコマンドのようにジェスチャーをバッファローカルにすることが可能です。
例えば、以下のようにFileTypeイベントでファイルタイプごとに違う動作をマッピングできます。

augroup gesture
    autocmd!
    autocmd FileType vim call gesture#register().right().left().noremap(":\<C-u>source %\<CR>", {'buffer' : v:true})
augroup END

実装

プラグインを実装するにあたって、初めて触ったオプション・コマンド・関数を紹介します。

:set virtualedit=all

通常は文字がない場所にはカーソルを移動できないのですが、以下でそれを可能にします。

set virtualedit=all

ジェスチャーの開始・終了時にこのオプションを操作しています。
このオプションにこんなユースケースがあるとは思いませんでした。

:wundo, :rundo

列についてはvirtualeditで文字がなくてもカーソルを移動できるようになりますが、 行についてはできません。
なので、ジェスチャーの開始時にバッファを編集して十分な空行を追加し、ジェスチャーの終了時に戻しています。
その際にユーザーのundo履歴が破壊されないように、undo履歴を保存・復元する必要があります。
それを可能にするコマンドがwundo, rundoです。
wundoでファイルにundo履歴を保存し、rundoでファイルからundo履歴を読み込みます。

以下のような流れで保存・復元しています。

1. ジェスチャー開始(入力前)
    1. `tempname()`でundo履歴を保存するファイルを用意する
    2. `wundo`でundo履歴を保存する
    3. 空行を追加する
2. ジェスチャー入力
3. ジェスチャー終了(実行前)
    1. `undo`で追加した空行を削除する
    2. `rundo`で保存したundo履歴を読み込む

結構辛い実装になっているので、他に方法があれば知りたいです。

nvim_buf_set_virtual_text(), nvim_buf_add_highlight() (没)

nvim_buf_set_virtual_textは位置と[文字列, 適用するハイライトグループ名]の配列を与えて、バッファの行末後の文字のない部分に表示するapiで、
nvim_buf_add_highlightは位置とハイライトグループ名を与えてバッファの文字をハイライトするapiです。

以下のように両方を使えば文字の有無にかかわらずハイライトできるため、
これらを用いてジェスチャーの軌跡をハイライトする機能を作ろうとしました。

let s:b = bufnr('%')
let s:line_start = 0
let s:line_end = 0
let s:col_start = 10
let s:col_end = 15

call nvim_buf_set_virtual_text(s:b, s:line_start, s:line_end, [['    ', 'NONE'], ['virtual_text', 'TODO']], {})
call nvim_buf_add_highlight(s:b, 0, 'TODO', s:line_start, s:col_start, s:col_end)

当然同じバッファは同じハイライトになるので、1つのタブに同じバッファが複数あると軌跡が正確になりません。
これに気付かず途中まで作ってから没にしました。残念。

動機

Neovimでブラウザを操作する拙作ctrlb.nvimを使っていてもまだ、片手キーボード片手マウスという状況になることがあります。
ブラウザではマウスジェスチャー拡張を使っているので、Neovimとのコンテキストスイッチを減らすために欲しいなと思って作りました。
調べても過去の例が出てこなかったのでそもそも需要がなさそうですが、
マウスを触りたい気分になったら使ってみてください!

15
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
4