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

提供: MeryWiki
ナビゲーションに移動 検索に移動
Sukemaru (トーク | 投稿記録)
Sukemaru (トーク | 投稿記録)
更新
4行目: 4行目:


== 人柱版 ==
== 人柱版 ==
「表示行」で運用した場合の '''動作検証が不十分''' ですが、以下の追加オプションにご興味のある方は [[#ダウンロード (sukemaru 版)|ZIP 書庫]] 内の「人柱版(空行+空白行検索に対応)」フォルダの JS ファイルをお使いください。 <br>
「表示行」で運用した場合の '''動作検証が不十分''' ですが、以下の追加オプションにご興味のある方は [[#ダウンロード (sukemaru 版)|ZIP 書庫]] 内の「人柱版(空行+空白行検索に対応)」フォルダの JS ファイルをお使いください。 <br>
動作に異常があるようでしたら、ソースコード内の設定セクションで「人柱版の設定項目」を無効化( false )してください。
動作に異常があるようでしたら、ソースコード内の設定セクションで「人柱版の設定項目」を無効化( false )してください。


▼ 追加オプション ▼
▼ 追加オプション ▼
* 「検索処理の所要時間」をステータスバーに表示します(◆変数で設定)。
:* 人柱版では処理が遅くなる追加機能が多いので…。
* 「行の表示方法」の設定を Mery.ini から自動取得するかしないかを選択できます(◆変数で設定)。
:* ただし、ini ファイルが肥大化またはフラグメントしていると、処理が多少遅くなります。
* 検索文字列を検索/置換ダイアログの「検索履歴に残すか残さないか」を選択できます(◆変数で設定)。
* 検索文字列を検索/置換ダイアログの「検索履歴に残すか残さないか」を選択できます(◆変数で設定)。
:* 「残さない」場合は Mery の検索機能を使用せずにジャンプするため、通常版と共通の設定項目「検索文字列の強調表示を残す」も無効になります。
:* 「残さない」場合は Mery の検索機能を使用せずにジャンプするため、通常版と共通の設定項目「検索文字列の強調表示を残す」も無効になります。


* 検索のさいに「大文字/小文字を区別」するかしないかを選択できます(◆変数で設定)。
* 検索のさいに「大文字/小文字を区別」するかしないかを選択できます(◆変数で設定)。


* 空行にキャレットがある状態か、"末尾改行をふくめて" 空白行を範囲選択している状態から、「すべての空行+空白行」を検索できるオプションを追加しました(変数で設定)。
* 空行にキャレットがある状態か、"末尾改行をふくめて" 空白行を範囲選択している状態から、「すべての空行+空白行」を検索できるオプションを追加しました(変数で設定)。
20行目: 32行目:


: ※「空行+空白行」が対象のとき以外は正規表現を使わずに行単位で検索するので、選択範囲が複数行(末尾以外に改行 "\n" がある)の場合や、空白行オプションが無効で選択範囲が空行 "^\n" の場合は検索できません。
: ※「空行+空白行」が対象のとき以外は正規表現を使わずに行単位で検索するので、選択範囲が複数行(末尾以外に改行 "\n" がある)の場合や、空白行オプションが無効で選択範囲が空行 "^\n" の場合は検索できません。


* 検索対象が「空行+空白行」でないときは、ヒットした行数とは別に「ヒットした件数」をステータスバーに表示できます(◆変数で設定)。「ヒット件数」の判定は、大文字/小文字オプションの設定に依拠します。
* 検索対象が「空行+空白行」でないときは、ヒットした行数とは別に「ヒットした件数」をステータスバーに表示できます(◆変数で設定)。「ヒット件数」の判定は、大文字/小文字オプションの設定に依拠します。
 
 
* 検索対象が「空行+空白行」でないとき、ポップアップメニューの右隅に以下のマーキングを表示できます(「ヒット件数」オプションが有効の場合のみ)。
* 検索対象が「空行+空白行」でないとき、ポップアップメニューの右隅に以下のマーキングを表示できます(「ヒット件数」オプションが有効の場合のみ)。


:* JavaScript で検索文字列が変数/関数名のとき、定義行に「 ← var 」「 ← fn 」マーク
:* 検索文字列が行全体のとき、アクティブ行に「 ⏎ 」マーク
:* 検索文字列が行全体のとき、アクティブ行に「 ⏎ 」マーク
:* アクティブ行と完全に一致する重複行に「 ** 」マーク (※行検索でないときにも表示)
:* アクティブ行と完全に一致する重複行に「 ** 」マーク (※行検索でないときにも表示)
30行目: 45行目:
:* 検索文字列が複数回ヒットした行に「 x2 」マーク
:* 検索文字列が複数回ヒットした行に「 x2 」マーク
: ※「一致」の判定では、つねに大文字/小文字を区別します。
: ※「一致」の判定では、つねに大文字/小文字を区別します。


* ポップアップメニューを Space キーでもキャンセルできるようにしました。ただし、メニューに表示されるヒットした行の文字列に「アンパサンドと半角スペース "&␣"」があるときは Esc キーのみ( "&&␣" なら可)。
* ポップアップメニューを Space キーでもキャンセルできるようにしました。ただし、メニューに表示されるヒットした行の文字列に「アンパサンドと半角スペース "&␣"」があるときは Esc キーのみ( "&&␣" なら可)。


※大きな文書の場合、「空行/空白行/空白文字列」や「頻出する文字/単語」の検索では処理に時間がかかります。 <br>  
※大きな文書の場合、「空行/空白行/空白文字列」や「頻出する文字/単語」の検索では処理に時間がかかります。 <br>  
※「人柱版 設定項目」の変数の値をすべて無効 false にすれば通常版とおなじ動作になります。
※「人柱版 設定項目」の変数の値をすべて無効 false にすれば通常版とほぼおなじ動作になります。


[[ポップアップメニューで検索先にジャンプ#人柱版のソースコード|>> もとのページ]]
[[ポップアップメニューで検索先にジャンプ#人柱版のソースコード|>> もとのページ]]


== ソースコード ==
== ソースコード ==
<source lang="javascript">
<source lang="javascript" style="height:40em; overflow:auto;">
#title = "ジャンプ"
#title = "ジャンプ"
#tooltip = "ポップアップメニューで検索先にジャンプ"
#tooltip = "ポップアップメニューで検索先にジャンプ"
46行目: 64行目:
// 「空行+空白行」の検索に対応
// 「空行+空白行」の検索に対応
// 「ヒット件数(出現回数)」を表示
// 「ヒット件数(出現回数)」を表示
// 「検索履歴に残さない」を追加
// スペースキーでポップアップメニューをキャンセル可
// 「検索履歴に残さない」を追加(■通常版へ逆移植済み)
// 「行の表示方法」を Mery.ini から取得
// 「自動マーカー」オプションを廃止(デフォルト: 自動マーカー有効)
// 「検索所要時間」の表示(■通常版へ逆移植済み)
// 検索ジャンプ後のスクロールの挙動を調整(■通常版へ逆移植済み)
// 「変数/関数の定義行」のマーキング


/**
/**
54行目: 78行目:
  * Original Copyright: 手石 (2014/04/04)
  * Original Copyright: 手石 (2014/04/04)
  * ------------------------------------------------------------
  * ------------------------------------------------------------
  * Modified by: sukemaru (2018/11/12 - 2018/12/10)
  * Modified by: sukemaru (2018/11/12 - 2018/12/13)
  * ------------------------------------------------------------
  * ------------------------------------------------------------
  */
  */
var start = new Date().getTime(); // 所要時間の計測開始


// ---------- ▼ 通常版 設定項目 ▼ ----------
// ---------- ▼ 通常版 設定項目 ▼ ----------


// ■行の表示方法 (true; 論理座標 /false; 表示座標)
// ■行の表示方法 (true; 論理座標 /false; 表示座標)
// ※ ◆人柱版◆ の追加設定で autoLineModeEnable = true の場合は無効(自動設定)
var logical = true; // ■初期値 = true;  (論理座標)
var logical = true; // ■初期値 = true;  (論理座標)


// ■検索文字列の強調表示を残すか? (true; 残す /false; 残さない)
// ■検索文字列の強調表示を残すか? (true; 残す /false; 残さない)
var highlightEnable = true; // ■初期値 = true; (残す)
// ※ ◆人柱版◆ の追加設定で historyDisable = true の場合は無効(強調表示しない)
var highlightEnable = false; // ■初期値 = true; (残す)


// ■選択範囲なしの状態から実行したときに自動マーカーを表示するか? (true; 表示する / false; 表示しない)
// ※ [表示]メニュー >> [マーカー] >> [自動マーカー] が OFF なら効果なし。
var markerEnable = true; // ■初期値 = true; (表示する)


// ---------- ▼ 人柱版 設定項目 ▼ ----------
// ---------- ▼ 人柱版 設定項目 ▼ ----------
// ◆初期値:人柱版のオプションを有効にする( true
// ◆人柱版のオプションを有効にすると処理が遅くなります
 
// ◆検索処理の所要時間を表示するか? (true; する /false; しない)
var timerEnable = true; // ◆初期値 = true; (する)
 
// ◆行の表示方法を Mery.ini から自動で読み込むか? (true; 読み込む /false; 読み込まない)
// ※ ini ファイルが肥大化またはフラグメントしていると処理が遅くなる
var autoLineModeEnable = true; // ◆初期値 = false; (読み込まない)


// ◆検索文字列を「検索履歴」に残さない? (true; 残さない / false; 残す)
// ◆検索文字列を「検索履歴」に残さない? (true; 残さない / false; 残す)
85行目: 118行目:
// ◆ヒット件数(出現回数)を表示するか? (true; する /false; しない)
// ◆ヒット件数(出現回数)を表示するか? (true; する /false; しない)
var countEnable = true; // ◆初期値 = true; (する)
var countEnable = true; // ◆初期値 = true; (する)


// ---------- ▼ 手石版 設定項目 ▼ ----------
// ---------- ▼ 手石版 設定項目 ▼ ----------
93行目: 127行目:
// ---------- ▲ 設定項目 ここまで ▲ ----------
// ---------- ▲ 設定項目 ここまで ▲ ----------


Redraw = markerEnable;
 
/* ■ポップアップメニューの「行番号の表示方法」 */
var $LineColumnView = logical ? 0 : 1;
 
// Mery.ini から「行の表示方法」を取得する
if (autoLineModeEnable ) {
  var WshShell = new ActiveXObject( "WScript.Shell" );
  var Fso = new ActiveXObject( "Scripting.FileSystemObject" );
  // ini を探す(Zip版なら Mery.exe のフォルダ、インストーラ版なら %appdata%\Mery フォルダ)
  var portableIni = editor.FullName.slice( 0, 0 - "mery.exe".length ) + "Mery.ini";
  var iniPath = Fso.FileExists( portableIni ) ? portableIni :
                WshShell.ExpandEnvironmentStrings( "%APPDATA%\\Mery\\Mery.ini" );
  var iniFile = Fso.OpenTextFile( iniPath, 1 );
  var iniText = iniFile.ReadAll();
  $LineColumnView = Number( iniText.match( /^LineColumnView=[01]$/m ).shift().slice( -1 ) );
  iniFile.Close();
}
 
// ini から取得した設定値にもとづいて変数に
var posMode = $LineColumnView ? mePosView : mePosLogical;
var lineMode = $LineColumnView ? meGetLineView : 0;
 
var d = document,  s = document.selection;
var sx = ScrollX,  sy = ScrollY;
var sx = ScrollX,  sy = ScrollY;
var d = document,  s = document.selection;
 
// 選択範囲の先頭位置(行番号)と、文書全体の行数(論理行/表示行)
var ty = s.GetTopPointY( posMode );
var lines = d.GetLines( lineMode );
 


/* 検索文字列の準備 */
/* 検索文字列の準備 */
var blank = new RegExp( "^[\t  ]*$" , "" ); // 空白行の正規表現
var blank = new RegExp( "^[\t  ]*$" , "" ); // 空白行の正規表現
var re; // "空白行の正規表現検索" フラグ
// 選択範囲なしならキャレット位置の単語を選択する
// 選択範囲なしならキャレット位置の単語を選択する
if ( s.IsEmpty ) {
if ( s.IsEmpty ) {
119行目: 181行目:
   }
   }
   if ( blankEnable && st.match( blank ) )
   if ( blankEnable && st.match( blank ) )
     var re = true; // "空白行の正規表現検索" フラグ
     re = true; // "空白行の正規表現検索" フラグ
}
}
// 文書末尾の空白行の場合でも "空白行の正規表現検索" フラグ
else if ( blankEnable && st.match( blank ) && st == d.GetLine( ty, lineMode ) )
  re = true;


// 選択範囲の先頭位置と、文書全体の行数(論理行/表示行)
var ty = s.GetTopPointY( logical ? mePosLogical : mePosView );
var lines = d.GetLines( logical ? 0 : meGetLineView );
var cancel = 5; // "キャンセル" フラグ
var cancel = 5; // "キャンセル" フラグ


133行目: 195行目:
else if ( st.match( /\n/g ) )
else if ( st.match( /\n/g ) )
   cancel = 2;
   cancel = 2;


// ポップアップメニューでジャンプ先リスト
// ポップアップメニューでジャンプ先リスト
138行目: 201行目:


/* 文書全体を行単位で分割し、配列に */
/* 文書全体を行単位で分割し、配列に */
   if ( logical ) // 論理行
   for( var a = [ "" ], i = 1; i <= lines ; i ++ ) // a[0] = "" / i = 行番号
    var a = d.Text.split( "\n" );
    a.push( d.GetLine( i, lineMode ) );
  else // 表示行
    for( var i = 1, a = []; i <= lines ; i ++ )
      a.push( d.GetLine( i, meGetLineView ) );
  a.unshift( "" ); // a[0] に空要素を追加


/* ポップアップメニューの最上部に表示するアイテム */
 
/* ポップアップメニューの準備 */
   var m = CreatePopupMenu();
   var m = CreatePopupMenu();
   // 文字列の検索では先頭に「キャンセル」のアイテムを表示
   // 「文字列」の検索では先頭に「キャンセル」のアイテムを表示
   if ( ! re ) {
   if ( ! re ) {
     m.Add( "        キャンセル & ", 0 );
     m.Add( "        キャンセル & ", 0 ); // Space キーでキャンセル可
     m.Add( "", 0, meMenuSeparator );
     m.Add( "", 0, meMenuSeparator );
     // Grand total, Hit
     // Grand total, Hit, Variables
     var gt = 0, h = 0;
     var mArray = [],  t,  gt = 0, hit = 0,  v = 0,  check = "",  flags;
     // アクティブ行(検索開始行)全体の文字列を取得
     // アクティブ行(検索開始行)全体の文字列を取得
     if ( countEnable )
     if ( countEnable )
       var activeLine = d.GetLine( ty, logical ? 0 : meGetLineView );
       var activeLine = d.GetLine( ty, lineMode );
   }
   }
   // 「空行+空白行」の検索では配列を使用する
   // 「空行+空白行」の検索ではパターンごとの配列を使用する
   else {
   else {
     var hArray = [],  nArray = [],  wArray = [];
     var hArray = [],  nArray = [],  wArray = [];
     // Hit ( match completely ), Blank line ( n + w ), New line (^$), White space (^\s+$)  
     // Hit ( match completely ), Blank line ( n + w ), New line ( ^$ ), White space ( ^[\s ]+$ )
     var h = 0,  b = 0,  n = 0,  w = 0;
     var h = 0,  b = 0,  n = 0,  w = 0;
   }
   }
 
 
/* 検索文字列がヒットした行をポップアップメニューのアイテムとして取得する */
/* 検索文字列がヒットした行をポップアップメニューのアイテムとして取得する */
   for( var i = 1; i < a.length; i ++ ) { // i = 行番号 => y
  var stQuote = st.replace( /\W/g , "\\$&" );
  var reg = new RegExp( stQuote , caseEnable ? "g" : "gi" );
 
   for( var i = 1; i < a.length; i ++ ) { // a[0] = 空要素、i = 行番号 => y
     // 空行からの検索なら「空行+空白行」以外はスキップ
     // 空行からの検索なら「空行+空白行」以外はスキップ
     if ( re && ! a[i].match( blank ) )
     if ( re && ! a[i].match( blank ) )
       continue;
       continue;


     // 検索文字列がヒットしたら
     // 「検索文字列」がヒットしたら
     if ( ( ! re && findCaseTF( a[i], st ) >= 0 ) || ( re && a[i].match( blank ) ) ) {
     if ( ( ! re && findCaseTF( a[i], st ) >= 0 ) || ( re && a[i].match( blank ) ) ) {
      // 30件ごとに「キャンセル」行を追加
      if ( ! re && h != 0 && h % 30 == 0 ) {
        m.Add( "", 0, meMenuSeparator );
        m.Add( "        キャンセル", 0 );
        m.Add( "", 0, meMenuSeparator );
      }
       // 行内でのヒット回数
       // 行内でのヒット回数
       if ( ! re && countEnable ) {
       if ( ! re && countEnable ) {
         var t = a[i].match(
         t = a[i].match( reg );
          RegExp( st.replace( /\W/g , "\\$&" ) , caseEnable ? "g" : "gi" )
        );
         gt += t.length; // 総ヒット回数(大文字/小文字を区別できる)
         gt += t.length; // 総ヒット回数(大文字/小文字を区別できる)
        check = Marking(); // 右側につけるマーキング
       }
       }
      // 右側につけるマーキング
      var check = ( ! re && countEnable ) ? Marking() : "";
       // アクティブ行は左に ✓ チェックマーク付き
       // アクティブ行は左に ✓ チェックマーク付き
       var flags = ( i == ty ) ? meMenuChecked : 0; // + meMenuGrayed
       flags = ( i == ty ) ? meMenuChecked : 0; // + meMenuGrayed
 


/* ポップアップメニューにアイテム(行)を追加する */
/* ポップアップメニューにアイテム(行)を追加する */
      // 「文字列」の検索では、行頭空白を除去、空白文字を半角スペースに置換、文字数を圧縮
       if ( ! re ) {
       if ( ! re ) {
         // 行頭空白は除去、空白文字は半角スペースに置換、文字数を圧縮して取得
         // 変数/関数の定義行はメニューの先頭にも置く
         m.Add( Labeling( a[i], i ) + check, i, flags ); // ヒットした行
         if ( countEnable && check.match( /←/ ) ) {
         h ++; // ヒットした行数
          m.Add( Labeling( a[i], i ) + check, i, flags );
          v ++;
        }
        // 「検索文字列がヒットした行」を配列に格納
        mArray.push( [ Labeling( a[i], i ) + check, i, flags ] ); // ヒットした行
         hit ++; // ヒットした行数
       }
       }
       // 「空行+空白行」の検索ではサブメニュー用の配列に分別
       // 「空行+空白行」の検索ではサブメニュー用の配列に分別
       else { // if ( re )
       else { // if ( re )
         b ++; // 空行+空白行数: Blank line ( n + w )
         b ++; // 空行+空白行の総数
         if ( a[i] == st ) {
         if ( a[i] == st ) {
           hArray.push( [ Labeling( a[i], i ) + check, i, flags ] ); // 一致した行
           hArray.push( [ Labeling( a[i], i ), i, flags ] ); // 一致した行
           h ++; // 一致した行数: Hit ( match completely )
           h ++; // 一致した行数
         }
         }
         if ( st && a[i] == "" ) {
         if ( st && ! a[i] ) {
           nArray.push( [ Labeling( a[i], i ) + check, i, flags ] ); // 空行
           nArray.push( [ Labeling( a[i], i ), i, flags ] ); // 空行
           n ++; // 空行数: New line ( /^$/ )
           n ++; // 空行数
         }
         }
         else if ( a[i].length ) {
         else if ( a[i] ) {
           wArray.push( [ Labeling( a[i], i ) + check, i, flags ] ); // 空白行
           wArray.push( [ Labeling( a[i], i ), i, flags ] ); // 空白行
           w ++; // 空白行数: White space ( /^\s+$/ )
           w ++; // 空白行数
         }
         }
       }
       }
     } // end if{ 検索文字列がヒットしたら }
     } // end if () { 検索文字列がヒットしたら }
   } // end for( ヒットした行を取得する )
   } // end for () { ヒットした行を取得する }
 
 
  // 配列から「検索文字列がヒットした行」をポップアップメニューに追加する
  if ( ! re && hit ) {
    if ( v )
      m.Add( "", 0, meMenuSeparator );
    for ( var i = 0; i < hit; i ++ ) {
      // 30件ごとに「キャンセル」行を追加
      if ( i != 0 && i % 30 == 0 ) {
        m.Add( "", 0, meMenuSeparator );
        m.Add( "    キャンセル", 0 );
        m.Add( "", 0, meMenuSeparator );
      }
      m.Add( mArray[i][0], mArray[i][1], mArray[i][2] );
    }
  }


   // 配列から「空行+空白行」をポップアップメニューに追加する
   // 配列から「空行+空白行」をポップアップメニューに追加する
   if ( re ) {
   if ( re && b ) {
     if ( b != h ) { // ( n + w ) > h
     if ( b != h ) { // ( n + w ) > h
       m.Add( " すべての空行+空白行: " + SeparateNum( b ) + " 行", 0 ); // , meMenuGrayed
       m.Add( " すべての空行+空白行: " + SeparateNum( b ) + " 行", 0 ); // , meMenuGrayed
235行目: 314行目:
       }
       }
     }
     }
     if ( w && ( n + h != b ) ) { // ( wArray.length > 0 ) && ( n + h != b )
     if ( w && n + h != b ) { // ( wArray.length > 0 ) && ( n + h != b )
       var sm2 = CreatePopupMenu();
       var sm2 = CreatePopupMenu();
       m.AddPopup( " すべての空白行: " + SeparateNum( w ) + " 行 (&W)", sm2 );
       m.AddPopup( " すべての空白行: " + SeparateNum( w ) + " 行 (&W)", sm2 );
249行目: 328行目:
       }
       }
     }
     }
     if ( ( n && st ) || ( w && ( n + h != b ) ) )
     if ( ( n && st ) || ( w && n + h != b ) )
       m.Add( "", 0, meMenuSeparator );
       m.Add( "", 0, meMenuSeparator );
     // キャンセルを兼ねる(Space キーでキャンセル可) , meMenuGrayed
     // キャンセルを兼ねる(Space キーでキャンセル可) , meMenuGrayed
     m.Add( " ▼    一致した行: " + SeparateNum( h ) + " 行  ▼\t& ", 0 );
     m.Add( " ▼    一致した行: " + SeparateNum( h ) + " 行  ▼\t& ", 0 );
     for ( var i = 0; i < h; i ++ ) { // h == mArray.length
     for ( var i = 0; i < h; i ++ ) { // h == hArray.length
       // 30件ごとに「キャンセル」行を追加
       // 30件ごとに「キャンセル」行を追加
       if ( i != 0 && i % 30 == 0 ) {
       if ( i != 0 && i % 30 == 0 ) {
262行目: 341行目:
       m.Add( hArray[i][0], hArray[i][1], hArray[i][2] );
       m.Add( hArray[i][0], hArray[i][1], hArray[i][2] );
     }
     }
  } // end if () { 配列から「空行+空白行」をポップアップメニューに追加する }
  // 検索開始からポップアップメニューを表示するまでの所要時間を取得
  // (Javascript 時間計測のマクロでの分解能は 16ms とのこと)
  if ( timerEnable ) {
  var elapsed = new Date().getTime() - start;
  var timerStatus =
    "  [ " + ( ( elapsed / 1000 ) + "000" ).replace( /(\.)(\d{3})(0*)/, "$1 $2" ) + " 秒 ]";
   }
   }
 
   // ポップアップメニューを表示するのは、検索ヒット行が複数のとき
   // ポップアップメニューを表示するのは、検索ヒット行が複数のとき
   if ( h > 1 || b > 1) {
   if ( hit > 1 || b > 1) {
     // ポップアップメニューを出すまえにステータスバー表示させること
     // ポップアップメニューを出すまえにステータスバー表示させること
     Status = (
     Status = (
       ( re ? " 一致した行数: " + SeparateNum( h ) + " 行"
       ( re ? " 一致した行数: " + SeparateNum( h ) + " 行"
              + " / すべての空(白)行数: " + SeparateNum( b ) + " 行" :
            + " / すべての空(白)行数: " + SeparateNum( b ) + " 行 / " :
  /* ! re */ ( gt ? ( " ヒット数: " + SeparateNum( gt ) + " 件 /" ) : "" )
    /*!re*/ ( gt ? ( " ヒット数: " + SeparateNum( gt ) + " 件 " ) :
              + " ヒットした行数: " + SeparateNum( h ) + " 行"
                      " ヒットした行数: "  
            )
            + SeparateNum( hit ) + " 行 / " //  ヒット:した行数
       )
       )
       + " / 全体の行数: " + SeparateNum( lines )
       + "全体の行数: " + SeparateNum( lines ) //
       + ( logical ? " 行 (論理行)" : " 行 (表示行)" )
       + ( $LineColumnView ? " 行 (表示行)" : " 行 (論理行)" )
      + ( timerEnable ? timerStatus : "" )
     );
     );


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


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


       // 空白行でジャンプしたときは検索履歴に残さない
       // 空白行でジャンプしたときは検索履歴に残さない
       if ( re && d.GetLine( y, logical ? 0 : meGetLineView ) )
       if ( re && d.GetLine( y, lineMode ) )
         // 行(空白文字列)全体を範囲選択する(※完全な空行 ^\n では範囲選択しない)
         // 行(空白文字列)全体を範囲選択する(※完全な空行 ^\n では範囲選択しない)
         s.SetAnchorPoint( logical ? mePosLogical : mePosView, 1, y + 1 );
         s.SetAnchorPoint( posMode, 1, y + 1 );


       // 文字列でジャンプしたとき
       // 文字列でジャンプしたとき
       else if ( ! re ) {
       else if ( ! re ) {
         if ( historyDisable ) {
         if ( historyDisable ) {
           // 検索履歴に残さない
           // 検索履歴に残さない ※「検索文字列の強調表示」なし
           s.SetActivePos( s.GetActivePos() + findCaseTF( d.GetLine( y, logical ? 0 : meGetLineView ), st ) );
           s.SetActivePos( s.GetActivePos() + findCaseTF( d.GetLine( y, lineMode ), st ) );
           s.SetAnchorPos( s.GetActivePos() + st.length );
           s.SetAnchorPos( s.GetActivePos() + st.length );
         }
         }
         else {
         else {
           // 検索履歴に残す
           // 検索履歴に残す ※ Mery の検索機能で文字列を範囲選択
           s.Find( st, meFindNext ); // Mery の検索機能で文字列を範囲選択
           s.Find( st, meFindNext );
           d.HighlightFind = highlightEnable; // 「検索文字列の強調表示」を解除する/しない
           d.HighlightFind = highlightEnable; // 「検索文字列の強調表示」を解除する/しない
         }
         }
       }
       }
       ScrollY = s.GetActivePointY( mePosView );
       ScrollY = ( ScrollY == sy ) ? ScrollY :
       cancel = 0; // cancel フラグを解除
                                    s.GetActivePointY( mePosView );
       cancel = 0; // cancel フラグを解除
     }
     }


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


/* ジャンプしなかったときは、キャレット位置/選択範囲を復帰 */
/* ジャンプしなかったときは、キャレット位置/選択範囲を復帰 */
if ( cancel ) {
if ( cancel ) {
   if ( p || p === 0 )
   if ( pos || pos === 0 )
     s.SetActivePos( p );
     s.SetActivePos( pos );
   else if ( anc < act )
   else if ( anc < act )
     s.SetActivePos( act, true );
     s.SetActivePos( act, true );
324行目: 419行目:
     s.SetActivePos( act, true );
     s.SetActivePos( act, true );
   }
   }
   // ステータスバーにメッセージを表示
  ScrollX = sx;  ScrollY = sy;
 
   // キャンセルフラグの値が 1 or 2 のとき、所要時間の計測終了
  if ( timerEnable && ! elapsed ) {
    var elapsed = new Date().getTime() - start;
    var timerStatus =
      "  [ " + ( ( elapsed / 1000 ) + "000" ).replace( /(\.)(\d{3})(0*)/, "$1 $2" ) + " 秒 ]";
  }
 
  // ステータスバーにキャンセルメッセージを表示
   Status = (
   Status = (
     ( ( cancel == 1 ) ? " 検索文字列がありません(空行)。" :
     ( ( cancel == 1 ) ? " 検索文字列がありません(空行)。 " :
       ( cancel == 2 ) ? " 検索文字列に【 改行 】を含めることはできません。" :
       ( cancel == 2 ) ? " 検索文字列に【 改行 】を含めることはできません。 " :
       ( cancel == 3 ) ? " キャンセルしました。 ヒット: "
       ( cancel == 3 ) ? " ヒット数: " // 検索ジャンプをキャンセルしました。 
                         + ( gt ? ( SeparateNum( gt ) + " 件 ・ " ) : "" )
                         + ( gt ? ( SeparateNum( gt ) + " 件 ・ " ) : " " )  
                         + SeparateNum( h ) + " 行" :
                         + SeparateNum( h ) + " 行 / " :
       ( cancel == 4 ) ? " ほかの行には見つかりませんでした。" :
       ( cancel == 4 ) ? " ヒットなし。 " : // ほかの行には見つかりませんでした。
       /*  else  */    " 無効な検索文字列。" // ← 使われないはず
       /*  else  */    " 無効な検索文字列。 " // ← 使われないはず
     )
     )
     + " / 全体の行数: " + SeparateNum( lines )
     + "全体の行数: " + SeparateNum( lines )
     + ( logical ? " 行 (論理行)" : " 行 (表示行)" )
     + ( $LineColumnView ? " 行 (表示行)" : " 行 (論理行)" )
    + ( timerEnable ? timerStatus : "" )
   );
   );
  ScrollX = sx;  ScrollY = sy;
}
}
Redraw = true;
 


/* 関数 findCaseTF( str, st ) */  
/* 関数 findCaseTF( str, st ) */  
// 文字列 str 内で st を検索するさいに「大文字/小文字を区別」する/しない
// 文字列 str 内で st を検索するさいに「大文字/小文字を区別」する/しない
// ヒットすればヒットした位置(  0 ~ )、ヒットしなければ -1 を返す
function findCaseTF( str, st ) {
function findCaseTF( str, st ) {
   return caseEnable ? str.indexOf( st ) :
   return caseEnable ? str.indexOf( st ) :
         /* else */  str.toLowerCase().indexOf( st.toLowerCase() )
         /* else */  str.toLowerCase().indexOf( st.toLowerCase() )
}
}


/* 関数 Marking() */
/* 関数 Marking() */
// ポップアップメニューの右側に表示するマーキング
// ポップアップメニューの右側に表示するマーキング  
function Marking() {
function Marking() {
   var _check = (
   var _check = (
    // 変数の宣言の行: "var st" ... / "var hoge = piyo , st" ...
    a[i].match( RegExp( "\\b(?:var |let |const )(?:\\s*|[^\(]+[,]\\s+)[$]?"
                      + stQuote + "(?=\\s*(?:[=,;]|$))" , "" ) ) ?
      "\t ← v a r" : // ) ← ※カッコの数のツジツマあわせ
    // 変数の宣言の行: "var hoge = fuga(), st "  ...
    ( a[i].match( RegExp( "\\b(?:var |let |const ).*?\\(.*?[,]\\s*[$]?"
                        + stQuote + "(?=\\s*(?:[=,;]|$))" , "" ) )
    && SearchCloseBrc( a[i], stQuote ) ) ? // 閉じていないカッコがあるかの簡易的なチェック
      "\t ← v a r" : // ) ← ※同上
    // 変数に値を代入している行: "st = fuga"
    a[i].match( RegExp( "\\b" + stQuote + "(?=\\s*=(?!=))" , "" ) ) ?
      "\t ← s u b" :
    // 関数の定義の行
    a[i].match( RegExp( "\\bfunction +[$]?" + stQuote + "(?=[\\s(])" , "" ) ) ?
      "\t ← f n" : // ) ← ※同上
     ( i != ty && a[i] == activeLine ) ? "\t **" : // ①アクティブ行と完全一致する行(重複行)
     ( i != ty && a[i] == activeLine ) ? "\t **" : // ①アクティブ行と完全一致する行(重複行)
     ( t && t.length > 1 )    ? "\t x" + t.length : // ②検索文字列が複数ヒットした行
     ( t && t.length > 1 )    ? "\t x" + t.length : // ②検索文字列が複数ヒットした行
357行目: 482行目:
     ( i != ty && a[i] == st ) ? "\t *" : // ④検索文字列と完全一致する行
     ( i != ty && a[i] == st ) ? "\t *" : // ④検索文字列と完全一致する行
                                 "" // ⑤検索文字列を含む行
                                 "" // ⑤検索文字列を含む行
   );
   ) ;
 
   return _check;
   return _check;
}
}


/* 関数 Labeling( str, num ) */
/* 関数 Labeling( str, num ) */
// ポップアップメニューに表示する文字列の整形
// ポップアップメニューに表示する文字列の整形
function Labeling( line, i ) {
function Labeling( line, i ) { // Labeling( a[i], i ) ※ line = a[i] ヒットした行全体
   // 行頭空白を除去、空白文字は半角スペースに置換、文字数を圧縮して取得
   // 行頭空白を除去、空白文字は半角スペースに置換、文字数を圧縮して取得
   // 判別しづらいメタ文字を全角に置きかえる
   // 判別しづらい半角メタ文字を全角に置きかえる
   var label = line.replace( /^[\t  ]+/ , "" )
   var label = line.replace( /^[\t  ]+/ , "" )
                   .replace( /\{/g , "{" ).replace( /\}/g , "" )
                   .replace( /(\t|[ ]{2,}|[ ])+/g , "   " )
                   .replace( /\:/g , ":" ).replace( /\;/g , ";" )
                   .replace( /[.,:;(){}]/g ,
                  .replace( /(\t|[ ]{2,}|[ ])+/g , "  " );
                    function( $0 ) { return String.fromCharCode( $0.charCodeAt( 0 ) + 0xFEE0 ) }
                  );
   label = (
   label = (
     ( ! line.length )  ? "( 空行 )" :
     ( ! line.length )  ? "( 空行 )" :
378行目: 506行目:
     /* else */                        label
     /* else */                        label
   );
   );
   return ( "    " + i ).slice( -4 ) + " : " + label;
   return ( "    " + i ).slice( -4 ) + " : " + label;
}
}


/* 関数 SeparateNum( num ) */
/* 関数 SeparateNum( num ) */
// 数値を3ケタ区切りに
// 数値を3ケタ区切りに
function SeparateNum( num ) {
function SeparateNum( num ) {
  if ( ! num ) { num = ""; }
   return num.toString().replace( /(\d)(?=(?:\d{3})+$)/g , "$1," );
   return num.toString().replace( /(\d)(?=(?:\d{3})+$)/g , "$1," );
}
/* 関数 SearchCloseBrc( str, word ) */
// 閉じていないカッコがないかを簡易的にチェックする(閉じていなければ false )
function SearchCloseBrc( str, word ) {
  var txt = str.substr( str.search( /\b(?:var |let |const )/ ), str.search( RegExp( "\\b" + word + "\\b" , "" ) ) );
  var oBrc = txt.match( /\(/g ); // 開きカッコの数
  var cBrc = txt.match (/\)/g ); // 閉じカッコの数
  return ( ! cBrc || oBrc.length > cBrc.length ) ? 0 : 1;
}
}
</source>
</source>
[[ポップアップメニューで検索先にジャンプ#人柱版のソースコード|>> もとのページ]]
[[ポップアップメニューで検索先にジャンプ#人柱版のソースコード|>> もとのページ]]

2018年12月13日 (木) 20:53時点における版

>> もとのページ

ポップアップメニューで検索先にジャンプ(sukemaru 版)

人柱版

「表示行」で運用した場合の 動作検証が不十分 ですが、以下の追加オプションにご興味のある方は ZIP 書庫 内の「人柱版(空行+空白行検索に対応)」フォルダの JS ファイルをお使いください。
動作に異常があるようでしたら、ソースコード内の設定セクションで「人柱版の設定項目」を無効化( false )してください。


▼ 追加オプション ▼

  • 「検索処理の所要時間」をステータスバーに表示します(◆変数で設定)。
  • 人柱版では処理が遅くなる追加機能が多いので…。


  • 「行の表示方法」の設定を Mery.ini から自動取得するかしないかを選択できます(◆変数で設定)。
  • ただし、ini ファイルが肥大化またはフラグメントしていると、処理が多少遅くなります。


  • 検索文字列を検索/置換ダイアログの「検索履歴に残すか残さないか」を選択できます(◆変数で設定)。
  • 「残さない」場合は Mery の検索機能を使用せずにジャンプするため、通常版と共通の設定項目「検索文字列の強調表示を残す」も無効になります。


  • 検索のさいに「大文字/小文字を区別」するかしないかを選択できます(◆変数で設定)。


  • 空行にキャレットがある状態か、"末尾改行をふくめて" 空白行を範囲選択している状態から、「すべての空行+空白行」を検索できるオプションを追加しました(変数で設定)。
  • 「空行+空白行」オプションが有効 ( true ) の場合は、末尾改行 "\n" をふくめて「空白文字のみの行」を範囲選択しているときに、すべての「空行+空白行」を検索対象にします。末尾改行をふくめずに「空白文字のみの行」を範囲選択しているときは「一致する空白文字列」を検索対象にします。
  • 「空行+空白行」オプションが無効 ( false ) の場合、空白文字のみの行」を範囲選択しているときには「一致する空白文字列」を検索対象にします。空行で実行した場合にはポップアップメニューは表示されません。
※「空行+空白行」が対象のとき以外は正規表現を使わずに行単位で検索するので、選択範囲が複数行(末尾以外に改行 "\n" がある)の場合や、空白行オプションが無効で選択範囲が空行 "^\n" の場合は検索できません。


  • 検索対象が「空行+空白行」でないときは、ヒットした行数とは別に「ヒットした件数」をステータスバーに表示できます(◆変数で設定)。「ヒット件数」の判定は、大文字/小文字オプションの設定に依拠します。


  • 検索対象が「空行+空白行」でないとき、ポップアップメニューの右隅に以下のマーキングを表示できます(「ヒット件数」オプションが有効の場合のみ)。
  • JavaScript で検索文字列が変数/関数名のとき、定義行に「 ← var 」「 ← fn 」マーク
  • 検索文字列が行全体のとき、アクティブ行に「 ⏎ 」マーク
  • アクティブ行と完全に一致する重複行に「 ** 」マーク (※行検索でないときにも表示)
  • 検索文字列と完全に一致する行に「 * 」マーク
  • 検索文字列が複数回ヒットした行に「 x2 」マーク
※「一致」の判定では、つねに大文字/小文字を区別します。


  • ポップアップメニューを Space キーでもキャンセルできるようにしました。ただし、メニューに表示されるヒットした行の文字列に「アンパサンドと半角スペース "&␣"」があるときは Esc キーのみ( "&&␣" なら可)。


※大きな文書の場合、「空行/空白行/空白文字列」や「頻出する文字/単語」の検索では処理に時間がかかります。
※「人柱版 設定項目」の変数の値をすべて無効 false にすれば通常版とほぼおなじ動作になります。

>> もとのページ


ソースコード

#title = "ジャンプ"
#tooltip = "ポップアップメニューで検索先にジャンプ"
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",229
// ◆ 人柱版 ◆	「大文字/小文字の区別」に対応
// 				「空行+空白行」の検索に対応
// 				「ヒット件数(出現回数)」を表示
// 				 スペースキーでポップアップメニューをキャンセル可
// 				「検索履歴に残さない」を追加(■通常版へ逆移植済み)
// 				「行の表示方法」を Mery.ini から取得
// 				「自動マーカー」オプションを廃止(デフォルト: 自動マーカー有効)
// 				「検索所要時間」の表示(■通常版へ逆移植済み)
// 				 検索ジャンプ後のスクロールの挙動を調整(■通常版へ逆移植済み)
// 				「変数/関数の定義行」のマーキング

/**
 * ------------------------------------------------------------
 * "ポップアップメニューで検索先にジャンプ"
 * pooupJump.js	
 * Original Copyright: 手石 (2014/04/04)
 * ------------------------------------------------------------
 * Modified by: sukemaru (2018/11/12 - 2018/12/13)
 * ------------------------------------------------------------
 */

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


// ---------- ▼ 通常版 設定項目 ▼ ----------

// ■行の表示方法 (true; 論理座標 /false; 表示座標)
// ※ ◆人柱版◆ の追加設定で autoLineModeEnable = true の場合は無効(自動設定)
var logical = true;			// ■初期値 = true;  (論理座標)

// ■検索文字列の強調表示を残すか? (true; 残す /false; 残さない)
// ※ ◆人柱版◆ の追加設定で historyDisable = true の場合は無効(強調表示しない)
var highlightEnable = false;	// ■初期値 = true; (残す)


// ---------- ▼ 人柱版 設定項目 ▼ ----------
// ◆人柱版のオプションを有効にすると処理が遅くなります

// ◆検索処理の所要時間を表示するか? (true; する /false; しない)
var timerEnable = true;		// ◆初期値 = true; (する)

// ◆行の表示方法を Mery.ini から自動で読み込むか? (true; 読み込む /false; 読み込まない)
// ※ ini ファイルが肥大化またはフラグメントしていると処理が遅くなる
var autoLineModeEnable = true;		// ◆初期値 = false; (読み込まない)

// ◆検索文字列を「検索履歴」に残さない? (true; 残さない / false; 残す)
// ※ historyDisable = true の場合、通常版の設定 highliteEnable は無効になる(強調表示しない)
var historyDisable = true;	// ◆初期値 = true; (残さない)

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

// ◆空行/空白行を検索対象にするか? (true; する /false; しない)
var blankEnable = true;		// ◆初期値 = true; (する)

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


// ---------- ▼ 手石版 設定項目 ▼ ----------

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

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


/* ■ポップアップメニューの「行番号の表示方法」 */
var $LineColumnView = logical ? 0 : 1;

// Mery.ini から「行の表示方法」を取得する
if (autoLineModeEnable ) {
  var WshShell = new ActiveXObject( "WScript.Shell" );
  var Fso = new ActiveXObject( "Scripting.FileSystemObject" );
  // ini を探す(Zip版なら Mery.exe のフォルダ、インストーラ版なら %appdata%\Mery フォルダ)
  var portableIni = editor.FullName.slice( 0, 0 - "mery.exe".length ) + "Mery.ini";
  var iniPath = Fso.FileExists( portableIni ) ? portableIni :
                WshShell.ExpandEnvironmentStrings( "%APPDATA%\\Mery\\Mery.ini" );
  var iniFile = Fso.OpenTextFile( iniPath, 1 );
  var iniText = iniFile.ReadAll();
  $LineColumnView = Number( iniText.match( /^LineColumnView=[01]$/m ).shift().slice( -1 ) );
  iniFile.Close();
}

// ini から取得した設定値にもとづいて変数に
var posMode = $LineColumnView ? mePosView : mePosLogical;
var lineMode = $LineColumnView ? meGetLineView : 0;

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

// 選択範囲の先頭位置(行番号)と、文書全体の行数(論理行/表示行)
var ty = s.GetTopPointY( posMode );
var lines = d.GetLines( lineMode );


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

// 選択範囲なしならキャレット位置の単語を選択する
if ( s.IsEmpty ) {
  var p = 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 && ! ( blankEnable && st.match( blank ) ) )
    s.SetActivePos( act - 1, true );
  if ( act < anc && ! ( blankEnable && st.match( blank ) ) ) {
    s.SetAnchorPos( anc - 1 );
    s.SetActivePos( act, true );
  }
  if ( blankEnable && st.match( blank ) )
    re = true;	// "空白行の正規表現検索" フラグ
}
// 文書末尾の空白行の場合でも "空白行の正規表現検索" フラグ
else if ( blankEnable && st.match( blank ) && st == d.GetLine( ty, lineMode ) )
  re = true;

var cancel = 5;		// "キャンセル" フラグ

// 検索文字列がないとき(空行)
if ( ! blankEnable && ! st )
  cancel = 1;
// 検索条件エラー(選択範囲に改行あり)
else if ( st.match( /\n/g ) )
  cancel = 2;


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

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


/* ポップアップメニューの準備 */
  var m = CreatePopupMenu();
  // 「文字列」の検索では先頭に「キャンセル」のアイテムを表示
  if ( ! re ) {
    m.Add( "         キャンセル	& ", 0 );	// Space キーでキャンセル可
    m.Add( "", 0, meMenuSeparator );
    // Grand total, Hit, Variables
    var mArray = [],  t,  gt = 0,  hit = 0,  v = 0,  check = "",  flags;
    // アクティブ行(検索開始行)全体の文字列を取得
    if ( countEnable )
      var activeLine = d.GetLine( ty, lineMode );
  }
  // 「空行+空白行」の検索ではパターンごとの配列を使用する
  else {
    var hArray = [],  nArray = [],  wArray = [];
    // Hit ( match completely ), Blank line ( n + w ), New line ( ^$ ), White space ( ^[\s ]+$ )
    var h = 0,  b = 0,  n = 0,  w = 0;
  }


/* 検索文字列がヒットした行をポップアップメニューのアイテムとして取得する */
  var stQuote = st.replace( /\W/g , "\\$&" );
  var reg = new RegExp( stQuote , caseEnable ? "g" : "gi" );

  for( var i = 1; i < a.length; i ++ ) {	// a[0] = 空要素、i = 行番号 => y
    // 空行からの検索なら「空行+空白行」以外はスキップ
    if ( re && ! a[i].match( blank ) )
      continue;

    // 「検索文字列」がヒットしたら
    if ( ( ! re && findCaseTF( a[i], st ) >= 0 ) || ( re && a[i].match( blank ) ) ) {
      // 行内でのヒット回数
      if ( ! re && countEnable ) {
        t = a[i].match( reg );
        gt += t.length;		// 総ヒット回数(大文字/小文字を区別できる)
        check = Marking();	// 右側につけるマーキング
      }
      // アクティブ行は左に ✓ チェックマーク付き
      flags = ( i == ty ) ? meMenuChecked : 0;	// + meMenuGrayed


/* ポップアップメニューにアイテム(行)を追加する */
      // 「文字列」の検索では、行頭空白を除去、空白文字を半角スペースに置換、文字数を圧縮
      if ( ! re ) {
        // 変数/関数の定義行はメニューの先頭にも置く
        if ( countEnable && check.match( /←/ ) ) {
          m.Add( Labeling( a[i], i ) + check, i, flags );	
          v ++;
        }
        // 「検索文字列がヒットした行」を配列に格納
        mArray.push( [ Labeling( a[i], i ) + check, i, flags ] );	// ヒットした行
        hit ++;		// ヒットした行数
      }

      // 「空行+空白行」の検索ではサブメニュー用の配列に分別
      else {	// if ( re )
        b ++;		// 空行+空白行の総数
        if ( a[i] == st ) {
          hArray.push( [ Labeling( a[i], i ), i, flags ] );	// 一致した行
          h ++;		// 一致した行数
        }
        if ( st && ! a[i] ) {
          nArray.push( [ Labeling( a[i], i ), i, flags ] );	// 空行
          n ++;		// 空行数
        }
        else if ( a[i] ) {
          wArray.push( [ Labeling( a[i], i ), i, flags ] );	// 空白行
          w ++;		// 空白行数
        }
      }
    }	// end if () { 検索文字列がヒットしたら }
  }	// end for () { ヒットした行を取得する }


  // 配列から「検索文字列がヒットした行」をポップアップメニューに追加する
  if ( ! re && hit ) {
    if ( v )
      m.Add( "", 0, meMenuSeparator );
    for ( var i = 0; i < hit; i ++ ) {
      // 30件ごとに「キャンセル」行を追加
      if ( i != 0 && i % 30 == 0 ) {
        m.Add( "", 0, meMenuSeparator );
        m.Add( "    キャンセル", 0 );
        m.Add( "", 0, meMenuSeparator );
      }
      m.Add( mArray[i][0], mArray[i][1], mArray[i][2] );
    }
  }

  // 配列から「空行+空白行」をポップアップメニューに追加する
  if ( re && b ) {
    if ( b != h ) {			// ( n + w ) > h
      m.Add( " すべての空行+空白行: " + SeparateNum( b ) + " 行", 0 );	// , meMenuGrayed
      m.Add( "", 0, meMenuSeparator );
    }
    if ( n && st ) {			// ( nArray.length > 0 ) && ( st.length > 0 )
      var sm1 = CreatePopupMenu();
      m.AddPopup( " すべての空行: " + SeparateNum( n ) + " 行 (&N)", sm1 );
      for ( var i = 0; i < n; i ++ ) {
        // 30件ごとに「キャンセル」行を追加
        if ( i % 30 == 0 ) {
          if ( i != 0 )
            sm1.Add( "", 0, meMenuSeparator );
          sm1.Add( "    キャンセル", 0 );
          sm1.Add( "", 0, meMenuSeparator );
        }
        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( " すべての空白行: " + SeparateNum( w ) + " 行 (&W)", sm2 );
      for ( var i = 0; i < w; i ++ ) {
        // 30件ごとに「キャンセル」行を追加
        if ( i % 30 == 0 ) {
          if ( i != 0 )
            sm2.Add( "", 0, meMenuSeparator );
          sm2.Add( "    キャンセル", 0 );
          sm2.Add( "", 0, meMenuSeparator );
        }
        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 ) + " 行   ▼\t& ", 0 );
    for ( var i = 0; i < h; i ++ ) {	// h == hArray.length
      // 30件ごとに「キャンセル」行を追加
      if ( i != 0 && i % 30 == 0 ) {
        m.Add( "", 0, meMenuSeparator );
        m.Add( "         キャンセル", 0 );
        m.Add( "", 0, meMenuSeparator );
      }
      m.Add( hArray[i][0], hArray[i][1], hArray[i][2] );
    }
  }	// end if () { 配列から「空行+空白行」をポップアップメニューに追加する }


  // 検索開始からポップアップメニューを表示するまでの所要時間を取得
  // (Javascript 時間計測のマクロでの分解能は 16ms とのこと)
  if ( timerEnable ) {
  var elapsed = new Date().getTime() - start;
  var timerStatus =
    "  [ " + ( ( elapsed / 1000 ) + "000" ).replace( /(\.)(\d{3})(0*)/, "$1 $2" ) + " 秒 ]";
  }

  // ポップアップメニューを表示するのは、検索ヒット行が複数のとき
  if ( hit > 1 || b > 1) {
    // ポップアップメニューを出すまえにステータスバー表示させること
    Status = (
      ( re ? " 一致した行数: " + SeparateNum( h ) + " 行"
             + " / すべての空(白)行数: " + SeparateNum( b ) + " 行 / " :
     /*!re*/ ( gt ? ( " ヒット数: " + SeparateNum( gt ) + " 件 ・ " ) :
                      " ヒットした行数: " 
             )
             + SeparateNum( hit ) + " 行 / "	//  ヒット:した行数
      )
      + "全体の行数: " + SeparateNum( lines )		// 
      + ( $LineColumnView ? " 行 (表示行)" : " 行 (論理行)" )
      + ( timerEnable ? timerStatus : "" )
    );


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

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

      // 空白行でジャンプしたときは検索履歴に残さない
      if ( re && d.GetLine( y, lineMode ) )	
        // 行(空白文字列)全体を範囲選択する(※完全な空行 ^\n では範囲選択しない)
        s.SetAnchorPoint( posMode, 1, y + 1 );

      // 文字列でジャンプしたとき
      else if ( ! re ) {
        if ( historyDisable ) {
          // 検索履歴に残さない ※「検索文字列の強調表示」なし
          s.SetActivePos( s.GetActivePos() + findCaseTF( d.GetLine( y, lineMode ), st ) );
          s.SetAnchorPos( s.GetActivePos() + st.length );
        }
        else {
          // 検索履歴に残す ※ Mery の検索機能で文字列を範囲選択
          s.Find( st, meFindNext );
          d.HighlightFind = highlightEnable;	// 「検索文字列の強調表示」を解除する/しない
        }
      }
      ScrollY = ( ScrollY == sy ) ? ScrollY :
                                    s.GetActivePointY( mePosView );
      cancel = 0;				// cancel フラグを解除
    }


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

  // ほかの行でヒットしなかったとき ( h == 0 )
  else  cancel = 4;
}	// end else { ポップアップメニューでジャンプ先リスト }

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

  // キャンセルフラグの値が 1 or 2 のとき、所要時間の計測終了
  if ( timerEnable && ! elapsed ) {
    var elapsed = new Date().getTime() - start;
    var timerStatus =
      "  [ " + ( ( elapsed / 1000 ) + "000" ).replace( /(\.)(\d{3})(0*)/, "$1 $2" ) + " 秒 ]";
  }

  // ステータスバーにキャンセルメッセージを表示
  Status = (
    ( ( cancel == 1 ) ? " 検索文字列がありません(空行)。 " :
      ( cancel == 2 ) ? " 検索文字列に【 改行 】を含めることはできません。 " :
      ( cancel == 3 ) ? " ヒット数: "	// 検索ジャンプをキャンセルしました。 
                        + ( gt ? ( SeparateNum( gt ) + " 件 ・ " ) : " " ) 
                        + SeparateNum( h ) + " 行 / " :
      ( cancel == 4 ) ? " ヒットなし。 " :	// ほかの行には見つかりませんでした。
       /*  else  */     " 無効な検索文字列。 "	// ← 使われないはず
    )
    + "全体の行数: " + SeparateNum( lines )
    + ( $LineColumnView ? " 行 (表示行)" : " 行 (論理行)" )
    + ( timerEnable ? timerStatus : "" )
  );
}


/* 関数 findCaseTF( str, st ) */ 
// 文字列 str 内で st を検索するさいに「大文字/小文字を区別」する/しない
// ヒットすればヒットした位置(  0 ~ )、ヒットしなければ -1 を返す
function findCaseTF( str, st ) {
  return caseEnable ? str.indexOf( st ) :
         /* else */   str.toLowerCase().indexOf( st.toLowerCase() )
}


/* 関数 Marking() */
// ポップアップメニューの右側に表示するマーキング 
function Marking() {
  var _check = (
    // 変数の宣言の行: "var st" ... / "var hoge = piyo , st" ...
    a[i].match( RegExp( "\\b(?:var |let |const )(?:\\s*|[^\(]+[,]\\s+)[$]?"
                      + stQuote + "(?=\\s*(?:[=,;]|$))" , "" ) ) ?
      "\t ← v a r" :		// ) ← ※カッコの数のツジツマあわせ

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

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

    // 関数の定義の行
    a[i].match( RegExp( "\\bfunction +[$]?" + stQuote + "(?=[\\s(])" , "" ) ) ?
      "\t ← f n" :		// ) ← ※同上

    ( 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;
}


/* 関数 Labeling( str, num ) */
// ポップアップメニューに表示する文字列の整形
function Labeling( line, i ) {	// Labeling( a[i], i )	※ line = a[i] ヒットした行全体
  // 行頭空白を除去、空白文字は半角スペースに置換、文字数を圧縮して取得
  // 判別しづらい半角メタ文字を全角に置きかえる
  var label = line.replace( /^[\t  ]+/ , "" )
                  .replace( /(\t|[ ]{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 > menuWidth + 1 ) ? label.slice( 0, menuWidth ) + "..." :
     /* else */                        label
   );

  return ( "    " + i ).slice( -4 ) + " : " + label;
}


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


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

>> もとのページ

スポンサーリンク