引用符/コメント

提供:MeryWiki
ナビゲーションに移動 検索に移動

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


引用符/コメント

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

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

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


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

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

  • 各行の先頭にメタ記号を追加
  • がついたものは、半角スペースつきで記号を挿入します。
  • 「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(アイコン入り)」

  • 第二版: 2018/10/30
  • 第一版: 2018/10/28


ソースコード

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

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

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

// 引用符の種類
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( "", 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( "/*  *  */	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;
  
  // 選択範囲がないときのカーソル位置を取得
  if ( s.IsEmpty )
    var pos = 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 = document.selection.Text;	// 拡張された行全体のテキスト

  // 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 );  Quit(); }
      break;


    case 17:
      // 各行の先頭の引用符/コメントマークを1つ削除
      var dt = deleteQuote( st );
      // 選択範囲に引用符/コメントマークがなかった場合 undo 履歴を残さない
      if ( dt == st ) {
        if ( pos ) { s.SetActivePos( pos );  Quit(); }
        break;
      }
      // 1つ削除完了
      s.Text = dt;

      // マクロ実行前に選択範囲がなかった場合は、カーソルを元の位置に戻す
      if ( pos && dt.length ) {
        s.SetActivePos( pos - ( st.length - dt.length ) );
        Quit();
      }
      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;
      }
      // 選択範囲に引用符/コメントマークがなかった場合 undo 履歴を残さない
      if ( dt == org ) {
        if ( pos ) { s.SetActivePos( pos );  Quit(); }
        break;
      }
      // すべて削除完了
      s.Text = dt;

      // マクロ実行前に選択範囲がなかった場合は、カーソルを元の位置に戻す
      if ( pos && dt.length) {
        s.SetActivePos( pos - ( org.length - dt.length ) );
        Quit();
      }
      break;


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

      // マクロ実行前に選択範囲がなかった場合は、カーソルを元の位置に戻す
      if ( pos ) { s.SetActivePos( pos + cb.length );  Quit(); }
      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 );  Quit(); }
      break;

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


    case 42:
      /* 
       * ・先頭行の字下げを基準に2行目以降も字下げするが、
       *   先頭行より字下げ量が少ない行はレイアウトが崩れる(「アンコメント」するとさらに崩れる)。
       * ・先頭に最少量の字下げする空白行を置くとレイアウトを維持でき、
       *  「アンコメント」してもレイアウトを保持できる。
       */
      var p1 = "/* ",  p2 = " */",  ast = " * ",  n = "\n";
      // 空行のばあい、コメントマーク /* \n & * & \n */ だけ挿入して、カーソルをコメント枠のなかに移動
      if ( pos ) {
        s.Text =  p1 + n + ast + n + p2;
        s.SetActivePos( pos + ( p1 + n + ast ).length  );
        Quit();
      }
      // 空行以外のばあい、選択範囲全体を /* *コメントアウト */
      s.Text = CommentOutJS( st, p1, p2, ast );
      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 );  Quit(); }
        break;
      }
      // 先頭と末尾の空行を削除してアンコメント完了
      var blank = /[\t  ]*$/gm;
      s.Text = dc.replace( blank , "" ).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 );  Quit(); }
        break;
      }
      // アンコメント完了
      s.Text = dc
      break;


    default:
      break;

  }

  // 選択範囲を復元(選択範囲を 移動/コピー/切り取り しやすいように末尾改行まで含める)
  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( arg1, arg2, arg3, arg4 ) */
function CommentOutJS( arg1, arg2, arg3, arg4 ) {
  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 , "" );	// 検索フラグつきの正規表現変数に格納
  var _pre = blanc + arg2 + "\n";	// "/*" の前に「空白文字列 ␣ 」、後ろに改行
  var _sur = "\n" + blanc + arg3;	// " */" の前に改行と「空白文字列 ␣ 」

  // 各行の先頭の空白文字列を削除し、「空白文字列」と ast = " * " を追加する
  for ( var i = 0; i < a.length; i ++ )
    a[i] = blanc + arg4 + a[i].replace( re , "" );
  // 各行を "\n" で区切って連結し、先頭に _pre = "␣/*\n"、末尾に _sur = "\n␣ */" を付け足す
  return _pre + a.join( "\n" ) + _sur;
}


/* *関数 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;
    // 先頭行から接頭辞 p[0], 接頭辞 p[1] を検索し、置換処理(削除)する
    for ( var i = _line0; i < a.length; i ++ ) {
      _reg = RegExp( arg2[j] , "" );
      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] (アスタリスク、中黒)を検索・置換する
  if ( arg2[2].length ) {
    // 接頭辞(よりも前)の行と接尾辞(よりも後ろ)の行は、検索・置換の対象外にする
    for ( var i = ( _hit ) ? _line0 + 1 : 0; i < _line1; i ++ ) {
      _re = RegExp( "^[  \\t]*" + arg2[2] , "" );
      // 行頭記号がヒットしたら行頭空白文字の後ろの2文字までを置換処理する
      if ( a[i].match( _re ) ) {
        _id = a[i].indexOf( a[i].match( /[^  \t]/ ) ) + 2;
        _reg = RegExp( arg2[2] , "" );
        _a1 = a[i].slice( 0, _id ).replace( _reg , "" );
        _a2 = a[i].slice( _id );
        a[i] = _a1 + _a2;
      }
    }
  }
  // 各行を "\n" で区切って連結しなおす
  return a.join( "\n" );
}

謝辞

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

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

スポンサーリンク