LoginSignup
6
8

More than 5 years have passed since last update.

サクラエディタでEclipse風コメントアウト

Last updated at Posted at 2017-08-13

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氏のマクロを元にしています。

関連情報

gist サクラエディタ用マクロ Eclipse風コメントアウト

6
8
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
8