AutoHotKey

AutoHotKeyで、簡単なインクリメンタルサーチ辞書を作成する

目的

プログラミング言語の辞書的なものを作りたい。

参考

スクリプト構成

ドロップダウンリストを作成

上記のコマンドを利用することで、AutoHotKeyにおいてドロップダウンリストを利用できる。

この際、Sortオプションを指定することで、リストアイテムをインクリメンタルサーチすることができる。

ボタンを作成

ドロップダウンリストからの選択後、ボタンをクリックする(またはエンターキーを押す)ことで、次の処理に進めるようにする。

ドロップダウンリストの選択結果を変数に格納して、ペーストする

選択結果を変数に格納した後、上記のユーザ定義関数を利用して文字列をペーストする。

実際のスクリプト

長いのでEmbedしない。

後で内容変わるかもしれないが、現状のスクリプトの内容について簡単に補足。

DropDownListで選択するテキストは、(デフォルトでは)|区切りの引数で与える。この部分をヒアドキュメントにするとともに、joinオプションでヒアドキュメントの改行を展開時に|へ変換する。これにより、「単語」の部分を、単純な改行区切りで記述することができる。

GUI, Destroyを挟まないと、二回目に使用した時にその変数もうあるぞボケ的なエラーが出た。

実際に使用するとこんな感じ。
スクリーンショット

GUIウインドウ右上の「×」でウインドウを閉じると、次回実行時にエラーが出るので、誤って発動させた場合は何も選択せずにエンターキーを押すと良い。

AutoHotKeyで複数行のスニペットをヒアドキュメントで展開する

AutoHotKeyでスニペット展開を行うために、専用の関数を定義します。

この関数をホットストリングから呼び出すことで、TextExpander的なスニペット展開が可能になります。

しかし、複数行のスニペットを展開する場合は、TextExpanderほど単純ではありません。

当初は適切な方法がわからず、1行ずつ展開させていました。

;複数行展開するホットストリングの例
::test::
    PasteString("line1")
    send, {ENTER}
    PasteString("line2")
    return

展開結果

line1
line2

いちおうできてはいるのですが、オートインデントの影響を受ける上、PasteString関数の展開ごとにSleepを取っているので、ばかにならない時間がかかります。

しかし、ヒアドキュメントを利用すれば、複数行のテキストをPasteString関数に渡し、一発で展開することができます。

以下の例では、PasteString関数の引数をヒアドキュメントにしています。

;ヒアドキュメントによる複数行展開の例
::jj::
    PasteString("
    (LTrim;スクリプトの見栄えのために入れているタブを削除
    line1
    line2
    )")
    return

問題は、ここに変数を含める場合です。例えば、日付を自動挿入したい場合などが相当します。

以下の例では、引数の中で変数を展開しようとしています。

;変数を含める例
::jj::
    SetEnv, String, line
    PasteString("
    (LTrim
    %String%1
    %String%2
    )")
    return

展開結果

%String%1
%String%2

このように、二重引用符によってPasteString関数の引数がテキストとして扱われるため、変数が展開されません。一方、引数を二重引用符で括らないと、そもそもPasteString関数が正常に実行されません。

そこで、ヒアドキュメントを用いて、変数を含む複数行のテキストを予め別の変数に格納し、そのままPasteString関数に渡してみます。

;変数を含める例
::jj::
    ;テキストをString変数に格納
    SetEnv, String, line
    ;String変数を含む複数行のテキストをMultiLineString変数に格納
    SetEnv, MultiLineString,
    (LTrim
    %String%1
    %String%2
    )
    PasteString(MultiLineString)
    return

展開結果

line1
line2

このように、予め変数に格納してからPasteString関数に渡すことで、高速に複数行のスニペットを展開できます。

スニペットにおける変数の利用例

この方法を利用すると、例えば「現在日時」を含むスニペットを作成することができます。

また、クリップボードをスニペットに含めることも容易です。

AutoHotKeyのスクリプトファイル保存時「ついでに」リロード

上記ページのスクリプトを改変しました。

;ウインドウタイトルに
#IfWinActive C:\apps\AutoHotkey\AutoHotkey.ahk
^s::
    Send,^s
    Sleep,250
    Reload
return
#IfWinActive

oeditでの使用を前提にしていますが、編集中ファイルのフルパス表示しててCtrl+sが上書き保存なエディタならなんでもいけるはず。

最初はメニューバーから上書き保存していましたが、Ctrl+sをsendでもちゃんと保存します。その後スクリプトをリロードしますが、保存前の状態でリロードされることがあるので、sleepを挟んでいます。

ポイントは、元々あるショートカットキーに対して、本来の動作+αを簡単に割り振れるということです。ウインドウテキストとかで分岐させれば、使用するショートカットキーの組み合わせ数を抑えることができます。

Postach.ioフロントマター入力用スニペット

承前。

Postach.ioのDropbox投稿に必須のフロントマターを、簡単に入力できるようにした。

総じて、タイトルは書き終わってから入れたほうが楽だと思われ。

TextExpanderの場合

title: %filltext:name=タイトル%
date: %Y-%m-%d %H:%M
tags: 

タイトルをフィルイン。まあなくてもいい。
日時を自動挿入。

AutoHotKeyの場合(2014/07/27修正)

日時を自動挿入する。

Sublime Textの場合

<snippet>
<content>
title: 
date: $1
tags: 

$2
</content>
<tabTrigger>::</tabTrigger>
<!-- <scope></scope> -->
<description>Postach.ioフロントマターのテンプレート</description>
</snippet>

日時を展開する方法が見当たらないので、手動で入力した後、タブキーで$2位置に飛べるように。

AutoHotKeyのスニペット展開で、特定アプリのみ挙動を変える

※追記アリ

一部アプリとの干渉

上記ページの方法で、IMEの状態を問わず、AutoHotKeyを利用したTextExpanderライクなスニペット展開ができます。
スニペットの指定は、例えば以下のようになります。

1
2
3
::ahk::
PasteString("AutoHotKey")
return

ただし、例えばResophNotesでは正常に展開されず、単なる「クリップボードからのペースト」になりました。どうもクリップボードのハンドルがうまくいっていないようだったので、以下のようにすると作動しました。

1
2
3
4
PasteString(String){
Clipboard := String
Send,^v
}

ただし、こうすると当然クリップボードに残ってしまうので、たまに鬱陶しいことになります。eClipも併用しているので使えないこともないのですが。

そこで、以下のような形で切り分けを行いました。

解決策

まず、ResophNotes専用の関数を別に用意します。これは、同じスクリプトファイル内に同名の関数を記述することができないためです。

1
2
3
4
PasteString2(String){
Clipboard := String
Send,^v
}

ホットストリングの内部で、IfWinActiveコマンドを用いて分岐処理を行います。QWidgetはResophNotesのウインドウクラスです。

1
2
3
4
5
6
::ahk::
IfWinActive, ahk_class QWidget
PasteString2("AutoHotKey")
else
PasteString("AutoHotKey")
return

このようにすると、ResophNotesではPasteString2が、その他のウインドウではPasteStringが実行されます。

なお、複数のウインドウを指定する場合、以下のように記述します。

1
2
3
4
5
If WinActive(ahk_class QWidget) or WinActive("ahk_class Notepad")
PasteString2("AutoHotKey")
else
PasteString("AutoHotKey")
return

既存のスニペットを置換するため、oeditで以下の通り正規表現置換を行いました。

検索
: :{2}(.*):{2}\nPasteString\("(.*)"\)\nreturn

置換
: ::\1::\nIfWinActive, ahk_class QWidget\n\tPasteString2("\2")\nelse\n\tPasteString("\2")\nreturn

これで、特定のウインドウを除き、クリップボードを汚さないスニペット展開が可能になりました。

ところがどっこい

あとで思い直して、sleep時間を延ばすだけで解決しました。

アプリが重くて、Ctrl+Vの処理が、クリップボード復元処理の後に行われてしまったのが原因の模様。

AutoHotKeyでTextExpander

これも全てPhraseExpressってヤツのせいなんだ

MacでDashやらTextExpanderやらを覚えて数ヶ月。Windowsでもアプリケーション汎用的なスニペット展開アプリが欲しくなってきました。

最初に試したのが自称「TextExpander for Windows」ことPhraseExpressだったのですが、何も考えずに登録すると「確定待ち状態で展開される」上、動作があからさまに遅いものでした。

「TextExpanderよりAutoHotKeyに近い」とか書いてて思ったわけです。数年ぶりにAutoHotKey使おうよ、と。

AutoHotKeyの現在

なにげにまとまった日本語資料がなかったので簡単にまとめると…

  • AutoHotKeyの開発は一度停滞した
  • Forkとして「AutoHotKey L」が開発された
  • 一部機能が本家に取り入れられたりした
  • それにとどまらず、派生版が本家として配布されるに至った

「AutoHotKey L」がどこでも配布されていないし、「AutoHotKey L」特有の機能が、本家(AutoHotkey: macro and automation Windows scripting language)でダウンロードしたヤツでも利用できたので、間違いないと思われます。

で、インストールとか基本的な利用方法はAutoHotkey Wiki参照。

TextExpanderとして利用するためのPasteString関数

AutoHotKeyでは、ホットストリングを利用して、TextExpander的なスニペット展開ができます。

前にも書きましたが、辞書登録と比較した場合のTextExpanderの基本的な優位点は、「候補の中から選ぶ必要がない」ことと、「確定の手間が省ける」ことです。

現在のAutoHotKeyでは、sendコマンドで日本語も挿入できるのですが、IMEオン状態で実行すると「確定待ち状態で展開される」ことに変わりはありません。

そこで、文字列を一発入力するAutoHotkeyスクリプト - AutoHotkeyサンプル集を丸パクリして、PasteString関数をスクリプトファイルに組み込みます。

ついでに、日付を展開できるようにFormatTimeコマンドを利用して関数化します。

1
2
3
4
PasteDate(){
FormatTime, s,, yyyy-MM-dd
PasteString(s)
}

また、ホットストリングの終了文字を;に限定したかったので、以下のように指定しました。エスケープ文字に注意する必要があります。

1
#Hotstring EndChars ';

その上で、個々のスニペットは以下のように登録します。スクリプト実行型にする必要があります。

1
2
3
4
5
6
7
::ahk::
PasteString("AutoHotKey")
return
::dt::
PasteDate()
return

こうすると、「ahk;」とタイプすると「AutoHotKey」が、「dt;」とタイプすると「2014-07-20」のような文字列が展開されます。ペースト処理なので、IMEオンでも確定状態で展開されます。

スニペットを登録する際も、適当にテンプレートをコピペして穴埋めするだけで済むので非常に楽です。

さらに、スニペットを登録するスニペットを作ってみました。