1 #title = "検p"
  2 #tooltip = "検索してポップアップしてジャンプ"
  3 #include "lib\sgc4jsee.jsee"
  4 
  5 /**
  6  * @fileOverview
  7  * 選択文字列があればそれを、なければ入力ダイアログの入力値を検索して、結果をポップアップメニューに表示します。<br />
  8  * また、ポップアップメニューで選択された行へジャンプします。<br />
  9  * 既存の検索機能を使わないため、検索履歴やハイライトに影響を与えません。
 10  * <p>
 11  * 検索された各行がINIファイルの"bigInterval"に設定されている値より離れている場合、ポップアップメニューの各行の間に空行を挿入します。
 12  * </p>
 13  * <p>
 14  * 部分編集時は、その範囲内で検索します。
 15  * </p>
 16  * <p>
 17  * 選択文字列があったときの検索は、必ず大文字と小文字を区別します。<br />
 18  * 入力ダイアログでの検索の場合、の入力値の先頭が";"であれば、大文字と小文字を区別するようになります。<br />
 19  * 検索したい文字列の先頭が";"の文字列を検索したい場合は"\;"、先頭が"\;"の文字列を検索したい場合は"\\;"と入力してください。<br />
 20  * </p>
 21  * 
 22  * @author gecca from 雪月花 (http://setsugecca.org/)
 23  * @version 1.00 for EmEditor v10.0
 24  */
 25 
 26 (function() {
 27 	var tempFilePath = JseeUtil.getTempDirPath() + "EmEditorMacros_検索結果をポップアップしてジャンプ.tmp";
 28 	
 29 	var redraw = Redraw;
 30 	Redraw = false;
 31 	
 32 	// INIファイル読み込み
 33 	var ini = JseeUtil.getSnipIniObj("検索結果をポップアップしてジャンプ");
 34 	
 35 	// デフォルトで検索時に大文字と小文字を区別しない
 36 	var ignoreCase = (ini.ignoreCase == "true");
 37 	
 38 	// 大文字と小文字の区別を切り替える先頭のマーク
 39 	var mark = ini.mark || ";";
 40 	
 41 	// 行の間隔がいくら開いたら区切りを挿入するか。区切りを挿入しない場合は0以下の値を指定。
 42 	var bigInterval = ini.bigInterval;
 43 	if(!bigInterval || isNaN(bigInterval)) {
 44 		alert("bigIntervalに数値を設定してください。\n\n" + JseeUtil.getSnipIniName() + "を開きます。");
 45 		JseeUtil.selectSnipIniValue("検索結果をポップアップしてジャンプ", "bigInterval");
 46 		return;
 47 	}
 48 	
 49 	// 選択範囲を保存
 50 	var activeX = document.selection.GetActivePointX(eePosLogical);
 51 	var activeY = document.selection.GetActivePointY(eePosLogical);
 52 	var anchorX = document.selection.GetAnchorPointX(eePosLogical);
 53 	var anchorY = document.selection.GetAnchorPointY(eePosLogical);
 54 	
 55 	// 検索対象文字列 (検索後に選択するための、生の検索対象文字列)
 56 	var target;
 57 	// 検索対象文字列 (検索用に正規表現エスケープされたもの)
 58 	var target4search = (function() {
 59 		target = document.selection.Text;
 60 		
 61 		// 選択状態でなければ、検索する文字列を入力するダイアログを表示
 62 		if(!target) {
 63 			target = prompt("検索する文字列を入力してください。"
 64 					+ "(先頭が\"" + mark + "\"なら大文字と小文字を区別" + (ignoreCase ? "する" : "しない") + ")",
 65 					JseeUtil.readFile(tempFilePath));
 66 			if(!target) {
 67 				return;
 68 			}
 69 		}
 70 		
 71 		// 選択文字列、または入力値を保存
 72 		JseeUtil.writeFile(tempFilePath, JseeUtil.escEscSeq(target));
 73 		
 74 		// マークがあれば大文字と小文字の区別を切り替えて、マークを除去
 75 		var re = new RegExp("^" + mark);
 76 		if(re.test(target)) {
 77 			ignoreCase = !ignoreCase;
 78 			target = target.replace(re, "");
 79 		}
 80 		
 81 		// エスケープ用の"\"を除去
 82 		target = target.replace(new RegExp("^\\\\" + mark), mark).replace(/^\\\\/, "\\");
 83 		
 84 		// 正規表現をエスケープ
 85 		return JseeUtil.quote(target);
 86 	})();
 87 	if(!target4search) return;
 88 	
 89 	// 0埋めするために、最後の行の桁数を取得
 90 	var linesCntDigit = String(document.GetLines()).length;
 91 	
 92 	// ショートカットキーを作成するためのイテレイター (&付き)
 93 	var getKey = (function() {
 94 		var i = 0;
 95 		var str = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 96 		return function() {
 97 			if(i < str.length) {
 98 				return "&" + str.charAt(i++);
 99 			} else {
100 				return "  ";
101 			}
102 		}
103 	})();
104 	
105 	// ポップアップメニューの区切りに設定するfunction
106 	var separatorFunction = separatorFunctionFactory(bigInterval);
107 	
108 	// 対象文字列へのマッチャー
109 	var matcher = new RegExp(target4search, (ignoreCase ? "i" : ""));
110 	
111 	// メニュー項目配列
112 	var menuItems = [];
113 	
114 	// 部分編集に対応するため、最上部の行数を取得
115 	var overhead = (function() {
116 		var activeX = document.selection.GetActivePointX(eePosLogical);
117 		var activeY = document.selection.GetActivePointY(eePosLogical);
118 		var anchorX = document.selection.GetAnchorPointX(eePosLogical);
119 		var anchorY = document.selection.GetAnchorPointY(eePosLogical);
120 		document.selection.StartOfDocument(false);
121 		var overhead = document.selection.GetActivePointY(eePosLogical) - 1;
122 		document.selection.SetActivePoint(eePosLogical, activeX, activeY);
123 		document.selection.SetAnchorPoint(eePosLogical, anchorX, anchorY);
124 		return overhead;
125 	})();
126 	
127 	// ドキュメントの内容を調査 (部分編集の場合はその範囲内)
128 	var prevMatchedLineNum;
129 	var lines = JseeUtil.getAllLines();
130 	for(var i=0;i<lines.length;i++) {
131 		var line = lines[i];
132 		if(!matcher.test(line)) {
133 			continue;
134 		}
135 		// ループで配列内に保持したアドレスを上書かないようスコープを切る
136 		(function() {
137 			// 前回マッチした行数から大きく離れている場合は区切りを追加
138 			if(bigInterval > 0 && prevMatchedLineNum && i - prevMatchedLineNum > bigInterval) {
139 				menuItems.push(["", separatorFunction]);
140 			}
141 			
142 			// ジャンプ先を保持
143 			var x = RegExp.index + 1;
144 			var y = i + 1 + overhead;
145 			
146 			// メニュー項目を作成
147 			var menuItem = [
148 				// ショートカットキー
149 				getKey() + " | "
150 					// 0埋めした行数
151 					+ JseeUtil.zeroFill(y, linesCntDigit) + " | "
152 					// マッチした行の内容
153 					+ line.replace(/^\s*/, "").replace(/\s+/g, " ").replace(/&/g, "&&"),
154 				function() {
155 					// ジャンプ
156 					document.selection.SetActivePoint(eePosLogical, x, y);
157 					// 検索対象文字列を選択
158 					Redraw = redraw;
159 					document.selection.SetActivePoint(eePosLogical, x + target.length, y, true);
160 				}
161 			];
162 			
163 			// メニュー項目配列に追加
164 			menuItems.push(menuItem);
165 		})();
166 		
167 		prevMatchedLineNum = i;
168 	}
169 	
170 	if(!menuItems.length) {
171 		alert(target + "は見つかりませんでした。");
172 		return;
173 	}
174 	
175 	// メニューを表示
176 	JseeUtil.createPopupMenu(menuItems);
177 })();
178 
179 function separatorFunctionFactory(bigInterval) {
180 	return function separatorFunction() {
181 		if(confirm("検知された行の間隔が" + bigInterval + "より大きかったため、\n"
182 				+ "区切りが挿入されました。\n\n"
183 				+ "この値はINIファイルの設定で変更できます。\n\n"
184 				+ JseeUtil.getSnipIniName() + "を開きますか?")) {
185 			JseeUtil.selectSnipIniValue("検索結果をポップアップしてジャンプ", "bigInterval");
186 		}
187 	}
188 }
189