検索ジャンプの人柱版
ポップアップメニューで検索先にジャンプ(sukemaru 版)
人柱版
動作検証が不十分ですが、ご興味のある方は ZIP書庫 内の「人柱版(空行+空白行検索に対応)」フォルダの JS ファイルをお使いください。
▼ 追加オプション ▼
- 検索のさいに「大文字/小文字を区別」するかしないかを選択できます(変数で設定)。
- 空行にキャレットがある状態か、"末尾改行をふくめて" 空白行を範囲選択している状態から、「すべての空行+空白行」を検索できるオプションを追加しました(変数で設定)。
- 「空行+空白行」オプションが有効 ( true ) の場合は、末尾改行 "\n" をふくめて「空白文字のみの行」を範囲選択しているときに、すべての「空行+空白行」を検索対象にします。末尾改行をふくめずに「空白文字のみの行」を範囲選択しているときは「一致する空白文字列」を検索対象にします。
- 「空行+空白行」オプションが無効 ( false ) の場合、空白文字のみの行」を範囲選択しているときには「一致する空白文字列」を検索対象にします。空行で実行した場合にはポップアップメニューは表示されません。
- ※「空行+空白行」が対象のとき以外は正規表現を使わずに行単位で検索するので、選択範囲が複数行(末尾以外に改行 "\n" がある)の場合や、空白行オプションが無効で選択範囲が空行 "^\n" の場合は検索できません。
- 検索対象が「空行+空白行」でないときは、ヒットした行数とは別に「ヒットした件数」をステータスバーに表示できます(◆変数で設定)。「ヒット件数」の判定は、大文字/小文字オプションの設定に依拠します。
- 検索対象が「空行+空白行」でないとき、ポップアップメニューの右隅に以下のマーキングを表示できます(「ヒット件数」オプションが有効の場合のみ)。
- 検索文字列が行全体のとき、アクティブ行に「 ⏎ 」マーク
- アクティブ行と完全に一致する重複行に「 ** 」マーク (※行検索でないときにも表示)
- 検索文字列と完全に一致する行に「 * 」マーク
- 検索文字列が複数回ヒットした行に「 x2 」マーク
- ※「一致」の判定では、つねに大文字/小文字を区別します。
- ポップアップメニューを Space キーでもキャンセルできるようにしました。ただし、メニューに表示されるヒットした行の文字列に「アンパサンドと半角スペース "&␣"」があるときは Esc キーのみ( "&&␣" なら可)。
※大きな文書の場合、「空行/空白行/空白文字列」や「頻出する文字/単語」の検索では処理に時間がかかります。
※「人柱版 設定項目」の変数の値をすべて無効 false にすれば通常版とおなじ動作になります。
ソースコード
#title = "ジャンプ"
#tooltip = "ポップアップメニューで検索先にジャンプ"
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",229
// ◆ 人柱版 ◆ 「大文字/小文字の区別」に対応
// 「空行+空白行」の検索に対応
// 「ヒット件数(出現回数)」を表示
/**
* ------------------------------------------------------------
* "ポップアップメニューで検索先にジャンプ"
* pooupJump.js
* Original Copyright: 手石 (2014/04/04)
* ------------------------------------------------------------
* Modified by: sukemaru (2018/11/12 - 2018/11/25)
* ------------------------------------------------------------
*/
// ---------- ▼ 通常版 設定項目 ▼ ----------
// ■行の表示方法 (true; 論理座標 /false; 表示座標)
var logical = true; // ■初期値 = true; (論理座標)
// ■検索文字列の強調表示を残すか? (true; 残す /false; 残さない)
var highlightEnable = true; // ■初期値 = true; (残す)
// ■選択範囲なしの状態から実行したときに自動マーカーを表示するか? (true; 表示する / false; 表示しない)
// ※ [表示]メニュー >> [マーカー] >> [自動マーカー] が OFF なら効果なし。
var markerEnable = true; // ■初期値 = true; (表示する)
// ---------- ▼ 人柱版 設定項目 ▼ ----------
// ◆検索文字列の大文字/小文字を区別するか? (true; 区別する / false; 区別しない)
var caseEnable = false; // ◆初期値 = false; (区別しない)
// ◆空行/空白行を検索対象にするか? (true; する /false; しない)
var blankEnable = true; // ◆初期値 = true; (する)
// ◆ヒット件数(出現回数)を表示するか? (true; する /false; しない)
var countEnable = true; // ◆初期値 = true; (する)
// ---------- ▼ 手石版 設定項目 ▼ ----------
// ●ポップアップメニューに表示する文字数
var menuWidth = 66;
// ---------- ▲ 設定項目 ここまで ▲ ----------
Redraw = markerEnable;
var sx = ScrollX, sy = ScrollY;
var d = document, s = document.selection;
/* 検索文字列の準備 */
var blank = new RegExp( "^[\t ]*$" , "" ); // 空白行の正規表現
// 選択範囲なしならキャレット位置の単語を選択する
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 ) )
var re = true; // "空白行の正規表現検索" フラグ
}
// 選択範囲の先頭位置と、文書全体の行数(論理行/表示行)
var ty = s.GetTopPointY( logical ? mePosLogical : mePosView );
var lines = d.GetLines( logical ? 0 : meGetLineView );
var cancel = 5; // "キャンセル" フラグ
// 検索文字列がないとき(空行)
if ( ! blankEnable && ! st )
cancel = 1;
// 検索条件エラー(選択範囲に改行あり)
else if ( st.match( /\n/g ) )
cancel = 2;
// ポップアップメニューでジャンプ先リスト
else {
/* 文書全体を行単位で分割し、配列に */
if ( logical ) // 論理行
var a = d.Text.split( "\n" );
else // 表示行
for( var i = 1, a = []; i <= lines ; i ++ )
a.push( d.GetLine( i, meGetLineView ) );
a.unshift( "" ); // a[0] に空要素を追加
/* ポップアップメニューの最上部に表示するアイテム */
var m = CreatePopupMenu();
// 文字列の検索では先頭に「キャンセル」のアイテムを表示
if ( ! re ) {
m.Add( " キャンセル & ", 0 ); // Space キーでキャンセル可
m.Add( "", 0, meMenuSeparator );
var gt = 0 , h = 0;
// アクティブ行(検索開始行)の文字列を取得
if ( countEnable )
var activeLine = d.GetLine( ty, logical ? 0 : meGetLineView );
}
// 「空行+空白行」の検索では配列を使用する
else {
var mArray = [], sm1Array = [], sm2Array = [];
var h = 0, b = 0, n = 0, w = 0;
}
/* 検索文字列がヒットした行をポップアップメニューのアイテムとして取得する */
for( var i = 1; i < a.length; i ++ ) { // i = 行番号 => y
// 空行からの検索なら「空行+空白行」以外はスキップ
if ( re && ! a[i].match( blank ) )
continue;
// 「大文字/小文字を区別」する/しない
var findCaseTF =
caseEnable ? a[i].indexOf( st ) :
/* else */ a[i].toLowerCase().indexOf( st.toLowerCase() );
// 検索文字列がヒットしたら
if ( ( ! re && findCaseTF >= 0 ) || ( re && a[i].match( blank ) ) ) {
// 行内でのヒット回数
if ( ! re && countEnable ) { // && ! st.match( blank )
var t = a[i].match(
RegExp( st.replace( /\W/g , "\\$&" ) , caseEnable ? "g" : "gi" )
);
gt += t.length; // 総ヒット回数(大文字/小文字を区別できる)
}
// 右側につけるマーキング
var check = ( ! re && countEnable ) ? Marking() : "";
// アクティブ行は左に ✓ チェックマーク付き
var flags = ( i == ty ) ? meMenuChecked : 0; // + meMenuGrayed
/* ポップアップメニューにアイテム(行)を追加する */
if ( ! re ) {
// 行頭空白は除去、空白文字は半角スペースに置換、文字数を圧縮して取得
m.Add( Labeling( a[i], i ) + check, i, flags );
h ++; // ヒットした行数
}
// 「空行+空白行」の検索ではサブメニュー用の配列に分別
else { // if ( re )
b ++; // 空行+空白行数
if ( a[i] == st ) {
mArray.push( [ Labeling( a[i], i ) + check, i, flags ] );
h ++; // 一致した行数
}
if ( st && a[i] == "" ) {
sm1Array.push( [ Labeling( a[i], i ) + check, i, flags ] );
n ++; // 空行数
}
else if ( a[i].length ) {
sm2Array.push( [ Labeling( a[i], i ) + check, i, flags ] );
w ++; // 空白行数
}
}
}
} // end for()
// 配列から「空行+空白行」をポップアップメニューに追加する
if ( re ) {
m.Add( " すべての空行+空白行: " + SeparateNum( b ) + " 行", 0 );
m.Add( "", 0, meMenuSeparator );
if ( n && st ) { // n == sm1Array.length
var sm1 = CreatePopupMenu();
m.AddPopup( " すべての空行: " + SeparateNum( n ) + " 行 (&N)", sm1 );
for ( var i = 0; i < n; i ++ )
sm1.Add( sm1Array[i][0], sm1Array[i][1], sm1Array[i][2] );
}
if ( w && ( n + h != b ) ) { // w == sm2Array.length
var sm2 = CreatePopupMenu();
m.AddPopup( " すべての空白行: " + SeparateNum( w ) + " 行 (&W)", sm2 );
for ( var i = 0; i < w; i ++ )
sm2.Add( sm2Array[i][0], sm2Array[i][1], sm2Array[i][2] );
}
m.Add( "", 0, meMenuSeparator );
// キャンセルを兼ねる , meMenuGrayed
m.Add( " ▼ 一致した行: " + SeparateNum( h ) + " 行 ▼\t& ", 0 );
for ( var i = 0; i < h; i ++ ) // h == mArray.length
m.Add( mArray[i][0], mArray[i][1], mArray[i][2] );
}
// ポップアップメニューを表示するのは、検索ヒット行が複数のとき
if ( h > 1 || b > 1) {
// ステータスバー
Status =
( re ? " 一致した行数: " + SeparateNum( h ) + " 行"
+ " / すべての空(白)行数: " + SeparateNum( b ) + " 行" :
/* ! re */ ( gt ? ( " ヒット数: " + SeparateNum( gt ) + " 件 /" ) : "" )
+ " ヒットした行数: " + SeparateNum( h ) + " 行"
)
+ " / 全体の行数: " + SeparateNum( lines )
+ ( logical ? " 行 (論理行)" : " 行 (表示行)" );
/* カーソル位置にポップアップメニューを表示 */
var y = m.Track( mePosMouse );
/* 選択した行番号(行頭)にジャンプして検索文字列を選択 */
if ( y > 0 ) {
s.SetActivePoint( logical ? mePosLogical : mePosView, 1, y );
// 空白行でジャンプしたときは検索履歴に残さない
if ( re && d.GetLine( y ) )
s.SetAnchorPoint( logical ? mePosLogical : mePosView, 1, y + 1 );
// 文字列でジャンプしたときは検索履歴に残す
else if ( ! re )
s.Find( st, meFindNext );
d.HighlightFind = highlightEnable; // 「検索文字列の強調表示を解除」する/しない
cancel = 0;
}
/* キャンセルした場合 */
// ポップアップメニューをキャンセル
else cancel = 3;
}
// ほかの行でヒットなし
else cancel = 4;
} // end else { ポップアップメニューでジャンプ先リスト }
/* ジャンプしなかったときは、キャレット位置/選択範囲を復帰 */
if ( cancel ) {
if ( p )
s.SetActivePos( p );
else if ( anc < act )
s.SetActivePos( act, true );
else if ( act < anc ) {
s.SetAnchorPos( anc );
s.SetActivePos( act, true );
}
Status = // ステータスバーにメッセージを表示
( ( cancel == 1 ) ? " 検索文字列がありません(空行)。" :
( cancel == 2 ) ? " 検索文字列に【 改行 】を含めることはできません。" :
( cancel == 3 ) ? " キャンセルしました。 ヒット: "
+ ( gt ? ( SeparateNum( gt ) + " 件 ・ " ) : "" )
+ SeparateNum( h ) + " 行" :
( cancel == 4 ) ? " ほかの行には見つかりませんでした。" :
/* else */ " 無効な検索文字列。"
)
+ " / 全体の行数: " + SeparateNum( lines )
+ ( logical ? " 行 (論理行)" : " 行 (表示行)" );
ScrollX = sx; ScrollY = sy;
}
Redraw = true;
/* 関数 Marking() */
// ポップアップメニューの右側に表示するマーキング
function Marking() {
var _check = (
( 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 ) {
// 行頭空白を除去、空白文字は半角スペースに置換、文字数を圧縮して取得
var label = line.replace( /^[\t ]+/ , "" )
.replace( /(\t|[ ]{2,}|[ ])+/g , " " );
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 ) {
return num.toString().replace( /(\d)(?=(?:\d{3})+$)/g , "$1," );
}
スポンサーリンク