1 #title = "@文"
  2 #tooltip = "文字列操作ユーティリティ"
  3 #include "lib\sgc4jsee.jsee"
  4 
  5 /**
  6  * @fileOverview
  7  * ポップアップメニューの選択により、以下のような文字列操作機能を提供します。
  8  * <ol>
  9  * <li>選択文字列を含む行を... 削除</li>
 10  * <li>選択文字列を含む行を... 抽出</li>
 11  * <li>空行を... 削除</li>
 12  * <li>空行を... 1行にまとめる</li>
 13  * <li>入力文字列を... 正規表現で抽出</li>
 14  * <li>入力文字列を... 先頭へ挿入</li>
 15  * <li>入力文字列を... 末尾へ挿入</li>
 16  * <li>空白を... 行末から削除</li>
 17  * <li>空白を... 空白行を空行化</li>
 18  * <li>ドキュメントを反転</li>
 19  * <li>重複行をハイライト</li>
 20  * </ol>
 21  * 
 22  * @author gecca from 雪月花 (http://setsugecca.org/)
 23  * @version 1.00 for EmEditor v10.0
 24  */
 25 
 26 // 改行コード対応などを簡素に実装するため、String#replaceではなくJseeUtil#getAllLinesとArray#joinを使用。
 27 
 28 (function() {
 29 	// ポップアップメニューを表示
 30 	JseeUtil.createPopupMenu([
 31 		["選択文字列を含む行を...", function() {}],
 32 		["  削除 (&D)", removeBySelection],
 33 		["  抽出 (&E)", extractBySelection],
 34 		["入力文字列を...", function() {}],
 35 		["  正規表現で抽出 (&R)", regexpExtractWithPrompt],
 36 		["  先頭へ挿入 (&[)", insertIntoHomeWithPrompt],
 37 		["  末尾へ挿入 (&])", insertIntoEndWithPrompt],
 38 		["空行を...", function() {}],
 39 		["  削除 (&B)", removeBlankLines],
 40 		["  1行にまとめる (&U)", unifyBlankLines],
 41 		["空白を...", function() {}],
 42 		["  行末から削除 (&@)", deleteEndWhiteSpace],
 43 		["  空白行を空行化 (&W)", onlyWhiteSpaceLine2blankLine],
 44 		["ドキュメントを反転 (&|)", reverseDocument],
 45 		["重複行をハイライト (&2)", highlightDuplicated],
 46 	]);
 47 	
 48 	return;
 49 	
 50 	/**
 51 	 * @function
 52 	 * ドキュメントに書き出す
 53 	 * @param {String} str 書き出す文字列
 54 	 */
 55 	function write(str) {
 56 		document.selection.SelectAll();
 57 		document.selection.Text = str;
 58 		document.selection.StartOfDocument(false);
 59 	}
 60 	
 61 	/**
 62 	 * @function
 63 	 * 選択文字列を含む行を削除
 64 	 */
 65 	function removeBySelection() {
 66 		// 対象文字列を取得
 67 		var text = document.selection.Text;
 68 		if(!text) {
 69 			alert("選択してください。");
 70 			return;
 71 		}
 72 		
 73 		// 処理を実行
 74 		var lines = JseeUtil.getAllLines(true);
 75 		for(var i=0;i<lines.length;i++) {
 76 			if(lines[i].indexOf(text) >= 0) {
 77 				lines[i] = "";
 78 			}
 79 		}
 80 		
 81 		// 結果を出力
 82 		write(lines.join(""));
 83 	}
 84 	
 85 	/**
 86 	 * @function
 87 	 * 選択文字列を含む行を抽出
 88 	 */
 89 	function extractBySelection() {
 90 		// 対象文字列を取得
 91 		var text = document.selection.Text;
 92 		if(!text) {
 93 			alert("選択してください。");
 94 			return;
 95 		}
 96 		
 97 		// 処理を実行
 98 		var lines = JseeUtil.getAllLines(true);
 99 		for(var i=0;i<lines.length;i++) {
100 			if(lines[i].indexOf(text) < 0) {
101 				lines[i] = "";
102 			}
103 		}
104 		
105 		// 結果を出力
106 		write(lines.join(""));
107 	}
108 	
109 	/**
110 	 * @function
111 	 * 空行を削除
112 	 */
113 	function removeBlankLines() {
114 		var existsBlankLine = false;
115 		
116 		// 処理を実行
117 		var lines = JseeUtil.getAllLines(true);
118 		for(var i=0;i<lines.length;i++) {
119 			if(lines[i] == "\r\n" || lines[i] == "\r" || lines[i] == "\n") {
120 				lines[i] = "";
121 				existsBlankLine = true;
122 			}
123 		}
124 		
125 		// 結果を出力
126 		if(existsBlankLine) {
127 			write(lines.join(""));
128 		} else {
129 			alert("空行はありません。");
130 		}
131 	}
132 	
133 	/**
134 	 * @function
135 	 * 連続する空行を1行にまとめる
136 	 */
137 	function unifyBlankLines() {
138 		var existsMultiBlankLines = false;
139 		
140 		// 処理を実行
141 		var lines = JseeUtil.getAllLines(true);
142 		var isBlankBefore = false;
143 		for(var i=0;i<lines.length;i++) {
144 			if(lines[i] == "\r\n" || lines[i] == "\r" || lines[i] == "\n") {
145 				if(isBlankBefore) {
146 					// 今回も前回も改行の場合は無視
147 					lines[i] = "";
148 					existsMultiBlankLines = true;
149 				}
150 				isBlankBefore = true;
151 			} else {
152 				isBlankBefore = false;
153 			}
154 		}
155 		
156 		// 結果を出力
157 		if(existsMultiBlankLines) {
158 			write(lines.join(""));
159 		} else {
160 			alert("連続する空行はありません。");
161 		}
162 	}
163 	
164 	/**
165 	 * @function
166 	 * 入力文字列を正規表現で抽出
167 	 */
168 	function regexpExtractWithPrompt() {
169 		// 対象文字列を取得
170 		var regexpPattern = prompt("入力文字列を正規表現で抽出", document.selection.Text);
171 		if(!regexpPattern) {
172 			return;
173 		}
174 		
175 		var isMatched = false;
176 		
177 		// 処理を実行
178 		var lines = JseeUtil.getAllLines(true);
179 		try {
180 			var re = new RegExp(regexpPattern);
181 		} catch(e) {
182 			alert(e.message);
183 			return;
184 		}
185 		for(var i=0;i<lines.length;i++) {
186 			if(!re.test(lines[i])) {
187 				lines[i] = "";
188 			} else {
189 				isMatched = true;
190 			}
191 		}
192 		
193 		// 結果を出力
194 		if(isMatched) {
195 			write(lines.join(""));
196 		} else {
197 			alert("入力された正規表現にマッチする行はありません。");
198 		}
199 	}
200 	
201 	/**
202 	 * @function
203 	 * 入力文字列を先頭へ挿入
204 	 */
205 	function insertIntoHomeWithPrompt() {
206 		var markToInsertBlankLine = ";";
207 		
208 		var input = prompt("入力文字列を先頭へ挿入 (空行に挿入する場合は冒頭に" + markToInsertBlankLine + ")", "");
209 		if(!input) {
210 			return;
211 		}
212 		
213 		// 空行にも挿入するかどうかを判別し、冒頭のマークを入力文字列から除去
214 		var willInsertBlankLine = false;
215 		if(input.substring(0, markToInsertBlankLine.length) == markToInsertBlankLine) {
216 			willInsertBlankLine = true;
217 			input = input.substring(markToInsertBlankLine.length);
218 		}
219 		
220 		var lines = JseeUtil.getAllLines(true);
221 		for(var i=0;i<lines.length;i++) {
222 			// 空行にも挿入する場合、または有効行である場合は挿入
223 			if(willInsertBlankLine || (lines[i] && lines[i].charAt(0) != '\r' && lines[i].charAt(0) != '\n')) {
224 				lines[i] = input + lines[i];
225 			}
226 		}
227 		write(lines.join(""));
228 	}
229 	
230 	/**
231 	 * @function
232 	 * 入力文字列を末尾へ挿入
233 	 */
234 	function insertIntoEndWithPrompt() {
235 		var markToInsertBlankLine = ";";
236 		
237 		var input = prompt("入力文字列を末尾へ挿入 (空行に挿入する場合は冒頭に" + markToInsertBlankLine + ")", "");
238 		if(!input) {
239 			return;
240 		}
241 		
242 		// 空行にも挿入するかどうかを判別し、冒頭のマークを入力文字列から除去
243 		var willInsertBlankLine = false;
244 		if(input.substring(0, markToInsertBlankLine.length) == markToInsertBlankLine) {
245 			willInsertBlankLine = true;
246 			input = input.substring(markToInsertBlankLine.length);
247 		}
248 		
249 		var lines = JseeUtil.getAllLines(true);
250 		for(var i=0;i<lines.length;i++) {
251 			// 空行にも挿入する場合、または有効行である場合は挿入
252 			if(willInsertBlankLine || (lines[i] && lines[i].charAt(0) != '\r' && lines[i].charAt(0) != '\n')) {
253 				/^(.*?)([\r\n]*)$/.test(lines[i]);
254 				lines[i] = RegExp.$1 + input + RegExp.$2;
255 			}
256 		}
257 		write(lines.join(""));
258 	}
259 	
260 	/**
261 	 * @function
262 	 * 行末の空白を削除
263 	 */
264 	function deleteEndWhiteSpace() {
265 		var isDeleted = false;
266 		
267 		var lines = JseeUtil.getAllLines(true);
268 		var re = /^(.*\S)[^\r\n\S]+([\r\n]*)$/;
269 		for(var i=0;i<lines.length;i++) {
270 			if(re.test(lines[i])) {
271 				lines[i] = RegExp.$1 + RegExp.$2;
272 				isDeleted = true;
273 			}
274 		}
275 		
276 		if(isDeleted) {
277 			write(lines.join(""));
278 		} else {
279 			alert("行末に空白のある行はありません。");
280 		}
281 	}
282 	
283 	/**
284 	 * @function
285 	 * 空白行を空行化
286 	 */
287 	function onlyWhiteSpaceLine2blankLine() {
288 		var existsOnlyWhiteSpaceLine = false;
289 		
290 		var lines = JseeUtil.getAllLines(true);
291 		var re = /^(\s*?)([\r\n]*)$/;
292 		for(var i=0;i<lines.length;i++) {
293 			if(re.test(lines[i])) {
294 				if(RegExp.$1) {
295 					lines[i] = RegExp.$2;
296 					existsOnlyWhiteSpaceLine = true;
297 				}
298 			}
299 		}
300 		
301 		if(existsOnlyWhiteSpaceLine) {
302 			write(lines.join(""));
303 		} else {
304 			alert("空白行はありません。");
305 		}
306 	}
307 	
308 	/**
309 	 * @function
310 	 * ドキュメントを反転
311 	 */
312 	function reverseDocument() {
313 		if(document.GetLines() <= 1) {
314 			return;
315 		}
316 		
317 		// 最下行に文字があるかどうか
318 		var existsCharAtLastLine = document.GetLine(document.GetLines()).length > 0;
319 		
320 		// 反転
321 		document.selection.SelectAll();
322 		if(!existsCharAtLastLine) {
323 			// 最下行に文字がなければ、全行をそのまま反転
324 			document.selection.Text = JseeUtil.getAllLines(true).reverse().join("");
325 		} else {
326 			// 最下行に文字があれば、最下行に改行を補完し、反転後の最下行の改行は削る
327 			var reversedLines = JseeUtil.str2lines(document.selection.Text + JseeUtil.getBr(), true).reverse();
328 			reversedLines[reversedLines.length - 1] = reversedLines[reversedLines.length - 1].replace(/[\r\n]+$/, "");
329 			document.selection.Text = reversedLines.join("");
330 		}
331 	}
332 	
333 	/**
334 	 * @function
335 	 * 重複行をハイライト
336 	 */
337 	function highlightDuplicated() {
338 		var lines = JseeUtil.getAllLines();
339 		
340 		var quotedDuplicatedLines = [];
341 		for(var i=0;i<lines.length;i++) {
342 			if(!lines[i]) {
343 				continue;
344 			}
345 			
346 			var quotedLine = JseeUtil.quote(lines[i]);
347 			for(var j=i+1;j<lines.length;j++) {
348 				if(lines[i] == lines[j]) {
349 					if(!JseeUtil.contains(quotedDuplicatedLines, quotedLine)) {
350 						quotedDuplicatedLines.push(quotedLine);
351 					}
352 					break;
353 				}
354 			}
355 			
356 		}
357 		
358 		if(quotedDuplicatedLines.length != 0) {
359 			document.selection.Find("^(" + quotedDuplicatedLines.join("|") + ")$", eeFindNext | eeFindReplaceRegExp | eeFindAround);
360 		} else {
361 			alert("重複行はありません。");
362 		}
363 	}
364 })();
365