Eclipse風のコメントアウト
範囲選択してCTRL + /
でコメントアウト、アンコメントをトグルで行えて快適です。
サクラエディタにも欲しくなりました。
なので作りました。
(20180407追記 : よりEclipseの動作に近づけました。選択範囲が全て空行ならコメントアウト、選択範囲にコード行を含むならコメントアウト、それ以外はアンコメント、としました。)
(20170814追記 : アンコメント時に選択範囲の復元をするよう修正しました。ミスで、参照元記事の上のほうにある旧バージョンを参照してしまっていました。)
commentOutEclipseLike.js
/*
* @file commentOutEclipseLike.js
* @brief 選択範囲、もしくはカーソル行をコメントアウト/アンコメントするサクラエディタマクロ
* @author cat2151
*
* phodra氏作成の同様のマクロを改変したもの。
* http://sakura-editor.sourceforge.net/cgi-bin/cyclamen/cyclamen.cgi?log=macro&tree=c591
*
* ・コメントアウト行を選択した場合はアンコメント。そうでない場合はコメントアウト。
* ・クイックハックなのでリファクタリング不足。
*/
if(!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^\s+|\s+$/g,'');
};
}
toggleCommentOut();
// コメントアウトをトグルで行う
// 選択時は選択範囲の先頭行の行頭、それ以外は現在行の行頭、にコメントステートメントがあればアンコメント、それ以外はコメントアウトする
function toggleCommentOut() {
// エディタがビューモード(読み取り専用)なら終了。
if( Editor.ExpandParameter("${R?読専$:$:$}") == "読専" ) return;
// 拡張子別にコメントステートメントを定義
var comment = new Array();
comment["cpp"] = "//"; comment["c"] = "//"; comment["h"] = "//"; comment["nut"] = "//";
comment["js"] = "//"; comment["java"] = "//";
comment["php"] = "//"; comment["uws"] = "//";
comment["mac"] = "//"; comment["ppa"] = "//";
comment["vbs"] = "'"; comment["bas"] = "'"; comment["frm"] = "'";
comment["cls"] = "'"; comment["vb"] = "'";
comment["cgi"] = "#"; comment["pl"] = "#"; comment["pm"] = "#"; comment["sh"] = "#";
comment["asm"] = ";"; comment["ini"] = ";"; comment["inf"] = ";"; comment["s"] = ";";
comment["cnf"] = ";"; comment["conf"] = ";";
comment["tex"] = "%";
comment["cmd"] = "REM ";
comment["bat"] = "REM ";
comment["txt"] = " "; comment["noext"] = " ";
// 拡張子を切り出し
var fname = Editor.ExpandParameter("$f");
var ext = fname.substring( fname.lastIndexOf(".") + 1);
ext = ext==fname? "noext": ext.toLowerCase();
// 登録されていない拡張子であれば終了。
if( comment[ext] == null ) return;
// 選択範囲の先頭行または、現在行を取得(コメントアウトされているかどうかの判断用)
{
var selmode = Editor.IsTextSelected();
var fromLineNum, toLineNum;
var isComment = false;
if( selmode ){ // 選択状態
// コメントアウトされているか判断
isComment = !containsCodeOrContainsOnlySpace(comment, ext);
}else{ // 選択していない
// ロジック座標を取得
fromLineNum = parseInt(Editor.ExpandParameter("$y"));
// コメントアウトされているか判断
if (GetLineStr(fromLineNum).trim().substring(0, comment[ext].length) == comment[ext]) isComment = true;
}
if (isComment){ // コメントアウトされている
unComment(comment);
} else { // コメントアウトされていない
commentOut(comment);
}
}
}
function containsCodeOrContainsOnlySpace(comment, ext) {
// レイアウト座標を取得
var lay_fl = Editor.GetSelectLineFrom();
var lay_tl = Editor.GetSelectLineTo();
// レイアウト座標をロジック座標に変換
// ExpandParameterではカーソル位置しか分からないので……
fromLineNum = Editor.LayoutToLogicLineNum( lay_fl );
toLineNum = Editor.LayoutToLogicLineNum( lay_tl );
var numOfComments = 0;
for (var i = fromLineNum; i < toLineNum; i++) {
// 行頭空白をとりのぞき、文字列Aを得る
var strA = GetLineStr(i).trim();
// 文字列Aが空の場合、空行とみなし、チェック対象外とする
if (strA == "") continue;
if (strA.substring(0, comment[ext].length) != comment[ext]) {
// コード行の場合、つまり文字列Aの先頭がコメントステートメント以外の場合
return true;
}
numOfComments++;
}
// すべて空行の場合
if (!numOfComments) return true;
// コード行がなく、コメント行が1行以上ある場合
return false;
}
// コメントアウト
function commentOut(comment) {
// 拡張子を切り出し
var fname = Editor.ExpandParameter("$f");
var ext = fname.substring( fname.lastIndexOf(".") + 1);
ext = ext==fname? "noext": ext.toLowerCase();
// 登録されていない拡張子であれば終了。
if( comment[ext] == null ) return;
// ちらつき防止
Editor.SetDrawSwitch(0);
// 選択モード(この変数は復元する際にも使用する)
var selmode = Editor.IsTextSelected();
if( selmode ){ // 選択状態
// レイアウト座標を取得
var lay_fl = Editor.GetSelectLineFrom();
var lay_tl = Editor.GetSelectLineTo();
// レイアウト座標をロジック座標に変換
// 選択位置のロジック座標を取得する関数はない?
var fl = Editor.LayoutToLogicLineNum( lay_fl );
var tl = Editor.LayoutToLogicLineNum( lay_tl );
var fc = Editor.LineColumnToIndex( lay_fl, Editor.GetSelectColmFrom() );
var tc = Editor.LineColumnToIndex( lay_tl, Editor.GetSelectColmTo() );
// 行選択でマクロに入った場合、一つ上の行までにする
var endl = tc==1? tl-1: tl;
// 範囲内の行を配列としてとる
var lines = new Array();
var str = "";
for( var i=fl; i<=endl; i++ ){
str = Editor.GetLineStr(i)
lines.push( comment[ext] + str );
}
// 必要な範囲を行選択
Editor.MoveCursor( fl, 1, 0);
Editor.MoveCursor( endl, str.length+1, 1);
// 選択範囲を上書き
Editor.InsText( lines.join("") );
// 選択範囲を復元
Editor.MoveCursor( fl, fc+comment[ext].length, 0);
Editor.MoveCursor( tl, tc==1? tc: tc+comment[ext].length, selmode);
}else{ // 選択していない
// ロジック座標を取得
// 選択範囲がないのでレイアウトから変換する必要はない
var l = parseInt(Editor.ExpandParameter("$y"));
var c = parseInt(Editor.ExpandParameter("$x"));
// 行頭に移動
Editor.MoveCursor( l, 1, 0);
// コメントステートメントを挿入
Editor.InsText(comment[ext]);
// 位置を復元
Editor.MoveCursor( l, c+comment[ext], selmode);
}
// 描画フラグを戻してから再描画
Editor.SetDrawSwitch(1);
Editor.ReDraw(0);
}
// アンコメント
function unComment(comment) {
//拡張子を切り出し
var fname = Editor.ExpandParameter("$f");
var ext = fname.substring( fname.lastIndexOf(".") + 1);
ext = ext==fname? "noext": ext.toLowerCase();
// 登録されていない拡張子であれば終了。
if( comment[ext] == null ) return;
// ちらつき防止
Editor.SetDrawSwitch(0);
// 置換対象にする部分の正規表現
// なぜかエスケープしないといけない
var uncmt = new RegExp("\(\^\\s\*\?\)" + comment[ext]);
// 選択モード(この変数は復元する際にも使用する)
var selmode = Editor.IsTextSelected();
if( selmode ){ // 選択状態
// レイアウト座標を取得
var lay_fl = Editor.GetSelectLineFrom();
var lay_tl = Editor.GetSelectLineTo();
// レイアウト座標をロジック座標に変換
// 選択位置のロジック座標を取得する関数はない?
var fl = Editor.LayoutToLogicLineNum( lay_fl );
var tl = Editor.LayoutToLogicLineNum( lay_tl );
var fc = Editor.LineColumnToIndex( lay_fl, Editor.GetSelectColmFrom() );
var tc = Editor.LineColumnToIndex( lay_tl, Editor.GetSelectColmTo() );
// 行選択でマクロに入った場合、一つ上の行までにする
var endl = tc==1? tl-1: tl;
// 範囲内の行を配列としてとる
var lines = new Array();
var reped = new Array();
var str = "";
var cnt = 0;
for( var i=fl; i<=endl; i++ ){
str = Editor.GetLineStr(i);
var rep = str.replace( uncmt, "$1" );
lines.push( str );
reped.push( rep );
if( str == rep ) cnt++;
}
// 処理していない行が全体の行数に等しいときは何もしない
if( cnt < reped.length ){
// 必要な範囲を行選択
Editor.MoveCursor( fl, 1, 0);
Editor.MoveCursor( endl, str.length+1, 1);
// 選択範囲を上書き
Editor.InsText( reped.join("") );
// 範囲復元時のX位置
// コメントステートメントより後ならコメントステートメントの字数分ずらす
// コメントステートメントの中ならコメントステートメントが現れた位置
// コメントステートメントより前ならそのままの位置
var idx = lines[0].indexOf( comment[ext] );
var gap_f =
fc>idx+comment[ext].length? fc-comment[ext].length:
fc>idx+1? idx+1:
fc;
idx = lines[lines.length-1].indexOf( comment[ext] );
var gap_t =
tc>idx+comment[ext].length? tc-comment[ext].length:
tc>idx+1? idx+1:
tc;
// 選択範囲を復元
Editor.MoveCursor( fl, gap_f, 0);
Editor.MoveCursor( tl, gap_t, selmode);
}
}else{ // 選択していない
// ロジック座標を取得
// 選択範囲がないのでレイアウトから変換する必要はない
var l = parseInt(Editor.ExpandParameter("$y"));
var c = parseInt(Editor.ExpandParameter("$x"));
// 対象行の文字列を取得
var str = Editor.GetLineStr(l);
// コメントステートメントを除去した文字列
var rep = str.replace( uncmt, "$1" );
if( str != rep ){
// 行選択
Editor.MoveCursor( l, 1, 0);
Editor.MoveCursor( l, str.length+1, 1);
// 置換した文字列で上書き
Editor.InsText( rep );
// 復元時のカーソルX位置
// コメントステートメントより後ならコメントステートメントの字数分ずらす
// コメントステートメントの中ならコメントステートメントが現れた位置
// コメントステートメントより前ならそのままの位置
var idx = str.indexOf( comment[ext] );
var gap =
c>idx+comment[ext].length? c-comment[ext].length:
c>idx+1? idx+1:
c;
// カーソル位置を復元
Editor.MoveCursor( l, gap, selmode);
}
}
// 描画フラグを戻してから再描画
Editor.SetDrawSwitch(1);
Editor.ReDraw(0);
}
phodra氏のマクロを元にしています。