AutoHotkeyでどこでもVimっぽくカーソル移動したい（モードレス版）

<!-- vi: se tw=1000 et sw=2 ts=2 sts=2 ft=markdown wrap nolbr : -->


# 概要
AutoHotkey を用いて、vi っぽくカーソルを移動してみようという試みです。

前の記事「[AutoHotkeyでどこでもVimっぽく編集を行いたい（モーダル版）](https://qiita.com/azuwai2/items/97ba665b913803b2db61#%E6%A6%82%E8%A6%81)」のオマケの様なものです。

あちらの記事では、ahk ファイルをデデーンと上げただけだったので、今度は少し順を追って説明して行こうと思います。


こちらでは、スペースバー同時押し（SandS）というアプローチをとっているため、空白文字のキーリピートによる連続入力ができない、などの副作用があります。また、ロールオーバー打鍵時のスペースバー残りにより、所謂シフト残りのようなことが起こることがあります。（なるべく避けようとしていますが）


# 環境
 * windows10 Home 1909
 * AutHotkey v.1.1.33.02


日本語キーボード(106/109キー)環境を想定して作成されています。そのため、英語キーボード(101/104キー)を使用している場合などでは、予期せぬ動きをすることがあります。

そもそも作った本人が、常駐はさせていても、このスクリプトの機能をバリバリと呼び出すことがあまりないので、特に問題になっていません。
＃ vimode.ahk がある現状、もはやそれほどヘビーに使う物ではありませんし。。

それでも「今ほんのちょっとカーソルを動かしたいだけなのに！」という時、存外に役に立つのです。

[前の記事](https://qiita.com/azuwai2/items/97ba665b913803b2db61#%E6%A6%82%E8%A6%81)で紹介した、拙作 vimode.ahk と同時に常駐させて使用していますが、これといった問題は起こっていません。


# そもそも vi とは

耳馴染みのない「モーダルエディタ」という種類に分類される、なんだかカッコイイテキストエディタです。

~~言葉としては、モーダルダイアログとかの方が馴染みがあるかもしれませんが、なんだか響きがモータル（定命の者）と似ていて、格好良くないでしょうか？~~

とても一言では説明できる物ではないのですが、乱暴に一口で言おうとすると、純然たる「文字の入力」と、それ以外の操作（カーソルを移動したり削除したりコピペしたり等々）をモードを分けて行う物、でしょうか。

~~一般的なモードレスエディターが、ストリートファイター系のコマンドが「レバー→ボタン」と例えるのならば、モーダルエディタである vi / Vim は、バーチャファイター系のコマンドのようにボタンの羅列でファイルと格闘することになります（全然違う）~~

その威力が十全に振るわれる様は「カーソルが蝶のよう画面を舞い飛び、ファイルが粘土のように姿を変える」とまで例えられたとか、例えられないとか。

親指シフターの言う「指が喋り出す」に通じるキャッチーさ、かつ、胡散臭さがあります。余談ですが、私は親指シフトは挫折しましたからね。指で軽やかにお喋りをするためにはキーボードの選択肢が限られてしまうという問題があり、常用を断念しました。そして今では中指シフトです。ええ、指で喋ってますとも。少なくともローマ字入力に比べれば断然、喋ってる気がする。

おそらく少数派であろうと思いますが、私は WZ Editor の「viモード(WZマクロ)」から vi道へ入門しました。
C風のソースだったので自分でも弄り回したものです。

その後、JVim3.0 をしばらく使い、その後は、Kaoriya版の GVim6.2 ～ GVim8.2 までを追いかけて使わせて貰っています。

今では、三度の飯とドッコイドッコイなくらいには Vim が好きです。
もしもこの世から Vim が無くなったら等と考え出したら、ご飯も喉を通らなくなってしまうでしょう。
是非、Vim にはイモータル（不滅）でいて欲しいものです。

ただし、その素晴らしきモーダルエディタたる vi / Vim 風にカーソルを移動しよう、というトピックスであるにも関わらず、「モードレス版」とはこれ如何に？というのがここからのお話の筋道になります。

# SandS とは

スペースバーにスペースバー以外の働きを持たせようという考え方です。

スペースバーを単体で押して離した場合は、単なるスペースバーとして作用するようにしておくため、スペースを入力することができなくなってしまうわけではありません。

スペースバーを押し下げている間に、他のキーを同時に押した場合、異なった作用をさせよう。という趣旨です。

元々は SandS (Space and Shift) の名の通り、スペースバーにシフトキー（小文字→大文字）の機能を持たせる物ですが、これが発展して「スペースバーをシフトキーとして使うだけではもったいないのでは？ そうだ、Alt同時押しのようにまるで異なる機能を持たせてしまおう！」というものも、十把一絡げに SandS と呼ばれているようです？（要検証）

正しくは、「ワンショットモディファイア」というべきなのでしょうか。
（それともこれは、今は亡き「窓使いの憂鬱」用語なのでしょうか？）

SandS について、詳しくは別記事「[AutoHotkeyでSandS](https://qiita.com/azuwai2/items/e65af02c061ce80ccf91)」として切り出しました。

## AutoHotkey によるワンショットモディファイア

AutoHotkey では、`甲 & 乙::`により別機能を定義して、`甲::`にて本来の機能を定義することで、大抵のキーを簡単にワンショットモディファイア化することができます。

ただし、本来の SandS を定義するには、もうちょっとコツが必要です。

なぜなら、**Shift と 同時押しされるキーはたくさんある**からです。今の考え方では、`「Space & a::」「Space & b::」「Space & c::」「Space & d::」・・・`と全部定義していかなければなりません。

しかも日本語キーボードと英語キーボードでは、記号周りでは Shift 同時押しで表示されるものが違うのです。そんなものをイチイチ書くのはナンセンスです。


## サンプル(AutoHotkey)
ごく簡単なサンプルコードを提示します。
実験的なコードなので、実際に使おうとしてはいけません。

サンプルの題材としては、タイトル通りに hjkl カーソルです。
「Space and Shift」ではなく、単なる「Space and 何某」というワンショットモディファイアです。

```SandS_hjkl.ahk
Space::Send,{Space}
+Space::+Space
^Space::^Space

Space & h::Send,{Left}
Space & j::Send,{Down}
Space & k::Send,{Up}
Space & l::Send,{Right}
```

### スペースバー単打の定義
最初のホットキー`Space::Send,{Space}`は、単打でスペースバーを打ったときにはスペースだよ、ということを指示しています。
ホットキーではなく、`Space::Space`というリマップでも構いません。なんとなくこう書いてみただけです。
（ホットキーとリマップの違いはAutohotKeyマニュアルなどを参照。「::」の後ろにコマンドではなくキー名が直接書いてあればリマップと思っていれば、たぶん問題ないです。）

コレがないと、スペースバーを離してもスペースが入力されなくなってしまうので必須です。
AutoHotkeyでは、`甲 & 乙::` という定義を行っている場合、`甲`の元の機能が失われしまうので、このように定義してあげる必要があります。

### 修飾キー＋スペースバーの定義
次はリマップが2つあります。

    +Space::+Space
    ^Space::^Space

コレがないと、Shift+Space や Ctrl+Space が効かなくなってしまいます。
Excel で行選択や列選択ができなくなってしまうので必須です。（Excelを使っていないなら要らないのかも？）
必要に応じて「#Space::#Space」等も書いた方が良いかもしれないですね。

スペースバーが押し下げられている最中、というのが SandS のキモなのですが、他の修飾キーとスペースバーの組み合わせは適宜、定義してあげないといけません。

### hjkl カーソルの定義
下の4行がメイン部分です。

    Space & h::Send,{Left}
    Space & j::Send,{Down}
    Space & k::Send,{Up}
    Space & l::Send,{Right}

今更ですが、AutoHotkeyではホットキーの「::」の直後にコマンドを続けることで、1行で記述を済ますことができます。

とっても簡単ですね！

でもやっぱりコレではいろいろと足りていないのです。
~~たとえば、Shiftと同時押しで範囲選択しながらカーソル移動したいなー、と思ったらその定義もしなければなりません。Altと同時押しで、進んだり戻ったりしたいなーと思った場合にも、やはり定義が必要です。~~　その程度なら{Blind}でよかったです。

Shift同時押しだと全然違うことをやらせたいとか、その辺りを実際に使いながら育てていった結果として、出来上がった物が今回の成果物になります。


# 今回の成果物の機能説明
常駐させると、hjkl 等のおなじみの vi でカーソルを移動させる系のコマンドが使えるようになります。
一部の編集系も入っています。

大まかな機能を以下に列挙します。

|キー | Space同時押し | Space、Shift同時押し  | Space、Ctrlt、Shift同時押し |備考|
|:-:  |:-:            |:-:                    |:-:                          | :--|
| h   | ←            | Shift + ←            | Ctrl + Shift + ←           | カーソル移動、選択しつつ移動、選択しつつ単語単位で移動  |
| j   | ↓            | Shift + ↓            | Ctrl + Shift + ↓           | 同上  |
| k   | ↑            | Shift + ↑            | Ctrl + Shift + ↑           | 同上  |
| l   | →            | Shift + →            | Ctrl + Shift + →           | 同上  |
| f   | PgDn          | Shift + PgDn          | Ctrl + Shift + PgDn         | 同上  |
| b   | PgUp          | Shift + PgUp          | Ctrl + Shift + PgUp         | 同上  |
| 0   | Home          | Shift + Home          | Ctrl + Shift + Home         | 同上。本来は行内の移動なので、Ctrl同時押しは無くてよいのですが、{Blind}のせいでなんかそれなりに動きます。  |
| 4   | End           | Shift + End           | Ctrl + Shift + End          | 同上。$キーのつもりですが、Space & で記述するに当たっては「4」と書かざるを得ませんでした|
| o   | End,Enter     | Home,Enter,Up,End     | 定義無し                    | 行末へ行って改行、行頭へ行って改行して全行に戻る |
| x   | Delete        | BackSpace             | 定義無し                    | 削除、後退 |
| d   | Ctrl + x      | 定義無し              | 定義無し                    | 切り取り |
| i   | Tab           | 定義無し              | 定義無し                    | Tab挿入 |
| /   | Ctrl + f      | 定義無し              | 定義無し                    | 検索ダイアログ |
| y   | Ctrl + c      | 定義無し              | 定義無し                    | コピー |
| p   | Ctrl + v      | Excelで値貼り付け     | 定義無し                    | 貼り付け |
| u   | Ctrl + z      | 定義無し              | 定義無し                    | アンドゥ |
| r   | Ctrl + y      | 定義無し              | 定義無し                    | リドゥ |


他にも、Excelに特化したキーバインドがいくつか定義してあります。
ソースを見ればわかりますが、気が向いたときにでも、説明にも追々追記するかもしれません。

ある意味、スペースバーを押している間だけ、vi の通常モード（と言うには機能が余りにも貧弱ですが…）にいる。モーダルである、とも言えるのかもしれません。・・・いや、やはり私はこれをモーダルとは認めない！（即時前言撤回）

# 今回の成果物（スクリプト本体）

ファイルとしては、BOM付きの UTF-8 で保存してください。
拡張子 ahk が AutoHotkey に関連づけられている環境であれば、ファイルを起動するだけで常駐を開始します。

```vilike.ahk
#InstallKeybdHook

*Space::Send,{Blind}{Space}

;上下左右
Space & h::Send,{Blind}{Left}
Space & j::Send,{Blind}{Down}
Space & k::Send,{Blind}{Up}
Space & l::Send,{Blind}{Right}

;PageDn / PageUp
Space & f::Send,{Blind}{PgDn}

space & b::Send,{Blind}{PgUp}

;Home / End
Space & 0::Send,{Blind}{Home}
Space & 4::Send,{Blind}{End}

;行追加（i,I,a,Aは意味がないので定義しない）
Space & o::
	GetKeyState,state,Shift
	if state=U
	{
		Send,{End}
		Send,{Enter}
	}
	else
	{
		Send,{Home}
		Send,{Enter}
		Send,{Up}
		Send,{End}
	}
	return

Space & x::
	GetKeyState,state,Shift
	if state=U
		Send,{BS}
	else
		Send,{Delete}
	return

;Space & d::Send,^x
Space & d::
	GetKeyState,state,Shift
	if state=U
		Send,^x
	else
		Send,{Delete}
	return

Space & i::Send,{Tab}
Space & /::Send,^f

Space & y::Send,^c
Space & p::
	GetKeyState,state,Shift
	if state=U
		Send,^v
	else
	{
		Send,!e
		Send,s
		Send,v
		Send,{Enter}
	}
	return

Space & u::Send,^z
Space & r::Send,^y

;Excel
#ifWinActive ahk_class XLMAIN
Space & 1::Send,^{1}
Space & a::Send,{F2}
Space & i::
	Send,{F2}
	Send,^{Home}
	return
Space & c::
	Send,{F2}
	Send,{Home}
	Send,+{End}
	return
Space & (::Send,^{PgUp}
Space & )::Send,^{PgDn}
#ifWinActive


#ifWinActive セルの書式設定
Space & ,::Send,^{PgUp}
Space & .::Send,^{PgDn}
;Space & a::Send,!a
;Space & b::Send,!b
;Space & c::Send,!c
;Space & d::Send,!d
;Space & e::Send,!e
;Space & f::Send,!f
;Space & g::Send,!g
;Space & h::Send,!h
;Space & i::Send,!i
;Space & j::Send,!j
;Space & k::Send,!k
;Space & l::Send,!l
;Space & m::Send,!m
;Space & n::Send,!n
;Space & o::Send,!o
;Space & p::Send,!p
;Space & q::Send,!q
;Space & r::Send,!r
Space & s::Send,!s
;Space & t::Send,!t
;Space & u::Send,!u
;Space & v::Send,!v
;Space & w::Send,!w
;Space & x::Send,!x
;Space & y::Send,!y
;Space & z::Send,!z
#ifWinActive


;シフト残りで変換し損ねて確定してしまうのを防止。
Space & Enter::
	Send,{Space}	;変換
	Send,{Enter}	;確定
	return

Space & ,::
	Send,{Space}	;変換
	Send,{,}
	return

Space & .::
	Send,{Space}	;変換
	Send,{.}
	return

Space & z::Send,!{vkf3}
```

# 最後に

実は、こちらの vilike.ahk の方が、vimode.ahk よりも前にできあがっていた物になります。
vimode.ahk の方は、当時はまだ vimode.mayu という名前でした。

AutoHotkey_L が出てきたり、窓使いの憂鬱の開発が終了したりといろいろありましたが、
何はともあれ、今でも AutoHotkey という素晴らしいソフトウェアと共にあれることに感謝します。

もちろん Vim ともね！