「検索ジャンプの include版」の版間の差分

提供: MeryWiki
ナビゲーションに移動 検索に移動
Sukemaru (トーク | 投稿記録)
修正と更新
MSY-07 (トーク | 投稿記録)
空行の除去、ウィキリンクの除去
 
(2人の利用者による、間の5版が非表示)
1行目: 1行目:
[[ポップアップメニューで検索先にジャンプ#include版のソースコード|>> もとのページ]]
ポップアップメニューで検索先にジャンプ(sukemaru 版)
== include版 ==
== include版 ==
 
[[includeライブラリ]] を使用し、設定内容を外部ファイルに保存します(◆settingEnable 変数で設定。デフォルト: 有効)。
includeライブラリを使用し、設定内容を外部ファイルに保存します(◆settingEnable 変数で設定。デフォルト: 有効)。


設定内容の読み込み/書き出しが可能になったので、<span style="color:#c00;">設定の変更はポップアップメニュー内の項目のチェック ON/OFF でできるようになります。 </span><br>
設定内容の読み込み/書き出しが可能になったので、<span style="color:#c00;">設定の変更はポップアップメニュー内の項目のチェック ON/OFF でできるようになります。 </span><br>
12行目: 6行目:
[[ファイル:Mery_ジャンプ_SS_2.png|link=https://www.haijin-boys.com/wiki/images/0/0f/Mery_ジャンプ_SS.png|スクリーンショット]]
[[ファイル:Mery_ジャンプ_SS_2.png|link=https://www.haijin-boys.com/wiki/images/0/0f/Mery_ジャンプ_SS.png|スクリーンショット]]


<div class="warningbox">
<div id="注1" class="warningbox">
* あらかじめ [[includeライブラリ]] をインストールしてください。
* あらかじめ [[includeライブラリ]] をインストールしてください。
* 設定内容の保存場所は Mery\Macros\MacroSettings\ジャンプ.json です<br> ※ <ジャンプ> の部分はこのマクロのファイル名)。
* 設定内容の保存場所は '''Mery\Macros\MacroSettings\<ジャンプ>.json''' <br> または '''%AppData%\Mery\MacroSettings\<ジャンプ>.jso'''n です。<br> (※ <ジャンプ> の部分はこのマクロのファイル名)
</div>
</div>


* 「▼ 通常版 初期設定項目 ▼」以降の設定項目は『初期値』としてのみ利用され、ソースコード内の設定項目を直接書き換えする必要はありません。<br> ただし、settingEnable 変数をを無効にする場合は、ソースコード内で変更する必要があります。 <br> settingEnable を無効にした場合は「[[#人柱版|人柱版]]」と同じ状態になるので、設定変更はソースコードの直接編集(変数の true/false の変更)でおこなう必要があります。
* 「▼ 通常版 初期設定項目 ▼」以降の設定項目は『初期値』としてのみ利用され、ソースコード内の設定項目を直接書き換えする必要はありません。<br> ただし、settingEnable 変数を完全に無効にする場合は、ソースコード内で変更する必要があります。<br> → settingEnable を無効にした場合は「人柱版」と同じ状態になるので、設定変更はソースコードの直接編集(変数の true/false の変更)でおこなう必要があります。


* はじめての設定変更のさいにエラーが発生する場合は、以下の ①~③ を試行してください。
# ポップアップメニューの「設定内容を初期化する」を実行して、ファイルを確認する<br> ( → JSON ファイルを新規作成して設定値を書き出します)。
# ポップアップメニューの「JSON ファイルを開く」を実行して、ファイルを確認する。
# JSON ファイルが正常に作成されない場合は、同梱の「ジャンプ.json」を '''上記の "設定ファイルの保存場所" フォルダ''' に配置してください。


* はじめての設定変更のさいにエラーが発生する場合は、
:<span style="color:#c00;">※「JSON ファイルの初期化」コマンドでファイルチェックのコードに不備があったので修正しました。 (2019/04/16)</span>
# ポップアップメニューの「設定内容を初期化する」を実行して、ファイルを確認してください( → JSON ファイルを新規作成して設定値を書き出します)。
# ポップアップメニューの「JSON ファイルを開く」を実行して、ファイルを確認してください。
# JSON ファイルが正常に作成されない場合は、同梱の「ジャンプ.json」を  Mery\Macros\MacroSettings\ フォルダに配置してください。


* 設定変更メニューを組み込んでいるため、「検索設定の変更」オプションが有効のときは、検索語が他の行でヒットしなかった場合でもポップアップメニューを表示します。<br> ただし、複数行選択状態で実行した場合(「表示行」で選択範囲が折り返しを含む場合も)、「英単語のみ」オプションが有効なときや「空行/空白行」がオプション無効のときに検索条件にたいして「無効な検索文字列」を検索した場合は、ポップアップメニューを表示せず、ステータスバーにエラー内容を表示します。


▼ 追加オプション ▼
※ include版ではドキュメントの行数制限(9,999,999行)があります。


* 検索のさいに「英単語のみ検索」するかしないかを選択できます(◆変数で設定)。
▼ 追加機能 ▼


:* ただし、このオプションを有効にして検索できるのは英単語のみです。<br> 欧文でも ascii 範囲外のアルファベットを使った単語では正常に機能しません。<br><span style="color:#c00;">このオプションを有効にすると日本語の文字列は検索できません。</span>
* 検索のさいに「英単語のみ検索」するかしないかを選択できます(◆変数で設定)。<br> これにより、ソースコードの1文字変数(''e.g. '''var s''';'')や '''<nowiki><a></nowiki>''' タグなどの検索がしやすくなります。
:* 有効にした場合は正規表現を使用しての検索になるので、処理がやや遅くなります。


:* 「英単語のみ」オプションが有効のときは、マクロ実行前の選択範囲の有無によらず、キャレット位置の '''単語''' を自動選択して既存の選択範囲から置きかえます 。
:* <span style="color:#c00;">「英単語のみ」オプションを有効にして検索できるのは英単語(JavaScript の正規表現で '''\w+''' )のみです。</span> <br> 単語の自動選択には Mery ビルトインのメソッドを使用しているので、英文でなくても単語の範囲選択までは行いますが、<span style="color:#c00;">日本語の文字列は検索できません。</span> <br> また、英文・欧文でも各種記号や空白文字、ascii 範囲外のアルファベットを使った単語でも検索できません。
:* このオプションを有効にした場合は正規表現を使用しての検索になるので、処理がやや遅くなります。


[[ポップアップメニューで検索先にジャンプ#include版のソースコード|>> もとのページ]]
<div id="注2" class="warningbox">
* メニューに検索ヒットした行が表示されないときは、<br> メニューの「英単語のみ」のチェックをはずしてください。
</div>


* 文字列の検索のさいに「ヒット件数表示」オプションが有効な場合、選択範囲が「何件目」のヒット箇所かの表示を追加します(ジャンプしたあとの選択範囲が「何件目」のヒット箇所かも表示します)。


== ソースコード ==
== ソースコード ==
最終更新: 2018/12/20
最終更新: 2019/04/16 (Quitメソッドを削除/JSON ファイルの初期化のコードを修正)


※ポップアップメニューの表示に unicode 文字を使用していますので、シフトJISなどで保存しないでください。
※ポップアップメニューの表示に unicode 文字を使用していますので、シフトJISなどで保存しないでください。
<source lang="javascript">
<syntaxhighlight lang="javascript">
#title = "ジャンプ"
#title = "ジャンプ"
#tooltip = "ポップアップメニューで検索先にジャンプ"
#tooltip = "ポップアップメニューで検索先にジャンプ"
49行目: 50行目:


/**
/**
*
* ※※ メニューに検索ヒットした行が表示されないときは、メニューの「英単語のみ」のチェックをはずしてください ※※
*
  * ------------------------------------------------------------
  * ------------------------------------------------------------
  * "ポップアップメニューで検索先にジャンプ"
  * "ポップアップメニューで検索先にジャンプ"
  * Original Copyright: 手石 (2014/04/04)
  * Original Copyright: 手石 (2014/04/04)
  * ------------------------------------------------------------
  * ------------------------------------------------------------
  * Modified by: sukemaru (2018/11/12 - 2018/12/20)
  * Modified by: sukemaru (2018/11/12 - 2019/04/16)
  * ------------------------------------------------------------
  * ------------------------------------------------------------
  *
  *
  * ◆ include版 ◆
  * 【include版】での追加機能
  * 「includeライブラリ」を使用
  * 「includeライブラリ」を使用
*  検索設定はポップアップメニューから変更可
  *  設定内容を外部 JSON ファイルに書き出し/読み込み
  *  設定内容を外部 JSON ファイルに書き出し/読み込み
*  設定項目はポップアップメニューから変更可
  * 「英単語のみ検索する」に対応
  * 「英単語のみ検索する」に対応
  *  選択範囲が何件目のヒットかを表示
  *  選択範囲が何件目のヒットかを表示可
*  ヒットした行が 30 より多いときはメニューに「※さいごの行※」へのショートカットを追加
*
* 【人柱版】からの追加機能
* 「大文字/小文字の区別」に対応(→ ■通常版へ逆移植済み)
* 「空行+空白行」の検索に対応
* 「ヒット件数(出現回数)」を表示可
*  スペースキーでポップアップメニューをキャンセル可
* 「検索履歴に残さない」を追加(→ ■通常版へ逆移植済み)
* 「行の表示方法」を Mery.ini から取得可
* 「自動マーカー」オプションを廃止(デフォルト: 自動マーカー有効)
* 「検索所要時間」の表示可(→ ■通常版へ逆移植済み)
*  検索ジャンプ後のスクロールの挙動を調整(→ ■通常版へ逆移植済み)
* 「変数/関数の定義行」のマーキング
*    (※2019/03/12 編集モードを限定 "JavaScript", "MeryMacroJS")
  *
  *
  */
  */


var start = new Date(); // 所要時間の計測開始
var start = new Date(); // 所要時間の計測開始
 
var setting = {};
var setting = {};


// ---------- ▼ include版 追加設定項目 ▼ ----------
// ---------- ▼ 【include版】 追加設定項目 ▼ ----------


// ◆このマクロの「設定を変更するための項目」をポップアップメニューに表示するか?
// ◆このマクロの「設定を変更するための項目」をポップアップメニューに表示するか?
   setting.settingEnable = true; // ◆初期値 = true; (表示しない)
   setting.settingEnable = true; // ◆初期値 = true; (表示する)
 


// ◆英単語のみを検索する? (true; する / false; しない)
// ◆英単語のみを検索する? (true; する / false; しない)
   setting.wholeWordEnable = false; // ◆初期値 = false; (しない)
   setting.wholeWordEnable = false; // ◆初期値 = false; (しない)


// ---------- ▼ 通常版 初期設定項目 ▼ ----------
// ■行の表示方法 (true; 論理座標 /false; 表示座標)
// ※ ◆人柱版◆ の追加設定で autoLineModeEnable が true の場合は無効(自動設定)
  setting.logical = true; // ■初期値 = true;  (論理座標)
// ■検索文字列の強調表示を残すか? (true; 残す / false; 残さない)
// ※ ◆人柱版◆ の追加設定で historyDisable が true の場合は無効(強調表示しない)
  setting.highlightEnable = true; // ■初期値 = true; (残す)
// ---------- ▼ 人柱版 初期設定項目 ▼ ----------
// ◆人柱版のオプションを有効にすると処理が遅くなります


// ◆検索処理の所要時間を表示するか? (true; する / false; しない)
// ---------- ▼ 【通常版】 初期設定項目 ▼ ----------
  setting.timerEnable = true; // ◆初期値 = true; (する)


// ◆行の表示方法を Mery.ini から自動で読み込むか? (true; 読み込む /false; 読み込まない)
  setting.logical = true;
   setting.autoLineModeEnable = false; // ◆初期値 = false; (読み込まない)
   setting.highlightEnable = true;


// ◆検索文字列を「検索履歴」に残さない? (true; 残さない / false; 残す)
// ---------- ▼ 【人柱版 → 通常版 逆移植】 設定項目 ▼ ----------
// ※ historyDisable が true の場合、通常版の設定 highliteEnable は無効になります(強調表示しない)
  setting.historyDisable = false; // ◆初期値 = true; (残す)


// ◆検索文字列の「大文字/小文字」を区別するか? (true; 区別する / false; 区別しない)
  setting.timerEnable = true;
   setting.caseEnable = false; // ◆初期値 = false; (区別しない)
  setting.historyDisable = false;
   setting.caseEnable = false;


// ◆空行/空白行を検索対象にするか? (true; する / false; しない)
// ---------- ▼ 【人柱版】 初期設定項目 ▼ ----------
  setting.blankEnable = false; // ◆初期値 = true; (します)


// ◆ヒット件数(出現回数)を表示するか? (true; 表示する / false; 表示しない)
  setting.autoLineModeEnable = false;
   setting.countEnable = false; // ◆初期値 = true; (表示します)
   setting.blankEnable = true;
  setting.countEnable = true;


// ---------- ▼ 手石版 初期設定項目 ▼ ----------
// ---------- ▼ 【手石版】 初期設定項目 ▼ ----------


// ●ポップアップメニューに表示する文字数
   setting.menuWidth = 65;
   setting.menuWidth = 65;


124行目: 121行目:


// JSON ファイルから設定を読み込む
// JSON ファイルから設定を読み込む
if ( setting.settingEnable ) {
if ( initialSettings.settingEnable ) {
   setting = IO.Deserialize( setting, json );
   setting = IO.Deserialize( setting, json );
}
}


/* ポップアップメニューの「行番号の表示方法」 */
/* ■ポップアップメニューの「行番号の表示方法」 */
// Mery.ini から「行の表示方法」を取得する
// Mery.ini から「行の表示方法」を取得する
if ( setting.autoLineModeEnable ) {
if ( setting.autoLineModeEnable ) {
   var iniText = IO.LoadFromFile( MeryInfo.GetIniPath(), "utf-8" );
   var iniText = IO.LoadFromFile( MeryInfo.GetIniPath(), "utf-8" );
   $LineColumnView = Number( iniText.match( /^LineColumnView=[01]$/m ).shift().slice( -1 ) );
   var $LineColumnView = + iniText.match( /^LineColumnView=[01]$/m ).shift().slice( -1 );
   setting.logical = ! $LineColumnView;
   setting.logical = ! $LineColumnView;
}
}
142行目: 139行目:
var sx = ScrollX,  sy = ScrollY;
var sx = ScrollX,  sy = ScrollY;


// 選択範囲の先頭位置(行番号)と、文書全体の行数(論理行/表示行)
// 文書全体の行数(論理行/表示行)
var ty = s.GetTopPointY( posMode );
var lines = d.GetLines( lineMode );
var lines = d.GetLines( lineMode );
var lineStatus = "全体の行数: " + SeparateNum( lines )
var lineStatus = "全体の行数: " + SeparateNum( lines )
                 + ( setting.logical ? " 行 ( 論理行 )" : " 行 ( 表示行 )" );
                 + ( setting.logical ? " 行 ( 論理行 )" : " 行 ( 表示行 )" );
// メニューにピン止めする固定アイテムの字下げ = EN SPACE * ( 文書全体の行数のケタ数 / 2 )
// 9,999,999行 制限オーバーのときは終了
var linesWidth = lines.toString().length; // 全体の行数のケタ数
if ( lines >= 10000000 ) {
var menuIndent = "          ".slice( 0 - Math.round( linesWidth / 2 ) );
   Status = "9,999,999行 制限オーバー。 文書"
 
        + lineStatus + TimerElapsed( new Date(), start );
/* 検索文字列の準備 */
var blank = new RegExp( "^[\t  ]*$" , "" ); // 空白行の正規表現
var re; // "空白行の正規表現検索" フラグ
// 選択範囲なしならキャレット位置の単語を選択する
if ( s.IsEmpty ) {
  var pos = s.GetActivePos(); // "選択範囲なし" のフラグ
  s.SelectWord();
}
var st = s.Text; // 検索文字列
 
// 選択範囲の末尾が改行 "\n" なら検索文字列から除外する
if ( ! st || st.charAt( st.length - 1 ) == "\n" ) {
   st = st.replace( /\n$/g , "" );
  // 自動マーカーを効かせるために選択範囲も調整する
  var anc = s.GetAnchorPos();
  var act = s.GetActivePos(); // "末尾調整" フラグ
  if ( anc < act && ! ( setting.blankEnable && st.match( blank ) ) ) {
    s.SetActivePos( act - 1, true );
  }
  if ( act < anc && ! ( setting.blankEnable && st.match( blank ) ) ) {
    s.SetAnchorPos( anc - 1 );
    s.SetActivePos( act, true );
  }
  if ( setting.blankEnable && st.match( blank ) ) {
    re = true; // "空白行の正規表現検索" フラグ
  }
}
// 文書末尾の空白行の場合でも "空白行の正規表現検索" フラグ
else if ( ty == lines && st == d.GetLine( ty, lineMode )
      && setting.blankEnable && st.match( blank ) ) {
  re = true;
}
}
else {


// "キャンセル" フラグ
  // メニューにピン止めする固定アイテムの字下げ = EN SPACE * ( 文書全体の行数のケタ数 / 2 )
var cancel = 5;
   var linesWidth = lines.toString().length; // 全体の行数のケタ数
if ( ! setting.blankEnable && ! st ) {
  var menuIndent = "          ".slice( 0 - Math.round( linesWidth / 2 ) ); // (U+2002)
   cancel = 1; // 検索文字列がないとき(空行)
}
else if ( st.match( /\n/g ) ) {
  cancel = 2; // 検索条件エラー(選択範囲に改行あり)
}


// ポップアップメニューでジャンプ先リスト
  /* 検索文字列の準備 */
else {
  var blank = new RegExp( "^[\t  ]*$" , "" ); // 空白行の正規表現
  var re; // "空白行の正規表現検索" フラグ


/* ポップアップメニューの準備 */
  // 「英単語のみ」オプションが有効のときは、選択範囲の有無によらず
  var m = CreatePopupMenu(),  flags;
  // キャレット付近の単語を自動選択する(キャンセルしても単語選択のまま残す)
  if ( setting.wholeWordEnable && ! s.Text.match( /^[\t  ]*\n/ ) ) {
    if ( s.GetActivePos() < s.GetAnchorPos() ) {
      s.Collapse( meCollapseEnd );
    }
    s.SelectWord();
  }


   // include 版の設定変更メニュー
   // 選択範囲なしならキャレット位置の単語を自動選択する
   if ( setting.settingEnable ) {
   if ( s.IsEmpty ) {
     SettingsMenu();
     var pos = s.GetActivePos(); // "選択範囲なし" のフラグ
    s.SelectWord();
    // 先頭部分に $ があるならそれも含める (JavaScript の $変数名 の検索に使用)
    if ( d.Text.charAt( s.GetAnchorPos() - 1 ) == "$" ) {
      var $act = s.GetActivePos();
      s.SetAnchorPos( s.GetAnchorPos() - 1 );
      s.SetActivePos( $act, true );
    }
   }
   }


   // 「文字列」の検索では先頭に「キャンセル」のアイテムを表示
   var st = s.Text; // 検索文字列
   if ( ! re ) {
   var ty = s.GetTopPointY( posMode ); // 選択範囲の先頭位置(行番号)
    m.Add( menuIndent + "キャンセル & ", 0 ); // Space キーでキャンセル可
  var by = s.GetBottomPointY( posMode ); // 選択範囲の末尾位置(行番号)
    m.Add( "", 0, meMenuSeparator );
 
    // Grand total, Hit, Variables
  // 選択範囲の末尾が改行 "\n" なら検索文字列から除外する
    var mArray = [], rArray = [],  t ,  me ,  gt = 0,  hit = 0,  v = 0,  check = "";
  if ( ! st || st.charAt( st.length - 1 ) == "\n" ) {
    var stQuote = st.replace( /\W/g , "\\$&" );
  st = st.replace( /\n$/g , "" );
     if ( setting.countEnable ) {
     // 自動マーカーを効かせるために選択範囲も調整する
      // アクティブ行(検索開始行)全体の文字列を取得
    var anc = s.GetAnchorPos();
      var activeLine = d.GetLine( ty, lineMode );
    var act = s.GetActivePos(); // "末尾調整" フラグ
      var tx = s.GetTopPointX( posMode );
    if ( anc < act && ! ( setting.blankEnable && blank.test( st ) ) ) {
      // 行内のヒット数とヒット総数カウント用
       s.SetActivePos( act - 1, true );
       var reg = new RegExp(
    }
          ( setting.wholeWordEnable ? ( "\\b" + stQuote + "\\b" ) : stQuote )
    if ( act < anc && ! ( setting.blankEnable && blank.test( st ) ) ) {
        , ( setting.caseEnble ? "g" : "gi" ) );
      s.SetAnchorPos( anc - 1 );
      s.SetActivePos( act, true );
    }
    if ( setting.blankEnable && blank.test( st ) ) {
      re = true; // "空白行の正規表現検索" フラグ
     }
     }
   }
   }
   // 「空行+空白行」の検索ではパターンごとの配列を使用する
   // 文書末尾の空白行の場合でも "空白行の正規表現検索" フラグ
   else {
   else if ( ty == lines && st == d.GetLine( ty, lineMode )
    var hArray = [],  nArray = [],  wArray = [];
            && setting.blankEnable && blank.test( st ) ) {
    // Hit ( match completely ), Blank line ( n + w ), New line ( ^$ ), White space ( ^[\s ]+$ )
     re = true;
     var h = 0,  b = 0,  n = 0,  w = 0;
   }
   }


/* 文書全体を行単位で分割し、配列に */
  /* "キャンセル" フラグ */  
   for ( var a = [ "" ], i = 1; i <= lines ; i ++ ) { // a[0] = "" / i = 行番号
   var cancel = 7;
    a.push( d.GetLine( i, lineMode ) );
  // 検索文字列がないとき(空行)
  if ( ! setting.blankEnable && ! st ) {
    cancel = 1;
  }
  // 検索条件エラー(複数行: 選択範囲に改行あり)
  else if ( new RegExp( "\\n" , "" ).test( st ) ) {
    cancel = 2;
  }
  // 検索条件エラー(複数行: 「表示行」& 選択範囲に折り返しあり)
  else if ( ty != by && ! setting.logical ) {
    cancel = 3;
  }
  // 検索条件エラー(「英単語のみ」有効で検索文字列が非・英単語のとき)
  else if ( ! re && setting.wholeWordEnable && st.match( /\W/ ) ) {
    cancel = 4;
   }
   }


/* 検索文字列がヒットした行をポップアップメニューのアイテムとして取得する */
  // ポップアップメニューでジャンプ先リスト
   for ( var i = 1, len = a.length; i < len; i ++ ) { // a[0] = 空要素、i = 行番号 => y
   else {
    // アクティブ行は左に ✓ チェックマーク付き
    flags = ( i == ty ) ? meMenuChecked : 0; // + meMenuGrayed


     // 「検索文字列」がヒットしたら(「大文字/小文字」「英単語のみ」オプションに対応)
  /* ポップアップメニューの準備 */
     if ( ! re && FindCaseTF( a[i] ) >= 0 ) {
    var m = CreatePopupMenu(),  flags;
       // 総ヒット数フラグが有効の場合
 
     // 【include版】の設定変更メニュー
    if ( initialSettings.settingEnable ) {
      SettingsMenu();
    }
 
    // 「文字列」の検索では先頭に「キャンセル」のアイテムを表示
     if ( ! re ) {
      m.Add( menuIndent + "キャンセル & ", 0 ); // Space キーでキャンセル可
      m.Add( "", 0, meMenuSeparator );
      // Menu Array, Total, Grand Total, Hit, Variables, Me
      var mArray = [], gtArray = [],  t ,  me ,  gt = 0,  hit = 0,  v = 0,  check = "";
       var stQuote = st.replace( /\W/g , "\\$&" );
       if ( setting.countEnable ) {
       if ( setting.countEnable ) {
         rArray.push( [ i, gt ] );
         // アクティブ行(検索開始行)全体の文字列を取得
         // 行内でのヒット回数
        var activeLine = d.GetLine( ty, lineMode );
         t = a[i].match( reg );
        var tx = s.GetTopPointX( posMode );
        // 選択範囲が「何件目のヒット」か
        // 「行内のヒット数」と「ヒット総数」カウント用の正規表現
        if ( i == ty ) {
        var countReg = new RegExp(
          me = ( t.length > 1 && a[i].substr( 0, tx - 1 ).match( RegExp( reg ) ) )
            ( setting.wholeWordEnable ? ( "\\b" + stQuote + "\\b" ) : stQuote )
                  ? gt + 1 + a[i].substr( 0, tx - 1 ).match( RegExp( reg ) ).length
          , ( setting.caseEnable ? "g" : "gi" )
                  : gt + 1;
        );
      }
    }
    // 「空行+空白行」の検索ではパターンごとの配列を使用する
    else {
      var hArray = [],  nArray = [],  wArray = [];
      // Hit (match completely), Blank line ( n + w ), New line ( ^\n ), White space ( ^[\s ]+$ )
      var h = 0,  b = 0,  n = 0,  w = 0;
    }
 
  /* 文書全体を行単位で分割し、配列に */
    for ( var a = [ "" ], i = 1; i <= lines ; i ++ ) { // a[0] = ""、 i = 行番号
      a.push( d.GetLine( i, lineMode ) );
    }
 
  /* 検索文字列がヒットした行をポップアップメニューのアイテムとして取得する */
    for ( var i = 1, len = a.length; i < len; i ++ ) { // a[0] = 空要素、i = 行番号 → y
 
      // アクティブ行は左に ✓ チェックマーク付き
      flags = ( i == ty ) ? meMenuChecked : 0; // + meMenuGrayed
 
      // 「検索文字列」がヒットしたら(「大文字/小文字」「英単語のみ」オプションに対応)
      if ( ! re && FindCaseTF( a[i] ) >= 0 ) {
         // 「ヒット総数」フラグが有効の場合
         if ( setting.countEnable ) {
          gtArray.push( [ i, gt ] );
          t = a[i].match( countReg ); // 行内でのヒット回数
          // 選択範囲の検索文字列が「何件目のヒット」か
          if ( i == ty ) {
            me = ( t.length > 1 && a[i].substr( 0, tx - 1 ).match( countReg ) )
                    ? gt + 1 + a[i].substr( 0, tx - 1 ).match( countReg ).length
                    : gt + 1;
          }
          gt += t.length; // 総ヒット回数(大文字/小文字を区別できる)
          check = Marking(); // 右側につけるマーキング
 
          // 変数/関数の定義行はメニューの先頭にも置く
          if ( new RegExp( "←" , "" ).test( check ) ) {
            m.Add( Labeling( a[i], i ) + check, i, flags );
            v ++; // "変数/関数の定義行" のフラグ
          }
        }
        // 行頭空白を除去、空白文字を半角スペースに置換、文字数を圧縮
        // 「検索文字列がヒットした行」を配列に格納
        mArray.push( [ Labeling( a[i], i ) + check, i, flags ] ); // ヒットした行
        hit ++; // ヒットした行数
      } // end if () { 検索文字列がヒットしたら }
 
      // 「空行+空白行」の検索ではサブメニュー用の配列に分別
      else if ( re && blank.test( a[i] ) ) {
        b ++; // 空行+空白行の総数: Blank line ( n + w )
        if ( a[i] == st ) {
          hArray.push( [ Labeling( a[i], i ), i, flags ] ); // 一致した行
          h ++; // 一致した行数: Hit ( match completely )
        }
        if ( st && ! a[i] ) {
          nArray.push( [ Labeling( a[i], i ), i, flags ] ); // 空行 a[i] == ""
          n ++; // 空行数: New line ( /^$/ )
        }
        else if ( a[i] ) {
          wArray.push( [ Labeling( a[i], i ), i, flags ] ); // 空白行 a[i].length > 0
          w ++; // 空白行数: White space ( /^\s+$/ )
         }
         }
        gt += t.length; // 総ヒット回数(大文字/小文字を区別できる)
      } // end else if () { 「空行+空白行」の検索 }
        check = Marking(); // 右側につけるマーキング
    } // end for () { ヒットした行を取得する }


        // 変数/関数の定義行はメニューの先頭にも置く
    // 配列から「検索文字列がヒットした行」をポップアップメニューに追加する
        if ( check.match( /←/ ) ) {
    if ( ! re && hit ) {
          m.Add( Labeling( a[i], i ) + check, i, flags );
      if ( v ) {
          v ++;
        m.Add( "", 0, meMenuSeparator );
      }
      if ( hit > 30 ) {
        // ヒットした行が 30 より多いときは「※さいごの行※」を追加
        m.Add( " ※  さいごの行  ( 行番号: " + mArray[ hit - 1 ][1] + " 行目 )  ※", mArray[ hit - 1 ][1] );
        m.Add( "", 0, meMenuSeparator );
      }
      if ( v < hit ) {
        for ( var i = 0; i < hit; i ++ ) {
          // 10件ごとにセパレータ、30件ごとに「キャンセル」行を追加
          AddCancelLines( m, i );
          // 配列から「検索文字列がヒットした行」をポップアップメニューに追加
          m.Add( mArray[i][0], mArray[i][1], mArray[i][2] );
         }
         }
       }
       }
      // 行頭空白を除去、空白文字を半角スペースに置換、文字数を圧縮
     }
      // 「検索文字列がヒットした行」を配列に格納
      mArray.push( [ Labeling( a[i], i ) + check, i, flags ] ); // ヒットした行
      hit ++; // ヒットした行数
     } // end if () { 検索文字列がヒットしたら }


     // 「空行+空白行」の検索ではサブメニュー用の配列に分別
     // 配列から「空行+空白行」をポップアップメニューに追加する
     else if ( re && a[i].match( blank ) ) {
     if ( re && b ) {
       b ++; // 空行+空白行の総数: Blank line ( n + w )
       if ( b != h ) { // ( n + w ) > h
      if ( a[i] == st ) {
        // 「すべての空行+空白行」の総数をピン止め
         hArray.push( [ Labeling( a[i], i ), i, flags ] ); // 一致した行
         m.Add( menuIndent + "すべての空行+空白行: " + SeparateNum( b.toString() ) + " 行", 0 ); // , meMenuGrayed
         h ++; // 一致した行数: Hit ( match completely )
         m.Add( "", 0, meMenuSeparator );
       }
       }
       if ( st && ! a[i] ) {
       if ( n && st ) { // ( nArray.length > 0 ) && ( st.length > 0 )
         nArray.push( [ Labeling( a[i], i ), i, flags ] ); // 空行 a[i] == ""
        var sm1 = CreatePopupMenu();
        n ++; // 空行数: New line ( /^$/ )
         m.AddPopup( menuIndent + "すべての空行: " + SeparateNum( n ) + " 行 (&N)", sm1 );
        for ( var i = 0; i < n; i ++ ) {
          AddCancelLines( sm1, i ); // 30件ごとに「キャンセル」行を追加
          // 「空行」を「すべての空行」サブメニューに追加
          sm1.Add( nArray[i][0], nArray[i][1], nArray[i][2] );
        }
       }
       }
       else if ( a[i] ) {
       if ( w && n + h != b ) { // ( wArray.length > 0 ) && ( n + h != b )
         wArray.push( [ Labeling( a[i], i ), i, flags ] ); // 空白行 a[i].length > 0
        var sm2 = CreatePopupMenu();
         w ++; // 空白行数: White space ( /^\s+$/ )
         m.AddPopup( menuIndent + "すべての空白行: " + SeparateNum( w ) + " 行 (&W)", sm2 );
        for ( var i = 0; i < w; i ++ ) {
          AddCancelLines( sm2, i ); // 30件ごとに「キャンセル」行を追加
          // 「空白行」を「すべての空白行」サブメニューに追加
          sm2.Add( wArray[i][0], wArray[i][1], wArray[i][2] );
         }
       }
       }
    } // end else if () { 「空行+空白行」の検索 }
      if ( ( n && st ) || ( w && n + h != b ) ) {
  } // end for () { ヒットした行を取得する }
        m.Add( "", 0, meMenuSeparator );
 
  // 配列から「検索文字列がヒットした行」をポップアップメニューに追加する
  if ( ! re && hit ) {
    if ( v )
      m.Add( "", 0, meMenuSeparator );
    if ( v < hit ) {
      for ( var i = 0; i < hit; i ++ ) {
        // 10件ごとにセパレータ、30件ごとに「キャンセル」行を追加
        AddCancelLines( m, i );
        m.Add( mArray[i][0], mArray[i][1], mArray[i][2] );
       }
       }
    }
       // キャンセルを兼ねる(Space キーでキャンセル可) , meMenuGrayed
  }
       m.Add( " ▼  一致した行: " + SeparateNum( h.toString() ) + " 行  ▼\t& ", 0 );
 
       if ( h > 30 ) {
  // 配列から「空行+空白行」をポップアップメニューに追加する
         // ヒットした行が 30 より多いときは「※さいごの行※」を追加
  if ( re && b ) {
        m.Add( " ※  さいごの行  ( 行番号: " + hArray[ h - 1 ][1] + " 行目 )  ※", hArray[ h - 1 ][1] );
    if ( b != h ) { // ( n + w ) > h
       m.Add( menuIndent + "すべての空行+空白行: " + SeparateNum( b ) + " 行", 0 ); // , meMenuGrayed
       m.Add( "", 0, meMenuSeparator );
    }
    if ( n && st ) { // ( nArray.length > 0 ) && ( st.length > 0 )
      var sm1 = CreatePopupMenu();
      m.AddPopup( menuIndent + "すべての空行: " + SeparateNum( n ) + " 行 (&N)", sm1 );
       for ( var i = 0; i < n; i ++ ) {
         AddCancelLines( sm1, i ); // 30件ごとに「キャンセル」行を追加
        sm1.Add( nArray[i][0], nArray[i][1], nArray[i][2] );
       }
       }
    }
       for ( var i = 0; i < h; i ++ ) { // h == hArray.length
    if ( w && n + h != b ) { // ( wArray.length > 0 ) && ( n + h != b )
         AddCancelLines( m, i ); // 30件ごとに「キャンセル」行を追加
      var sm2 = CreatePopupMenu();
         // 「一致した行」をメニューに追加
      m.AddPopup( menuIndent + "すべての空白行: " + SeparateNum( w ) + " 行 (&W)", sm2 );
        m.Add( hArray[i][0], hArray[i][1], hArray[i][2] );
       for ( var i = 0; i < w; i ++ ) {
         AddCancelLines( sm2, i ); // 30件ごとに「キャンセル」行を追加
         sm2.Add( wArray[i][0], wArray[i][1], wArray[i][2] );
       }
       }
    } // end if () { 配列から「空行+空白行」をポップアップメニューに追加する }
    // 空白行なしで \s*\n を検索にかけた場合(一致する「空行+空白行」が 0 行)
    if ( re && ! b && ! h  ) {
      m.Add( menuIndent + "キャンセル\t& ", 0 );
     }
     }
    if ( ( n && st ) || ( w && n + h != b ) ) {
      m.Add( "", 0, meMenuSeparator );
    }
    // キャンセルを兼ねる(Space キーでキャンセル可) , meMenuGrayed
    m.Add( " ▼  一致した行: " + SeparateNum( h ) + " 行  ▼\t& ", 0 );
    for ( var i = 0; i < h; i ++ ) { // h == hArray.length
      AddCancelLines( m, i ); // 30件ごとに「キャンセル」行を追加
      m.Add( hArray[i][0], hArray[i][1], hArray[i][2] );
    }
  } // end if () { 配列から「空行+空白行」をポップアップメニューに追加する }


/* 検索開始からポップアップメニューを表示するまでの所要時間を取得 */
  /* 検索開始からポップアップメニューを表示するまでの所要時間を取得 */
  if ( setting.timerEnable ) {
     var timerStatus = setting.timerEnable ? TimerElapsed( new Date(), start ) : "";
     var timerStatus = TimerElapsed( new Date(), start );
  }


/* ポップアップメニューを表示するのは、検索ヒット行が複数のとき */
  /* ポップアップメニューを表示するのは、検索ヒット行が複数のとき */
  if ( hit > 1 || b > 1 || setting.settingEnable ) {
    if ( hit > 1 || b > 1 || setting.settingEnable ) {
      // ポップアップメニューを出すまえにステータスバー表示させること
      var findStatus = FindStatus();
      Status = findStatus;


    // ポップアップメニューを出すまえにステータスバー表示させること
  /* カーソル位置にポップアップメニューを表示 */
    var findStatus = FindStatus( me );
      var y = m.Track( mePosMouse ); // Track( 0 ) ならキャレット位置
    Status = findStatus;


/* カーソル位置にポップアップメニューを表示 */
  /* 【include版】の設定変更サブメニュー項目を選択した場合 */
    var y = m.Track( mePosMouse ); // Track( 0 ) ならキャレット位置
      if ( y >= 10000000 ) {
        SettingsChange( y );
        var isChanged = true; // 選択範囲復旧のためのフラグ
      }


/* include 版の設定変更サブメニュー項目を選択した場合 */
  /* 選択した行番号(行頭)にジャンプして、検索文字列を範囲選択 */
    if ( y >= 100000 ) {
      else if ( y ) {
      SettingsChange( y );
        s.SetActivePoint( posMode, 1, y );
      var isChanged = true; // 選択範囲復旧のためのフラグ
    }


/* 選択した行番号(行頭)にジャンプして、検索文字列を範囲選択 */
         // 文字列でジャンプしたとき
    else if ( y ) {
         if ( ! re ) {
      s.SetActivePoint( posMode, 1, y );
          if ( setting.historyDisable ) {
 
            // 検索履歴に残さない ※「検索文字列の強調表示」なし
      // 空白行でジャンプしたときは検索履歴に残さない
            s.SetActivePos( s.GetActivePos() + FindCaseTF( d.GetLine( y, lineMode ) ) );
      if ( re && d.GetLine( y, lineMode ) ) {
            s.SetAnchorPos( s.GetActivePos() + st.length );
         // 行(空白文字列)全体を範囲選択する(※完全な空行 ^\n では範囲選択しない)
            d.HighlightFind = false;
         s.SetAnchorPoint( posMode, 1, y + 1 );
          }
      }
          else {
      // 文字列でジャンプしたとき
            // 検索履歴に残す ※ Mery の検索メソッドで文字列を範囲選択
      else if ( ! re ) {
            s.Find( st, meFindNext );
        if ( setting.historyDisable ) {
            // 「検索文字列の強調表示」を解除する/しない
          // 検索履歴に残さない ※「検索文字列の強調表示」なし
            d.HighlightFind = setting.highlightEnable;
          s.SetActivePos( s.GetActivePos() + FindCaseTF( d.GetLine( y, lineMode ) ) );
          }
          s.SetAnchorPos( s.GetActivePos() + st.length );
          // ジャンプ後の選択範囲が何件目のヒットかを取得する
        }
          if ( setting.countEnable ) {
        else {
            for ( i = 0; i < gtArray.length; i ++ ) {
          // 検索履歴に残す ※ Mery の検索機能で文字列を範囲選択
              if ( gtArray[i][0] == y ) {
          s.Find( st, meFindNext );
                var im = gtArray[i][1] + 1;
          d.HighlightFind = setting.highlightEnable; // 「検索文字列の強調表示」を解除する/しない
                break;
        }
              }
        // ジャンプ後の選択範囲が何件目のヒットかを取得する
        if ( setting.countEnable ) {
          for ( i = 0; i < rArray.length; i ++ ) {
            if ( rArray[i][0] == y ) {
              var im = rArray[i][1] + 1;
              break;
             }
             }
           }
           }
         }
         }
        // 空白行でジャンプしたときは検索履歴に残さない
        else if ( re && d.GetLine( y, lineMode ) ) {
          // 行(空白文字列)全体を範囲選択する(※完全な空行 ^\n では範囲選択しない)
          s.SetAnchorPoint( posMode, 1, y + 1 );
        }
        ScrollY = ( ScrollY == sy ) ? ScrollY
                                    : s.GetActivePointY( mePosView );
        cancel = 0;
      } // end else ( ジャンプして、検索文字列を範囲選択 )
  /* キャンセルした場合 */
      else {
        cancel = 5; // ポップアップメニューをキャンセル ( y == 0 )
       }
       }
      ScrollY = ( ScrollY == sy ) ? ScrollY
     } // end if () { ポップアップメニューを表示 }
                                  : s.GetActivePointY( mePosView );
      cancel = 0;
     } // end else ( ジャンプして、検索文字列を範囲選択 )


/* キャンセルした場合 */
     else {
     else {
       cancel = 3; // ポップアップメニューをキャンセル ( y == 0 )
       cancel = 6; // ほかの行でヒットなし ( hit == 1 || b == 1 )
     }
     }
   } // end if () { ポップアップメニューを表示 }
   } // end else { ポップアップメニューでジャンプ先リスト }


   else {
   /* ジャンプしなかったときは、キャレット位置/選択範囲を復帰 */
     cancel = 4; // ほかの行でヒットなし ( hit == 1 || b == 1 )
  if ( cancel || isChanged ) {
     if ( pos || pos === 0 ) {
      s.SetActivePos( pos );
    }
    else if ( act || act === 0 ) {
      s.SetAnchorPos( anc );
      s.SetActivePos( act, true );
    }
    ScrollX = sx;  ScrollY = sy;
   }
   }
} // end else { ポップアップメニューでジャンプ先リスト }


/* ジャンプしなかったときは、キャレット位置/選択範囲を復帰 */
  // キャンセルフラグが 1 ~ 4 (検索条件エラー)のとき、所要時間の計測終了
if ( cancel || isChanged ) {
  timerStatus = ( setting.timerEnable )
  if ( pos || pos === 0 ) {
                    ? ( ! timerStatus )
     s.SetActivePos( pos );
                          ? TimerElapsed( new Date(), start )
  }
                          : timerStatus
  else if ( act || act === 0 ) {
                    : "";
    s.SetAnchorPos( anc );
 
     s.SetActivePos( act, true );
  /* ステータスバーに終了メッセージを表示 */
   }
  Status = (
  ScrollX = sx;  ScrollY = sy;
      // メニュー表示後にジャンプしなかったときと、設定を変更したとき
      ( cancel == 5 || isChanged ) ? findStatus : // ヒット数表示のまま
      // メニュー表示後にジャンプしたとき
      ( cancel == 0 ) ? FindStatus() : // ヒット数表示を更新
      // 検索条件エラーでメニューを表示しなかったとき
     ( ( cancel == 1 ) ? " 検索文字列がありません ( 空行 ); " :
      ( cancel == 2 ) ? " 無効な検索文字列 ( 改行 ); " :
      ( cancel == 3 ) ? " 無効な検索文字列 ( 折り返し ); " :
      ( cancel == 4 ) ? " 無効な検索文字列 ( 非・英単語 ); " :
      ( cancel == 6 ) ? " ヒットなし。 " :
      /*  else  */    " 無効な検索文字列。 " // ← 使われないはず
     ) + lineStatus + timerStatus
   );
}
}


// キャンセルフラグの値が 1 or 2 のとき、所要時間の計測終了
// ------------------------------------------------------------
timerStatus = ( setting.timerEnable )
                  ? ( ! timerStatus )
                        ? TimerElapsed( new Date(), start )
                        : timerStatus
                  : "";


/* ステータスバーに終了メッセージを表示 */
/* 関数 SeparateNum( num ) */
Status =
// 数値を3ケタ区切りに
    ( isChanged || cancel == 3 ) ? findStatus : // ヒット数表示のまま
function SeparateNum( num ) {
    ( cancel == 0 ) ? FindStatus( im ) : // ヒット数表示
  // if ( num === 0 ) {
   ( ( cancel == 1 ) ? " 検索文字列がありません ( 空行 )。 " :
  //  num = "0";
    ( cancel == 2 ) ? " 無効な検索文字列 ( 改行 )。 " :
  // }
    ( cancel == 4 ) ? " ヒットなし。 " :
  return num.toString().replace( /(\d)(?=(?:\d{3})+$)/g , "$1," );
    /*  else  */    " 無効な検索文字列。 " // ← 使われないはず
}
  ) + lineStatus + timerStatus;
Quit();
 
// ------------------------------------------------------------


/* 関数 TimerElapsed( end, start ) */
/* 関数 TimerElapsed( end, start ) */
// start からの経過時間を [ s.sss 秒 ] で返す
// start からの経過時間を [ s.sss 秒 ] で返す
// (Javascript 時間計測のマクロでの分解能は 16ms とのこと)
function TimerElapsed( end, start ) {
function TimerElapsed( end, start ) {
   var elapsed = end - start;
   var elapsed = end - start;
442行目: 511行目:
// 文字列 str 内で st を検索するさいに「大文字/小文字を区別」する/しない
// 文字列 str 内で st を検索するさいに「大文字/小文字を区別」する/しない
// および 「英単語のみ検索」する/しない オプションに対応
// および 「英単語のみ検索」する/しない オプションに対応
// ヒットすればヒットした位置(  0 ~ )、ヒットしなければ -1 を返す
function FindCaseTF( str ) {
function FindCaseTF( str ) {
   return setting.wholeWordEnable
   return setting.wholeWordEnable
             ? str.search( RegExp( "\\b" + stQuote + "\\b" , setting.caseEnable ? "g" : "gi" ) )
             ? str.search( new RegExp( "\\b" + stQuote + "\\b"  
                                    , setting.caseEnable ? "g" : "gi" ) )
             : setting.caseEnable
             : setting.caseEnable
                 ? str.indexOf( st )
                 ? str.indexOf( st )
453行目: 524行目:
// ポップアップメニューの右側に表示するマーキング
// ポップアップメニューの右側に表示するマーキング
function Marking() {
function Marking() {
  var _stQuote = stQuote.replace( /(?:^\\ |\\ $)/g , "" );
   var _check = (
   var _check = (
     // 変数の宣言の行: "var st" ... / "var hoge = piyo , st " ...
     ( i == ty && a[i] == st ) ? "\t ⏎" : // ①アクティブ行(行検索のとき)✓↵
     a[i].match( RegExp( "\\b(?:var |let |const )(?:\\s*|[^\(]+[,]\\s+)[$]?"
    ( t.length > 1 ) ? "\t x" + t.length : // ②検索文字列が複数ヒットした行
                        + stQuote + "(?=\\s*(?:[=,;]|$))" , "" ) )
    ( i != ty && a[i] == activeLine )    ? "\t **" : // ③アクティブ行と完全一致する重複行
        ? "\t ← v a r" : // ※カッコの員数あわせ→)※
     ( a[i] == st )            ? "\t *" : // ④検索文字列と完全一致する行
                                "\t   " // ⑤検索文字列を含む行;
  );
  var JS_check = (
    // 編集モードが "JavaScript" か "MeryMacroJS" のとき
    ( d.Mode.match( /javascript|mery[ _]?macros?[ _]?js/i )
        ? new RegExp( "\\b(?:var |let |const )(?:\\s*|[^(]+[,]\\s*)[$]?" // ※)※
                      + _stQuote + "(?=\\s*(?:[=,;]|$))" , "" ).test( a[i] )
            // 変数の宣言の行: "var st" ... / "var hoge = piyo , st " ...
            ? "\t ← var"


    // 変数の宣言の行: "var hoge = fuga(), " + st
            : ( new RegExp( "\\b(?:var |let |const ).*?\\(.*?[,]\\s*[$]?"
    ( a[i].match( RegExp( "\\b(?:var |let |const ).*?\\(.*?[,]\\s*[$]?"
                          + _stQuote + "(?=\\s*(?:[=,;]|$))" , "" ).test( a[i] ) // ※)※
                        + stQuote + "(?=\\s*(?:[=,;]|$))" , "" ) ) // ※員数あわせ→)※
                && SearchCloseBrc( a[i], stQuote ) ) // 閉じていないカッコのチェック
      // 閉じていないカッコがあるかの簡易的なチェック
                  // 変数の宣言の行: "var hoge = fuga(), st " ...
      && SearchCloseBrc( a[i], stQuote ) )
                  ? "\t ← var"
        ? "\t ← v a r" :


    // 変数に値を代入している行 st + " = fuga"
                  : ( ! blank.test( st )
    ( ! st.match( blank ) &&
                      && new RegExp( "\\b" + _stQuote
      a[i].match( RegExp( "\\b" + stQuote + "(?=\\s*(?:\\+=|\\+\\+|-=|--|=(?!=)))" , "" ) ) )
                                  + "(?=\\s*(?:\\+=|\\+\\+|-=|--|=(?!=)))" , "" ).test( a[i] ) )
        ? "\t ← s u b" :
                        // 変数に値を代入している行: "st = fuga"
                        ? "\t ← sub"


    // 関数の定義の行
                        : new RegExp( "\\bfunction +[$]?"  
    a[i].match( RegExp( "\\bfunction +[$]?" + stQuote + "(?=[\\s(])" , "" ) )
                                    + _stQuote + "(?=[\\s(])" , "" ).test( a[i] )  // ※)※
        ? "\t ← f n" : // ※員数あわせ→)※
                            // 関数の定義の行: "function st" ...
                            ? "\t ← fn"


    ( i != ty && a[i] == activeLine ) ? "\t **" : // ①アクティブ行と完全一致する行(重複行)
                            : ""
    ( t && t.length > 1 )    ? "\t x" + t.length : // ②検索文字列が複数ヒットした行
        : ""
    ( i == ty && a[i] == st ) ? "\t ⏎" : // ③アクティブ行(行検索のとき)✓↵
     )
     ( i != ty && a[i] == st ) ? "\t *" : // ④検索文字列と完全一致する行
                                "" // ⑤検索文字列を含む行
   );
   );
   return _check;
   return _check + JS_check;
}
}


488行目: 568行目:
function Labeling( line, i ) { // Labeling( a[i], i ) ※ line = a[i] ヒットした行全体
function Labeling( line, i ) { // Labeling( a[i], i ) ※ line = a[i] ヒットした行全体
   // 行頭空白を除去、空白文字は全角スペースに置換、文字数を圧縮して取得
   // 行頭空白を除去、空白文字は全角スペースに置換、文字数を圧縮して取得
  // 判別しづらい半角メタ文字を全角に置きかえる
   var label = line.replace( /^[\t  ]+/ , "" )
   var label = line.replace( /^[\t  ]+/ , "" )
                   .replace( /(\t|[ ]{3,}|[ ]{2,})+/g , " › " )
                   .replace( /(?:\t|[ ]{3,}|[ ]{2,})+/g , " › " )
                   .replace( /[.,:;(){}]/g , function( $0 ) {
                   .replace( /[.,:;(){}]/g , // 判別しづらい半角メタ文字を全角に置換
                    return String.fromCharCode( $0.charCodeAt( 0 ) + 0xFEE0 )
                    function( $0 ) {
                  } );
                      return String.fromCharCode( $0.charCodeAt( 0 ) + 0xFEE0 )
   label = (
                    } );
    ( ! line.length )  ? "( 空行 )" :
   label = ! line.length
    ( ! label.length ) ?
? "( 空行 )"
    ( line == st )     ? "( 空白行 " + line.length + " 文字 * )" :
: ! label.length
                        "( 空白行 " + line.length + " 文字 )" :
? ( line == st )
    ( label.length > setting.menuWidth + 1 ) ? label.slice( 0, setting.menuWidth ) + "..." :
? "( 空白行 " + line.length + " 文字 * )"
                                  /* else */ label
: "( 空白行 " + line.length + " 文字 )"
  );
: ( label.length > setting.menuWidth )
? label.slice( 0, setting.menuWidth ) + "..."
: label;
  // 行番号の右寄せは EN SPACE " " (U+2002) でケタ埋め
   return ( "          " + i ).slice( 0 - linesWidth ) + " : " + label;
   return ( "          " + i ).slice( 0 - linesWidth ) + " : " + label;
}
}
524行目: 606行目:
}
}


/* 関数 FindStatus( num ) */
/* 関数 FindStatus() */
// ステータスバーに表示するヒット数や所要時間の情報など
// ステータスバーに表示するヒット数や所要時間の情報など
function FindStatus( num ) {
function FindStatus() {
   var statusStr =
   var statusStr = (
     ( re ? " 一致: " + SeparateNum( h ) + " 行" + " / 空白行数: " + SeparateNum( b ) + " 行 / "
     ( re ? " 一致: " + SeparateNum( h ) + " 行 / 空白行数: " + SeparateNum( b ) + " 行 / "
         : ( gt ? " ヒット数: " + SeparateNum( gt ) + " 件 ( " + SeparateNum( num ) + " 件目 ) ・ "
         : ( gt ? " ヒット数: " + SeparateNum( gt ) + " 件 ( "
                  // me: ジャンプする前の位置 / im: ジャンプした後の位置
                  + SeparateNum( im ? im : me ) + " 件目 ) ・ "
                 : " ヒット: " )
                 : " ヒット: " )
           + SeparateNum( hit ) + " 行 / "
           + SeparateNum( hit ) + " 行 / "
     ) + lineStatus + timerStatus;
     ) + lineStatus + timerStatus
    return statusStr;
   );
}
   return statusStr;
 
/* 関数 SeparateNum( num ) */
// 数値を3ケタ区切りに
function SeparateNum( num ) {
   if ( ! num ) {
    num = "";
  }
   return num.toString().replace( /(\d)(?=(?:\d{3})+$)/g , "$1," );
}
}


548行目: 624行目:
// 閉じていないカッコがないかを簡易的にチェックする(閉じていなければ false )
// 閉じていないカッコがないかを簡易的にチェックする(閉じていなければ false )
function SearchCloseBrc( str, word ) {
function SearchCloseBrc( str, word ) {
   var txt = str.substr( str.search( /\b(?:var |let |const )/ ),
   var txt = str.substr( str.search( /\b(?:var |let |const )/ )
                        str.search( RegExp( "\\b" + word + "\\b" , "" ) )
                      , str.search( new RegExp( "\\b" + word + "\\b" , "" ) )
                       );
                       );
   var oBrc = txt.match( /\(/g ); // 開きカッコの数
   var oBrc = txt.match( /\(/g ); // 開きカッコの数
560行目: 636行目:
function SettingsMenu() {
function SettingsMenu() {
   var subMenu = CreatePopupMenu();
   var subMenu = CreatePopupMenu();
   var grayOut1 = setting.autoLineModeEnable ?  meMenuGrayed : 0;
   var grayFlag_1 = ( setting.autoLineModeEnable || ! setting.settingEnable )
                      ?  meMenuGrayed : 0;
   var logicalFlag = setting.logical ? meMenuChecked : 0;
   var logicalFlag = setting.logical ? meMenuChecked : 0;
   var viewFlag = setting.logical ? 0 : meMenuChecked;
   var viewFlag = setting.logical ? 0 : meMenuChecked;
   var grayOut2 = setting.historyDisable ?  meMenuGrayed : 0;
   var grayFlag_2 = ( setting.historyDisable || ! setting.settingEnable )
   var highlightFlag = ( setting.highlightEnable && ! setting.historyDisable ) ? meMenuChecked : 0;
                      ?  meMenuGrayed : 0;
   var highlightFlag = ( setting.highlightEnable && ! setting.historyDisable )
                          ? meMenuChecked : 0;
   var timerFlag = setting.timerEnable ? meMenuChecked : 0;
   var timerFlag = setting.timerEnable ? meMenuChecked : 0;
   var autoLineModeFlag = setting.autoLineModeEnable ? meMenuChecked : 0;
   var autoLineModeFlag = setting.autoLineModeEnable ? meMenuChecked : 0;
   var historyFlag = setting.historyDisable ? meMenuChecked : 0;
   var historyFlag = setting.historyDisable ? meMenuChecked : 0;
   var caseFlag = setting.caseEnable ? meMenuChecked : 0;
   var caseFlag = setting.caseEnable ? meMenuChecked : 0;
  var wholeWordFlag = setting.wholeWordEnable ? meMenuChecked : 0;
   var blankFlag = setting.blankEnable ? meMenuChecked : 0;
   var blankFlag = setting.blankEnable ? meMenuChecked : 0;
   var countFlag = setting.countEnable ? meMenuChecked : 0;
   var countFlag = setting.countEnable ? meMenuChecked : 0;
  var wholeWordFlag = setting.wholeWordEnable ? meMenuChecked : 0;
   var maxWidthFlag = setting.menuWidth > 100 ? meMenuGrayed : 0;
   var maxWidthFlag = setting.menuWidth > 100 ? meMenuGrayed : 0;
   var minWidthFlag = setting.menuWidth < 30 ? meMenuGrayed : 0;
   var minWidthFlag = setting.menuWidth < 30 ? meMenuGrayed : 0;
  var settingFlag = setting.settingEnable ? meMenuChecked : 0;
  var grayFlag = ! setting.settingEnable ? meMenuGrayed : 0;


   m.Add( "", 0, meMenuSeparator );
   m.Add( "", 0, meMenuSeparator );
   m.AddPopup( menuIndent + "設定を変更する (&S)", subMenu );
   m.AddPopup( menuIndent + "設定を変更する (&S)", subMenu );
   m.Add( "", 0, meMenuSeparator );
   m.Add( "", 0, meMenuSeparator );
   subMenu.Add( "「大文字/小文字」 を区別する (&C)", 100007, caseFlag );
   subMenu.Add( "「大文字/小文字」 を区別する (&C)", 10000007, caseFlag + grayFlag );
   subMenu.Add( "「英単語のみ」 検索する (&W)", 100008, wholeWordFlag );
   subMenu.Add( "「英単語のみ」 検索する (&W)", 10000008, wholeWordFlag + grayFlag );
   subMenu.Add( "「空行/空白行」 を検索対象にする (&B)", 100009, blankFlag );
   subMenu.Add( "「空行/空白行」 を検索対象にする (&B)", 10000009, blankFlag + grayFlag );
   subMenu.Add( "", 0, meMenuSeparator );
   subMenu.Add( "", 0, meMenuSeparator );
   subMenu.Add( "「検索履歴」 に残さない (&H)", 100006, historyFlag );
   subMenu.Add( "「検索履歴」 に残さない (&H)", 10000006, historyFlag + grayFlag );
   subMenu.Add( "「検索文字列の強調表示」 を残す (&F)", 100003, highlightFlag + grayOut2 );
   subMenu.Add( "「検索文字列の強調表示」 を残す (&F)", 10000003, highlightFlag + grayFlag_2 );
   subMenu.Add( "", 0, meMenuSeparator );
   subMenu.Add( "", 0, meMenuSeparator );
   subMenu.Add( "「行の表示方法」 を自動設定する (&A)", 100005, autoLineModeFlag );
   subMenu.Add( "「行の表示方法」 を自動設定する (&A)", 10000005, autoLineModeFlag + grayFlag );
   subMenu.Add( "  行数を 「論理行」 で表示する (&L)", 100001, logicalFlag + grayOut1 );
   subMenu.Add( "  行数を 「論理行」 で表示する (&L)", 10000001, logicalFlag + grayFlag_1 );
   subMenu.Add( "  行数を 「表示行」 で表示する (&V)", 100002, viewFlag );
   subMenu.Add( "  行数を 「表示行」 で表示する (&V)", 10000002, viewFlag + grayFlag_1 );
   subMenu.Add( "", 0, meMenuSeparator );
   subMenu.Add( "", 0, meMenuSeparator );
   subMenu.Add( "「検索処理の所要時間」 を表示する (&T)", 100004, timerFlag );
   subMenu.Add( "「検索処理の所要時間」 を表示する (&T)", 10000004, timerFlag + grayFlag );
   subMenu.Add( "「ヒット件数」(出現総数)を表示する (&G)", 100010, countFlag );
   subMenu.Add( "「ヒット件数」(出現総数)を表示する (&G)", 10000010, countFlag + grayFlag );
   subMenu.Add( "", 0, meMenuSeparator );
   subMenu.Add( "", 0, meMenuSeparator );
   subMenu.Add( "  メニューの表示幅を 10文字 増やす (&P) \t" + setting.menuWidth
   subMenu.Add( "  メニューの表示幅を 10文字 増やす (&P) \t" + setting.menuWidth
    + ( setting.menuWidth > 100 ? "" : " → " + ( setting.menuWidth + 10 ) ), 100011, maxWidthFlag );
              + ( setting.menuWidth > 100 ? "" : " → " + ( setting.menuWidth + 10 ) )
              , 10000011, maxWidthFlag + grayFlag );
   subMenu.Add( "  メニューの表示幅を 10文字 減らす (&M) \t" + setting.menuWidth
   subMenu.Add( "  メニューの表示幅を 10文字 減らす (&M) \t" + setting.menuWidth
    + ( setting.menuWidth < 30 ? "" : " → " + ( setting.menuWidth - 10 ) ), 100012, minWidthFlag );
              + ( setting.menuWidth < 30 ? "" : " → " + ( setting.menuWidth - 10 ) )
              , 10000012, minWidthFlag + grayFlag );
   subMenu.Add( "", 0, meMenuSeparator );
   subMenu.Add( "", 0, meMenuSeparator );
   subMenu.Add( "", 0, meMenuSeparator );
   subMenu.Add( "", 0, meMenuSeparator );
   subMenu.Add( "  このマクロの JS ファイルを開く (&O)", 100013 );
   subMenu.Add( "  設定変更を許可する (&S)", 10000000, settingFlag );
   subMenu.Add( "  このマクロの JSON ファイルを開く (&J)", 100014 );
   subMenu.Add( "  設定内容を初期化する (&I)", 10000015, grayFlag );
   subMenu.Add( "", 0, meMenuSeparator );
   subMenu.Add( "", 0, meMenuSeparator );
   subMenu.Add( "  設定内容を初期化する (&I)", 100015 );
   subMenu.Add( "  このマクロの JS ファイルを開く (&O)", 10000013 );
  subMenu.Add( "  このマクロの JSON ファイルを開く (&J)", 10000014, grayFlag );
}
}


610行目: 694行目:
   var sIsChanged = true;
   var sIsChanged = true;
   switch ( num ) {
   switch ( num ) {
     case 100001: case 100002:
     case 10000000:
      setting.settingEnable = ! setting.settingEnable;
      break;
    case 10000001:
    case 10000002:
       setting.logical = ! setting.logical;
       setting.logical = ! setting.logical;
       break;
       break;
     case 100003:
     case 10000003:
       setting.highlightEnable = ! setting.highlightEnable;
       setting.highlightEnable = ! setting.highlightEnable;
       break;
       break;
     case 100004:
     case 10000004:
       setting.timerEnable = ! setting.timerEnable;
       setting.timerEnable = ! setting.timerEnable;
       break;
       break;
     case 100005:
     case 10000005:
       setting.autoLineModeEnable = ! setting.autoLineModeEnable;
       setting.autoLineModeEnable = ! setting.autoLineModeEnable;
       break;
       break;
     case 100006:
     case 10000006:
       setting.historyDisable = ! setting.historyDisable;
       setting.historyDisable = ! setting.historyDisable;
       break;
       break;
     case 100007:
     case 10000007:
       setting.caseEnable = ! setting.caseEnable;
       setting.caseEnable = ! setting.caseEnable;
       break;
       break;
     case 100008:
     case 10000008:
       setting.wholeWordEnable = ! setting.wholeWordEnable;
       setting.wholeWordEnable = ! setting.wholeWordEnable;
       break;
       break;
     case 100009:
     case 10000009:
       setting.blankEnable = ! setting.blankEnable;
       setting.blankEnable = ! setting.blankEnable;
       break;
       break;
     case 100010:
     case 10000010:
       setting.countEnable = ! setting.countEnable;
       setting.countEnable = ! setting.countEnable;
       break;
       break;
     case 100011:
     case 10000011:
       setting.menuWidth += 10;
       setting.menuWidth += 10;
       break;
       break;
     case 100012:
     case 10000012:
       setting.menuWidth -= 10;
       setting.menuWidth -= 10;
       break;
       break;
     case 100013:
     case 10000013:
       sIsChanged = false;
       sIsChanged = false;
       OpenJumpJS();
       OpenJumpJS( "setting.settingEnable = " );
       break;
       break;
     case 100014:
     case 10000014:
       sIsChanged = false;
       sIsChanged = false;
       OpenJumpJson();
       OpenJumpJson( json );
       break;
       break;
     case 100015:
     case 10000015:
       sIsChanged = false;
       sIsChanged = false;
       CheckJsonFile();
       CheckJsonFile( json );
       break;
       break;
     default:
     default:
664行目: 752行目:
}
}


/* 関数 OpenJumpJS() */
/**
// このマクロを開いて設定項目の行にジャンプする
* 関数 OpenJumpJS( str )
function OpenJumpJS() {
* このマクロの JS ファイルを開いて設定項目の行にジャンプする
* 引数: 検索文字列(設定項目の行の文字列)
* ※引数がないときは文頭
*/
function OpenJumpJS( str ) {
  Redraw = false;
  var targetStr = str || "";
   // Count は 1 からの整数値、Item は 0 からの整数値
   // Count は 1 からの整数値、Item は 0 からの整数値
   var eCount = editors.Count;
   var eCount = editors.Count;
   var isOpen = false,  ee ,  dd;
   var dCount,  dItem,  ee ,  dd;
  var isOpen = false;
   OuterLoop:
   OuterLoop:
   for ( var j = 0; j < eCount; j ++ ) {
   for ( var j = 0; j < eCount; j ++ ) {
     var dCount = editors.Item( j ).documents.Count;
     dCount = editors.Item( j ).documents.Count;
     for ( var i = 0; i < dCount; i ++ ) {
     for ( var i = 0; i < dCount; i ++ ) {
       if ( editors.Item( j ).documents.Item( i ).FullName == ScriptFullName ) {
       dItem = editors.Item( j ).documents.Item( i );
      if ( dItem.FullName == ScriptFullName ) {
         isOpen = true;
         isOpen = true;
         ee = j;
         ee = j;
691行目: 787行目:
   }
   }
   var js = editors.Item( ee ).ActiveDocument;
   var js = editors.Item( ee ).ActiveDocument;
   var settingPos = js.Text.indexOf( "setting.settingEnable" );
   var settingPos = js.Text.indexOf( targetStr );
   js.selection.SetActivePos( settingPos );
   js.selection.SetActivePos( settingPos + targetStr.length );
   // 狙った行にスクロールしないようなので、強引にやる
  js.selection.WordRight( true ); // true を範囲選択
   var yy = js.selection.GetActivePointY( mePosLogical );
  var vy = js.selection.GetActivePointY( mePosView );
   new ActiveXObject( "WScript.Shell" ).Run( '"' + editor.FullName + '" /l ' + yy + ' "' + ScriptFullName + '"' );
  ScrollY = vy;
   // 狙った行にスクロールしないようなら、強引にやる
   var ly = js.selection.GetActivePointY( mePosLogical );
   var WshShell = new ActiveXObject( "WScript.Shell" );
  WshShell.Run( '"' + editor.FullName + '" /l ' + ly + ' "'
                    + ScriptFullName + '"' );
  ScrollY = vy;
  Redraw = true;
}
}


/* 関数 OpenJumpJson() */
/**
// JSON ファイルを開く
* 関数 OpenJumpJson( jsonName )
function OpenJumpJson() {
* JSON ファイルを開く
   var jsonPath = IO.Path.Add( MeryInfo.GetMacroFolderPath(), "MacroSettings", json + ".json" );
*/
   IO.Serialize( setting, json );
function OpenJumpJson( jsonName ) {
   var Fso = new ActiveXObject( "Scripting.FileSystemObject" );
  var jsonDir = JsonDir();
  var jsonPath = jsonDir + "\\" + jsonName + ".json";
   IO.Serialize( setting, jsonName );
   Sleep( 500 );
   Sleep( 500 );
   if ( IO.Path.IsExist( jsonPath ) ) {
   if ( Fso.FileExists( jsonPath )
     var confirmMessage = Confirm( "設定ファイルは正常です\n"
      && JsonContents( jsonPath ).length ) {
                                  + jsonPath + "\n\n"
     var confirmStr = "設定ファイルは正常です \n\n"
                                  + IO.LoadFromFile( jsonPath ).replace( /^\{|\}$/g , "  " )
                  + JsonContents( jsonPath )
                                                              .replace( /,/g , ",\n  " )
                  + "設定ファイルを開きますか? ";
                                  + "\n\n設定ファイルを開きますか?" );
     if ( Confirm( confirmStr ) ) {
     if ( confirmMessage ) {
       var WshShell = new ActiveXObject( "WScript.Shell" );
       new ActiveXObject( "WScript.Shell" ).Run( '"' + editor.FullName + '" "' + jsonPath + '"' );
      WshShell.Run( '"' + editor.FullName
                  + '" "' + jsonPath + '"' );
     }
     }
   }
   }
   else {
   else {
     Alert( "設定ファイルがありません\n"
     Alert( "設定ファイルがありません \n" + jsonDir + "  " );
          + IO.Path.Add( MeryInfo.GetMacroFolderPath(), "MacroSettings" ) );
   }
   }
}
}


/* 関数 CheckJsonFile() */
/**
// JSON ファイルの初期化/実在確認
* 関数 CheckJsonFile( jsonName )
function CheckJsonFile() {
* JSON ファイルの初期化/実在確認
   if ( Confirm( "設定ファイルを初期化しますか?" ) ) {
*/
    var jsonPath = IO.Path.Add( MeryInfo.GetMacroFolderPath(), "MacroSettings", json + ".json" );
function CheckJsonFile( jsonName ) {
     IO.Serialize( initialSettings, json );
   var jsonDir = JsonDir();
  var jsonPath = jsonDir + "\\" + jsonName + ".json";
  var confirmStr = jsonPath + "  \n\n"
                + "設定ファイルを初期化しますか? "
  if ( Confirm( confirmStr ) ) {
     IO.Serialize( initialSettings, jsonName );
     Sleep( 500 );
     Sleep( 500 );
     Alert(
     var alertStr = ( IO.Path.IsExist( jsonPath )
      IO.Path.IsExist( jsonPath )
                    && JsonContents( jsonPath ).length )
          ? "設定ファイルは正常です\n" + jsonPath + "\n\n"
                ? "設定ファイルは正常です \n\n"
            + IO.LoadFromFile( jsonPath ).replace( /^\{|\}$/g , "  " )
                  + JsonContents( jsonPath )
                                        .replace( /,/g , ",\n  " )
                : "設定ファイルがありません \n"
          : "設定ファイルがありません\n"
                  + jsonDir + "  ";
            + IO.Path.Add( MeryInfo.GetMacroFolderPath(), "MacroSettings" )
    Alert( alertStr );
    );
  }
     Quit();
}
 
/**
* 関数 JsonDir()
* JSON ファイルの親フォルダのパス
*/
function JsonDir() {
  var Fso = new ActiveXObject( "Scripting.FileSystemObject" );
  var jsonDir;
  if ( Fso.FileExists( editor.FullName.replace( /\.exe$/i, ".ini" ) ) ) {
    jsonDir = editor.FullName.replace( /Mery\.exe$/i, "" )
            + "Macros\\MacroSettings";
  }
  else {
    var WshShell = new ActiveXObject( "WScript.Shell" );
     jsonDir = WshShell.ExpandEnvironmentStrings( "%APPDATA%" )
            + "\\Mery\\MacroSettings";
   }
   }
  return jsonDir;
}
}
</source>


/**
* 関数 JsonContents( jsonPath )
* JSON ファイルの文字列をメッセージボックス用に整形する
*/
function JsonContents( jsonPath ) {
  var contents = ( jsonPath + "  \n"
  + IO.LoadFromFile( jsonPath )
    .replace( /^\{/, "\n{\n  " )
    .replace( /\}$/g, "\n}\n\n" )
    .replace( /,/g, " ,\n  " )
    .replace( /:/g, ": " )
  // .replace( /[a-z]/g,
  //  function( $0 ) {
  //    return String.fromCharCode( $0.charCodeAt( 0 ) + 0xFEE0 )
  //  } )
  );
  return contents;
}
</syntaxhighlight>


== メモ ==
== メモ ==
745行目: 892行目:
: 新規アップロード
: 新規アップロード
* 2018/12/17
* 2018/12/17
: ・設定用 JSON ファイルをポップアップメニューから初期化し、内容を参照できるようにした
: ・ソースコードのミスを修正
: ・ソースコードのミスを修正
: ・設定用 JSON ファイルをポップアップメニューから初期化し、内容を参照できるようにした
* 2018/12/20
* 2018/12/20
: ・文字列の検索のさいに「選択範囲が何件目のヒットか」を表示
: ・ソースコードのミスを修正
* 2019/03/13
: ・行数制限を追加(9,999,999行)
: ・「変数/関数の定義行」の表示に対応する編集モードを "JavaScript", "MeryMacroJS" に限定した
: ・検索条件エラーの条件を細分化
: ・ソースコードのミスを修正
: ・ソースコードのミスを修正
: ・文字列の検索のさいに「選択範囲が何件目のヒットか」を表示
:: ・「大文字/小文字の区別」での「ヒット件数」のカウントの不具合を修正
: ・行番号の表示桁数の制限をなくした(5000行以上のファイルにはあまり適さないとおもう… 処理速度的に)
:: ・「行の表示方法」が表示座標のときに発生するエラーを修正
:: ・「英単語のみ検索」での範囲選択の動作を修正 ...など
* 2019/04/07
: ・Quit() メソッドを削除
* 2019/04/16
: ・JSON ファイルの初期化/実在確認のコードを修正


行の表示方法が「表示座標」のとき、折り返し部分にかかった文字列にはマッチしないままです。<br> 実装すると動作速度が遅くなり、実用に適さなくなってしまいます…
----
行の表示方法が「表示座標」のとき、折り返し部分にかかった文字列にはマッチしないままです。<br> 実装すると動作速度が遅くなり、実用に適さなくなってしまいます。 …現状でも、5000行以上のファイルにはあまり適さないとおもう。

2024年9月9日 (月) 10:59時点における最新版

include版[編集]

includeライブラリ を使用し、設定内容を外部ファイルに保存します(◆settingEnable 変数で設定。デフォルト: 有効)。

設定内容の読み込み/書き出しが可能になったので、設定の変更はポップアップメニュー内の項目のチェック ON/OFF でできるようになります。

スクリーンショット

  • あらかじめ includeライブラリ をインストールしてください。
  • 設定内容の保存場所は Mery\Macros\MacroSettings\<ジャンプ>.json
    または %AppData%\Mery\MacroSettings\<ジャンプ>.json です。
    (※ <ジャンプ> の部分はこのマクロのファイル名)
  • 「▼ 通常版 初期設定項目 ▼」以降の設定項目は『初期値』としてのみ利用され、ソースコード内の設定項目を直接書き換えする必要はありません。
    ただし、settingEnable 変数を完全に無効にする場合は、ソースコード内で変更する必要があります。
    → settingEnable を無効にした場合は「人柱版」と同じ状態になるので、設定変更はソースコードの直接編集(変数の true/false の変更)でおこなう必要があります。
  • はじめての設定変更のさいにエラーが発生する場合は、以下の ①~③ を試行してください。
  1.  ポップアップメニューの「設定内容を初期化する」を実行して、ファイルを確認する
    ( → JSON ファイルを新規作成して設定値を書き出します)。
  2.  ポップアップメニューの「JSON ファイルを開く」を実行して、ファイルを確認する。
  3.  JSON ファイルが正常に作成されない場合は、同梱の「ジャンプ.json」を 上記の "設定ファイルの保存場所" フォルダ に配置してください。
※「JSON ファイルの初期化」コマンドでファイルチェックのコードに不備があったので修正しました。 (2019/04/16)
  • 設定変更メニューを組み込んでいるため、「検索設定の変更」オプションが有効のときは、検索語が他の行でヒットしなかった場合でもポップアップメニューを表示します。
    ただし、複数行選択状態で実行した場合(「表示行」で選択範囲が折り返しを含む場合も)、「英単語のみ」オプションが有効なときや「空行/空白行」がオプション無効のときに検索条件にたいして「無効な検索文字列」を検索した場合は、ポップアップメニューを表示せず、ステータスバーにエラー内容を表示します。

※ include版ではドキュメントの行数制限(9,999,999行)があります。

▼ 追加機能 ▼

  • 検索のさいに「英単語のみ検索」するかしないかを選択できます(◆変数で設定)。
    これにより、ソースコードの1文字変数(e.g. var s;)や <a> タグなどの検索がしやすくなります。
  • 「英単語のみ」オプションが有効のときは、マクロ実行前の選択範囲の有無によらず、キャレット位置の 単語 を自動選択して既存の選択範囲から置きかえます 。
  • 「英単語のみ」オプションを有効にして検索できるのは英単語(JavaScript の正規表現で \w+ )のみです。
    単語の自動選択には Mery ビルトインのメソッドを使用しているので、英文でなくても単語の範囲選択までは行いますが、日本語の文字列は検索できません。
    また、英文・欧文でも各種記号や空白文字、ascii 範囲外のアルファベットを使った単語でも検索できません。
  • このオプションを有効にした場合は正規表現を使用しての検索になるので、処理がやや遅くなります。
  • メニューに検索ヒットした行が表示されないときは、
    メニューの「英単語のみ」のチェックをはずしてください。
  • 文字列の検索のさいに「ヒット件数表示」オプションが有効な場合、選択範囲が「何件目」のヒット箇所かの表示を追加します(ジャンプしたあとの選択範囲が「何件目」のヒット箇所かも表示します)。

ソースコード[編集]

最終更新: 2019/04/16 (Quitメソッドを削除/JSON ファイルの初期化のコードを修正)

※ポップアップメニューの表示に unicode 文字を使用していますので、シフトJISなどで保存しないでください。

#title = "ジャンプ"
#tooltip = "ポップアップメニューで検索先にジャンプ"
#include "include/IO.js"
#include "include/MeryInfo.js"
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",229

/**
 * ------------------------------------------------------------
 * "ポップアップメニューで検索先にジャンプ"
 * Original Copyright: 手石 (2014/04/04)
 * ------------------------------------------------------------
 * Modified by: sukemaru (2018/11/12 - 2019/04/16)
 * ------------------------------------------------------------
 *
 * 【include版】での追加機能
 * 「includeライブラリ」を使用
 *   検索設定はポップアップメニューから変更可
 *   設定内容を外部 JSON ファイルに書き出し/読み込み
 * 「英単語のみ検索する」に対応
 *   選択範囲が何件目のヒットかを表示可
 *   ヒットした行が 30 より多いときはメニューに「※さいごの行※」へのショートカットを追加
 *
 * 【人柱版】からの追加機能
 * 「大文字/小文字の区別」に対応(→ ■通常版へ逆移植済み)
 * 「空行+空白行」の検索に対応
 * 「ヒット件数(出現回数)」を表示可
 *   スペースキーでポップアップメニューをキャンセル可
 * 「検索履歴に残さない」を追加(→ ■通常版へ逆移植済み)
 * 「行の表示方法」を Mery.ini から取得可
 * 「自動マーカー」オプションを廃止(デフォルト: 自動マーカー有効)
 * 「検索所要時間」の表示可(→ ■通常版へ逆移植済み)
 *   検索ジャンプ後のスクロールの挙動を調整(→ ■通常版へ逆移植済み)
 * 「変数/関数の定義行」のマーキング
 *     (※2019/03/12 編集モードを限定 "JavaScript", "MeryMacroJS")
 *
 */

var start = new Date();		// 所要時間の計測開始

var setting = {};

// ---------- ▼ 【include版】 追加設定項目 ▼ ----------

// ◆このマクロの「設定を変更するための項目」をポップアップメニューに表示するか?
  setting.settingEnable = true;		// ◆初期値 = true; (表示する)

// ◆英単語のみを検索する? (true; する / false; しない)
  setting.wholeWordEnable = false;	// ◆初期値 = false; (しない)


// ---------- ▼ 【通常版】 初期設定項目 ▼ ----------

  setting.logical = true;
  setting.highlightEnable = true;	

// ---------- ▼ 【人柱版 → 通常版 逆移植】 設定項目 ▼ ----------

  setting.timerEnable = true;	
  setting.historyDisable = false;
  setting.caseEnable = false;

// ---------- ▼ 【人柱版】 初期設定項目 ▼ ----------

  setting.autoLineModeEnable = false;
  setting.blankEnable = true;
  setting.countEnable = true;

// ---------- ▼ 【手石版】 初期設定項目 ▼ ----------

  setting.menuWidth = 65;

// ---------- ▲ 設定項目 ここまで ▲ ----------


var initialSettings = setting;
var json = ScriptName.replace( /\.js$/i , "" );

// JSON ファイルから設定を読み込む
if ( initialSettings.settingEnable ) {
  setting = IO.Deserialize( setting, json );
}

/* ■ポップアップメニューの「行番号の表示方法」 */
// Mery.ini から「行の表示方法」を取得する
if ( setting.autoLineModeEnable ) {
  var iniText = IO.LoadFromFile( MeryInfo.GetIniPath(), "utf-8" );
  var $LineColumnView = + iniText.match( /^LineColumnView=[01]$/m ).shift().slice( -1 );
  setting.logical = ! $LineColumnView;
}
// 「行番号の表示方法」の定数を変数に
var posMode = setting.logical ? mePosLogical : mePosView;
var lineMode = setting.logical ? 0 : meGetLineView;

var d = document,  s = document.selection;
var sx = ScrollX,  sy = ScrollY;

// 文書全体の行数(論理行/表示行)
var lines = d.GetLines( lineMode );
var lineStatus = "全体の行数: " + SeparateNum( lines )
                 + ( setting.logical ? " 行 ( 論理行 )" : " 行 ( 表示行 )" );
// 9,999,999行 制限オーバーのときは終了
if ( lines >= 10000000 ) {
  Status = "9,999,999行 制限オーバー。 文書"
         + lineStatus + TimerElapsed( new Date(), start );
}
else {

  // メニューにピン止めする固定アイテムの字下げ = EN SPACE * ( 文書全体の行数のケタ数 / 2 )
  var linesWidth = lines.toString().length;	// 全体の行数のケタ数
  var menuIndent = "          ".slice( 0 - Math.round( linesWidth / 2 ) );	// (U+2002)

  /* 検索文字列の準備 */
  var blank = new RegExp( "^[\t  ]*$" , "" );	// 空白行の正規表現
  var re;						// "空白行の正規表現検索" フラグ

  // 「英単語のみ」オプションが有効のときは、選択範囲の有無によらず
  // キャレット付近の単語を自動選択する(キャンセルしても単語選択のまま残す)
  if ( setting.wholeWordEnable && ! s.Text.match( /^[\t  ]*\n/ ) ) {
    if ( s.GetActivePos() < s.GetAnchorPos() ) {
      s.Collapse( meCollapseEnd );
    }
    s.SelectWord();
  }

  // 選択範囲なしならキャレット位置の単語を自動選択する
  if ( s.IsEmpty ) {
    var pos = s.GetActivePos();		// "選択範囲なし" のフラグ
    s.SelectWord();
    // 先頭部分に $ があるならそれも含める (JavaScript の $変数名 の検索に使用)
    if ( d.Text.charAt( s.GetAnchorPos() - 1 ) == "$" ) {
      var $act = s.GetActivePos();
      s.SetAnchorPos( s.GetAnchorPos() - 1 );
      s.SetActivePos( $act, true );
    }
  }

  var st = s.Text;			// 検索文字列
  var ty = s.GetTopPointY( posMode );	// 選択範囲の先頭位置(行番号)
  var by = s.GetBottomPointY( posMode );	// 選択範囲の末尾位置(行番号)

  // 選択範囲の末尾が改行 "\n" なら検索文字列から除外する
  if ( ! st || st.charAt( st.length - 1 ) == "\n" ) {
   st = st.replace( /\n$/g , "" );
    // 自動マーカーを効かせるために選択範囲も調整する
    var anc = s.GetAnchorPos();
    var act = s.GetActivePos();	// "末尾調整" フラグ
    if ( anc < act && ! ( setting.blankEnable && blank.test( st ) ) ) {
      s.SetActivePos( act - 1, true );
    }
    if ( act < anc && ! ( setting.blankEnable && blank.test( st ) ) ) {
      s.SetAnchorPos( anc - 1 );
      s.SetActivePos( act, true );
    }
    if ( setting.blankEnable && blank.test( st ) ) {
      re = true;			// "空白行の正規表現検索" フラグ
    }
  }
  // 文書末尾の空白行の場合でも "空白行の正規表現検索" フラグ
  else if ( ty == lines && st == d.GetLine( ty, lineMode )
            && setting.blankEnable && blank.test( st ) ) {
    re = true;
  }

  /* "キャンセル" フラグ */ 
  var cancel = 7;
  // 検索文字列がないとき(空行)
  if ( ! setting.blankEnable && ! st ) {
    cancel = 1;
  }
  // 検索条件エラー(複数行: 選択範囲に改行あり)
  else if ( new RegExp( "\\n" , "" ).test( st ) ) {
    cancel = 2;
  }
  // 検索条件エラー(複数行: 「表示行」& 選択範囲に折り返しあり)
  else if ( ty != by && ! setting.logical ) {
    cancel = 3;
  }
  // 検索条件エラー(「英単語のみ」有効で検索文字列が非・英単語のとき)
  else if ( ! re && setting.wholeWordEnable && st.match( /\W/ ) ) {
    cancel = 4;
  }

  // ポップアップメニューでジャンプ先リスト
  else {

  /* ポップアップメニューの準備 */
    var m = CreatePopupMenu(),  flags;

    // 【include版】の設定変更メニュー
    if ( initialSettings.settingEnable ) {
      SettingsMenu();
    }

    // 「文字列」の検索では先頭に「キャンセル」のアイテムを表示
    if ( ! re ) {
      m.Add( menuIndent + "キャンセル	& ", 0 );	// Space キーでキャンセル可
      m.Add( "", 0, meMenuSeparator );
      // Menu Array, Total, Grand Total, Hit, Variables, Me
      var mArray = [], gtArray = [],  t ,  me ,  gt = 0,  hit = 0,  v = 0,  check = "";
      var stQuote = st.replace( /\W/g , "\\$&" );
      if ( setting.countEnable ) {
        // アクティブ行(検索開始行)全体の文字列を取得
        var activeLine = d.GetLine( ty, lineMode );
        var tx = s.GetTopPointX( posMode );
        // 「行内のヒット数」と「ヒット総数」カウント用の正規表現
        var countReg = new RegExp(
            ( setting.wholeWordEnable ? ( "\\b" + stQuote + "\\b" ) : stQuote )
          , ( setting.caseEnable ? "g" : "gi" )
        );
      }
    }
    // 「空行+空白行」の検索ではパターンごとの配列を使用する
    else {
      var hArray = [],  nArray = [],  wArray = [];
      // Hit (match completely), Blank line ( n + w ), New line ( ^\n ), White space ( ^[\s ]+$ )
      var h = 0,  b = 0,  n = 0,  w = 0;
    }

  /* 文書全体を行単位で分割し、配列に */
    for ( var a = [ "" ], i = 1; i <= lines ; i ++ ) {	// a[0] = ""、 i = 行番号
      a.push( d.GetLine( i, lineMode ) );
    }

  /* 検索文字列がヒットした行をポップアップメニューのアイテムとして取得する */
    for ( var i = 1, len = a.length; i < len; i ++ ) {	// a[0] = 空要素、i = 行番号 → y

      // アクティブ行は左に ✓ チェックマーク付き
      flags = ( i == ty ) ? meMenuChecked : 0;	// + meMenuGrayed

      // 「検索文字列」がヒットしたら(「大文字/小文字」「英単語のみ」オプションに対応)
      if ( ! re && FindCaseTF( a[i] ) >= 0 ) {
        // 「ヒット総数」フラグが有効の場合
        if ( setting.countEnable ) {
          gtArray.push( [ i, gt ] );
          t = a[i].match( countReg );	// 行内でのヒット回数
          // 選択範囲の検索文字列が「何件目のヒット」か
          if ( i == ty ) {
            me = ( t.length > 1 && a[i].substr( 0, tx - 1 ).match( countReg ) )
                    ? gt + 1 + a[i].substr( 0, tx - 1 ).match( countReg ).length
                    : gt + 1;
          }
          gt += t.length;		// 総ヒット回数(大文字/小文字を区別できる)
          check = Marking();	// 右側につけるマーキング

          // 変数/関数の定義行はメニューの先頭にも置く
          if ( new RegExp( "←" , "" ).test( check ) ) {
            m.Add( Labeling( a[i], i ) + check, i, flags );
            v ++;		// "変数/関数の定義行" のフラグ
          }
        }
        // 行頭空白を除去、空白文字を半角スペースに置換、文字数を圧縮
        // 「検索文字列がヒットした行」を配列に格納
        mArray.push( [ Labeling( a[i], i ) + check, i, flags ] );	// ヒットした行
        hit ++;		// ヒットした行数
      }	// end if () { 検索文字列がヒットしたら }

      // 「空行+空白行」の検索ではサブメニュー用の配列に分別
      else if ( re && blank.test( a[i] ) ) {
        b ++;		// 空行+空白行の総数: Blank line ( n + w )
        if ( a[i] == st ) {
          hArray.push( [ Labeling( a[i], i ), i, flags ] );	// 一致した行
          h ++;		// 一致した行数: Hit ( match completely )
        }
        if ( st && ! a[i] ) {
          nArray.push( [ Labeling( a[i], i ), i, flags ] );	// 空行 a[i] == ""
          n ++;		// 空行数: New line ( /^$/ )
        }
        else if ( a[i] ) {
          wArray.push( [ Labeling( a[i], i ), i, flags ] );	// 空白行 a[i].length > 0
          w ++;		// 空白行数: White space ( /^\s+$/ )
        }
      }	// end else if () { 「空行+空白行」の検索 }
    }	// end for () { ヒットした行を取得する }

    // 配列から「検索文字列がヒットした行」をポップアップメニューに追加する
    if ( ! re && hit ) {
      if ( v ) {
        m.Add( "", 0, meMenuSeparator );
      }
      if ( hit > 30 ) {
        // ヒットした行が 30 より多いときは「※さいごの行※」を追加
        m.Add( " ※  さいごの行  ( 行番号: " + mArray[ hit - 1 ][1] + " 行目 )  ※", mArray[ hit - 1 ][1] );
        m.Add( "", 0, meMenuSeparator );
      }
      if ( v < hit ) {
        for ( var i = 0; i < hit; i ++ ) {
          // 10件ごとにセパレータ、30件ごとに「キャンセル」行を追加
          AddCancelLines( m, i );
          // 配列から「検索文字列がヒットした行」をポップアップメニューに追加
          m.Add( mArray[i][0], mArray[i][1], mArray[i][2] );
        }
      }
    }

    // 配列から「空行+空白行」をポップアップメニューに追加する
    if ( re && b ) {
      if ( b != h ) {			// ( n + w ) > h
        // 「すべての空行+空白行」の総数をピン止め
        m.Add( menuIndent + "すべての空行+空白行: " + SeparateNum( b.toString() ) + " 行", 0 );	// , meMenuGrayed
        m.Add( "", 0, meMenuSeparator );
      }
      if ( n && st ) {			// ( nArray.length > 0 ) && ( st.length > 0 )
        var sm1 = CreatePopupMenu();
        m.AddPopup( menuIndent + "すべての空行: " + SeparateNum( n ) + " 行 (&N)", sm1 );
        for ( var i = 0; i < n; i ++ ) {
          AddCancelLines( sm1, i );	// 30件ごとに「キャンセル」行を追加
          // 「空行」を「すべての空行」サブメニューに追加
          sm1.Add( nArray[i][0], nArray[i][1], nArray[i][2] );
        }
      }
      if ( w && n + h != b ) {		// ( wArray.length > 0 ) && ( n + h != b )
        var sm2 = CreatePopupMenu();
        m.AddPopup( menuIndent + "すべての空白行: " + SeparateNum( w ) + " 行 (&W)", sm2 );
        for ( var i = 0; i < w; i ++ ) {
          AddCancelLines( sm2, i );	// 30件ごとに「キャンセル」行を追加
          // 「空白行」を「すべての空白行」サブメニューに追加
          sm2.Add( wArray[i][0], wArray[i][1], wArray[i][2] );
        }
      }
      if ( ( n && st ) || ( w && n + h != b ) ) {
        m.Add( "", 0, meMenuSeparator );
      }
      // キャンセルを兼ねる(Space キーでキャンセル可) , meMenuGrayed
      m.Add( " ▼  一致した行: " + SeparateNum( h.toString() ) + " 行  ▼\t& ", 0 );
      if ( h > 30 ) {
        // ヒットした行が 30 より多いときは「※さいごの行※」を追加
         m.Add( " ※  さいごの行  ( 行番号: " + hArray[ h - 1 ][1] + " 行目 )  ※", hArray[ h - 1 ][1] );
      }
      for ( var i = 0; i < h; i ++ ) {	// h == hArray.length
        AddCancelLines( m, i );	// 30件ごとに「キャンセル」行を追加
        // 「一致した行」をメニューに追加
        m.Add( hArray[i][0], hArray[i][1], hArray[i][2] );
      }
    }	// end if () { 配列から「空行+空白行」をポップアップメニューに追加する }

    // 空白行なしで \s*\n を検索にかけた場合(一致する「空行+空白行」が 0 行)
    if ( re && ! b && ! h  ) {
      m.Add( menuIndent + "キャンセル\t& ", 0 );
    }

  /* 検索開始からポップアップメニューを表示するまでの所要時間を取得 */
    var timerStatus = setting.timerEnable ? TimerElapsed( new Date(), start ) : "";

  /* ポップアップメニューを表示するのは、検索ヒット行が複数のとき */
    if ( hit > 1 || b > 1 || setting.settingEnable ) {
      // ポップアップメニューを出すまえにステータスバー表示させること
      var findStatus = FindStatus();
      Status = findStatus;

  /* カーソル位置にポップアップメニューを表示 */
      var y = m.Track( mePosMouse );	// Track( 0 ) ならキャレット位置

  /* 【include版】の設定変更サブメニュー項目を選択した場合 */
      if ( y >= 10000000 ) {
        SettingsChange( y );
        var isChanged = true;		// 選択範囲復旧のためのフラグ
      }

  /* 選択した行番号(行頭)にジャンプして、検索文字列を範囲選択 */
      else if ( y ) {
        s.SetActivePoint( posMode, 1, y );

        // 文字列でジャンプしたとき
        if ( ! re ) {
          if ( setting.historyDisable ) {
            // 検索履歴に残さない ※「検索文字列の強調表示」なし
            s.SetActivePos( s.GetActivePos() + FindCaseTF( d.GetLine( y, lineMode ) ) );
            s.SetAnchorPos( s.GetActivePos() + st.length );
            d.HighlightFind = false;
          }
          else {
            // 検索履歴に残す ※ Mery の検索メソッドで文字列を範囲選択
            s.Find( st, meFindNext );
            // 「検索文字列の強調表示」を解除する/しない
            d.HighlightFind = setting.highlightEnable;
          }
          // ジャンプ後の選択範囲が何件目のヒットかを取得する
          if ( setting.countEnable ) {
            for ( i = 0; i < gtArray.length; i ++ ) {
              if ( gtArray[i][0] == y ) {
                var im = gtArray[i][1] + 1;
                break;
              }
            }
          }
        }
        // 空白行でジャンプしたときは検索履歴に残さない
        else if ( re && d.GetLine( y, lineMode ) ) {
          // 行(空白文字列)全体を範囲選択する(※完全な空行 ^\n では範囲選択しない)
          s.SetAnchorPoint( posMode, 1, y + 1 );
        }

        ScrollY = ( ScrollY == sy ) ? ScrollY
                                    : s.GetActivePointY( mePosView );
        cancel = 0;
      }	// end else ( ジャンプして、検索文字列を範囲選択 )

  /* キャンセルした場合 */
      else {
        cancel = 5;	// ポップアップメニューをキャンセル ( y == 0 )
      }
    }	// end if () { ポップアップメニューを表示 }

    else {
      cancel = 6;		// ほかの行でヒットなし ( hit == 1 || b == 1 )
    }
  }	// end else { ポップアップメニューでジャンプ先リスト }

  /* ジャンプしなかったときは、キャレット位置/選択範囲を復帰 */
  if ( cancel || isChanged ) {
    if ( pos || pos === 0 ) {
      s.SetActivePos( pos );
    }
    else if ( act || act === 0 ) {
      s.SetAnchorPos( anc );
      s.SetActivePos( act, true );
    }
    ScrollX = sx;  ScrollY = sy;
  }

  // キャンセルフラグが 1 ~ 4 (検索条件エラー)のとき、所要時間の計測終了
  timerStatus = ( setting.timerEnable )
                    ? ( ! timerStatus )
                          ? TimerElapsed( new Date(), start )
                          : timerStatus
                    : "";

  /* ステータスバーに終了メッセージを表示 */
  Status = (
      // メニュー表示後にジャンプしなかったときと、設定を変更したとき
      ( cancel == 5 || isChanged ) ? findStatus :	// ヒット数表示のまま
      // メニュー表示後にジャンプしたとき
      ( cancel == 0 ) ? FindStatus() :		// ヒット数表示を更新
      // 検索条件エラーでメニューを表示しなかったとき
    ( ( cancel == 1 ) ? " 検索文字列がありません ( 空行 ); " :
      ( cancel == 2 ) ? " 無効な検索文字列 ( 改行 ); " :
      ( cancel == 3 ) ? " 無効な検索文字列 ( 折り返し ); " :
      ( cancel == 4 ) ? " 無効な検索文字列 ( 非・英単語 ); " :
      ( cancel == 6 ) ? " ヒットなし。 " :
       /*  else  */     " 無効な検索文字列。 "	// ← 使われないはず
    ) + lineStatus + timerStatus
  );
}

// ------------------------------------------------------------

/* 関数 SeparateNum( num ) */
// 数値を3ケタ区切りに
function SeparateNum( num ) {
  // if ( num === 0 ) {
  //   num = "0";
  // }
  return num.toString().replace( /(\d)(?=(?:\d{3})+$)/g , "$1," );
}

/* 関数 TimerElapsed( end, start ) */
// start からの経過時間を [ s.sss 秒 ] で返す
// (Javascript 時間計測のマクロでの分解能は 16ms とのこと)
function TimerElapsed( end, start ) {
  var elapsed = end - start;
  return "  [ "
         + ( ( elapsed / 1000 ) + "000" ).replace( /(\.)(\d{3})(0*)/, "$1 $2" )
         + " 秒 ]";
}

/* 関数 FindCaseTF( str ) */
// 文字列 str 内で st を検索するさいに「大文字/小文字を区別」する/しない
// および 「英単語のみ検索」する/しない オプションに対応
// ヒットすればヒットした位置(  0 ~ )、ヒットしなければ -1 を返す
function FindCaseTF( str ) {
  return setting.wholeWordEnable
            ? str.search( new RegExp( "\\b" + stQuote + "\\b" 
                                    , setting.caseEnable ? "g" : "gi" ) )
            : setting.caseEnable
                ? str.indexOf( st )
                : str.toLowerCase().indexOf( st.toLowerCase() );
}

/* 関数 Marking() */
// ポップアップメニューの右側に表示するマーキング
function Marking() {
  var _stQuote = stQuote.replace( /(?:^\\ |\\ $)/g , "" );
  var _check = (
    ( i == ty && a[i] == st ) ? "\t ⏎" :		// ①アクティブ行(行検索のとき)✓↵
    ( t.length > 1 ) ? "\t x" + t.length :		// ②検索文字列が複数ヒットした行
    ( i != ty && a[i] == activeLine )    ? "\t **" :	// ③アクティブ行と完全一致する重複行
    ( a[i] == st )            ? "\t *" :		// ④検索文字列と完全一致する行
                                "\t   "		// ⑤検索文字列を含む行;
  );
  var JS_check = (
    // 編集モードが "JavaScript" か "MeryMacroJS" のとき
    ( d.Mode.match( /javascript|mery[ _]?macros?[ _]?js/i )
        ? new RegExp( "\\b(?:var |let |const )(?:\\s*|[^(]+[,]\\s*)[$]?"  // ※)※
                      + _stQuote + "(?=\\s*(?:[=,;]|$))" , "" ).test( a[i] )
            // 変数の宣言の行: "var st" ... / "var hoge = piyo , st " ...
            ? "\t ← var"

            : ( new RegExp( "\\b(?:var |let |const ).*?\\(.*?[,]\\s*[$]?"
                          + _stQuote + "(?=\\s*(?:[=,;]|$))" , "" ).test( a[i] )  // ※)※
                && SearchCloseBrc( a[i], stQuote ) )	// 閉じていないカッコのチェック
                  // 変数の宣言の行: "var hoge = fuga(), st " ...
                  ? "\t ← var"

                  : ( ! blank.test( st )
                      && new RegExp( "\\b" + _stQuote 
                                   + "(?=\\s*(?:\\+=|\\+\\+|-=|--|=(?!=)))" , "" ).test( a[i] ) )
                        // 変数に値を代入している行: "st = fuga"
                        ? "\t ← sub"

                        : new RegExp( "\\bfunction +[$]?" 
                                    + _stQuote + "(?=[\\s(])" , "" ).test( a[i] )  // ※)※
                            // 関数の定義の行: "function st" ...
                            ? "\t ← fn"

                            : ""
        : ""
    )
  );
  return _check + JS_check;
}

/* 関数 Labeling( str, num ) */
// ポップアップメニューに表示する文字列の整形
function Labeling( line, i ) {		// Labeling( a[i], i )	※ line = a[i] ヒットした行全体
  // 行頭空白を除去、空白文字は全角スペースに置換、文字数を圧縮して取得
  var label = line.replace( /^[\t  ]+/ , "" )
                  .replace( /(?:\t|[ ]{3,}|[ ]{2,})+/g , " › " )
                  .replace( /[.,:;(){}]/g ,	// 判別しづらい半角メタ文字を全角に置換
                     function( $0 ) {
                       return String.fromCharCode( $0.charCodeAt( 0 ) + 0xFEE0 )
                     } );
  label = ! line.length
		? "( 空行 )"
		: ! label.length
			? ( line == st )
				? "( 空白行 " + line.length + " 文字 * )"
				: "( 空白行 " + line.length + " 文字 )"
			: ( label.length > setting.menuWidth )
				? label.slice( 0, setting.menuWidth ) + "..."
				: label;
  // 行番号の右寄せは EN SPACE " " (U+2002) でケタ埋め
  return ( "          " + i ).slice( 0 - linesWidth ) + " : " + label;
}

/* 関数 AddCancelLines( menu, num ) */
// 30行ごとに「キャンセル」行を追加する
function AddCancelLines( menu, num ) {
  // 「文字列の検索」では10件ごとにセパレータを追加
  if ( ! re && num > 0 && num % 10 == 0 && num % 30 != 0 ) {
    menu.Add( "", 0, meMenuSeparator );
  }
  // 30件ごとに「キャンセル」行を追加
  if ( num % 30 == 0 ) {
    if ( num != 0 ) {
      menu.Add( "", 0, meMenuSeparator );
    }
    if ( num != 0 || menu != m) {
      menu.Add( menuIndent + "キャンセル", 0 );
      menu.Add( "", 0, meMenuSeparator );
    }
  }
}

/* 関数 FindStatus() */
// ステータスバーに表示するヒット数や所要時間の情報など
function FindStatus() {
  var statusStr = (
    ( re ? " 一致: " + SeparateNum( h ) + " 行 / 空白行数: " + SeparateNum( b ) + " 行 / "
         : ( gt ? " ヒット数: " + SeparateNum( gt ) + " 件 ( "
                  // me: ジャンプする前の位置 / im: ジャンプした後の位置
                  + SeparateNum( im ? im : me ) + " 件目 ) ・ "
                : " ヒット: " )
           + SeparateNum( hit ) + " 行 / "
    ) + lineStatus + timerStatus
  );
  return statusStr;
}

/* 関数 SearchCloseBrc( str, word ) */
// 閉じていないカッコがないかを簡易的にチェックする(閉じていなければ false )
function SearchCloseBrc( str, word ) {
  var txt = str.substr( str.search( /\b(?:var |let |const )/ )
                      , str.search( new RegExp( "\\b" + word + "\\b" , "" ) )
                      );
  var oBrc = txt.match( /\(/g );	// 開きカッコの数
  var cBrc = txt.match (/\)/g );	// 閉じカッコの数
  return ( ! cBrc || oBrc.length > cBrc.length ) ? 0 : 1;
}

/* 関数 SettingsMenu() */
// ポップアップメニューに設定変更用のサブメニューを表示する
function SettingsMenu() {
  var subMenu = CreatePopupMenu();
  var grayFlag_1 = ( setting.autoLineModeEnable || ! setting.settingEnable )
                       ?  meMenuGrayed : 0;
  var logicalFlag = setting.logical ? meMenuChecked : 0;
  var viewFlag = setting.logical ? 0 : meMenuChecked;
  var grayFlag_2 = ( setting.historyDisable || ! setting.settingEnable )
                       ?  meMenuGrayed : 0;
  var highlightFlag = ( setting.highlightEnable && ! setting.historyDisable )
                          ? meMenuChecked : 0;
  var timerFlag = setting.timerEnable ? meMenuChecked : 0;
  var autoLineModeFlag = setting.autoLineModeEnable ? meMenuChecked : 0;
  var historyFlag = setting.historyDisable ? meMenuChecked : 0;
  var caseFlag = setting.caseEnable ? meMenuChecked : 0;
  var blankFlag = setting.blankEnable ? meMenuChecked : 0;
  var countFlag = setting.countEnable ? meMenuChecked : 0;
  var wholeWordFlag = setting.wholeWordEnable ? meMenuChecked : 0;
  var maxWidthFlag = setting.menuWidth > 100 ? meMenuGrayed : 0;
  var minWidthFlag = setting.menuWidth < 30 ? meMenuGrayed : 0;
  var settingFlag = setting.settingEnable ? meMenuChecked : 0;
  var grayFlag = ! setting.settingEnable ? meMenuGrayed : 0;

  m.Add( "", 0, meMenuSeparator );
  m.AddPopup( menuIndent + "設定を変更する (&S)", subMenu );
  m.Add( "", 0, meMenuSeparator );
  subMenu.Add( "「大文字/小文字」 を区別する (&C)", 10000007, caseFlag + grayFlag );
  subMenu.Add( "「英単語のみ」 検索する (&W)", 10000008, wholeWordFlag + grayFlag );
  subMenu.Add( "「空行/空白行」 を検索対象にする (&B)", 10000009, blankFlag + grayFlag );
  subMenu.Add( "", 0, meMenuSeparator );
  subMenu.Add( "「検索履歴」 に残さない (&H)", 10000006, historyFlag + grayFlag );
  subMenu.Add( "「検索文字列の強調表示」 を残す (&F)", 10000003, highlightFlag + grayFlag_2 );
  subMenu.Add( "", 0, meMenuSeparator );
  subMenu.Add( "「行の表示方法」 を自動設定する (&A)", 10000005, autoLineModeFlag + grayFlag );
  subMenu.Add( "  行数を 「論理行」 で表示する (&L)", 10000001, logicalFlag + grayFlag_1 );
  subMenu.Add( "  行数を 「表示行」 で表示する (&V)", 10000002, viewFlag + grayFlag_1 );
  subMenu.Add( "", 0, meMenuSeparator );
  subMenu.Add( "「検索処理の所要時間」 を表示する (&T)", 10000004, timerFlag + grayFlag );
  subMenu.Add( "「ヒット件数」(出現総数)を表示する (&G)", 10000010, countFlag + grayFlag );
  subMenu.Add( "", 0, meMenuSeparator );
  subMenu.Add( "  メニューの表示幅を 10文字 増やす (&P) \t" + setting.menuWidth
               + ( setting.menuWidth > 100 ? "" : " → " + ( setting.menuWidth + 10 ) )
               , 10000011, maxWidthFlag + grayFlag );
  subMenu.Add( "  メニューの表示幅を 10文字 減らす (&M) \t" + setting.menuWidth
               + ( setting.menuWidth < 30 ? "" : " → " + ( setting.menuWidth - 10 ) )
               , 10000012, minWidthFlag + grayFlag );
  subMenu.Add( "", 0, meMenuSeparator );
  subMenu.Add( "", 0, meMenuSeparator );
  subMenu.Add( "  設定変更を許可する (&S)", 10000000, settingFlag );
  subMenu.Add( "  設定内容を初期化する (&I)", 10000015, grayFlag );
  subMenu.Add( "", 0, meMenuSeparator );
  subMenu.Add( "  このマクロの JS ファイルを開く (&O)", 10000013 );
  subMenu.Add( "  このマクロの JSON ファイルを開く (&J)", 10000014, grayFlag );
}

/* 関数 SettingsChange( num ) */
// ポップアップメニューで選択した項目の設定状態を変更する
function SettingsChange( num ) {
  var setting = IO.Deserialize( setting, json );
  var sIsChanged = true;
  switch ( num ) {
    case 10000000:
      setting.settingEnable = ! setting.settingEnable;
      break;
    case 10000001:
    case 10000002:
      setting.logical = ! setting.logical;
      break;
    case 10000003:
      setting.highlightEnable = ! setting.highlightEnable;
      break;
    case 10000004:
      setting.timerEnable = ! setting.timerEnable;
      break;
    case 10000005:
      setting.autoLineModeEnable = ! setting.autoLineModeEnable;
      break;
    case 10000006:
      setting.historyDisable = ! setting.historyDisable;
      break;
    case 10000007:
      setting.caseEnable = ! setting.caseEnable;
      break;
    case 10000008:
      setting.wholeWordEnable = ! setting.wholeWordEnable;
      break;
    case 10000009:
      setting.blankEnable = ! setting.blankEnable;
      break;
    case 10000010:
      setting.countEnable = ! setting.countEnable;
      break;
    case 10000011:
      setting.menuWidth += 10;
      break;
    case 10000012:
      setting.menuWidth -= 10;
      break;
    case 10000013:
      sIsChanged = false;
      OpenJumpJS( "setting.settingEnable = " );
      break;
    case 10000014:
      sIsChanged = false;
      OpenJumpJson( json );
      break;
    case 10000015:
      sIsChanged = false;
      CheckJsonFile( json );
      break;
    default:
      sIsChanged = false;
      break;
  }
  if ( sIsChanged ) {
    IO.Serialize( setting, json );
  }
}

/**
 * 関数 OpenJumpJS( str )
 * このマクロの JS ファイルを開いて設定項目の行にジャンプする
 * 引数: 検索文字列(設定項目の行の文字列)
 * ※引数がないときは文頭
 */
function OpenJumpJS( str ) {
  Redraw = false;
  var targetStr = str || "";
  // Count は 1 からの整数値、Item は 0 からの整数値
  var eCount = editors.Count;
  var dCount,  dItem,  ee ,  dd;
  var isOpen = false;
  OuterLoop:
  for ( var j = 0; j < eCount; j ++ ) {
    dCount = editors.Item( j ).documents.Count;
    for ( var i = 0; i < dCount; i ++ ) {
      dItem = editors.Item( j ).documents.Item( i );
      if ( dItem.FullName == ScriptFullName ) {
        isOpen = true;
        ee = j;
        dd = i;
        break OuterLoop;
      }
    }
  }
  if ( isOpen ) {
    editors.Item( ee ).documents.Item( dd ).Activate();
  }
  else {
    editor.NewFile();
    ee = ( editors.Count == 1 ) ? 0 : eCount;
    editors.Item( ee ).OpenFile( ScriptFullName );
  }
  var js = editors.Item( ee ).ActiveDocument;
  var settingPos = js.Text.indexOf( targetStr );
  js.selection.SetActivePos( settingPos + targetStr.length );
  js.selection.WordRight( true );	// true を範囲選択
  var vy = js.selection.GetActivePointY( mePosView );
  ScrollY = vy;
  // 狙った行にスクロールしないようなら、強引にやる
  var ly = js.selection.GetActivePointY( mePosLogical );
  var WshShell = new ActiveXObject( "WScript.Shell" );
  WshShell.Run( '"' + editor.FullName + '" /l ' + ly + ' "'
                    + ScriptFullName + '"' );
  ScrollY = vy;
  Redraw = true;
}

/**
 * 関数 OpenJumpJson( jsonName )
 * JSON ファイルを開く
 */
function OpenJumpJson( jsonName ) {
  var Fso = new ActiveXObject( "Scripting.FileSystemObject" );
  var jsonDir = JsonDir();
  var jsonPath = jsonDir + "\\" + jsonName + ".json";
  IO.Serialize( setting, jsonName );
  Sleep( 500 );
  if ( Fso.FileExists( jsonPath )
      && JsonContents( jsonPath ).length ) {
    var confirmStr = "設定ファイルは正常です \n\n"
                   + JsonContents( jsonPath )
                   + "設定ファイルを開きますか? ";
    if ( Confirm( confirmStr ) ) {
      var WshShell = new ActiveXObject( "WScript.Shell" );
      WshShell.Run( '"' + editor.FullName
                  + '" "' + jsonPath + '"' );
    }
  }
  else {
    Alert( "設定ファイルがありません \n" + jsonDir + "  " );
  }
}

/** 
 * 関数 CheckJsonFile( jsonName )
 * JSON ファイルの初期化/実在確認
 */
function CheckJsonFile( jsonName ) {
  var jsonDir = JsonDir();
  var jsonPath = jsonDir + "\\" + jsonName + ".json";
  var confirmStr = jsonPath + "  \n\n"
                 + "設定ファイルを初期化しますか? "
  if ( Confirm( confirmStr ) ) {
    IO.Serialize( initialSettings, jsonName );
    Sleep( 500 );
    var alertStr = ( IO.Path.IsExist( jsonPath )
                    && JsonContents( jsonPath ).length )
                 ? "設定ファイルは正常です \n\n"
                   + JsonContents( jsonPath )
                 : "設定ファイルがありません \n"
                   + jsonDir + "  ";
    Alert( alertStr );
  }
}

/** 
 * 関数 JsonDir()
 * JSON ファイルの親フォルダのパス
 */
function JsonDir() {
  var Fso = new ActiveXObject( "Scripting.FileSystemObject" );
  var jsonDir;
  if ( Fso.FileExists( editor.FullName.replace( /\.exe$/i, ".ini" ) ) ) {
    jsonDir = editor.FullName.replace( /Mery\.exe$/i, "" )
            + "Macros\\MacroSettings";
  }
  else {
    var WshShell = new ActiveXObject( "WScript.Shell" );
    jsonDir = WshShell.ExpandEnvironmentStrings( "%APPDATA%" )
            + "\\Mery\\MacroSettings";
  }
  return jsonDir;
}

/** 
 * 関数 JsonContents( jsonPath )
 * JSON ファイルの文字列をメッセージボックス用に整形する
 */
function JsonContents( jsonPath ) {
  var contents = ( jsonPath + "  \n"
  + IO.LoadFromFile( jsonPath )
     .replace( /^\{/, "\n{\n  " )
     .replace( /\}$/g, "\n}\n\n" )
     .replace( /,/g, " ,\n  " )
     .replace( /:/g, ": " )
  // .replace( /[a-z]/g,
  //   function( $0 ) {
  //     return String.fromCharCode( $0.charCodeAt( 0 ) + 0xFEE0 )
  //   } )
  );
  return contents;
}

メモ[編集]

  • 2018/12/16
新規アップロード
  • 2018/12/17
・設定用 JSON ファイルをポップアップメニューから初期化し、内容を参照できるようにした
・ソースコードのミスを修正
  • 2018/12/20
・文字列の検索のさいに「選択範囲が何件目のヒットか」を表示
・ソースコードのミスを修正
  • 2019/03/13
・行数制限を追加(9,999,999行)
・「変数/関数の定義行」の表示に対応する編集モードを "JavaScript", "MeryMacroJS" に限定した
・検索条件エラーの条件を細分化
・ソースコードのミスを修正
・「大文字/小文字の区別」での「ヒット件数」のカウントの不具合を修正
・「行の表示方法」が表示座標のときに発生するエラーを修正
・「英単語のみ検索」での範囲選択の動作を修正 ...など
  • 2019/04/07
・Quit() メソッドを削除
  • 2019/04/16
・JSON ファイルの初期化/実在確認のコードを修正

行の表示方法が「表示座標」のとき、折り返し部分にかかった文字列にはマッチしないままです。
実装すると動作速度が遅くなり、実用に適さなくなってしまいます。 …現状でも、5000行以上のファイルにはあまり適さないとおもう。

スポンサーリンク