秀丸エディタで今開いてるファイルのディレクトリ基点でポップアップメニューを表示して一発アクセスする

結論を言うと、

  • (1)指定位置に指定ディレクトリ配下のファイル一覧をポップアップメニューで表示するプログラムを用意
  • (2)秀丸エディタマクロから 1 を呼び出す

の二段階で実現できた。

成果物

qpmenu という名前で GitHub に公開してみた。

stakiran/qpmenu: Quick file access with popup menu for Windows

(1) は AutoHotkey で作った。(2) は秀丸エディタマクロで作った。

細かいインストール方法等は上記リンクを参照。git clone(あるいは Donwload ZIP ボタンでもいいけど)による入手と、mac ファイルの適用、あと AutoHotkey のインストールがわかるならインストールできると思う。

解説

技術的じゃない部分の話。

(1) メニュー表示するプログラムについて

物好きでなければ ソフトの小物たちさんの SbFolder を使うのが無難。2000年から続いてて、窓の杜でも取り上げられる品質だし、最近の更新も 2018/05/15 Ver 3.290 と新しいし、オプションも豊富できめ細かいし、動作も軽いと素晴らしい。

(追記) SbFolder を使ったバージョンも作ってみました。

github.com

ただ私は AutoHotkey の勉強がてら、自分で書いてみることにした。

要するに (1) のプログラムは何を使ってもよい。ただし、秀丸エディタ側から呼び出すために、

  • 表示位置をスクリーン座標で指定できること
  • 基点とするフォルダパスを指定できること

上記二つをサポートしている必要がある。SbFolder はサポートしている。

(2) 秀丸エディタマクロについて

上記 AutoHotkey プログラムを呼び出す用として書いたもの。SbFolder を呼び出したい場合は、一から書き直さなきゃいけない。

技術解説 qpmenu.ahk

今回作った qpmenu のコードを解説していく。まずはメニュー表示する AutoHotkey プログラムについて。

やってることは

  • A) 表示位置(X位置とY位置)と基点フォルダパスを受け取って、
  • B) 基点フォルダパス配下をサブフォルダ含めて走査してポップアップメニューをつくり、
  • C) つくったメニューを X 位置, Y 位置に表示させて、
  • D) 選択された項目(に相当するファイルパス)を開く

という感じ。

A) オプションの受け取り

受け取りには A_Args 変数を使っている。こいつにはコマンドライン引数が入っており、

for n, param in A_Args  ; For each parameter:
{
    MsgBox Parameter number %n% is %param%.
}

上記のようにして n 番目の引数 param を取り出せる。ただこれだけだと「X位置」「Y位置」「基点フォルダパス」をわかりやすく指定できないので、qpmenu では -x 10 -y 20 "C:\work\folder" みたいな指定方法を実現できるよう、あれこれ頑張っている。最終的には args.x とか args.unclassified[1] とかで各種値を取り出せるようにした。

※A_Args 変数はバージョン 1.1.27 からのサポートなので、古い AHK だと動作しない。

B) ポップアップメニュー作成

一番苦労した部分。

全体構造はこんな感じ。

parse(menuname, list_fullpath, counter, search_dir){
  Loop, Files, %search_dir%\*, F
  {
    ; ...
    Menu, %menuname%, Add, %itemname%%EMBED_COUNTER_DELIM%%curcount%, label_open
    ; ...
  }

  Loop, Files, %search_dir%\*, D
  {
    ; ...
    Menu, %menuname%, Add, %itemname%, :%new_menuname%
    ; ...
  }
}

Loop, Files, search_dir, F で search_dir フォルダ直下のファイル名を、Loop, Files, search_dir, D で search_dir フォルダ直下のフォルダ名を取得している。ファイルの場合はメニュー項目に追加していき、フォルダの場合は parse 関数を 再帰呼び出し してサブメニューを作っていく感じ。そう、再帰呼出しだ。プログラミングっぽい。

他にも工夫点が色々あって、語り出すと長くなりそうなので割愛。もう一つだけ重要なところ、メニュー作成方法について述べておく。

メニューは Menu 命令で制御する。以下のような感じ。

  • メニュー作成(Menu Add)
    • 1ファイルにつき1メニュー項目をつくる
    • 1フォルダにつき1サブメニューをつくる
  • メニュー項目のグレーアウト(Menu Disable)
    • 空フォルダの場合は「」とだけ書かれたグレーアウト項目をつくる
  • メニュー表示(Menu Show)

色々クセがあるのだが一番厄介なのは 「各メニュー項目」と「その項目を選んだ時に実行されるファイルパス」を紐付けること だった。特に Menu の仕様が貧弱で、項目選択時に取得できるのが「選択された項目の 項目名のみ」なのが痛かった。つまり 項目名というヒントだけで、その項目に対応するファイルパスを取得しないといけない

そのための qpmenu では 項目名 | 1 という風に、末尾に識別子の数字を付与することにした。見た目がかなりダサイが他に方法がないので致し方ない。この未熟仕様は今後改善していただきたいものだ(Menu Add 時に固有の識別子を指定できるようにすればいい)。

C) メニューを指定座標に表示

ここは(Bと比べると)比較的単純なのでコードで示す。

CoordMode Menu, Screen

; ...

posobj := determin_showpos(args)
showx := posobj.x
showy := posobj.y
Menu, %menuname_root%, Show, %showx%, %showy%

; ...

determin_showpos(args){
  ; デフォはマウス座標で, 他に指定があればそっちを使う感じに.
  pos := get_mouse_pos()

  if(is_not_empty_argument(args.x)){
    pos.x := args.x
  }
  if(is_not_empty_argument(args.y)){
    pos.y := args.y
  }

  Return pos
}

; ...

get_mouse_pos(){
  MouseGetPos, mousex, mousey

  mousepos := {}
  mousepos.x := mousex
  mousepos.y := mousey
  Return mousepos
}

長々書いたが、要するに、

  • Menu, ..., Show, %x%, %y% で 位置 (x, y) に表示できる
  • ただし座標指定をスクリーン絶対座標にするために CoordMode Menu, Screen を忘れないように!
  • マウス座標は MouseGetPos で取れる
  • qpmenu に -x とか -y とかで与えた座標は args から取ってくる(詳細解説は A を参照)

という感じ。

D) 選択された項目(に相当するファイルパス)を開く

要点は以下。

  • 選択されたメニュー項目名は A_ThisMenuItem で取れる
  • ファイルパス実行は Run, %fullpath% でいける

技術解説 qpmenu_from_hidemaru.mac

続いて秀丸エディタマクロの方。

やってること

  • (1)秀丸エディタで「今開いてるファイル」のパスを取得
  • (2)今のカーソル(キャレット)位置をスクリーン座標で取得
  • (3)1, 2 を使って qpmenu.ahk に渡すコマンドライン文字列を組み立てる
  • (4)組み立てたコマンドラインを run 文で実行

という流れ。

1 はハードコードで。

2 は xpixel キーワードと ypixel キーワード。

マクロスクリプト

$ahk_bin_path    = "C:\\Program Files\\AutoHotkey\\AutoHotkey.exe";
$qpmenu_path_ahk = currentmacrodirectory + "\\qpmenu.ahk";
$qpmenu_path_exe = currentmacrodirectory + "\\qpmenu.exe";
$qpmenu_path     = $qpmenu_path_ahk;

$DP    = "\"";
$arg_x = str(xpixel);
$arg_y = str(ypixel);
$arg_directory_of_opened_file = directory2;
$commandline = $DP + $ahk_bin_path + $DP + " " + $DP + $qpmenu_path + $DP + " -x " + $arg_x + " -y " + $arg_y + " " + $DP + $arg_directory_of_opened_file + $DP;

run $commandline;
if(result == 0){
    message "起動に失敗しました.\n> " + $commandline;
}

endmacro;

おわりに

秀丸エディタマクロを用いた連携にもだいぶ慣れてきた。どんどん便利にしていくぞい。