「引用符/コメント」の版間の差分

提供:MeryWiki
ナビゲーションに移動 検索に移動
(第三版)
16行目: 16行目:
  ※「\n」や「\t」と記述したばあい、そのままの文字列として「\n」や「\t」が返されます。
  ※「\n」や「\t」と記述したばあい、そのままの文字列として「\n」や「\t」が返されます。


<div class="warningbox">
'''2018/10/30''' <br>
初版の「JS コメントアウト1」と「XML コメントアウト」に'''バグ'''がありました。<br> 最新版のソースコード/ZIP をご利用ください。 → [[#ダウンロード]]
</div>


== 挿入/削除できる引用マークやコメントマーク ==
== 挿入/削除できる引用マークやコメントマーク ==
40行目: 35行目:
  ☐ (全角スペース)
  ☐ (全角スペース)
  › (TABコード)
  › (TABコード)
⏎ (空行: 改行コード) ※削除コマンドからの削除不可
   
   
  '''//''' ␣ (C系, JavaScript コメント)
  '''//''' ␣ (C系, JavaScript コメント)
70行目: 66行目:
== ダウンロード ==
== ダウンロード ==
<br>
<br>
ダウンロード: 「[[メディア:引用符/コメントマーク.zip|引用符/コメントマーク.zip]](アイコン入り)」
ダウンロード: 「[[メディア:引用符/コメント.zip]]」(アイコン入り)
* 第三版: 2019/04/07 (コマンドを追加: 空行の追加、任意の文字列の削除)
* 第二版: 2018/10/30
* 第二版: 2018/10/30
* 第一版: 2018/10/28
* 第一版: 2018/10/28
76行目: 73行目:


== ソースコード ==
== ソースコード ==
<source lang="javascript">
<source lang="javascript" style="height:120em; overflow:auto;">
#title = "引用符を追加..."
#title = "引用符を追加..."
#tooltip = "引用符/コメントマークを追加・削除"
#tooltip = "引用符/コメントマークを追加・削除"
83行目: 80行目:
// 選択範囲の行頭に引用符/コメントマークを追加・削除する
// 選択範囲の行頭に引用符/コメントマークを追加・削除する
// 公式wikiのマクロライブラリの「引用の追加」を改造した
// 公式wikiのマクロライブラリの「引用の追加」を改造した
// (Last modified: 2018/10/30)
// (Last modified: 2019/04/07)


/*
/*
94行目: 91行目:
  * 「引用符を追加」または「引用符/コメント」
  * 「引用符を追加」または「引用符/コメント」
  * --------------------------------------------------
  * --------------------------------------------------
* Kuro 版からの変更点
*
* ・ポップアップメニューの体裁を変更した。
* ・引用符の種類を増やした(※ 基本的に「メタ文字+半角スペース」)。
* ・「クリップボード」と「任意の文字列」を追加。
* ・配列に削除用の要素(半角スペースなしのメタ文字のみ差分)も余分に追加した。
*  ※「すべて削除」では、半角スペース、全角スペース、タブ文字での字下げをすべて削除する。
*  ※「クリップボード」、「任意の文字列」、「JS/XML コメントアウト」は、「1つ削除//すべて削除」では削除できない。
*
* ・選択範囲があったときには行全体に拡張・復帰するコードにした。
* ・選択範囲なしの状態からカーソル行にたいして「追加/1つ削除/すべて削除」を実行したときは、
*  実行前の位置にカーソルを返せるようにした(undo すると行全体が選択範囲になってしまうのはマクロの仕様)。
* ・「1つ削除」の不具合箇所を修正した。
*
* ※ 「任意の文字列」について ※
* ・文字コードでの入力には非対応(入力されたままの文字列を返す)。
* ・改行コードとタブ文字は、それぞれ「\\\n」と「\\\t」で入力されたものを「\n」と「\t」に置換する。
*  パスの入力に配慮しただけの簡易的な置換処理なので、「\」を4つ以上かさねた場合を考慮していない。
*
* ※ 「JS コメントアウト」と「JS アンコメント」について ※
* ・「引用の追加」では選択範囲を拡張してコメントアウトするので、
*  行の中間部分だけをコメントアウトするなら「カッコで囲う」マクロが適する。
* ・JSアンコメントはコメント文の行頭記号を消してしまうので要注意。
*  基本的にこのマクロでつけられた、「中間行にアスタリスクを打った JS コメント」しか考慮していない。
* ・ほかにも注意事項があるので、該当コード部分の注意書きを参照のこと!
*
* ポップアップメニューの項目は、m.Add( … ); の不要な行をコメントアウトすれば隠すことができる。
*  ただし、メニューから隠しても、配列 q にある要素は「1つ削除/すべて削除」の対象になる。
* (配列から削除する場合は "" の中身だけを消すこと。
*  "" として空要素を残しておかないと、メニューや処理コードの ID と一致しなくなる)
  */
  */


132行目: 99行目:
   ">" ,  "・ " ,  "・" ,  "· " ,  "·" ,  "* " ,  "*" ,  "--" ,  "-" ,  "//" ,
   ">" ,  "・ " ,  "・" ,  "· " ,  "·" ,  "* " ,  "*" ,  "--" ,  "-" ,  "//" ,
   "#" ,  ";" ,  "'" ,  "::" ,  ":" );
   "#" ,  ";" ,  "'" ,  "::" ,  ":" );
   // ※半角スペースの有無で「1つ削除/すべて削除」がマッチしなくなるので、
 
  //   ">> " 以降に半角スペース あり/なし の差分要素を適当に追加してある。
   /*
  // ※半角スペース差分(中黒=ビュレットは全角/半角差分も)の変更は、配列内の編集ではなく
  * ※半角スペースの有無で「1つ削除/すべて削除」がマッチしなくなるので、
   //  ポップアップメニュー項目のID(番号)変更で対応しないと、「1つ削除/すべて削除」が効かなくなる。
  *   ">> " 以降に半角スペース あり/なし の差分要素を適当に追加してあります。
  // (同一文字をふくむ要素は文字列の長いものが先に置かれていないとダメ)。
  * ※半角スペース差分(中黒=ビュレットは全角/半角差分も)の変更は、配列内の編集ではなく
  // 「・」は半角カナの中黒(U+FF65)、「·」は欧文用ユニコード文字のビュレット(U+00B7)
  *   ポップアップメニュー項目のID(番号)変更で対応しないと、「1つ削除/すべて削除」が効かなくなります
  * (同一文字をふくむ要素は文字列の長いものが先に置かれていないとダメ)。
  * 「・」は半角カナの中黒(U+FF65)、「·」は欧文用ユニコード文字のビュレット(U+00B7)
  */




145行目: 115行目:
   // r の数値は上の配列 q の並び順やテキスト変換処理の case r: に対応しているので変更しないこと!
   // r の数値は上の配列 q の並び順やテキスト変換処理の case r: に対応しているので変更しないこと!


m.Add( " 任意の文字列 (&E)", 50 );
m.Add( "任意の文字列 (&E)...", 50 );
m.Add( " クリップボード (&C)", 40 );
m.Add( " クリップボード (&C)", 40 );
m.Add( "", 0, meMenuSeparator );
m.Add( "", 0, meMenuSeparator );
158行目: 128行目:
m.Add( "⃞ 全角スペース (&2)", 6 );
m.Add( "⃞ 全角スペース (&2)", 6 );
m.Add( "› タブコード (&T)", 7 );
m.Add( "› タブコード (&T)", 7 );
m.Add( "⏎ 空行 (&N)", 52 );
m.Add( "", 0, meMenuSeparator );
m.Add( "", 0, meMenuSeparator );
m.Add( "\/\/ JS・C コメント (&J)", 8 );
m.Add( "\/\/ JS・C コメント (&J)", 8 );
168行目: 139行目:
m.Add( "REM BAT コメント (&R)", 15 );
m.Add( "REM BAT コメント (&R)", 15 );
m.Add( "", 0, meMenuSeparator );
m.Add( "", 0, meMenuSeparator );
m.Add( " 1つ削除 (&D)", 17 );
m.Add( "1つ削除 (&D)", 17 );
m.Add( " すべて削除 (&A)", 18 );
m.Add( "すべて削除 (&A)", 18 );
m.Add( "", 0, meMenuSeparator );
m.Add( "? 行頭から任意の文字列を削除 (&Q)...", 51 );
m.Add( "", 0, meMenuSeparator );
m.Add( "", 0, meMenuSeparator );
m.Add( "/*  *  */ JS コメントアウト 2 (&J)", 42 );
m.Add( "/*  *  */ JS コメントアウト 2 (&J)", 42 );
190行目: 163行目:
    
    
   // 選択範囲がないときのカーソル位置を取得
   // 選択範囲がないときのカーソル位置を取得
   if ( s.IsEmpty )
   // pos は「マクロ実行前に選択範囲なし」フラグとしても使う
    var pos = s.GetActivePos();
  var pos = s.IsEmpty ? s.GetActivePos() : "";
   // 選択範囲を取得する
   // 選択範囲を取得する
   var ax = s.GetTopPointX( mePosLogical );
   var ax = s.GetTopPointX( mePosLogical );
206行目: 179行目:


   // 選択範囲の文字列を取得
   // 選択範囲の文字列を取得
   var st = document.selection.Text; // 拡張された行全体のテキスト
   var st = s.Text; // 拡張された行全体のテキスト
  var exit = false; // Quit の代用フラグ


   // IDごとのテキスト変換処理
   // IDごとのテキスト変換処理
218行目: 192行目:
       /* とりあえずコメントアウトしておく */  
       /* とりあえずコメントアウトしておく */  
//      // マクロ実行前に選択範囲がなかった場合は、カーソルを元の位置に戻す
//      // マクロ実行前に選択範囲がなかった場合は、カーソルを元の位置に戻す
//      if ( pos ) { s.SetActivePos( pos + q[r].length ); Quit(); }
//      if ( pos ) {
//        s.SetActivePos( pos + q[r].length );
//        exit = true;
//      }
      break;
 
 
    case 52:
      // 空行を挿入 ※空行は削除コマンドの削除対象に含まれない
      var nl = "\n"; // 空改行
      s.Text = InsertQuote( st, nl ); // 各行の先頭に追加
      if ( pos ) {
        s.SetActivePos( pos + nl.length );
        exit = true;
      }
       break;
       break;


225行目: 213行目:
       // 各行の先頭の引用符/コメントマークを1つ削除
       // 各行の先頭の引用符/コメントマークを1つ削除
       var dt = deleteQuote( st );
       var dt = deleteQuote( st );
      // 選択範囲に引用符/コメントマークがなかった場合 undo 履歴を残さない
       if ( dt == st ) {
       if ( dt == st ) {
         if ( pos ) { s.SetActivePos( pos ); Quit(); }
         if ( pos ) {
          s.SetActivePos( pos );
          exit = true;
        }
         break;
         break;
       }
       }
       // 1つ削除完了
       else {
      s.Text = dt;
        s.Text = dt; // 削除完了
 
        if ( pos && dt.length ) {
      // マクロ実行前に選択範囲がなかった場合は、カーソルを元の位置に戻す
          s.SetActivePos( pos - ( st.length - dt.length ) );
      if ( pos && dt.length ) {
          exit = true;
        s.SetActivePos( pos - ( st.length - dt.length ) );
         }
         Quit();
        break;
       }
       }
      break;




246行目: 235行目:
       for ( var i = 0; i < 40; i ++ ) { // st を最大40回 deleteQuote() でループ処理
       for ( var i = 0; i < 40; i ++ ) { // st を最大40回 deleteQuote() でループ処理
         var dt = deleteQuote( st );
         var dt = deleteQuote( st );
         if ( dt == st ) break;
         if ( dt == st ) {
         else st = dt;
          break;
        }
         else {
          st = dt; // deleteQuote() 処理したテキスト dt を st に再代入
        }
       }
       }
      // 選択範囲に引用符/コメントマークがなかった場合 undo 履歴を残さない
       if ( dt == org ) {
       if ( dt == org ) {
         if ( pos ) { s.SetActivePos( pos ); Quit(); }
         if ( pos ) {
          s.SetActivePos( pos );
          exit = true;
        }
         break;
         break;
       }
       }
       // すべて削除完了
       else {
      s.Text = dt;
        s.Text = dt; // 削除完了
 
        if ( pos && dt.length) {
      // マクロ実行前に選択範囲がなかった場合は、カーソルを元の位置に戻す
          s.SetActivePos( pos - ( org.length - dt.length ) );
      if ( pos && dt.length) {
          exit = true;
        s.SetActivePos( pos - ( org.length - dt.length ) );
         }
         Quit();
        break;
       }
       }
      break;




268行目: 262行目:
       // クリップボードのテキストデータを取得して、各行の先頭に追加
       // クリップボードのテキストデータを取得して、各行の先頭に追加
       var cb = ClipboardData.GetData();
       var cb = ClipboardData.GetData();
       if ( cb )
       if ( cb ) {
         s.Text = insertQuote( st, cb );
         s.Text = InsertQuote( st, cb );
 
       }
       // マクロ実行前に選択範囲がなかった場合は、カーソルを元の位置に戻す
       if ( pos ) {
       if ( pos ) { s.SetActivePos( pos + cb.length ); Quit(); }
        s.SetActivePos( pos + cb.length );
        exit = true;
      }
       break;
       break;




    /* 任意の文字列 */
     case 50:
     case 50:
       // ※文字コードには非対応(テキストボックスに入力されたままの文字列を返す)
       // ダイアログのテキスト入力フィールドから文字列を取得
       // ダイアログのテキスト入力フィールドから文字列を取得して、各行の先頭に追加
       // 文字コードには非対応(入力されたままの文字列を返す)
       var p = Prompt(
       var p = Prompt(
         "前につける文字列:\t改行=\\\\\\n ; タブ=\\\\\\t  (注:¥記号3つ)", ""
         "前につける文字列:\t改行=\\\\\\n ; タブ=\\\\\\t  (注:¥記号3つ)", ""
       ).replace( /\\\\\\n/g , "\n" ).replace( /\\\\\\t/g , "\t" );
       ).replace( /\\\\\\n/g , "\n" ).replace( /\\\\\\t/g , "\t" );
       if ( p )
       if ( p ) {
         s.Text = insertQuote( st, p );
         s.Text = InsertQuote( st, p ); // 各行の先頭に追加
      }
      if ( pos ) {
        s.SetActivePos( pos + p.length );
        exit = true;
      }
      break;


       // マクロ実行前に選択範囲がなかった場合は、カーソルを元の位置に戻す
 
       if ( pos ) { s.SetActivePos( pos + p.length );  Quit(); }
 
    /* 任意の文字列を削除 */
    case 51:
      // ダイアログのテキスト入力フィールドから文字列を取得
      var p = Prompt(
        "行頭から削除する文字列:\tタブ=\\\\\\t  (注:¥記号3つ)", ""
       ).replace( /\\\\\\t/g , "\t" );
       var reg = new RegExp( "^" + Quote( p ) , "gm" );
      // var reg = new RegExp( "^" + p.replace( /[$()*+.?[\\\]^{|}]/g, "\\$&" ) , "gm" );
      dt = st.replace( reg , "" ); // 各行の先頭の指定文字列を削除
 
      if ( dt == st ) {
        if ( pos ) {
          s.SetActivePos( pos );
          exit = true;
        }
        break;
      }
      else {
        s.Text = dt; // 削除完了
        if ( pos && dt.length ) {
          s.SetActivePos( pos - ( st.length - dt.length ) );
          exit = true;
        }
      }
       break;
       break;


     case 41:  case 44:
     case 41:  case 44:
       // 「引用の追加」では行単位に拡張した選択範囲をまとめてコメントアウトするので、
       // 「引用の追加」では行単位に拡張した選択範囲をまとめてコメントアウトするので、
       // 行の中間部分だけをコメントアウトするなら「カッコで囲う」マクロを使うこと。
       // 行の中間部分だけをコメントアウトするなら「カッコで囲う」マクロを使うこと。
       var p1, p2;
       var p1, p2;
       if ( r == 41 ) { p1 = "/* "; p2 = " */"; } // /* JavaScript コメントアウト1 */
       if ( r == 41 ) { // /* JavaScript コメントアウト1 */
       else { p1 = "<!-- ",  p2 = " -->"; } // ( r == 44 ) // <!-- XML コメントアウト -->  
        p1 = "/* ";
       // 空行のばあい、コメントマーク p1 & p2 だけ挿入して、カーソルをコメント枠のなかに移動
        p2 = " */";
       if ( ! st ) {
      }
        document.Write( p1 + p2 );
       else { // ( r == 44 ) // <!-- XML コメントアウト -->  
         s.SetActivePos( pos + p1.length );
        p1 = "<!-- ";
         Quit();
        p2 = " -->";
      }
       s.Text = p1 + st + p2; // 選択範囲 st をコメントアウト
       if ( ! st ) { // 空行のばあい
         s.SetActivePos( pos + p1.length ); // カーソルをコメント枠のなかに移動
         exit = true;
       }
       }
      // 空行以外のばあい、選択範囲 st をコメントアウト
      s.Text = p1 + st + p2;
       break;
       break;




     case 42:
     case 42:
       /*  
       /**
       * ・先頭行の字下げを基準に2行目以降も字下げするが、
      * ※ 行頭の字下げされた位置でコメントアウトする(コメントドキュメント向け)
       *  先頭行より字下げ量が少ない行はレイアウトが崩れる(「アンコメント」するとさらに崩れる)。
      * ・選択範囲内の最小の字下げ位置にあわせる。
       * ・先頭に最少量の字下げする空白行を置くとレイアウトを維持でき、
       * ・基本的に「JS アンコメント」してもレイアウトを保持できるが、
       * 「アンコメント」してもレイアウトを保持できる。
       *  各行の字下げルールが同じである前提なので、半角スペースもタブコードも1文字として数える。
      * ・空白文字だけの行は空行と見做し、行頭記号 " * " を付けた後ろに空白文字を残さない。
       * ・「引用符/コメント」では行単位でコメントアウトするので、
       *   行の中間部分だけをコメントアウトするなら「カッコで囲う」マクロを使うこと。
       */
       */
       var p1 = "/* ",  p2 = " */",  ast = " * ",  n = "\n";
       var p1 = "/**",  p2 = " */",  ast = " * ",  nl = "\n";
       // 空行のばあい、コメントマーク /* \n & * & \n */ だけ挿入して、カーソルをコメント枠のなかに移動
       s.Text = CommentOutJS( st, p1, p2, ast ); // 選択範囲全体を /* *コメントアウト */
       if ( pos ) {
 
        s.Text =  p1 + n + ast + n + p2;
       if ( ! st ) { // マクロ実行前に空行だった場合はカーソルをコメント枠のなかに移動
         s.SetActivePos( pos + ( p1 + n + ast ).length  );
         s.SetActivePos( pos + ( p1 + nl + ast ).length  );
         Quit();
         exit = true;
      }
      // カーソルを接頭辞のうしろに移動
      else {
        s.SetActivePoint( mePosLogical, 1, ay );
        s.EndOfLine();
        exit = true;
       }
       }
      // 空行以外のばあい、選択範囲全体を /* *コメントアウト */
      s.Text = CommentOutJS( st, p1, p2, ast );
       break;
       break;


341行目: 379行目:
       *    各要素はあらかじめJSの正規表現で記述しておくこと。
       *    各要素はあらかじめJSの正規表現で記述しておくこと。
       */
       */
       // コメントの"接頭辞"と"接尾辞"と"行頭記号"を正規表現で配列に格納
       // コメントの "接頭辞" と "接尾辞" と "行頭記号" を正規表現で配列に格納
       var p = new Array( "\\/\\*+ ?" , " ?\\*\\/" , " ?(\\*|[・・]) ?" );
       var p = new Array( "\\/\\*+ ?" , " ?\\*\\/" , " ?(\\*|[・・]) ?" );
       // コメントアウトを解除
       // コメントアウトを解除
       var dc = deleteComment( st, p )
       var dc = DeleteComment( st, p )
       // 選択範囲に JSコメント がなかった場合 undo 履歴を残さない
       // 選択範囲に /* JSコメント */ がなかった場合 undo 履歴を残さない
       if ( dc == st ) {
       if ( dc == st ) {
         if ( pos ) { s.SetActivePos( pos ); Quit(); }
         if ( pos ) {
         break;
          s.SetActivePos( pos );
          exit = true;
        }
      }
      else {
        // 先頭と末尾の空行を削除してアンコメント完了
        // ※[\s ]* だけだと消したくない部分まで消されるので \n をつける
         s.Text = dc.replace( /[\s ]*$/gm , "" )
                  .replace( /^[\s ]*\n|\n[\s ]*$|/g , "" );
       }
       }
      // 先頭と末尾の空行を削除してアンコメント完了
      var blank = /[\t  ]*$/gm;
      s.Text = dc.replace( blank , "" ).replace( /^[\s ]*\n|\n[\s ]*$|/g , "" );
       break;
       break;


358行目: 401行目:
     case 45: // case 44 の <!--␣ XMLコメント ␣--> をアンコメントする
     case 45: // case 44 の <!--␣ XMLコメント ␣--> をアンコメントする
       // 選択範囲内のコメントマークをすべて削除
       // 選択範囲内のコメントマークをすべて削除
       var dc = st.replace( / ?<!-- ?| ?-- *> ?/g , "" );
       var dc = st.replace( / ?<!-+ ?| ?-+ *> ?/g , "" );
       // 選択範囲に <!-- XMLコメント --> がなかった場合 undo 履歴を残さない
       // 選択範囲に <!-- XMLコメント --> がなかった場合 undo 履歴を残さない
       if ( dc == st ) {
       if ( dc == st ) {
         if ( pos ) { s.SetActivePos( pos ); Quit(); }
         if ( pos ) {
         break;
          s.SetActivePos( pos );
          exit = true;
        }
      }
      else {
         s.Text = dc // アンコメント完了
       }
       }
      // アンコメント完了
      s.Text = dc
       break;
       break;


375行目: 421行目:


   // 選択範囲を復元(選択範囲を 移動/コピー/切り取り しやすいように末尾改行まで含める)
   // 選択範囲を復元(選択範囲を 移動/コピー/切り取り しやすいように末尾改行まで含める)
   s.SetActivePos( s.GetActivePos() + 1 );
   if ( ! exit ) {
  s.SetAnchorPoint( mePosLogical, 1, ay );
    s.SetActivePos( s.GetActivePos() + 1 );
    s.SetAnchorPoint( mePosLogical, 1, ay );


  // 選択範囲の中身が ^\n のみなら選択解除
    // 選択範囲の中身が ^\n のみなら選択解除
  if ( s.Text.match( /^\n$/g ) ) s.SetActivePos( s.GetAnchorPos() );
    if ( s.Text.match( /^\n$/g ) )  
 
      s.SetActivePos( s.GetAnchorPos() );
   ScrollX = sx; ScrollY = sy; // スクロール位置を復元
  }
   ScrollX = sx; ScrollY = sy; // スクロール位置を復元
   Redraw = true;
   Redraw = true;
}
}
390行目: 438行目:
function insertQuote( arg1, arg2 ) {
function insertQuote( arg1, arg2 ) {
   var a = arg1.split( "\n" );
   var a = arg1.split( "\n" );
   for ( var i = 0; i < a.length; i ++ )
   for ( var i = 0; i < a.length; i ++ ) {
     a[i] = arg2 + a[i];
     a[i] = arg2 + a[i];
  }
   return a.join( "\n" );
   return a.join( "\n" );
}
}
405行目: 454行目:
   for ( var i = 0; i < a.length; i ++ ) {
   for ( var i = 0; i < a.length; i ++ ) {
     for ( var j = 0; j < q.length; j ++ ) {
     for ( var j = 0; j < q.length; j ++ ) {
       if ( q[j].length == 0 )
       if ( q[j].length == 0 ) {
         continue;
         continue;
      }
       var qt = q[j];
       var qt = q[j];
       if ( a[i].substr( 0, qt.length ) == qt ) {
       if ( a[i].substr( 0, qt.length ) == qt ) {
418行目: 468行目:




/* 関数 CommentOutJS( arg1, arg2, arg3, arg4 ) */
/**
function CommentOutJS( arg1, arg2, arg3, arg4 ) {
* 関数 CommentOutJS( arg0, arg1, arg2, arg3 ) // CommentOutJS( st, p1, p2, ast )
   var a = arg1.split( "\n" ); // 選択範囲 st を "\n" で区切って配列 a に
*
   var id = a[0].search( /[^  \t]/ ); // 先頭行の文字列中で「非空白文字」が最初にあらわれる位置
* ※ 行頭のインデントを維持して字下げされた位置でコメントアウトするパターン(コメントドキュメント向け)
  var blanc = a[0].slice( 0, id ); // 先頭行の行頭の「空白文字列 ␣ 」を取得
*  ・選択範囲内の最小の字下げ位置にあわせる。
   var re = new RegExp( blanc , "" ); // 検索フラグつきの正規表現変数に格納
*  ・基本的に「JS アンコメント」してもレイアウトを保持できるが、
   var _pre = blanc + arg2 + "\n"; // "/*" の前に「空白文字列 ␣ 」、後ろに改行
*    各行の字下げルールが同じである前提なので、半角スペースもタブコードも1文字として数える。
  var _sur = "\n" + blanc + arg3; // " */" の前に改行と「空白文字列 ␣ 」
*  ・空白文字だけの行は空行と見做し、行頭記号 " * " を付けた後ろに空白文字を残さない。
*/
function CommentOutJS( arg0, arg1, arg2, arg3 ) {
 
   var a = arg0.split( "\n" ); // 選択範囲 st を "\n" で区切って配列 a に
   var b = []; // 各行の字下げ数 id を取得する配列
  // 各行の字下げ数からの最小値を取得
  for ( var i = 0; i < a.length; i ++ ) { // 「1行め」から繰りかえし処理
    var id = a[i].search( /[^ \t]/ ); // 字下げの空白文字数を取得(空白行では -1)
    b.push( ( id < 0 ) ? 100000 : id ); // ※ -1 は sort のジャマなのでデタラメな数値に置きかえる
  }
   b.sort( CompareForSort ); // 配列 b を昇順で並びかえ( => 最小値 b[0] だけ使う)
   var blanc = a[0].slice( 0, b[0] ); // 先頭行から最小の字下げ部分の「空白文字 ␣␣ 」を取得


   // 各行の先頭の空白文字列を削除し、「空白文字列」と ast = " * " を追加する
   // 字下げ位置に中間行の行頭記号 " * " を追加
   for ( var i = 0; i < a.length; i ++ )
   for ( var i = 0; i < a.length; i ++ ) { // 「1行め」から繰りかえし処理
     a[i] = blanc + arg4 + a[i].replace( re , "" );
     if ( a[i].match( /^[ \t]*$/ ) ) { // 空白行
   // 各行を "\n" で区切って連結し、先頭に _pre = "/*\n"、末尾に _sur = "\n␣ */" を付け足す
      a[i] = blanc + arg3;
   return _pre + a.join( "\n" ) + _sur;
    }
    else {
      a[i] = blanc + arg3 + a[i].slice( b[0] );
    }
  }
   // 各行を "\n" で区切って連結し、接頭辞と接尾辞を付け足す
  var prefix = blanc + arg1 + "\n"; // 接頭辞 "/*" の前に「字下げ ␣␣ 」、後ろに改行
  var suffix = "\n" + blanc + arg2; // 接尾辞 " */" の前に改行と「字下げ ␣␣ 」
   return prefix + a.join( "\n" ) + suffix;
}
}




/* *関数 deleteComment( arg1, arg2 ) */
  /**
  /*  
  * 関数 DeleteComment( arg1, arg2 )
  *  
   * 複数のコメントブロックが選択範囲内にある場合、最初のコメントブロックしかアンコメントしない
   * 複数のコメントブロックが選択範囲内にある場合、最初のコメントブロックしかアンコメントしない
   * 引数 arg2 は 配列[ "接頭辞" , "接尾辞" , "中間行の行頭記号" ] で、
   * 引数 arg2 は 配列[ "接頭辞" , "接尾辞" , "中間行の接頭辞" ] で、
   *  各要素はあらかじめJSの正規表現で記述されていないとダメ
   *  各要素はあらかじめJSの正規表現で記述されていないとダメ
   */
   */
function deleteComment( arg1, arg2 ) {
function DeleteComment( arg1, arg2 ) {
   var a = arg1.split( "\n" ); // 選択範囲 st を "\n" で区切って配列 a
 
   var a = arg1.split( "\n" ); // 選択範囲 st を "\n" で区切って配列 a に格納する
   var _reg,  _re,  _id,  _a1,  _a2;
   var _reg,  _re,  _id,  _a1,  _a2;
   var _hit = false,  _line0 = 0,  _line1 = a.length; // 初期化の必要そうな変数
   var _hit = false,  _line0 = 0,  _line1 = a.length; // 初期化の必要そうな変数


   // コメントの接頭辞と接尾辞を検索・置換する
   // コメントの接尾辞と接尾辞を検索・置換する
   for ( var j = 0; j < 2; j ++ ){
   for ( var j = 0; j < 2; j ++ ){
     if ( arg2[j] == "" ) // 接頭辞 p[0], 接尾辞 p[1] が定義されていないならスルー
     if ( arg2[j] == "" ) { // 接頭辞 p[0], 接尾辞 p[1] が定義されていないならスルー
       continue;
       continue;
     // 先頭行から接頭辞 p[0], 接頭辞 p[1] を検索し、置換処理(削除)する
     }
     for ( var i = _line0; i < a.length; i ++ ) {
     for ( var i = _line0; i < a.length; i ++ ) { // 先頭行からループ処理
       _reg = RegExp( arg2[j] , "" );
       _reg = new RegExp( arg2[j] , "" ); // 接頭辞 p[0], 接頭辞 p[1] の検索用正規表現変数
       if ( a[i].match( _reg ) ) {
       if ( a[i].match( _reg ) ) { // ヒットしたら
         a[i] = a[i].replace( _reg , "" );
         a[i] = a[i].replace( _reg , "" ); // 置換処理(削除)
        // 接頭辞を処理したら接尾辞の処理へ
         if ( j == 0 ) {
         if ( j == 0 ) {
           _hit = true; // ヒットフラグ = true
           _hit = true; // ヒットフラグ = true
           _line0 = i; // 接頭辞がヒットした行
           _line0 = i; // 接頭辞がヒットした行
           break;
           break; // 接頭辞を処理したら接尾辞の処理へ
         }
         }
        // 接尾辞を処理したらループを抜ける
         else { // ( j == 1 )
         else { // ( j == 1 )
           _line1 = i; // 接尾辞がヒットした行
           _line1 = i; // 接尾辞がヒットした行
           break;
           break; // 接尾辞を処理したらループを抜ける
         }
         }
       }
       }
471行目: 541行目:


   // 中間行の行頭記号 p[2] (アスタリスク、中黒)を検索・置換する
   // 中間行の行頭記号 p[2] (アスタリスク、中黒)を検索・置換する
  // 行頭空白文字の後ろの2文字までを置換対象にする |^  a*bcdefg には誤爆しないはず
   if ( arg2[2].length ) {
   if ( arg2[2].length ) {
     // 接頭辞(よりも前)の行と接尾辞(よりも後ろ)の行は、検索・置換の対象外にする
     // 接頭辞(よりも前)の行と接尾辞(よりも後ろ)の行は、検索・置換の対象外にする
     for ( var i = ( _hit ) ? _line0 + 1 : 0; i < _line1; i ++ ) {
     for ( var i = ( _hit ) ? _line0 + 1 : 0; i < _line1; i ++ ) {
       _re = RegExp( "^[  \\t]*" + arg2[2] , "" );
       _re = new RegExp( "^[  \\t]*" + arg2[2] , "" ); // 行頭空白+行頭記号 検索用の正規表現変数
      // 行頭記号がヒットしたら行頭空白文字の後ろの2文字までを置換処理する
       if ( a[i].match( _re ) ) { // 行頭記号がヒットしたら
       if ( a[i].match( _re ) ) {
         _id = a[i].indexOf( a[i].match( /[^  \t]/ ) ) + 2; // 置換範囲(空白文字数+2文字)
         _id = a[i].indexOf( a[i].match( /[^  \t]/ ) ) + 2;
         _reg = new RegExp( arg2[2] , "" ); // 行頭記号 置換用の正規表現変数
         _reg = RegExp( arg2[2] , "" );
         _a1 = a[i].slice( 0, _id ).replace( _reg , "" ); // 空白文字の後ろの2文字までを置換
         _a1 = a[i].slice( 0, _id ).replace( _reg , "" );
         _a2 = a[i].slice( _id ); // 行頭記号よりも後ろの文字列は置換対象にしない
         _a2 = a[i].slice( _id );
         a[i] = _a1 + _a2;
         a[i] = _a1 + _a2;
       }
       }
     }
     }
   }
   }
   // 各行を "\n" で区切って連結しなおす
   return a.join( "\n" ); // 各行を "\n" で区切って連結しなおす
   return a.join( "\n" );
}
 
 
/* 以下の関数は『sort メソッド (Array) (JavaScript) | MSDN』より
   https://msdn.microsoft.com/ja-jp/library/4b4fbfhk%28v=vs.94%29.aspx */
// Sorts array elements in ascending order numerically.
// sort メソッドデフォルトの ascii 昇順 (1, 10, 2, 20) ではなく、数値の大きさ (1, 2, 10, 20) でソート
function CompareForSort( first, second ) {
  if ( first == second )
    return 0;
  if ( first < second )
    return -1;
  else // ( first > second )
    return 1;  
}
}
/* 以下の関数は『JavaScript/正規表現 - Wikibooks』より
  https://ja.wikibooks.org/wiki/JavaScript/正規表現
  https://ja.wikibooks.org/wiki/JavaScript/%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE */
// PerlのquotemetaやRubyのRegExp.quoteのように、()や[]など正規表現のメタ文字と解釈される可能性のある文字をエスケープして返す関数は、Stringオブジェクトのreplaceメソッドを使用して簡単に作成することができます。
function Quote( str ) {
    return str.replace( /\W/g, function( $0 ) {
        return '\\' + $0;
    } );
};
</source>
</source>



2019年4月7日 (日) 15:02時点における版

引用符/コメント ポップアップメニュー.png


引用符/コメント

引用の追加」マクロのコードを改変しました。

  • ポップアップメニューから任意の 引用マーク箇条書きの行頭記号行コメントの記号 などの種類を選択して、選択範囲をふくむの各行の先頭に追加します。
    選択範囲がないばあいはカーソル行の先頭に挿入します。
  • 行番号のドラッグでの複数行選択やトリプルクリックでの行選択などで末尾改行が含まれているばあい、さいごの改行を無視します。
  • 行頭に挿入する文字列として クリップボード の文字列データや 入力ダイアログ で指定した文字列を使用することもできます。
・「クリップボード」の機能は「行の先頭に貼り付け」マクロとおなじものです。

・「任意の文字列」は改行コードやタブ文字も記述できますが、それぞれ入力ダイアログで「\\\n」と「\\\t」で入力されたもの (注:¥記号3つ) を改行コードとタブ文字に置換するようにしてあります。
※「\n」や「\t」と記述したばあい、そのままの文字列として「\n」や「\t」が返されます。


挿入/削除できる引用マークやコメントマーク

  • 各行の先頭にメタ記号を追加
  • がついたものは、半角スペースつきで記号を挿入します。
  • 「1つ削除」/「すべて削除」では、記号のうしろの半角スペースの有無を区別しません。
  • 」中黒 の半角差分の 「」半角カナの中黒(U+FF65)と、「·」欧文用ユニコード文字のビュレット(U+00B7) を削除対象として追加してあります。
> ␣ 		(メール 引用マーク)
>> 		(BBS アンカー記号)

 		(箇条書き 全角中黒)
␣ * ␣ 		(箇条書き アスタリスク)
- ␣ 		(箇条書き 半角ハイフンマイナス)
 		(注意書き ※印)

␣ 		(半角スペース)
☐ 		(全角スペース)
› 		(TABコード)
⏎ 		(空行: 改行コード) ※削除コマンドからの削除不可

// ␣ 		(C系, JavaScript コメント)
# ␣ 		(Perl, Ruby, Python コメント)
; ␣ 		(INI コメント)
' ␣ 		(VB コメント)
-- ␣ 		(SQL コメント)
: ␣ 		(MS-DOS ラベル)
:: ␣ 		(BAT コメント)
REM ␣ 		(BAT コメント)


※以下の3種は、「1つ削除」/「すべて削除」ではアンコメントされません。
 ポップアップメニュー内にそれぞれ専用の "アンコメント" コマンドがあります)


  • 選択範囲の行全体をひとまとめでコメントアウト
<!-- (XML コメント) -->

/* (C系, JavaScript コメント) */
  • 選択範囲の行全体を複数行形式でコメントアウト(※字下げ位置でコメントアウトします)
/*
 *  (C系, JavaScript ブロックコメント)
 */


ポップアップメニューの既存の項目を減らしたいばあいは、ソースコード内の "m.Add( … );" の行を // でコメントアウトするとその項目をメニューから隠せます。
ただし、メニューから隠れるだけで、「1つ削除」/「すべて削除」の対象から除外されるわけではありません。

ダウンロード


ダウンロード: 「メディア:引用符/コメント.zip」(アイコン入り)

  • 第三版: 2019/04/07 (コマンドを追加: 空行の追加、任意の文字列の削除)
  • 第二版: 2018/10/30
  • 第一版: 2018/10/28


ソースコード

#title = "引用符を追加..."
#tooltip = "引用符/コメントマークを追加・削除"
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",96

// 選択範囲の行頭に引用符/コメントマークを追加・削除する
// 公式wikiのマクロライブラリの「引用の追加」を改造した
// (Last modified: 2019/04/07)

/*
 * --------------------------------------------------
 * 引用の追加		( => 2018/10/14 公開停止)
 * Orginal Copyright (c) Kuro. All Rights Reserved.
 * www:    http://www.haijin-boys.com/
 * --------------------------------------------------
 * Modified by sukemaru
 * 「引用符を追加」または「引用符/コメント」
 * --------------------------------------------------
 */

// 引用符の種類
var q = new Array( "" ,		// 以下 r = 1~10、11~20、21~30、31~ の ID 順
  "> " ,  "・" ,  " * " ,  "- " ,  " " ,  " " ,  "\t" ,  "// " ,  "# " ,  "; " ,
  "' " ,  "-- " ,  ": " ,  ":: " ,  "REM " ,  "※" ,  "" ,  "" ,  ">> " ,  ">>" ,
  ">" ,  "・ " ,  "・" ,  "· " ,  "·" ,  "* " ,  "*" ,  "--" ,  "-" ,  "//" ,
  "#" ,  ";" ,  "'" ,  "::" ,  ":" );
  
  /* 
   * ※半角スペースの有無で「1つ削除/すべて削除」がマッチしなくなるので、
   *   ">> " 以降に半角スペース あり/なし の差分要素を適当に追加してあります。
   * ※半角スペース差分(中黒=ビュレットは全角/半角差分も)の変更は、配列内の編集ではなく
   *   ポップアップメニュー項目のID(番号)変更で対応しないと、「1つ削除/すべて削除」が効かなくなります
   * (同一文字をふくむ要素は文字列の長いものが先に置かれていないとダメ)。
   * 「・」は半角カナの中黒(U+FF65)、「·」は欧文用ユニコード文字のビュレット(U+00B7)
   */


// ポップアップメニューの項目とID
var m = CreatePopupMenu();
  // m.Add( "ラベル", r ); の各行は、任意に上下移動(並べ替え)してよいが、
  // r の数値は上の配列 q の並び順やテキスト変換処理の case r: に対応しているので変更しないこと!

m.Add( "?	任意の文字列 (&E)...", 50 );
m.Add( "	クリップボード (&C)", 40 );
m.Add( "", 0, meMenuSeparator );
m.Add( "> 	> メール引用符 (&>)", 1 );
m.Add( ">>	>> BBS アンカー (&>)", 20 );
m.Add( "・	・ 箇条書き (&/)", 2 );
m.Add( " * 	* 箇条書き (&*)", 3 );
m.Add( "- 	- 箇条書き (&-)", 4 );
m.Add( "※	※注意書き (&K)", 16 );
m.Add( "", 0, meMenuSeparator );
m.Add( "␣	半角スペース (&1)", 5 );
m.Add( "⃞	全角スペース (&2)", 6 );
m.Add( "›	タブコード (&T)", 7 );
m.Add( "⏎	空行 (&N)", 52 );
m.Add( "", 0, meMenuSeparator );
m.Add( "\/\/ 	JS・C コメント (&J)", 8 );
m.Add( "# 	Perl コメント (&P)", 9 );
m.Add( "; 	INI コメント (&I)", 10 );
m.Add( "’ 	VB コメント (&V)", 11 );
m.Add( "-- 	SQL コメント (&S)", 12 );
m.Add( ": 	MS-DOS ラベル (&M)", 13 );
m.Add( ":: 	BAT コメント (&B)", 14 );
m.Add( "REM 	BAT コメント (&R)", 15 );
m.Add( "", 0, meMenuSeparator );
m.Add( "●	1つ削除 (&D)", 17 );
m.Add( "●	すべて削除 (&A)", 18 );
m.Add( "", 0, meMenuSeparator );
m.Add( "?	行頭から任意の文字列を削除 (&Q)...", 51 );
m.Add( "", 0, meMenuSeparator );
m.Add( "/*  *  */	JS コメントアウト 2 (&J)", 42 );
m.Add( "/*  */	JS コメントアウト 1  (&J)", 41 );
m.Add( "	JS アンコメント (&U)", 43 );
m.Add( "", 0, meMenuSeparator );
m.Add( "<!--  -->	XML コメントアウト (&X)", 44 );
m.Add( "	XML アンコメント (&L)", 45 );
// m.Add( "", 0, meMenuSeparator );
// m.Add( "キャンセル", 0 );		// Escキーでキャンセルできるのでアクセラレータなし

// ポップアップメニューの表示
// m.Track(0); ならキャレット位置、m.Track(1); ならカーソル位置にサブメニューがポップアップ
var r = m.Track( mePosMouse = 1 );

if ( r > 0 ) {
  Redraw = false;
  var sx = ScrollX, sy = ScrollY;	// スクロール位置を保存
  var s = document.selection;
  
  // 選択範囲がないときのカーソル位置を取得
  // pos は「マクロ実行前に選択範囲なし」フラグとしても使う
  var pos = s.IsEmpty ? s.GetActivePos() : "";
  // 選択範囲を取得する
  var ax = s.GetTopPointX( mePosLogical );
  var ay = s.GetTopPointY( mePosLogical );
  var bx = s.GetBottomPointX( mePosLogical );
  var by = s.GetBottomPointY( mePosLogical );
  // 選択範囲の末尾が行頭にあるときの調整
  if ( ay != by && bx == 1 && document.Text.charAt( s.GetActivePos() ) )
    by --;
  // 選択範囲を拡張して確定する
  s.SetActivePoint( mePosLogical, 1, by );
  s.EndOfLine( false, mePosLogical );
  s.SetAnchorPoint( mePosLogical, 1, ay );

  // 選択範囲の文字列を取得
  var st = s.Text;	// 拡張された行全体のテキスト
  var exit = false;	// Quit の代用フラグ

  // IDごとのテキスト変換処理
  switch ( r ) {

    case 1:  case 2:  case 3:  case 4:  case 5:  case 6:  case 7:  case 8:  case 9:  case 10: 
    case 11:  case 12:  case 13:  case 14:  case 15:  case 16:  case 20:
      // 引用符を追加
      s.Text = insertQuote( st, q[r] );

      /* とりあえずコメントアウトしておく */ 
//       // マクロ実行前に選択範囲がなかった場合は、カーソルを元の位置に戻す
//       if ( pos ) {
//         s.SetActivePos( pos + q[r].length );
//         exit = true;
//       }
      break;


    case 52:
      // 空行を挿入	※空行は削除コマンドの削除対象に含まれない
      var nl = "\n";			// 空改行
      s.Text = InsertQuote( st, nl );	// 各行の先頭に追加
      if ( pos ) {
        s.SetActivePos( pos + nl.length );
        exit = true;
      }
      break;


    case 17:
      // 各行の先頭の引用符/コメントマークを1つ削除
      var dt = deleteQuote( st );
      if ( dt == st ) {
        if ( pos ) {
          s.SetActivePos( pos );
          exit = true;
        }
        break;
      }
      else {
        s.Text = dt;	// 削除完了
        if ( pos && dt.length ) {
          s.SetActivePos( pos - ( st.length - dt.length ) );
          exit = true;
        }
        break;
      }


    case 18:
      var org = st;
      // 各行の先頭の引用符/コメントマークをすべて削除
      for ( var i = 0; i < 40; i ++ ) {	// st を最大40回 deleteQuote() でループ処理
        var dt = deleteQuote( st );
        if ( dt == st ) {
          break;
        }
        else {
          st = dt;	// deleteQuote() 処理したテキスト dt を st に再代入
        }
      }
      if ( dt == org ) {
        if ( pos ) {
          s.SetActivePos( pos );
          exit = true;
        }
        break;
      }
      else {
        s.Text = dt;	// 削除完了
        if ( pos && dt.length) {
          s.SetActivePos( pos - ( org.length - dt.length ) );
          exit = true;
        }
        break;
      }


    case 40:
      // クリップボードのテキストデータを取得して、各行の先頭に追加
      var cb = ClipboardData.GetData();
      if ( cb ) {
        s.Text = InsertQuote( st, cb );
      }
      if ( pos ) {
        s.SetActivePos( pos + cb.length );
        exit = true;
      }
      break;


    /* 任意の文字列 */
    case 50:
      // ダイアログのテキスト入力フィールドから文字列を取得
      // 文字コードには非対応(入力されたままの文字列を返す)
      var p = Prompt(
        "前につける文字列:\t改行=\\\\\\n ; タブ=\\\\\\t  (注:¥記号3つ)", ""
      ).replace( /\\\\\\n/g , "\n" ).replace( /\\\\\\t/g , "\t" );
      if ( p ) {
        s.Text = InsertQuote( st, p );		// 各行の先頭に追加
      }
      if ( pos ) {
        s.SetActivePos( pos + p.length );
        exit = true;
      }
      break;



    /* 任意の文字列を削除 */
    case 51:
      // ダイアログのテキスト入力フィールドから文字列を取得
      var p = Prompt(
        "行頭から削除する文字列:\tタブ=\\\\\\t  (注:¥記号3つ)", ""
      ).replace( /\\\\\\t/g , "\t" );
      var reg = new RegExp( "^" + Quote( p ) , "gm" );
      // var reg = new RegExp( "^" + p.replace( /[$()*+.?[\\\]^{|}]/g, "\\$&" ) , "gm" );
      dt = st.replace( reg , "" );	// 各行の先頭の指定文字列を削除

      if ( dt == st ) {
        if ( pos ) {
          s.SetActivePos( pos );
          exit = true;
        }
        break;
      }
      else {
        s.Text = dt;	// 削除完了
        if ( pos && dt.length ) {
          s.SetActivePos( pos - ( st.length - dt.length ) );
          exit = true;
        }
      }
      break;


    case 41:  case 44:
      // 「引用の追加」では行単位に拡張した選択範囲をまとめてコメントアウトするので、
      // 行の中間部分だけをコメントアウトするなら「カッコで囲う」マクロを使うこと。
      var p1,  p2;
      if ( r == 41 ) {			// /* JavaScript コメントアウト1 */ 
        p1 = "/* ";
        p2 = " */";
      }
      else {	// ( r == 44 )		// <!-- XML コメントアウト --> 
        p1 = "<!-- ";
        p2 = " -->";
      }
      s.Text = p1 + st + p2;	// 選択範囲 st をコメントアウト
      if ( ! st ) {		// 空行のばあい
        s.SetActivePos( pos + p1.length );	// カーソルをコメント枠のなかに移動
        exit = true;
      }
      break;


    case 42:
      /** 
       * ※ 行頭の字下げされた位置でコメントアウトする(コメントドキュメント向け)
       * ・選択範囲内の最小の字下げ位置にあわせる。
       * ・基本的に「JS アンコメント」してもレイアウトを保持できるが、
       *   各行の字下げルールが同じである前提なので、半角スペースもタブコードも1文字として数える。
       * ・空白文字だけの行は空行と見做し、行頭記号 " * " を付けた後ろに空白文字を残さない。
       * ・「引用符/コメント」では行単位でコメントアウトするので、
       *   行の中間部分だけをコメントアウトするなら「カッコで囲う」マクロを使うこと。
       */
      var p1 = "/**",  p2 = " */",  ast = " * ",  nl = "\n";
      s.Text = CommentOutJS( st, p1, p2, ast );	// 選択範囲全体を /* *コメントアウト */

      if ( ! st ) {	// マクロ実行前に空行だった場合はカーソルをコメント枠のなかに移動
        s.SetActivePos( pos + ( p1 + nl + ast ).length  );
        exit = true;
      }
      // カーソルを接頭辞のうしろに移動
      else {
        s.SetActivePoint( mePosLogical, 1, ay );
        s.EndOfLine();
        exit = true;
      }
      break;


    case 43:	// case 41 & 42 の JavaScript コメント をアンコメントする
      /* 
       * ・この文章のようなインデントされたコメントブロックの字下げ位置を考慮するが、
       *   先頭のコメントマーク "/*" のスラッシュの前の字下げ(空白部分)が基準となる。
       * ・複数のコメントブロックが選択範囲内にある場合、最初のコメントブロックしかアンコメントしない。
       * ・中間行の行頭記号はアスタリスク「*」と全角/半角の中黒「・」「・」を削除対象とする。
       *   (行頭記号と前後の半角スペース各1を削除する)
       * ・JSコメントでない箇条書き文で実行した場合も行頭記号を削除する。
       *
       * ▼ 注意事項 ▼
       * ・選択範囲がJSコメントか箇条書き文であれば、選択範囲の先頭/末尾の余計な空白行も削除する。
       * ・中間行の末尾空白文字も削除し、空白行ならインデント部分も削除する。
       * 		
       * コメントマークは 配列 [ "接頭辞" , "接尾辞" , "中間行の行頭記号" ] で用意する。
       *     各要素はあらかじめJSの正規表現で記述しておくこと。
       */
      // コメントの "接頭辞" と "接尾辞" と "行頭記号" を正規表現で配列に格納
      var p = new Array( "\\/\\*+ ?" , " ?\\*\\/" , " ?(\\*|[・・]) ?" );
      // コメントアウトを解除
      var dc = DeleteComment( st, p )
      // 選択範囲に /* JSコメント */ がなかった場合 undo 履歴を残さない
      if ( dc == st ) {	
        if ( pos ) {
          s.SetActivePos( pos );
          exit = true;
        }
      }
      else {
        // 先頭と末尾の空行を削除してアンコメント完了
        // ※[\s ]* だけだと消したくない部分まで消されるので \n をつける
        s.Text = dc.replace( /[\s ]*$/gm , "" )
                   .replace( /^[\s ]*\n|\n[\s ]*$|/g , "" );
      }
      break;


    case 45:	// case 44 の <!--␣ XMLコメント ␣--> をアンコメントする
      // 選択範囲内のコメントマークをすべて削除
      var dc = st.replace( / ?<!-+ ?| ?-+ *> ?/g , "" );
      // 選択範囲に <!-- XMLコメント --> がなかった場合 undo 履歴を残さない
      if ( dc == st ) {
        if ( pos ) {
          s.SetActivePos( pos );
          exit = true;
        }
      }
      else {
        s.Text = dc	// アンコメント完了
      }
      break;


    default:
      break;

  }

  // 選択範囲を復元(選択範囲を 移動/コピー/切り取り しやすいように末尾改行まで含める)
  if ( ! exit ) {
    s.SetActivePos( s.GetActivePos() + 1 );
    s.SetAnchorPoint( mePosLogical, 1, ay );

    // 選択範囲の中身が ^\n のみなら選択解除
    if ( s.Text.match( /^\n$/g ) ) 
      s.SetActivePos( s.GetAnchorPos() );
  }
  ScrollX = sx;  ScrollY = sy;		// スクロール位置を復元
  Redraw = true;
}


/* 関数 insertQuote( arg1, arg2 ) */
  // Kuro版まま
function insertQuote( arg1, arg2 ) {
  var a = arg1.split( "\n" );
  for ( var i = 0; i < a.length; i ++ ) {
    a[i] = arg2 + a[i];	
  }
  return a.join( "\n" );
}


/* 関数 deleteQuote( arg1 ) */
  // 各行ごとに、配列 q の並び順で最初にマッチした引用符を削除
  // Kuro版から
  //   ①変数名を変更
  //   ②最後の if 文の「break;」を補遺し、「1つ削除」の不具合を修正
function deleteQuote( arg1, arg2 ) {
  var a = arg1.split( "\n" );
  for ( var i = 0; i < a.length; i ++ ) {
    for ( var j = 0; j < q.length; j ++ ) {
      if ( q[j].length == 0 ) {
        continue;
      }
      var qt = q[j];
      if ( a[i].substr( 0, qt.length ) == qt ) {
        a[i] = a[i].substr( qt.length );
        break;
      }
    }
  }
  return a.join( "\n" );
}


/**
 * 関数 CommentOutJS( arg0, arg1, arg2, arg3 )	// CommentOutJS( st, p1, p2, ast )
 *
 * ※ 行頭のインデントを維持して字下げされた位置でコメントアウトするパターン(コメントドキュメント向け)
 *   ・選択範囲内の最小の字下げ位置にあわせる。
 *   ・基本的に「JS アンコメント」してもレイアウトを保持できるが、
 *     各行の字下げルールが同じである前提なので、半角スペースもタブコードも1文字として数える。
 *   ・空白文字だけの行は空行と見做し、行頭記号 " * " を付けた後ろに空白文字を残さない。
 */
function CommentOutJS( arg0, arg1, arg2, arg3 ) {

  var a = arg0.split( "\n" );	// 選択範囲 st を "\n" で区切って配列 a に
  var b = [];			// 各行の字下げ数 id を取得する配列
  // 各行の字下げ数からの最小値を取得
  for ( var i = 0; i < a.length; i ++ ) {	// 「1行め」から繰りかえし処理
    var id = a[i].search( /[^ \t]/ );		// 字下げの空白文字数を取得(空白行では -1)
    b.push( ( id < 0 ) ? 100000 : id );		// ※ -1 は sort のジャマなのでデタラメな数値に置きかえる
  }
  b.sort( CompareForSort );		// 配列 b を昇順で並びかえ( => 最小値 b[0] だけ使う)
  var blanc = a[0].slice( 0, b[0] );	// 先頭行から最小の字下げ部分の「空白文字 ␣␣ 」を取得

  // 字下げ位置に中間行の行頭記号 " * " を追加
  for ( var i = 0; i < a.length; i ++ ) {	// 「1行め」から繰りかえし処理
    if ( a[i].match( /^[ \t]*$/ ) ) {		// 空白行
      a[i] = blanc + arg3;
    }
    else {
      a[i] = blanc + arg3 + a[i].slice( b[0] );
    }
  }
  // 各行を "\n" で区切って連結し、接頭辞と接尾辞を付け足す
  var prefix = blanc + arg1 + "\n";	// 接頭辞 "/*" の前に「字下げ ␣␣ 」、後ろに改行
  var suffix = "\n" + blanc + arg2;	// 接尾辞 " */" の前に改行と「字下げ ␣␣ 」
  return prefix + a.join( "\n" ) + suffix;
}


  /** 
   * 関数 DeleteComment( arg1, arg2 )
   * 
   * 複数のコメントブロックが選択範囲内にある場合、最初のコメントブロックしかアンコメントしない
   * 引数 arg2 は 配列[ "接頭辞" , "接尾辞" , "中間行の接頭辞" ] で、
   *   各要素はあらかじめJSの正規表現で記述されていないとダメ
   */
function DeleteComment( arg1, arg2 ) {

  var a = arg1.split( "\n" );		// 選択範囲 st を "\n" で区切って配列 a に格納する
  var _reg,  _re,  _id,  _a1,  _a2;
  var _hit = false,  _line0 = 0,  _line1 = a.length;	// 初期化の必要そうな変数

  // コメントの接尾辞と接尾辞を検索・置換する
  for ( var j = 0; j < 2; j ++ ){
    if ( arg2[j] == "" ) { 	// 接頭辞 p[0], 接尾辞 p[1] が定義されていないならスルー
      continue;
    }
    for ( var i = _line0; i < a.length; i ++ ) {	// 先頭行からループ処理
      _reg = new RegExp( arg2[j] , "" );	// 接頭辞 p[0], 接頭辞 p[1] の検索用正規表現変数
      if ( a[i].match( _reg ) ) {		// ヒットしたら
        a[i] = a[i].replace( _reg , "" );	// 置換処理(削除)
        if ( j == 0 ) {
          _hit = true;		// ヒットフラグ = true
          _line0 = i;		// 接頭辞がヒットした行
          break;		// 接頭辞を処理したら接尾辞の処理へ
        }
        else {	// ( j == 1 )
          _line1 = i;		// 接尾辞がヒットした行
          break;		// 接尾辞を処理したらループを抜ける
        }
      }
    }
  }

  // 中間行の行頭記号 p[2] (アスタリスク、中黒)を検索・置換する
  // 行頭空白文字の後ろの2文字までを置換対象にする	|^   a*bcdefg には誤爆しないはず
  if ( arg2[2].length ) {
    // 接頭辞(よりも前)の行と接尾辞(よりも後ろ)の行は、検索・置換の対象外にする
    for ( var i = ( _hit ) ? _line0 + 1 : 0; i < _line1; i ++ ) {
      _re = new RegExp( "^[  \\t]*" + arg2[2] , "" );	// 行頭空白+行頭記号 検索用の正規表現変数
      if ( a[i].match( _re ) ) {				// 行頭記号がヒットしたら
        _id = a[i].indexOf( a[i].match( /[^  \t]/ ) ) + 2;	// 置換範囲(空白文字数+2文字)
        _reg = new RegExp( arg2[2] , "" );			// 行頭記号 置換用の正規表現変数
        _a1 = a[i].slice( 0, _id ).replace( _reg , "" );	// 空白文字の後ろの2文字までを置換
        _a2 = a[i].slice( _id );	// 行頭記号よりも後ろの文字列は置換対象にしない
        a[i] = _a1 + _a2;
      }
    }
  }
  return a.join( "\n" );	// 各行を "\n" で区切って連結しなおす
}


/* 以下の関数は『sort メソッド (Array) (JavaScript) | MSDN』より
  https://msdn.microsoft.com/ja-jp/library/4b4fbfhk%28v=vs.94%29.aspx */ 
// Sorts array elements in ascending order numerically.
// sort メソッドデフォルトの ascii 昇順 (1, 10, 2, 20) ではなく、数値の大きさ (1, 2, 10, 20) でソート
function CompareForSort( first, second ) {
  if ( first == second )
    return 0;
  if ( first < second )
    return -1;
  else	// ( first > second )
    return 1; 
}


 /* 以下の関数は『JavaScript/正規表現 - Wikibooks』より
  https://ja.wikibooks.org/wiki/JavaScript/正規表現
  https://ja.wikibooks.org/wiki/JavaScript/%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE */
// PerlのquotemetaやRubyのRegExp.quoteのように、()や[]など正規表現のメタ文字と解釈される可能性のある文字をエスケープして返す関数は、Stringオブジェクトのreplaceメソッドを使用して簡単に作成することができます。
function Quote( str ) {
    return str.replace( /\W/g, function( $0 ) {
        return '\\' + $0;
    } );
};

謝辞

Kuro 氏の「引用の追加」は Mery のマクロの勉強をはじめたきっかけになったマクロです。
文字列操作、ポップアップメニューの使い方、JavaScript の配列や関数の使い方、if 文、switch 文、for 文、etc... 入門者の "とっかかり" として最適なマクロだったとおもいます(※もとの「引用の追加」は、このページのものよりもシンプル&コンパクトなマクロでした)。
たいへん有用なマクロを作ってくださった Kuro 氏に感謝申し上げます。

残念ながら「引用の追加」マクロは公開停止になってしまったので、この度わたしの手元でカスタマイズしたものを公開させていただきました。
改訂版の公開にご承諾いただいた Kuro 氏に重ねて御礼申し上げます。 (sukemaru)

スポンサーリンク