テキストファイルに記載されたファイルパスを開く秀丸エディタマクロ pathjump

たとえば a.txt に filename.ext という記載があったとして、ここにカーソルを合わせてる時に filename.ext を一発で開く……なんてことがしたかったので、マクロをつくった。

どんなマクロ?

d:\data\text\work_201903.txt を開いているとする。内容は以下。

……(中略)……
日報はこっちに移した nippou.txt#2019/03
……(中略)……

この時、「nippou.txt#2019/03」の部分にカーソルを合わせてから本マクロを呼び出すと、

  • d:\data\text\nippou.txt が開かれる
  • キーワード「2019/03」で検索が走り、最初にヒットしたところまでジャンプする

こんな挙動をする。

つまり記載した(相対)ファイルパスに一発でジャンプするマクロ。

記法としては以下三つがある。ファイル名のみ、行番号つき、検索キーワードつき。

  • filename.ext ← filename.ext を開く
  • filename.ext@20 ← filename.ext の 21 行目を開く
  • filename.ext#apple ← filename.ext を開いて apple で検索する

使い方

下記マクロを pathjump.mac など適当にファイルに保存後、秀丸エディタに登録。あとはキー割り当てなどを割り当てて呼び出しやすくする。私は F12 に割り当てている。

マクロ

// pathjumper
// カーソル位置にあるファイルパスを秀丸エディタで開くマクロ.
//
// 対応している表記
// - filename.ext         ← filename.ext を開く
// - filename.ext@20      ← filename.ext の 21 行目を開く
// - filename.ext#apple   ← filename.ext を開いて apple で検索する
//
// 仕様など
// - 表記中のファイルが存在しない場合は何もしない.
// - 表記中のファイルは「今開かれているファイルのあるディレクトリ」
//   を基準とした相対パスとして扱われる.
// - 表記として絶対パスは使えない.
//   例: C:\Data\text.txt
// - 表記の左右は行頭, 行末, 半角スペース, タブのいずれかでなければならない.
//    OK  xxx filename.ext yyy
//    NG  xxxfilename.extyyy
// - 行番号が大きすぎる場合は最終行にジャンプする

$curdir = directory;
#curx = x;
$curline = gettext(0, y, linelen ,y, 0);

if(strlen($curline) == 0){
    endmacro;
}
$c = midstr($curline, #curx, 1);
if($c == " " || $c == "\t"){
    endmacro;
}

// xxxxIxxx
//      ^
//      ここを一文字ずつ左にズレながら見る.
//      空白が見つかったら, その位置の一文字右が抽出開始位置.
//
// ただし先頭まで来ちゃったら開始位置は先頭とみなす.
#startpos = -1;
if(#curx == 0){
    #startpos = 0;
}else{
    #searchpos = #curx;
    while(#searchpos > 0){
        #searchpos = #searchpos - 1;
        $c = midstr($curline, #searchpos, 1);
        if($c == " " || $c == "\t"){
            #startpos = #searchpos + 1;
            break;
        }
    }
    if(#startpos == -1){
        #startpos = 0;
    }
}

#endpos = -1;
#lineendpos = linelen;
if(#curx == #lineendpos){
    #endpos = #lineendpos;
}else{
    #searchpos = #curx;
    while(#searchpos < #lineendpos){
        #searchpos = #searchpos + 1;
        $c = midstr($curline, #searchpos, 1);
        if($c == " " || $c == "\t"){
            //#endpos = #searchpos + 1;
            #endpos = #searchpos;
            break;
        }
    }
    if(#endpos == -1){
        #endpos = #lineendpos;
    }
}

#pickup_length = #endpos - #startpos;
$pickuped_word = midstr($curline, #startpos, #pickup_length);
$start_c = midstr($curline, #startpos, 1);
$end_c = midstr($curline, #endpos, 1);
//message "[" + $curline + "]\n[" + $pickuped_word + "]";

#idx_linejumpmark = strstr($pickuped_word, "@");
#idx_findjumpmark = strstr($pickuped_word, "#");
$openee_filename = $pickuped_word;
$openee_option = "";
if(#idx_linejumpmark != -1){
    $openee_filename = midstr($pickuped_word, 0, #idx_linejumpmark);
    $openee_linenumber = midstr($pickuped_word, #idx_linejumpmark + 1);
    $openee_option = "/k" + $openee_linenumber;
}else if(#idx_findjumpmark != -1){
    $openee_filename = midstr($pickuped_word, 0, #idx_findjumpmark);
    $openee_findoption = "/sCWRZ";
    $openee_findtext = midstr($pickuped_word, #idx_findjumpmark + 1);
    $openee_option = $openee_findoption + "," + "\"" + $openee_findtext + "\"";
}

// カレントディレクトリが秀丸エディタの macro 用フォルダになっているので
// 今開いているファイルのディレクトリを用いる.
$openee_fullpath = directory + "\\" + $openee_filename;

// 存在しないファイルパスで openfile すると新規になっちゃうのでガードする.
// 開けない → 無効, だとわかるので何もせずに終わるだけでいい.
if(existfile($openee_fullpath) == false){
    endmacro;
}

$commandline_for_openfile = $openee_option + " " + "\"" + $openee_fullpath + "\"";
//message $commandline_for_openfile;

openfile $commandline_for_openfile;
endmacro;

// [テスト]
// 筆者環境で実行することを想定.
//
// houtliner.mac
// xxx houtliner.mac@100
// houtliner.mac@9999 yyy
// xxx houtliner.mac#OK_if_not_outline_file yyy

実装の話

以下マクロプログラミングの話が続く。

アルゴリズム全体

こんな感じ

  • 1: カーソル X 位置を記憶
  • 2: 1を基点に、一文字ずつ左を見ていって、区切りに到達したら止まる
  • 3: 1を基点に、一文字ずつ右を見ていって、区切りに到達したら止まる
  • 4: 2と3の情報を使って、区切り間の文字列を取り出す
  • 5: 4 の文字列からファイル名部分とオプション(行番号 or キーワード)部分を取り出す
  • 6: 5 の情報を使って openfile 命令を呼び出す

ファイルパス表記はどうやって抽出する?

上記の 2 と 3 にあたるところ。

最初は「単語選択」→「コピー」みたいに操作を呼び出して済まそうと思ったけど、単語選択操作の精度がポンコツすぎたのでやめて、自力でパースすることにした。

パースをシンプルにするために「区切りとは何か」の明確なルールを設けた。以下のいずれか。

  • 行頭
  • 行末
  • 半角スペース
  • タブ

これ以外は受け付けない。たとえば

var = "filename.ext"

↑ こんな行から filename.ext を取り出すことはできない。「"」も区切りにすれば済むじゃないか、とはそのとおりだが、きりがないのでやめた。

抽出したファイルパスを開いて行番号ジャンプまで行うにはどうすればいい?

最初は run 命令でファイルを開く、down 命令で一行スクロール……みたいにゴリゴリ操作をエミュレートする方向で考えていたけど、run 命令でファイルを開いてもマクロの制御がそっち(run で開いたファイル)に移らないので没。

どうしたものかと悩んで、秀丸エディタのオプションを漁ったり、マクロ命令を漁ったりしてたら、別の方法が見つかる。openfile 命令。

  • 秀丸エディタには /k100 で 100 行目を開く、みたいなオプションがある
  • openfile 命令を使うと、このオプションがそのまま使える

というわけで openfile を採用。

テストはどうやってるの?

すまない、手作業でテキトーにやっただけ……。

別にファイルを破壊するマクロでもないので、挙動が気になるなら適当に試していただければ。