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

提供: MeryWiki
移動先: 案内検索

概要[編集]

ポップアップメニューで検索にヒットした箇所へジャンプできます。

手石 版[編集]

機能[編集]

  • 選択した文字列(選択していないときはカーソルのある語)を検索します。
  • ヒットした行をポップアップメニューに表示し、選択するとその行へジャンプします。
  • 同一行にヒットする箇所が複数あったときは最初のヒット箇所にジャンプします。
  • 検索文字列の強調解除は、変更していなければ「Alt+F3」です。

コード (手石 版)[編集]

// pooupJump.js
// 2014/04/04

var sel = document.selection
if ( sel.isEmpty ){ sel.SelectWord(); };
var s = sel.Text;
var ty = sel.GetTopPointY( mePosView );
var p = sel.GetActivePos();
sel.Find( s, meFindNext + meFindAround );
sel.SetActivePos( p );

// ポップアップメニューでジャンプ先リスト
var menu_width = 66;
if ( "\t .,;:(){}[]+-=\"\\".indexOf( s ) < 0 && s.length > 0 ){
    var menu = CreatePopupMenu();
    var ary = [];
    for( var i = 0, n = document.GetLines( meGetLineView ); i < n ; i++ ){
        ary.push( document.GetLine( i+1, meGetLineView ));
    };
    var p = 0;
    for( var i = 0, n = ary.length; i < n ; i++ ){
        if ( ary[i].toLowerCase().indexOf( s.toLowerCase() ) >= 0 ){
            var item = ary[i].replace( /^\s*|\t/g, "" )
            item = ( item.length > menu_width + 1 )? item.slice( 0, menu_width ) + "..." : item ;
            var flags = ( (i+1) == ty )? meMenuChecked : 0 ; // meMenuGrayed  
            menu.Add( ( "    " + (i + 1) ).slice( -4 ) + " : " + item, i + 1, flags );
            p++;
        };
    };
    if ( p > 1 ){ // ポップアップメニューを表示するのは、検索ヒット行が複数のとき。
        var r = menu.Track( mePosMouse );  // mePosMouse 
        if ( r > 0 ){
            var line = document.GetLine( r, meGetLineView );
            sel.SetActivePoint( mePosView, line.indexOf( s ) + s.length + 1, r, false );
            sel.CharLeft( true, s.length );
            ScrollY = r-10;
        };
    };
};

履歴[編集]

  • 2014/04/04 初版

その他[編集]

  • 不具合があったら教えていただけると幸いです。


sukemaru 版[編集]

手石版のソースコードをベースにして、自分好みにカスタマイズしました。手石版の機能にあげられている部分はそのまま踏襲・流用しています。


ポップアップメニューを表示させるだけ(キャンセルする)であれば検索履歴には残らないようにしてありますので、検索ダイアログの最新の検索文字列を置きかえることなく、重複行の確認や選択文字列が出現する行の確認をできます。

※ ポップアップメニューは Esc キーでキャンセルできます。
「行の表示方法」オプションが "false" (表示座標 = 表示行)で、折り返しにより検索文字列が途切れている場合はヒットしません(Mery 標準の検索ダイアログでは検出できるのですが…)。

変更点[編集]

▼ 手石版からの変更点 ▼

  • エディタ設定の「行の表示方法」にあわせられるように、ポップアップメニューに表示する行番号は表示行/論理行を選択できます(■ソースコード内の変数で設定)。
  • 検索文字列がほかの行でヒットしなかった場合や、ポップアップメニューからジャンプしなかった(キャンセル)場合に、検索履歴に残さないようにしました。
    そのさい、マクロの実行前に選択範囲なしだったときは単語選択もキャンセルします。
  • 検索文字列の強調表示を解除できます(■ソースコード内の変数で設定)。
  • ヒットした行数や文書全体の行数をステータスバーに表示します。
  • 重複行のチェックをしやすくなるよう、論理行全体を範囲選択しているときは末尾改行を除外するようにしました。
  • ポップアップメニューをキャンセルすればムダな検索履歴も残らないので、検索文字列の禁止文字条件をなくしました。

※ 正規表現を使わずに行単位で検索するので、改行は末尾の "\n" ×1 以外ダメ(空行も不可)。


▼ 人柱版から移植した追加機能 (2018/12/13) ▼

  • 「検索処理の所要時間」をステータスバーに表示できます(◆ソースコード内の変数で設定)。
  • キャンセルせずに検索ジャンプしたときにも検索文字列を検索/置換ダイアログの「検索履歴」に残さないようにできます(◆ソースコード内の変数で設定)。
    「残さない」場合は Mery の検索機能を使用せずにジャンプするため、通常版の設定項目「■検索文字列の強調表示を残す」も無効になります。
  • 検索のさいに「大文字/小文字を区別」するかしないかを選択できます(◆ソースコード内の変数で設定)


ダウンロード (sukemaru 版)[編集]

ダウンロード: 「ファイル:ジャンプ.zip (アイコン入り)」 (2018/12/20 更新)

以下のマクロと、それぞれ専用の「マテリアルデザインっぽいアイコン」を同梱してあります。
おまけマクロのソースコードは 『小マクロ集』 に置いてあります。
  • ジャンプ(ポップアップメニューで検索先にジャンプ)
    通常版 最終更新: 2018/12/13
    人柱版 最終更新: 2018/12/13
    include版 最終更新: 2018/12/20
    を同梱
おまけマクロ


コード (sukemaru 版)[編集]

最終更新: 2018/12/13

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

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

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

// ■行の表示方法 (true; 論理座標 /false; 表示座標)
var logical = true;

// ■検索文字列の強調表示を残すか? (true; 残す /false; 残さない)
var highlightEnable = true;

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


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

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

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


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


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

// 論理行/表示行の設定を変数に
var posMode = logical ? mePosLogical : mePosView;
var lineMode = logical ? 0 : meGetLineView;

// 選択範囲なしならカーソル位置の単語を選択
if ( s.IsEmpty ) {
  var p = s.GetActivePos();	// "選択範囲なし" フラグ
  s.SelectWord();
}
var st = s.Text;	// 検索文字列
// 選択範囲の末尾が改行 "\n" なら検索文字列から除外する
if ( st.length > 1 && st.charAt( st.length - 1 ) == "\n" ) {
  st = st.slice( 0, st.length - 1 );
  // 自動マーカーを効かせるために選択範囲も調整する
  var adjust = true;	// 調整フラグ
  var anc = s.GetAnchorPos(),  act = s.GetActivePos();
  if ( anc < act ) { s.SetActivePos( act - 1, true ); }
  if ( act < anc ) { s.SetAnchorPos( anc - 1 );  s.SetActivePos( act, true ); }
}
// 選択範囲の先頭と文書全体の行数(論理行/表示行)
var ty = s.GetTopPointY( posMode );
var lines = d.GetLines( lineMode );

var cancel = 5;
if ( ! st )  cancel = 1;	// 検索文字列がない(空行)
else if ( st.match( /\n/g ) )  cancel = 2;	// 検索条件エラー(改行)

// ポップアップメニューでジャンプ先リスト
else {
  // 文書全体を行単位で分割して配列にする
    for( var i = 1, a = [ "" ]; i <= lines ; i ++ )
      a.push( d.GetLine( i, lineMode ) );

  // 検索文字列がヒットした行をポップアップメニューのアイテムとして取得する
  var m = CreatePopupMenu();
  for( var i = 1, h = 0; i < a.length; i ++ ) {	// a[0] = 空要素、i = 行番号 = y
    if ( findCaseTF( a[i], st ) >= 0 ) {
      // 行頭空白は除去、空白文字は半角スペースに置換、文字数を圧縮して取得
      // 判別しづらい半角メタ文字を全角に置きかえる
      var label = a[i].replace( /^\s+/, "" ).replace( /\t+|[ ]{3,}/g, "  " )
                      .replace( /[.,:;(){}]/g ,
                        function( $0 ) { return String.fromCharCode( $0.charCodeAt( 0 ) + 0xFEE0 ) }
                      );
      label = ( label.length > menuWidth + 1 ) ? label.slice( 0, menuWidth ) + "..." : label;
      var flags = ( i == ty ) ? meMenuChecked : 0;	// + meMenuGrayed
      m.Add( ( "    " + i ).slice( -4 ) + " : " + label, i, flags );
      h ++;	// ヒットした行数
    }
  }

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

  // ポップアップメニューを表示するのは、検索ヒット行が複数のとき
  if ( h > 1 ) {
    Status = " ヒットした行数: " + SeparateNum( h ) + " 行"
             + " / 全体の行数: " + SeparateNum( lines )
             + ( logical ? " 行 (論理行)" : " 行 (表示行)" )
             + ( timerEnable ? timerStatus : "" );

    var y = m.Track( mePosMouse );	// Track(0) ならキャレット位置にポップアップ
    if ( y > 0 ) {
      // 選択した行番号にジャンプして検索文字列を選択
      s.SetActivePoint( posMode, 1, y );
      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;	// 「検索文字列の強調表示」を解除する/しない
      }
      cancel = 0;	// cancel フラグを解除
    }
    else cancel = 3;	// ポップアップメニューをキャンセル
  }
  else cancel = 4;	// ほかの行でヒットなし
}

// ジャンプしなかったとき
if ( cancel ) {
  if ( p ) s.SetActivePos( p );
  else if ( adjust && anc < act ) s.SetActivePos( act, true );
  else if ( adjust && act < anc ) s.SetAnchorPos( anc ),  s.SetActivePos( act, true );

  // キャンセルフラグの値が 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 ) ? " 検索ジャンプをキャンセルしました。 ヒット: "
                               + SeparateNum( h ) + " 行" :
             ( cancel == 4 ) ? " ほかの行には見つかりませんでした。" :
            /* cancel == 5 */  " 無効な検索文字列。" )
           + " / 全体の行数: " + SeparateNum( lines )
           + ( logical ? " 行 (論理行)" : " 行 (表示行)" )
           + ( timerEnable ? timerStatus : "" );
  ScrollX = sx;  ScrollY = sy;
}

Redraw = true;


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


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


人柱版[編集]

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


▼ 追加オプション ▼

  • 「検索処理の所要時間」をステータスバーに表示します(◆変数で設定)。
  • 人柱版では処理が遅くなる追加機能が多いので…。
  • 「行の表示方法」の設定を Mery.ini から自動取得するかしないかを選択できます(◆変数で設定)。
  • ただし、ini ファイルが肥大化またはフラグメントしていると、処理が多少遅くなります。
  • 検索文字列を検索/置換ダイアログの「検索履歴に残すか残さないか」を選択できます(◆変数で設定)。
  • 「残さない」場合は Mery の検索機能を使用せずにジャンプするため、通常版と共通の設定項目「検索文字列の強調表示を残す」も無効になります。
  • 検索のさいに「大文字/小文字を区別」するかしないかを選択できます(◆変数で設定)。
  • 空行にキャレットがある状態か、"末尾改行をふくめて" 空白行を範囲選択している状態から、「すべての空行+空白行」を検索できるオプションを追加しました(変数で設定)。
  • 「空行+空白行」オプションが有効 ( true ) の場合は、末尾改行 "\n" をふくめて「空白文字のみの行」を範囲選択しているときに、すべての「空行+空白行」を検索対象にします。末尾改行をふくめずに「空白文字のみの行」を範囲選択しているときは「一致する空白文字列」を検索対象にします。
  • 「空行+空白行」オプションが無効 ( false ) の場合、空白文字のみの行」を範囲選択しているときには「一致する空白文字列」を検索対象にします。空行で実行した場合にはポップアップメニューは表示されません。
※「空行+空白行」が対象のとき以外は正規表現を使わずに行単位で検索するので、選択範囲が複数行(末尾以外に改行 "\n" がある)の場合や、空白行オプションが無効で選択範囲が空行 "^\n" の場合は検索できません。
  • 検索対象が「空行+空白行」でないときは、ヒットした行数とは別に「ヒットした件数」をステータスバーに表示できます(◆変数で設定)。「ヒット件数」の判定は、大文字/小文字オプションの設定に依拠します。
  • 検索対象が「空行+空白行」でないとき、ポップアップメニューの右隅に以下のマーキングを表示できます(「ヒット件数」オプションが有効の場合のみ)。
  • JavaScript で検索文字列が変数/関数名のとき、定義行に「 ← var 」「 ← fn 」マーク
  • 検索文字列が行全体のとき、アクティブ行に「 」マーク
  • アクティブ行と完全に一致する重複行に「 ** 」マーク (※行検索でないときにも表示)
  • 検索文字列と完全に一致する行に「 * 」マーク
  • 検索文字列が複数回ヒットした行に「 x2 」マーク
※「一致」の判定は、「大文字/小文字」オプションに依拠します。
  • ポップアップメニューを Space キーでもキャンセルできるようにしました。ただし、メニューに表示されるヒットした行の文字列に「アンパサンドと半角スペース "&␣"」があるときは Esc キーのみ( "&&␣" なら可)。

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


人柱版のソースコード[編集]

最終更新: 2018/12/13
>> 別ページに掲載


include版[編集]

includeライブラリを使用し、設定内容を外部ファイルに保存します(◆settingEnable 変数で設定。デフォルト: 有効)。
設定内容の読み込み/書き出しが可能になったので、設定の変更はポップアップメニュー内の項目のチェック ON/OFF でできるようになります。
ご興味のある方は ZIP 書庫 内の「include版(メニューでの設定変更に対応)」フォルダの JS ファイルをお試しください。

スクリーンショット

  • あらかじめ includeライブラリ をインストールしてください。
  • 設定内容の保存場所は Mery\Macros\MacroSettings\ジャンプ.json です
    ※ <ジャンプ> の部分はこのマクロのファイル名)。
  • 「▼ 通常版 初期設定項目 ▼」以降の設定項目は『初期値』としてのみ利用され、ソースコード内の設定項目を直接書き換えする必要はありません。
    ただし、settingEnable 変数をを無効にする場合は、ソースコード内で変更する必要があります。
    settingEnable を無効にした場合は「人柱版」と同じ状態になるので、設定変更はソースコードの直接編集(変数の true/false の変更)でおこなう必要があります。


  • はじめての設定変更のさいにエラーが発生する場合は
  1.  ポップアップメニューの「設定内容を初期化する」を実行して、ファイルを確認してください( → JSON ファイルを新規作成して設定値を書き出します)。
  2.  ポップアップメニューの「JSON ファイルを開く」を実行して、ファイルを確認してください。
  3.  JSON ファイルが正常に作成されない場合は、同梱の「ジャンプ.json」を Mery\Macros\MacroSettings\ フォルダに配置してください。


▼ 追加オプション ▼

  • 検索のさいに「英単語のみ検索」するかしないかを選択できます(◆変数で設定)。
  • ただし、このオプションを有効にして検索できるのは英単語のみです。
    欧文でも ascii 範囲外のアルファベットを使った単語では正常に機能しません。
    このオプションを有効にすると日本語の文字列は検索できません。
  • 有効にした場合は正規表現を使用しての検索になるので、処理がやや遅くなります。


include版のソースコード[編集]

最終更新: 2018/12/20
>> 別ページに掲載

メモ[編集]

  • (2018/11/12 sukemaru)
Get/SetActivePoint() や GetLines() のオプションパラメータ( mePosView や meGetLineView )を変数にすると機能しなくなるようで条件文 if ( logical ) や ( logical ) ? : をひとつひとつ書かかざるをえず、余計に長くなってしまいました。
→ ちゃんと機能するようにできました。(2018/12/13)
  • (2018/11/14 sukemaru)
・ZIP の「ジャンプ.js」がソースコードのものより古い版だったので差し替えました。
・11/12 のソースコードでは表示行だと最終行を取得しなかったので修正しました。
  • (2018/11/19 sukemaru)
ヒット数や行数を表示する機能をつけてから、ムダに利用頻度があがった気がします。
  • (2018/11/21 sukemaru)
むしろジャンプしないほうが便利な? 検索ポップアップメニューになりました。
  • (2018/11/23 sukemaru)
空行を数えられます…。
  • (2018/11/25 sukemaru)
重いです。
  • (2018/12/10 sukemaru)
論理行表示で「人柱版」を使っていますが、とくに問題なさそうです。
いまとなっては「通常版」が短いマクロであるかのように錯覚してしまいます。
  • (2018/12/13 sukemaru)
・「人柱版」の追加機能のうち処理速度に影響しないものを選んで「通常版」に逆移植しました。
・「人柱版」のほうには Mery.ini から「行の表示方法」設定の読み込みを積めこみましたが、検索ダイアログの「大文字/小文字」オプションの変更は ini ファイルに即時反映されないため取り込む意味がなくて残念ながら不採用です。
  • (2018/12/16-17 sukemaru)
・includeライブラリ、すごい便利です。自分で無理やりコードを書くよりもかんたんで、処理速度も速い!
・「単語のみ」を日本語対応にする方法がわかりませんでしたので、「英単語のみ」です。これだけでもソースコード内の検索の効率がよくなったので "よし" ということで…。
・include版のコードのミスを修正しました。
  • (2018/12/20 sukemaru)
・include版のコードのミスを修正しました。

行の表示方法が「表示座標」のとき、折り返し部分にかかった文字列にはマッチしないままです。
実装すると動作速度が遅くなり、実用に適さなくなってしまいます…

スポンサーリンク