emacsclint と同等の機能を AutoHotkey で実現する
はじめに
前回までの設定によって、Windows 10 で Emacs をそれなりに使えるように整えることができた。
Emacs を起動し、それから open-file 関数を使ってファイルを開くだけならば、これ以上とりわけ設定する必要はない、と思うかもしれない。だが、emacsclientのように、Emacs を起動すると同時に特定のファイルが開ければ使い勝手はかなりよいだろうし、起動中の Emacs で特定のファイルを外部から渡し、それをすぐに編集できるようにすることも同様だ。他には、他のウィンドウに隠れている Emacs のウィンドウを前面に動かすことや、最小化されている Emacs のウィンドウを表示することも出来ればよいだろう。
ここでは、emacsclient と同機能を AutoHotkey のスクリプトを利用して実現する。
emacsclint の機能概要
ksugitaさんが gnupack の更新時に説明をしていたので、それらを参照してほしい。
- gnupack 12.01-2015.01.11 リリース - gnupackの開発メモ
- gnupack 12.02-2015.01.17 リリース - gnupackの開発メモ
- gnupack 12.03-2015.02.01 リリース - gnupackの開発メモ
実現するものとして、"gnupack 12.03-2015.02.01 リリース"時点の "emacsclient" の挙動を念頭におく。この説明を整理するとこうなる。
- emacs と emacsclient の二つのプログラムが存在する
- emacsclient にファイルを渡すと既に起動している emacs 上でファイルを開く
- かつては emacs 実行後に emacsclint を実行しなければならなかった
- 今では emacs 未実行時に emacsclint を実行する場合、 emacs が実行される
そして、emacsclient を利用するためには、 [Home] Emacs Client にあるとおり、server-start を使ってサーバーを起動させること。こうすれば、emacsclient にファイルを渡すことで、既に起動している Emacs で渡したファイルを編集することができるようになる。
実現する内容
[Windows]gnupackのemacsclientw.exeを快適に使えるようにする。 - Qiitaで実現されている以下の機能を、emacsclient と server-start を使わずに実現する。
>emacsが起動してないときには起動
>起動してる時はアクティブ化
>ファイルを送ると読み込む
ここでは emacs を起動する際の処理として、runemacs.exe を直接実行するだけでなく、msys2 を使用して Windows 10 に Emacs 25.0.94.2 (IMEパッチ適用) を導入した - blechmusikの日記 で設定したように、バッチファイル経由で runemacs.exe を実行することにも対応する。
AutoHotkey 用スクリプトの使い方と内容
特定のフォルダに StartEmacs.ini を作成し、そこに
[path] emacs="my_runemacs.bat"
のように、emacs起動のために実行する実行ファイル(やバッチファイル)のパスを=の右側に書き込む。
それから同じフォルダの中に 後述のAutoHotkey用スクリプトを作成し、実行バイナリ化すれば、望みどおりの処理が可能となる。ファイルのドロップ処理は先人が作成しているものがあるので、それを利用する。ここでは複数ファイルを emacs に渡せるようにした。
スクリプトの書き方については、StartEmacs.ahk · GitHub を参考にした。
;; -*-mode: ahk; coding:utf-8-with-signature-dos ; tab-width : 4 -*-" /* -----emacsを起動するためのスクリプト--- emacsclientw.exe とサーバー機能を使っているかのように runemacs.exe を使う (emacsclientwとserverファイルのどちらも使用しない) emacsが起動してないときには起動、起動してる時はアクティブ化、ファイルを送ると読み込む。 msys2版emacsで使うことを想定している。 AutoHotKey_Lw / Ahk2Exe_Lで動作。 [1] コンパイルして適当な場所に置く emacs(を起動するバッチファイル)と同じ場所に置くとよい。 [2] iniファイルを作成する このプログラムと同じ場所にStartEmacs.iniを作成する。 内容は以下のとおり。 [path] emacs=<runemacs.exe(を起動するバッチファイル)のパス> [3] 起動する ファイルを送れば開く(複数ファイルの編集可)。 ショートカット作成や関連付けに使う。 以下のページを参考にしました。 [Windows]gnupackのemacsclientw.exeを快適に使えるようにする。 - Qiita http://qiita.com/acple@github/items/4be7eb774a28d574281e ---------------------------------------------------------------- */ #NoEnv SendMode Input SetWorkingDir %A_ScriptDir% #SingleInstance force DetectHiddenWindows,On ;; iniファイル名を指定 ini_file = StartEmacs.ini ;; ============================================================= ;; メイン実行部 ;; iniファイルからファイルのパスを取得する path_emacs := read_ini_file_path(ini_file) ;; 引数からファイルの情報を取得する files := GetArgs() ;; emacsを起動する(既に起動していない場合) ; 既に起動しているならば、フォアグラウンドに移す start_emacs(path_emacs) ; 起動後すぐに drag and drop をすると ; ファイルの編集に失敗することがあったので ; 一定時間をあけておく sleep, 300 ;; ファイルを個別にドラッグ・アンド・ドロップ drag_and_drop_files_to_emacs(files) ExitApp ; ======================================== read_ini_file_path(ini_file) { Try { If not (FileExist(ini_file)) { Throw "ini_file のパスが見つかりません`n" . "設定値:" . ini_file } IniRead, path_emacs, %ini_file%, path, emacs If not (FileExist(path_emacs)) { Throw "emacs起動用プログラムのパスが見つかりません`n" . "設定値:" . path_emacs } } Catch e { MsgBox, ファイル指定のエラー`n%e%`nプログラムを終了します ExitApp } return path_emacs } GetArgs() { global _ArgCount=%0% _Args := Object() Loop, %_ArgCount% { _Args.push(%A_Index%) } return _Args } ; ======================================== start_emacs(path_emacs) { ; WinGet, hwnd, ID, ahk_class Emacs hwnd := WinExist("ahk_class Emacs") ; hwnd := WinExist("ahk_exe Emacs") ; Emacs が既に起動している場合 if (hwnd) { ; emacs のアクティブ化 WinActivate, ahk_id %hwnd% return } Run, % path_emacs return } drag_and_drop_files_to_emacs(files) { Try { Loop, % files.length() { the_file := files[A_Index] ; 複数ファイルの存在を確認する if not (FileExist(the_file)) { Throw "編集対象のファイルが見つかりません`n" . "ファイル名:" . the_file } ; ファイルを Emacs に一つだけドロップする PostMessage, 0x233, HDrop(the_file), 0,, ahk_class Emacs sleep, 100 } } Catch e { MsgBox, ファイル指定のエラー`n%e%`nプログラムを終了します ExitApp } return } ; ======================================== ; My Scripts ; http://lukewarm.s101.xrea.com/myscripts/ ; ファイルドロップ関数 ; 指定ウィンドウに改行区切りで指定されたファイルをドロップする ; #17 [Make AHK drop files into other applications: post #17] - Posted 19 March 2014 - 07:54 AM ; http://autohotkey.com/board/topic/41467-make-ahk-drop-files-into-other-applications/?p=638376 ; #Include DragDrop.ahk HDrop(fnames,x=0,y=0) { characterSize := 2 fns:=RegExReplace(fnames,"\n$") fns:=RegExReplace(fns,"^\n") hDrop:=DllCall("GlobalAlloc" ,"UInt",0x42 ,"UInt",20+(StrLen(fns)*characterSize)+characterSize*2) p:=DllCall("GlobalLock","UInt",hDrop) NumPut(20, p+0) ;offset NumPut(x, p+4) ;pt.x NumPut(y, p+8) ;pt.y NumPut(0, p+12) ;fNC NumPut(1, p+16) ;fWide p2:=p+20 Loop,Parse,fns,`n,`r { DllCall("RtlMoveMemory" ,"UInt",p2 ,"Str",A_LoopField ,"UInt",StrLen(A_LoopField)*characterSize) p2+=StrLen(A_LoopField)*characterSize + characterSize } DllCall("GlobalUnlock","UInt",hDrop) Return hDrop }