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

提供:MeryWiki
ナビゲーションに移動 検索に移動
(第三版)
(マルチカーソルに対応。ZIP 書庫を更新)
1行目: 1行目:
<div style="float:right">__TOC__</div>
<div style="float:right">
__TOC__
<br>
画像は 2019/04/07 版のものです。<br> 2020/06/23 版では、サブメニューに削除系コマンドをまとめてあります。
</div>
[[ファイル:引用符/コメント_ポップアップメニュー.png|link=]]
[[ファイル:引用符/コメント_ポップアップメニュー.png|link=]]
<br clear=all>




== 引用符/コメント ==
== 引用符/コメント ==
'''引用の追加'''」マクロのコードを改変しました。
Kuro 氏の「'''引用の追加'''」マクロのコードを改変しました。


* ポップアップメニューから任意の '''引用マーク''' や '''箇条書きの行頭記号'''、'''行コメントの記号''' などの種類を選択して、選択範囲をふくむの各行の先頭に追加します。<br> 選択範囲がないばあいはカーソル行の先頭に挿入します。
* ポップアップメニューから任意の '''引用マーク''' や '''箇条書きの行頭記号'''、'''行コメントの記号''' などの種類を選択して、選択範囲をふくむの各行の先頭に追加します。<br> 選択範囲がないばあいはカーソル行の先頭に挿入します。
20行目: 25行目:


* 各行の先頭にメタ記号を追加
* 各行の先頭にメタ記号を追加
: ポップアップメニュー内に表示するラベル(注釈)や並び順をカスタマイズすると、Markdown 用のマークアップなどに活用しやすくなります。
<br>
: 以下は、公開・配布しているソースコードの初期状態で利用できる引用マークやコメントマークです。
:* '''␣''' がついたものは、半角スペースつきで記号を挿入します。
:* '''␣''' がついたものは、半角スペースつきで記号を挿入します。
:*「1つ削除」/「すべて削除」では、記号のうしろの半角スペースの有無を区別しません。
:*「1つ削除」/「すべて削除」では、記号のうしろの半角スペースの有無を区別しません。
29行目: 37行目:
  '''・''' (箇条書き 全角中黒)
  '''・''' (箇条書き 全角中黒)
  ␣ '''*''' ␣ (箇条書き アスタリスク)
  ␣ '''*''' ␣ (箇条書き アスタリスク)
'''*''' ␣ (箇条書き アスタリスク)
  '''-''' ␣ (箇条書き 半角ハイフンマイナス)
  '''-''' ␣ (箇条書き 半角ハイフンマイナス)
  '''※''' (注意書き ※印)
  '''※''' (注意書き ※印)
61行目: 70行目:
  ''' */'''
  ''' */'''


; 削除系コマンド
<br>
* '''1つ削除'''
: 選択範囲内の各行の行頭から、設定項目の「引用符の種類」に登録されている記号や文字列と一致するものを1つずつ削除します (※ 改行記号 <code>"\n"</code> は対象外 ⇒ 「空行を削除」コマンド)。
<br>
* '''すべて削除'''
: 選択範囲内の各行の行頭から、設定項目の「引用符の種類」に登録されている記号や文字列と一致するものをすべて削除します (※ 改行記号 <code>"\n"</code> は対象外)。
<br>
* '''空行を削除'''
: 選択範囲内の空行を削除します。
<br>
* '''行頭の空白文字を削除'''
: 選択範囲内の各行の行頭から、全角/半角空白とタブ文字をすべて削除します。
: ※ このマクロでは、行末の空白文字列の削除はできません ( ⇒ 「[[カッコで囲う]]」マクロや「[[テキスト整形]]」マクロを利用してください)。
<br>
* '''行頭から任意の文字列を削除'''
: 選択範囲内の各行の行頭から、入力ダイアログで指定された文字列を削除します。
<br>
* '''行頭から任意の文字数を削除'''
: 選択範囲内の各行の行頭から、入力ダイアログで指定された文字数だけ削除します。
<br>


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


== ダウンロード ==
== ダウンロード ==
<br>
<br>
ダウンロード: 「[[メディア:引用符/コメント.zip]]」(アイコン入り)
ダウンロード: 「[[ファイル:引用符/コメント.zip]]」(アイコン入り)
* 第三版: 2019/04/07 (コマンドを追加: 空行の追加、任意の文字列の削除)
* 第四版: 2020/06/23
: コマンドを追加: 「任意の文字数を削除」
: マルチカーソル/複数選択範囲に対応
* 第三版: 2019/04/07
: コマンドを追加: 「空行の追加」、「任意の文字列を削除」
* 第二版: 2018/10/30
* 第二版: 2018/10/30
* 第一版: 2018/10/28
* 第一版: 2018/10/28
76行目: 112行目:
#title = "引用符を追加..."
#title = "引用符を追加..."
#tooltip = "引用符/コメントマークを追加・削除"
#tooltip = "引用符/コメントマークを追加・削除"
#icon = "quote2[3].ico"
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",96
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",96


// 選択範囲の行頭に引用符/コメントマークを追加・削除する
/**
// 公式wikiのマクロライブラリの「引用の追加」を改造した
  * -----------------------------------------------------------------------------
// (Last modified: 2019/04/07)
 
/*
  * --------------------------------------------------
  * 引用の追加 ( => 2018/10/14 公開停止)
  * 引用の追加 ( => 2018/10/14 公開停止)
  * Orginal Copyright (c) Kuro. All Rights Reserved.
  * Orginal Copyright (c) Kuro. All Rights Reserved.
  * www:    http://www.haijin-boys.com/
  * www:    http://www.haijin-boys.com/
  * --------------------------------------------------
  * -----------------------------------------------------------------------------
  * Modified by sukemaru
  * Modified by sukemaru (2018/10/28 - 2020/06/23)
  * 「引用符を追加」または「引用符/コメント」
  * 「引用符を追加」または「引用符/コメント」
  * --------------------------------------------------
* 選択範囲の行頭に引用符/コメントマークを追加・削除する
  * -----------------------------------------------------------------------------
* ▼ Kuro 版「引用の追加」マクロからの変更点 ▼
*
* ・ポップアップメニューの体裁を変更した。
* ・引用符の種類を増やした(※ 基本的に「メタ文字+半角スペース」)。
* ・「クリップボード」と「任意の文字列」を追加。
* ・配列に削除用の要素(半角スペースなしのメタ文字のみの差分)も余分に追加した。
*  ※「すべて削除」では、半角スペース、全角スペース、タブ文字での字下げをすべて削除する。
*  ※「クリップボード」、「任意の文字列」、「空行」、「JS/XML コメントアウト」は、
*    「1つ削除//すべて削除」では削除されない。
*
* ・選択範囲があったときには行全体に拡張・復帰するようにした。
* ・選択範囲なしの状態からカーソル行にたいして「追加/1つ削除/すべて削除」を実行したときは、
*  実行前の位置にカーソルを返せるようにした。
* ・「1つ削除」の不具合箇所を修正した。
  */
  */


// 引用符の種類
// ---------- ▼ 設定項目 ▼ ---------- //
var q = new Array( "" , // 以下 r = 1~10、11~20、21~30、31~ の ID 順
 
// ■ 「任意の文字列」で入力した文字列の一時記憶方法
var tagType = 2;
  // 0 : 一時記憶なし
  // 1 : タブ(文書)ごとに一時記憶する(Document.Tag)
  // 2 : ウインドウごとに一時記憶する(Editor.Tag)
  // 3 : すべてのタブとウインドウ共通で一時記憶する(window.Tag)
 
 
// ■ 引用符の種類(追加用)
var q = [ "" , // 以下 r = 1~10、11~20、21~30、31~ の ID 順
   "> " ,  "・" ,  " * " ,  "- " ,  " " ,  " " ,  "\t" ,  "// " ,  "# " ,  "; " ,
   "> " ,  "・" ,  " * " ,  "- " ,  " " ,  " " ,  "\t" ,  "// " ,  "# " ,  "; " ,
   "' " ,  "-- " ,  ": " ,  ":: " ,  "REM " ,  "※" ,  "" ,  "" ,  ">> " ,  ">>" ,
   "' " ,  "-- " ,  ": " ,  ":: " ,  "REM " ,  "※" ,  ">>" ,  "* " ,  "" ,  "" ,
   ">" ,  "" ,  "" ,  "· " ,  "·" ,  "* " ,  "*" ,  "--" ,  "-" ,  "//" ,
   "" ,  "" ,  "" ,  "" ,  "" ,  "" ,  "" ,  "" ,  "" ,  "" ,
   "#" ,  ";" ,  "'" ,  "::" ,  ":" );
   "" ,  "" ,  "" ,  "" ,  "" ,  "" ,  "" ,  "" ,  "" ,  "" ,
 
"" ];
  /*
 
  * ※半角スペースの有無で「1つ削除/すべて削除」がマッチしなくなるので、
  *  ">> " 以降に半角スペース あり/なし の差分要素を適当に追加してあります。
  * ※半角スペース差分(中黒=ビュレットは全角/半角差分も)の変更は、配列内の編集ではなく
  *  ポップアップメニュー項目のID(番号)変更で対応しないと、「1つ削除/すべて削除」が効かなくなります
  * (同一文字をふくむ要素は文字列の長いものが先に置かれていないとダメ)。
  * 「・」は半角カナの中黒(U+FF65)、「·」は欧文用ユニコード文字のビュレット(U+00B7)
  */


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


// ポップアップメニューの項目とID
// ---------- ▲ 設定項目 ▲ ---------- //
 
var d = editor.ActiveDocument;
var s = editor.ActiveDocument.selection;
var m = CreatePopupMenu();
var m = CreatePopupMenu();
if ( d.ReadOnly ) {
  m.Add( " ドキュメントは 書き換え禁止 です & ", 0 );
}
else {
  var cb = ClipboardData.GetData(); // クリップボードのテキストデータを取得
  // ポップアップメニューの項目とID
   // m.Add( "ラベル", r ); の各行は、任意に上下移動(並べ替え)してよいが、
   // m.Add( "ラベル", r ); の各行は、任意に上下移動(並べ替え)してよいが、
   // r の数値は上の配列 q の並び順やテキスト変換処理の case r: に対応しているので変更しないこと!
   // r の数値は上の配列 q の並び順やテキスト変換処理の case r: に対応しているので変更しないこと!


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


// ポップアップメニューの表示
// ポップアップメニューの表示
158行目: 248行目:


if ( r > 0 ) {
if ( r > 0 ) {
   Redraw = false;
   var p;
   var sx = ScrollX, sy = ScrollY; // スクロール位置を保存
  var tagKey = "LineCommentQuote";
   var s = document.selection;
  if ( r === 1010 ) { p = AddStrPrompt( tagType, tagKey ); }
    
  if ( r === 1011 ) { p = DelStrPrompt( tagType, tagKey ); }
   // 選択範囲がないときのカーソル位置を取得
  if ( r === 1012 ) { p = DelByNumPrompt(); } // 「任意の文字数を削除」
   // pos は「マクロ実行前に選択範囲なし」フラグとしても使う
 
   var pos = s.IsEmpty ? s.GetActivePos() : "";
   var sx = ScrollX, sy = ScrollY;
  // 選択範囲を取得する
  // マルチカーソル対応
   var ax = s.GetTopPointX( mePosLogical );
   var arg = [ q, r, p, cb ];
   var ay = s.GetTopPointY( mePosLogical );
  // 選択範囲が1つで矩形選択ではないとき
  if ( ! s.Mode || s.Mode === meModeStream ) {
    LineCommentQuote_Main( arg );
  }
  // 矩形選択または複数選択のとき
  else {
    BeginUndoGroup();
    AddUndo();
    MultiFunction( LineCommentQuote_Main, arg );
    EndUndoGroup();
   }
   ScrollX = sx;  ScrollY = sy;
}
 
 
/**
* 関数 LineCommentQuote_Main( [ q, r, p, cb ] )
* 「引用符/コメント」マクロ
*/
function LineCommentQuote_Main( arg ) {
  var q  = arg[0];
  var r  = arg[1];
  var p  = arg[2];
  var cb = arg[3];
 
   // 選択範囲
  var d = editor.ActiveDocument;
  var s = editor.ActiveDocument.selection;
   var pos = s.IsEmpty ? s.GetActivePos() : -1;
   var tx = s.GetTopPointX( mePosLogical );
   var ty = s.GetTopPointY( mePosLogical );
   var bx = s.GetBottomPointX( mePosLogical );
   var bx = s.GetBottomPointX( mePosLogical );
   var by = s.GetBottomPointY( mePosLogical );
   var by = s.GetBottomPointY( mePosLogical );
   // 選択範囲の末尾が行頭にあるときの調整
   // 選択範囲の末尾が行頭にあるときの調整
   if ( ay != by && bx == 1 && document.Text.charAt( s.GetActivePos() ) )
   if ( ty < by && bx == 1 && d.Text.charAt( s.GetActivePos() ) ) {
     by --;
     by --;
   // 選択範囲を拡張して確定する
  }
   // 選択範囲を拡張
   s.SetActivePoint( mePosLogical, 1, by );
   s.SetActivePoint( mePosLogical, 1, by );
   s.EndOfLine( false, mePosLogical );
   s.EndOfLine( false, mePosLogical );
   s.SetAnchorPoint( mePosLogical, 1, ay );
   s.SetAnchorPoint( mePosLogical, 1, ty );


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


185行目: 306行目:
   switch ( r ) {
   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:
  case 1:  case 2:  case 3:  case 4:  case 5:
      // 引用符を追加
  case 6:  case 7:  case 8:  case 9:  case 10:  
      s.Text = insertQuote( st, q[r] );
  case 11:  case 12:  case 13:  case 14:  case 15:
  case 16:  case 17:  case 18:
    // 各行の先頭に引用符/コメントマークを追加
    s.Text = st.replace( /^/gm, q[r] );
//    /* とりあえずコメントアウトしておく */
//    // マクロ実行前に選択範囲がなかった場合は、カーソルを元の位置に戻す
//    if ( pos > -1 ) {
//      s.SetActivePos( pos + q[r].length );
//      exit = true;
//    }
    break;


      /* とりあえずコメントアウトしておく */  
  // クリップボード
//       // マクロ実行前に選択範囲がなかった場合は、カーソルを元の位置に戻す
  case 1000:
//      if ( pos ) {
    s.Text = st.replace( /^/gm, cb );
//        s.SetActivePos( pos + q[r].length );
    if ( pos > -1 ) { s.SetActivePos( pos + cb.length ); exit = true; }
//        exit = true;
    break;
//      }
      break;


  // 空行を挿入 ※空行は削除コマンドの削除対象に含まれない
  case 1006:
    var n = "\n"; // 空改行
    s.Text = st.replace( /^/gm, n );
    if ( pos > -1 ) { s.SetActivePos( pos + n.length );  exit = true; }
    break;


    case 52:
  // 任意の文字列(テキストボックス)
      // 空行を挿入 ※空行は削除コマンドの削除対象に含まれない
  case 1010:
      var nl = "\n"; // 空改行
    if ( p ) { s.Text = st.replace( /^/gm, p ); }
      s.Text = InsertQuote( st, nl ); // 各行の先頭に追加
    if ( pos > -1 ) { s.SetActivePos( pos + p.length ); exit = true; }
      if ( pos ) {
    break;
        s.SetActivePos( pos + nl.length );
        exit = true;
      }
      break;




    case 17:
  // 任意の文字列を削除
      // 各行の先頭の引用符/コメントマークを1つ削除
  case 1011:
      var dt = deleteQuote( st );
    var reg = new RegExp(
       if ( dt == st ) {
       "^" + p.replace( /[.*+?^=!:${}()|[\]\/\\]/g, "\\$&" )
        if ( pos ) {
    , "gm" );
          s.SetActivePos( pos );
    tmp = st.replace( reg, "" );
          exit = true;
    if ( tmp !== st ) {
        }
       s.Text = tmp;
        break;
      if ( pos > -1 && tmp.length ) {
       }
        s.SetActivePos( pos - ( st.length - tmp.length ) );
      else {
        exit = true;
        s.Text = dt; // 削除完了
        if ( pos && dt.length ) {
          s.SetActivePos( pos - ( st.length - dt.length ) );
          exit = true;
        }
        break;
       }
       }
    }
    else if ( pos > -1 ) { s.SetActivePos( pos );  exit = true; }
    break;


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


 
  // 1つ削除
    case 40:
  case 1013:
      // クリップボードのテキストデータを取得して、各行の先頭に追加
    // 各行ごとに、配列 q の並び順で最初にマッチした引用符/コメントマークを削除
      var cb = ClipboardData.GetData();
    tmp = DeleteQuote( st, q );
      if ( cb ) {
    if ( tmp !== st ) {
        s.Text = InsertQuote( st, cb );
      s.Text = tmp;
      }
       if ( pos > -1 && tmp.length ) {
       if ( pos ) {
         s.SetActivePos( pos - ( st.length - tmp.length ) );
         s.SetActivePos( pos + cb.length );
         exit = true;
         exit = true;
       }
       }
      break;
    }
    else if ( pos > -1 ) { s.SetActivePos( pos );  exit = true; }
    break;


 
  // すべて削除
    /* 任意の文字列 */
  case 1014:
    case 50:
    var org = st; // DeleteQuote() のループ処理前のテキスト
      // ダイアログのテキスト入力フィールドから文字列を取得
    // st を最大40回 DeleteQuote() でループ処理
      // 文字コードには非対応(入力されたままの文字列を返す)
    for ( var i = 0; i < 40; i ++ ) {
      var p = Prompt(
      tmp = DeleteQuote( st, q );
        "前につける文字列:\t改行=\\\\\\n ; タブ=\\\\\\t  (注:¥記号3つ)", ""
       // DeleteQuote() 処理の前後でテキストが一致したらループを抜ける
       ).replace( /\\\\\\n/g , "\n" ).replace( /\\\\\\t/g , "\t" );
      if ( tmp == st ) { break; }
      if ( p ) {
      st = tmp; // DeleteQuote() したテキスト tmp を st に再代入
        s.Text = InsertQuote( st, p ); // 各行の先頭に追加
    }
      }
    if ( tmp !== org ) {
       if ( pos ) {
      s.Text = tmp;
         s.SetActivePos( pos + p.length );
       if ( pos > -1 && tmp.length) {
         s.SetActivePos( pos - ( org.length - tmp.length ) );
         exit = true;
         exit = true;
       }
       }
      break;
     }
 
     else if ( pos > -1 ) { s.SetActivePos( pos ); 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 1015:
    // キャレット位置が空行上のとき
    if ( pos > -1 && tx == 1 && d.GetLine( ty, 0 ) == "" ) {
      s.Delete();  exit = true;
    }
    else {
      tmp = st.replace( /\n+/g, "\n" );
      if ( st !== tmp ) { s.Text = tmp; }
      else if ( pos > -1 ) { s.SetActivePos( pos );  exit = true; }
    }
    break;


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




    case 43: // case 41 & 42 の JavaScript コメント をアンコメントする
  /* JavaScript コメントアウト1 */
      /*
   // <!-- XML コメントアウト --> 
      * ・この文章のようなインデントされたコメントブロックの字下げ位置を考慮するが、
   case 1001:  case 1004:
      *   先頭のコメントマーク "/*" のスラッシュの前の字下げ(空白部分)が基準となる。
     // 「引用の追加」では行単位に拡張した選択範囲をまとめてコメントアウトするので、
      * ・複数のコメントブロックが選択範囲内にある場合、最初のコメントブロックしかアンコメントしない。
    // 行の中間部分だけをコメントアウトするなら「カッコで囲う」マクロを使うこと。
      * ・中間行の行頭記号はアスタリスク「*」と全角/半角の中黒「・」「・」を削除対象とする。
    var p1 = ( r == 1001 ) ? "/\* " : "<!-- ";
      *   (行頭記号と前後の半角スペース各1を削除する)
    var p2 = ( r == 1001 ) ? " */"  : " -->";
      * ・JSコメントでない箇条書き文で実行した場合も行頭記号を削除する。
    s.Text = p1 + st + p2;
      *
    if ( ! st ) {
      * ▼ 注意事項 ▼
      s.SetActivePos( pos + p1.length ); exit = true;
      * ・選択範囲がJSコメントか箇条書き文であれば、選択範囲の先頭/末尾の余計な空白行も削除する。
    }
      * ・中間行の末尾空白文字も削除し、空白行ならインデント部分も削除する。
    break;
      *
      * コメントマークは 配列 [ "接頭辞" , "接尾辞" , "中間行の行頭記号" ] で用意する。
      *     各要素はあらかじめ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;


  /* * JavaScript コメントアウト2 */
  case 1002:
    var p1 = "/\**",  p2 = " */",  ast = " * ",  n = "\n";
    s.Text = CommentOutJS( st, p1, p2, ast );
    // マクロ実行前に空行だった場合はカーソルをコメント枠のなかに移動
    if ( pos > -1 ) {
      s.SetActivePos( pos + ( p1 + n + ast ).length  );
      exit = true;
    }
    // カーソルを接頭辞のうしろに移動
    else {
      s.SetActivePoint( mePosLogical, 1, ty );
      s.EndOfLine();  exit = true;
    }
    break;


    case 45: // case 44 <!--␣ XMLコメント ␣--> をアンコメントする
  /* JSアンコメント */ // ※選択範囲内の各行頭のコメントマークにのみマッチ
      // 選択範囲内のコメントマークをすべて削除
  // case 41 & 42 JavaScript コメント をアンコメントする
      var dc = st.replace( / ?<!-+ ?| ?-+ *> ?/g , "" );
  case 1003:
      // 選択範囲に <!-- XMLコメント --> がなかった場合 undo 履歴を残さない
    // コメントの "接頭辞" と "接尾辞" と "行頭記号" を正規表現で配列に
      if ( dc == st ) {
    var p = new Array( "\\/\\*+ ?" , " ?\\*\\/" , " ?(\\*|[・・]) ?" );
        if ( pos ) {
    tmp = DeleteComment( st, p )
          s.SetActivePos( pos );
    if ( tmp !== st ) { s.Text = tmp; } // アンコメント完了
          exit = true;
    else if ( pos > -1 ) { s.SetActivePos( pos ); exit = true; }
        }
    break;
      }
      else {
        s.Text = dc // アンコメント完了
      }
      break;
 


     default:
  /* XML アンコメント */
      break;
  // case 44 の <!--␣ XMLコメント ␣--> をアンコメントする
  case 1005:
    tmp = st.replace( / ?<!-+ ?| ?-+ *> ?/g , "" );
     if ( tmp !== st ) { s.Text = tmp; }
    else if ( pos > -1 ) { s.SetActivePos( pos );  exit = true; }
    break;


   }
  default:
    break;
   } // end switch(r)


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


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




/* 関数 insertQuote( arg1, arg2 ) */
/* 関数 DeleteQuote( str, q ) */
  // Kuro版まま
function DeleteQuote( str, q ) {
function insertQuote( arg1, arg2 ) {
   var a = str.split( "\n" );
   var a = arg1.split( "\n" );
   for ( var i = 0, len = a.length; i < len; i ++ ) {
   for ( var i = 0; i < a.length; i ++ ) {
     for ( var j = 0, qLen = q.length, qt; j < qLen; j ++ ) {
    a[i] = arg2 + a[i];
       qt = q[j];
  }
      if ( ! qt ) { continue; }
  return a.join( "\n" );
       if ( a[i].substr( 0, qt.length ) === qt ) {
}
 
 
/* 関数 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 );
         a[i] = a[i].substr( qt.length );
         break;
         break;
466行目: 513行目:
   return a.join( "\n" );
   return a.join( "\n" );
}
}


/**
/**
477行目: 523行目:
  *  ・空白文字だけの行は空行と見做し、行頭記号 " * " を付けた後ろに空白文字を残さない。
  *  ・空白文字だけの行は空行と見做し、行頭記号 " * " を付けた後ろに空白文字を残さない。
  */
  */
function CommentOutJS( arg0, arg1, arg2, arg3 ) {
function CommentOutJS( str, p1, p2, ast ) {
  var a = str.split( "\n" );
  var b = [];


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


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


/**
* 関数 DeleteComment( arg1, arg2 )
*
* 複数のコメントブロックが選択範囲内にある場合、最初のコメントブロックしかアンコメントしない
* 引数 arg2 は 配列[ "接頭辞" , "接尾辞" , "中間行の行頭記号" ] で、
*  各要素はあらかじめ JavaScript の正規表現で記述されていないとダメ
*  arg2 = p = [ "\\/\\*+ ?" , " ?\\*\\/" , " ?(\\*|[・・]) ?" ];
*/
function DeleteComment( str, p ) {
  var a = str.split( "\n" ), len = a.length;
  var hit = false, line0 = 0, line1 = len;
  // コメントの接尾辞と接尾辞を検索・置換する
  for ( var j = 0, reg1; j < 2; j ++ ) {
    // 接頭辞 p[0], 接尾辞 p[1] の検索用
    reg1 = new RegExp( p[j], "" );


  /**
    for ( var i = 0; i < len; i ++ ) {
  * 関数 DeleteComment( arg1, arg2 )
      if ( reg1.test( a[i] ) ) {
  *
        a[i] = a[i].replace( reg1, "" );
  * 複数のコメントブロックが選択範囲内にある場合、最初のコメントブロックしかアンコメントしない
        // 接頭辞 p[0] がヒットした行
  * 引数 arg2 は 配列[ "接頭辞" , "接尾辞" , "中間行の接頭辞" ] で、
        if ( j == 0 ) { line0 = i;  hit = true;  break; }
  *  各要素はあらかじめJSの正規表現で記述されていないとダメ
        // 接尾辞 p[1] がヒットした行
  */
        else { line1 = i;  break; }
function DeleteComment( arg1, arg2 ) {
      }
    }
  }


   var a = arg1.split( "\n" ); // 選択範囲 st を "\n" で区切って配列 a に格納する
  // 中間行の行頭記号 p[2] (アスタリスク、中黒)を検索・削除
  var _reg,  _re,  _id,  _a1,  _a2;
  // 行頭空白文字の後ろの2文字までを置換対象にする
   var _hit = false, _line0 = 0,  _line1 = a.length; // 初期化の必要そうな変数
   var reg2 = new RegExp( "^[ \\t]*" + p[2], "" ); // 行頭空白+行頭記号の検索用
   var reg3 = new RegExp( p[2], "" ); // 行頭記号の置換(削除)用


   // コメントの接尾辞と接尾辞を検索・置換する
   // 接頭辞(よりも前)の行と接尾辞(よりも後ろ)の行は、検索・置換の対象外
   for ( var j = 0; j < 2; j ++ ){
   for ( var i = line0, limit = Math.min( len, line1 ), id, a1, a2;
     if ( arg2[j] == "" ) { // 接頭辞 p[0], 接尾辞 p[1] が定義されていないならスルー
  i < limit; i ++ ) {
       continue;
    // 行頭記号がヒットしたら
     if ( reg2.test( a[i] ) ) {
      // 置換範囲(空白文字数+2文字)
      id = a[i].indexOf( a[i].match( /[^ \t]/ ) ) + 2;
      a1 = a[i].slice( 0, id ).replace( reg3, "" );
      a2 = a[i].slice( id );
       a[i] = a1 + a2;
     }
     }
    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 , "" ); // 置換処理(削除)
  return a.join( "\n" )
        if ( j == 0 ) {
          .replace( /[\t  ]*$/gm, "" )
          _hit = true; // ヒットフラグ = true
          .replace( /^[\s ]*\n|\n[\s ]*$/g, "" );
          _line0 = i; // 接頭辞がヒットした行
}
          break; // 接頭辞を処理したら接尾辞の処理へ
 
        }
/**
        else { // ( j == 1 )
* 関数 GetTag( tagType, tagKey, property )
          _line1 = i; // 接尾辞がヒットした行
* 指定された Tag の値を返す
          break; // 接尾辞を処理したらループを抜ける
*/
         }
function GetTag( tagType, tagKey, property ) {
  try {
    var obj = ( typeof tagType === "object" ) ? tagType
            : ( tagType === 1 ) ? editor.ActiveDocument
            : ( tagType === 2 ) ? editor
            : ( tagType === 3 ) ? window
            : window;
    return ( obj.Tag.Exists( tagKey )
    && ( property ? property in obj.Tag( tagKey ) : true ) )
    ? property
      ? obj.Tag( tagKey )[ property ]
      : obj.Tag( tagKey )
    : null;
  } catch( e ) { Status = e;  return null; }
}
 
/**
* 関数 SetTag( value, tagType, tagKey, property )
* 指定された値を Tag に書き込む
*/
function SetTag( value, tagType, tagKey, property ) {
  try {
    var obj = ( typeof tagType === "object" ) ? tagType
            : ( tagType === 1 ) ? editor.ActiveDocument
            : ( tagType === 2 ) ? editor
            : ( tagType === 3 ) ? window
            : window;
    if ( property ) {
      if ( obj.Tag.Exists( tagKey ) ) {
         obj.Tag( tagKey )[ property ] = value;
       }
       }
      else { obj.Tag( tagKey ) = { property: value }; }
     }
     }
    else { obj.Tag( tagKey ) = value; }
  }
  catch( e ) { Status = e; }
  finally { return; }
}
/**
* 関数 AddStrPrompt( tagType, tagKey )
* 「任意の文字列」コマンド
* 入力ダイアログで指定された文字列を返す
*/
function AddStrPrompt( tagType, tagKey ) {
  // 前回使用した文字列があればダイアログの初期値に再利用
  var str = "";
  if ( tagType && ( t = GetTag( tagType, tagKey, "addStr" ) ) ) {
    str = t;
   }
   }
  // 入力ダイアログ
  var msg = "につける文字列:\t"
          + "改行 = \\\\\\n ; タブ = \\\\\\t  (注:¥記号3つ)";
  p = Prompt( "前" + msg, str ).replace( /\\\\\\t/g, "\t" )
                              .replace( /\\\\\\n/g, "\n" );
  if ( p && tagType ) {
    SetTag( p.replace( /\t/g, "\\\\\\t" ).replace( /\n/g, "\\\\\\n" )
    , tagType, tagKey, "addStr" );
  }
  return p;
}


  // 中間行の行頭記号 p[2] (アスタリスク、中黒)を検索・置換する
/**
   // 行頭空白文字の後ろの2文字までを置換対象にする |^   a*bcdefg には誤爆しないはず
* 関数 DelStrPrompt( tagType, tagKey )
   if ( arg2[2].length ) {
* 「任意の文字列を削除」コマンド
     // 接頭辞(よりも前)の行と接尾辞(よりも後ろ)の行は、検索・置換の対象外にする
* 入力ダイアログで指定された文字列を返す
    for ( var i = ( _hit ) ? _line0 + 1 : 0; i < _line1; i ++ ) {
*/
      _re = new RegExp( "^[  \\t]*" + arg2[2] , "" ); // 行頭空白+行頭記号 検索用の正規表現変数
function DelStrPrompt( tagType, tagKey ) {
      if ( a[i].match( _re ) ) { // 行頭記号がヒットしたら
   // 前回使用した文字列があればダイアログの初期値に再利用
        _id = a[i].indexOf( a[i].match( /[^  \t]/ ) ) + 2; // 置換範囲(空白文字数+2文字)
   var str = "";
        _reg = new RegExp( arg2[2] , "" ); // 行頭記号 置換用の正規表現変数
   if ( tagType && ( t = GetTag( tagType, tagKey, "delStr" ) ) ) {
        _a1 = a[i].slice( 0, _id ).replace( _reg , "" ); // 空白文字の後ろの2文字までを置換
     str = t;
        _a2 = a[i].slice( _id ); // 行頭記号よりも後ろの文字列は置換対象にしない
  }
        a[i] = _a1 + _a2;
  // 入力ダイアログ
      }
  var msg = "から削除する文字列:\t"
    }
          + "タブ = \\\\\\t  (注:¥記号3つ)";
  p = Prompt( "先頭(行頭)" + msg, str ).replace( /\\\\\\t/g, "\t" );
  if ( p && tagType ) {
    SetTag( p.replace( /\t/g, "\\\\\\t" ), tagType, tagKey, "delStr" );
   }
   }
   return a.join( "\n" ); // 各行を "\n" で区切って連結しなおす
   return p;
}
}


/**
* 関数 ToHalfWidth( strVal )
* 全角英数記号を半角変換して返す
*/
function ToHalfWidth( strVal ){
  // 半角変換(文字コードをシフト)
  return strVal.replace( /[!-~]/g, function( tmpStr ) {
    return String.fromCharCode( tmpStr.charCodeAt(0) - 0xFEE0 );
  } );
}


/* 以下の関数は『sort メソッド (Array) (JavaScript) | MSDN』より
/**
  https://msdn.microsoft.com/ja-jp/library/4b4fbfhk%28v=vs.94%29.aspx */
* 関数 DelByNumPrompt()
// 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;
function DelByNumPrompt() {
   if ( first < second )
   var msg = "から削除する文字数 (数字のみ有効):";
    return -1;
   return ToHalfWidth( Prompt( "先頭(行頭)" + msg, "" )
  else // ( first > second )
        ).replace( /\D/g , "" ); // 半角に変換して数字以外の文字は削除
    return 1;  
}
}




  /* 以下の関数は『JavaScript/正規表現 - Wikibooks』より
/**
   https://ja.wikibooks.org/wiki/JavaScript/正規表現
* 関数 MultiFunction( Fn, arg1 )
   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メソッドを使用して簡単に作成することができます。
* 第1引数: Function; 選択範囲ごとに適用する処理の関数
function Quote( str ) {
* 第2引数以降: Function に渡す引数をまとめた配列
     return str.replace( /\W/g, function( $0 ) {
  */
        return '\\' + $0;
function MultiFunction( Fn, arg ) {
    } );
  var d = editor.ActiveDocument;
};
  var s = d.selection;
 
   // 矩形選択範囲は行に分ける
  s.Mode = meModeMulti;
 
  // 選択範囲の座標を取得
  var sCount = s.Count;
  var Sel = [];
   for ( var i = 0; i < sCount; i ++ ) {
    Sel[i] = {
      act: s.GetActivePos( i ),
      anc: s.GetAnchorPos( i )
    };
  }
 
  // 各選択範囲を処理
  for ( var i = 0, diff = 0, dl; i < sCount; i ++ ) {
    dl = d.TextLength;
    s.SetActivePos( Sel[i].act + diff );
    s.SetAnchorPos( Sel[i].anc + diff );
 
    Fn( arg ); // LineCommentQuote_Main() 関数
 
    // Fn() の残した選択範囲(またはキャレット位置)を回収
    Sel[i].act = s.GetActivePos();
    Sel[i].anc = s.GetAnchorPos();
     diff += d.TextLength - dl; // 文字数の増減量(累積)
  }
 
  // マルチカーソル(複数選択範囲)を復帰
  for ( var i = 0; i < sCount; i ++ ) {
    s.AddPos( Sel[i].anc, Sel[i].act );
  }
}
</source>
</source>


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


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

2020年6月23日 (火) 21:56時点における版


画像は 2019/04/07 版のものです。
2020/06/23 版では、サブメニューに削除系コマンドをまとめてあります。

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


引用符/コメント

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

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

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


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

  • 各行の先頭にメタ記号を追加
ポップアップメニュー内に表示するラベル(注釈)や並び順をカスタマイズすると、Markdown 用のマークアップなどに活用しやすくなります。


以下は、公開・配布しているソースコードの初期状態で利用できる引用マークやコメントマークです。
  • がついたものは、半角スペースつきで記号を挿入します。
  • 「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 ブロックコメント)
 */


削除系コマンド


  • 1つ削除
選択範囲内の各行の行頭から、設定項目の「引用符の種類」に登録されている記号や文字列と一致するものを1つずつ削除します (※ 改行記号 "\n" は対象外 ⇒ 「空行を削除」コマンド)。


  • すべて削除
選択範囲内の各行の行頭から、設定項目の「引用符の種類」に登録されている記号や文字列と一致するものをすべて削除します (※ 改行記号 "\n" は対象外)。


  • 空行を削除
選択範囲内の空行を削除します。


  • 行頭の空白文字を削除
選択範囲内の各行の行頭から、全角/半角空白とタブ文字をすべて削除します。
※ このマクロでは、行末の空白文字列の削除はできません ( ⇒ 「カッコで囲う」マクロや「テキスト整形」マクロを利用してください)。


  • 行頭から任意の文字列を削除
選択範囲内の各行の行頭から、入力ダイアログで指定された文字列を削除します。


  • 行頭から任意の文字数を削除
選択範囲内の各行の行頭から、入力ダイアログで指定された文字数だけ削除します。


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


ダウンロード


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

  • 第四版: 2020/06/23
コマンドを追加: 「任意の文字数を削除」
マルチカーソル/複数選択範囲に対応
  • 第三版: 2019/04/07
コマンドを追加: 「空行の追加」、「任意の文字列を削除」
  • 第二版: 2018/10/30
  • 第一版: 2018/10/28


ソースコード

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

/**
 * -----------------------------------------------------------------------------
 * 引用の追加		( => 2018/10/14 公開停止)
 * Orginal Copyright (c) Kuro. All Rights Reserved.
 * www:    http://www.haijin-boys.com/
 * -----------------------------------------------------------------------------
 * Modified by sukemaru	(2018/10/28 - 2020/06/23)
 * 「引用符を追加」または「引用符/コメント」
 * 選択範囲の行頭に引用符/コメントマークを追加・削除する
 * -----------------------------------------------------------------------------
 * ▼ Kuro 版「引用の追加」マクロからの変更点 ▼
 * 
 * ・ポップアップメニューの体裁を変更した。
 * ・引用符の種類を増やした(※ 基本的に「メタ文字+半角スペース」)。
 * ・「クリップボード」と「任意の文字列」を追加。
 * ・配列に削除用の要素(半角スペースなしのメタ文字のみの差分)も余分に追加した。
 *   ※「すべて削除」では、半角スペース、全角スペース、タブ文字での字下げをすべて削除する。
 *   ※「クリップボード」、「任意の文字列」、「空行」、「JS/XML コメントアウト」は、
 *     「1つ削除//すべて削除」では削除されない。
 * 
 * ・選択範囲があったときには行全体に拡張・復帰するようにした。
 * ・選択範囲なしの状態からカーソル行にたいして「追加/1つ削除/すべて削除」を実行したときは、
 *   実行前の位置にカーソルを返せるようにした。
 * ・「1つ削除」の不具合箇所を修正した。
 */

// ---------- ▼ 設定項目 ▼ ---------- //

// ■ 「任意の文字列」で入力した文字列の一時記憶方法
var tagType = 2;
  // 0 : 一時記憶なし
  // 1 : タブ(文書)ごとに一時記憶する(Document.Tag)
  // 2 : ウインドウごとに一時記憶する(Editor.Tag)
  // 3 : すべてのタブとウインドウ共通で一時記憶する(window.Tag)


// ■ 引用符の種類(追加用)
var q = [ "" ,		// 以下 r = 1~10、11~20、21~30、31~ の ID 順
  "> " ,  "・" ,  " * " ,  "- " ,  " " ,  " " ,  "\t" ,  "// " ,  "# " ,  "; " ,
  "' " ,  "-- " ,  ": " ,  ":: " ,  "REM " ,  "※" ,  ">>" ,  "* " ,  "" ,  "" ,
  "" ,  "" ,  "" ,  "" ,  "" ,  "" ,  "" ,  "" ,  "" ,  "" ,
  "" ,  "" ,  "" ,  "" ,  "" ,  "" ,  "" ,  "" ,  "" ,  "" ,
"" ];


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

// ---------- ▲ 設定項目 ▲ ---------- //

var d = editor.ActiveDocument;
var s = editor.ActiveDocument.selection;
var m = CreatePopupMenu();

if ( d.ReadOnly ) {
  m.Add( " ドキュメントは 書き換え禁止 です & ", 0 );
}

else {
  var cb = ClipboardData.GetData();	// クリップボードのテキストデータを取得

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

  m.Add( "?	任意の文字列 (&E)...", 1010 );
  m.Add( "[ … ]	クリップボード (&C)", 1000, !cb *2 );

  m.Add( "", 0, meMenuSeparator );
  m.AddPopup( "▲	削除 (&D)", sm1 = CreatePopupMenu() );
    sm1.Add( "●	1つ削除 (&D)", 1013 );
    sm1.Add( "●	すべて削除 (&A)", 1014 );
    sm1.Add( "", 0, meMenuSeparator );
    sm1.Add( " ↲ 	空行を削除 (&B)", 1015 );
    sm1.Add( "␣␣	行頭の空白文字を削除 (&S)", 1016 );
    sm1.Add( "", 0, meMenuSeparator );
    sm1.Add( "?	行頭から任意の文字列を削除 (&Q)...", 1011 );
    sm1.Add( "? 	行頭から任意の文字数を削除 (&X)...", 1012 );

  m.Add( "", 0, meMenuSeparator );
  m.Add( "> 	> メール引用符 (&>)", 1 );
  m.Add( ">>	>> BBS アンカー (&>)", 17 );
  m.Add( "・	・ 箇条書き (&/)", 2 );
  m.Add( " * 	* 箇条書き (&*)", 3 );
  m.Add( "* 	* 箇条書き (&W)", 18 );
  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) ...", 1006 );

  m.Add( "", 0, meMenuSeparator );
  m.Add( "\/\/ 	JS・C コメント (&J)", 8 );	// アクセラレータは「JS コメントアウト」と重複
  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( "/*  *  */	JS コメントアウト 2 (&J)", 1002 );
  m.Add( "/*  */	JS コメントアウト 1  (&J)", 1001 );
  m.Add( "	JS アンコメント (&U)", 1003 );
  m.Add( "", 0, meMenuSeparator );
  m.Add( "<!--  -->	XML コメントアウト (&X)", 1004 );
  m.Add( "	XML アンコメント (&L)", 1005 );

  m.Add( "", 0, meMenuSeparator );
  m.Add( "キャンセル	& ", 0 );
}

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

if ( r > 0 ) {
  var p;
  var tagKey = "LineCommentQuote";
  if ( r === 1010 ) { p = AddStrPrompt( tagType, tagKey ); }
  if ( r === 1011 ) { p = DelStrPrompt( tagType, tagKey ); }
  if ( r === 1012 ) { p = DelByNumPrompt(); }	// 「任意の文字数を削除」

  var sx = ScrollX,  sy = ScrollY;
  // マルチカーソル対応
  var arg = [ q, r, p, cb ];
  // 選択範囲が1つで矩形選択ではないとき
  if ( ! s.Mode || s.Mode === meModeStream ) {
    LineCommentQuote_Main( arg );
  }
  // 矩形選択または複数選択のとき
  else {
    BeginUndoGroup();
    AddUndo();
    MultiFunction( LineCommentQuote_Main, arg );
    EndUndoGroup();
  }
  ScrollX = sx;  ScrollY = sy;
}


/**
 * 関数 LineCommentQuote_Main( [ q, r, p, cb ] )
 * 「引用符/コメント」マクロ
 */
function LineCommentQuote_Main( arg ) {
  var q  = arg[0];
  var r  = arg[1];
  var p  = arg[2];
  var cb = arg[3];

  // 選択範囲
  var d = editor.ActiveDocument;
  var s = editor.ActiveDocument.selection;
  var pos = s.IsEmpty ? s.GetActivePos() : -1;
  var tx = s.GetTopPointX( mePosLogical );
  var ty = s.GetTopPointY( mePosLogical );
  var bx = s.GetBottomPointX( mePosLogical );
  var by = s.GetBottomPointY( mePosLogical );
  // 選択範囲の末尾が行頭にあるときの調整
  if ( ty < by && bx == 1 && d.Text.charAt( s.GetActivePos() ) ) {
    by --;
  }
  // 選択範囲を拡張
  s.SetActivePoint( mePosLogical, 1, by );
  s.EndOfLine( false, mePosLogical );
  s.SetAnchorPoint( mePosLogical, 1, ty );

  // 選択範囲の文字列を取得
  var st = tmp = 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 17:  case 18:
    // 各行の先頭に引用符/コメントマークを追加
    s.Text = st.replace( /^/gm, q[r] );
//     /* とりあえずコメントアウトしておく */ 
//     // マクロ実行前に選択範囲がなかった場合は、カーソルを元の位置に戻す
//     if ( pos > -1 ) {
//       s.SetActivePos( pos + q[r].length );
//       exit = true;
//     }
    break;

  // クリップボード
  case 1000:
    s.Text = st.replace( /^/gm, cb );
    if ( pos > -1 ) { s.SetActivePos( pos + cb.length );  exit = true; }
    break;

  // 空行を挿入	※空行は削除コマンドの削除対象に含まれない
  case 1006:
    var n = "\n";	// 空改行
    s.Text = st.replace( /^/gm, n );
    if ( pos > -1 ) { s.SetActivePos( pos + n.length );  exit = true; }
    break;

  // 任意の文字列(テキストボックス)
  case 1010:
    if ( p ) { s.Text = st.replace( /^/gm, p ); }
    if ( pos > -1 ) { s.SetActivePos( pos + p.length );  exit = true; }
    break;


  // 任意の文字列を削除
  case 1011:
    var reg = new RegExp(
      "^" + p.replace( /[.*+?^=!:${}()|[\]\/\\]/g, "\\$&" )
    , "gm" );
    tmp = st.replace( reg, "" );
    if ( tmp !== st ) {
      s.Text = tmp;	
      if ( pos > -1 && tmp.length ) {
        s.SetActivePos( pos - ( st.length - tmp.length ) );
        exit = true;
      }
    }
    else if ( pos > -1 ) { s.SetActivePos( pos );  exit = true; }
    break;

  // 任意の文字数を削除
  case 1012:
    if ( p && st ) {
      tmp = DeleteCharByNum( st, p );
      if ( tmp !== st ) {
        s.Text = tmp;
        if ( pos > -1 && tmp.length ) {
          s.SetActivePos( pos - ( st.length - tmp.length ) );
          exit = true;
        }
      }
      else if ( pos > -1 ) { s.SetActivePos( pos );  exit = true; }
    }
    break

  // 1つ削除
  case 1013:
    // 各行ごとに、配列 q の並び順で最初にマッチした引用符/コメントマークを削除
    tmp = DeleteQuote( st, q );
    if ( tmp !== st ) {
      s.Text = tmp;
      if ( pos > -1 && tmp.length ) {
        s.SetActivePos( pos - ( st.length - tmp.length ) );
        exit = true;
      }
    }
    else if ( pos > -1 ) { s.SetActivePos( pos );  exit = true; }
    break;

  // すべて削除
  case 1014:
    var org = st;	// DeleteQuote() のループ処理前のテキスト
    // st を最大40回 DeleteQuote() でループ処理
    for ( var i = 0; i < 40; i ++ ) {
      tmp = DeleteQuote( st, q );
      // DeleteQuote() 処理の前後でテキストが一致したらループを抜ける
      if ( tmp == st ) { break; }
      st = tmp;		// DeleteQuote() したテキスト tmp を st に再代入
    }
    if ( tmp !== org ) {
      s.Text = tmp;
      if ( pos > -1 && tmp.length) {
        s.SetActivePos( pos - ( org.length - tmp.length ) );
        exit = true;
      }
    }
    else if ( pos > -1 ) { s.SetActivePos( pos );  exit = true; }
    break;

  // 空行を削除
  case 1015:
    // キャレット位置が空行上のとき
    if ( pos > -1 && tx == 1 && d.GetLine( ty, 0 ) == "" ) {
      s.Delete();  exit = true;
    }
    else {
      tmp = st.replace( /\n+/g, "\n" );
      if ( st !== tmp ) { s.Text = tmp; }
      else if ( pos > -1 ) { s.SetActivePos( pos );  exit = true; }
    }
    break;

  // 行頭の空白文字を削除
  case 1016:
    tmp = st.replace( /^[\t  ]+/gm, "" );
    if ( tmp !== st ) {
      s.Text = tmp;
      if ( pos > -1 && tmp.length ) {
        s.SetActivePos( pos - ( st.length - tmp.length ) );
        exit = true;
      }
    }
    else if ( pos > -1 ) { s.SetActivePos( pos );  exit = true; }
    break;


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

  /* * JavaScript コメントアウト2 */
  case 1002:
    var p1 = "/\**",  p2 = " */",  ast = " * ",  n = "\n";
    s.Text = CommentOutJS( st, p1, p2, ast );
    // マクロ実行前に空行だった場合はカーソルをコメント枠のなかに移動
    if ( pos > -1 ) {
      s.SetActivePos( pos + ( p1 + n + ast ).length  );
      exit = true;
    }
    // カーソルを接頭辞のうしろに移動
    else {
      s.SetActivePoint( mePosLogical, 1, ty );
      s.EndOfLine();  exit = true;
    }
    break;

  /* JSアンコメント */	// ※選択範囲内の各行頭のコメントマークにのみマッチ
  // case 41 & 42 の JavaScript コメント をアンコメントする
  case 1003:
    // コメントの "接頭辞" と "接尾辞" と "行頭記号" を正規表現で配列に
    var p = new Array( "\\/\\*+ ?" , " ?\\*\\/" , " ?(\\*|[・・]) ?" );
    tmp = DeleteComment( st, p )
    if ( tmp !== st ) { s.Text = tmp; }	// アンコメント完了
    else if ( pos > -1 ) { s.SetActivePos( pos );  exit = true; }
    break;

  /* XML アンコメント */
  // case 44 の <!--␣ XMLコメント ␣--> をアンコメントする
  case 1005:
    tmp = st.replace( / ?<!-+ ?| ?-+ *> ?/g , "" );
    if ( tmp !== st ) { s.Text = tmp; }
    else if ( pos > -1 ) { s.SetActivePos( pos );  exit = true; }
    break;

  default:
    break;
  }	// end switch(r)

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

    // 選択範囲の中身が \n のみなら選択解除
    if ( /^\n$/.test( s.Text ) ) {
      s.SetActivePos( s.GetAnchorPos() );
    }
  }
}


/* 関数 DeleteQuote( str, q ) */
function DeleteQuote( str, q ) {
  var a = str.split( "\n" );
  for ( var i = 0, len = a.length; i < len; i ++ ) {
    for ( var j = 0, qLen = q.length, qt; j < qLen; j ++ ) {
      qt = q[j];
      if ( ! qt ) { continue; }
      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( str, p1, p2, ast ) {
  var a = str.split( "\n" );
  var b = [];

  // 各行の字下げ数からの最小値を取得
  for ( var i = 0, len = a.length; i < len; i ++ ) {
    var id = a[i].search( /[^ \t]/ );	// 字下げの空白文字数を取得(空白行では -1)
    b.push( id < 0 ? 100000 : id );	// ※ -1 は sort のジャマなのでデタラメな数値に置きかえる
  }
  // 配列 b を昇順で並びかえ(先頭行から最小の字下げ部分の「空白文字 ␣␣ 」を取得)
  b.sort( function( a, b ) { return ( a < b ) ? -1 : ( a > b ) ? 1 : 0; } );
  var blanc = a[0].slice( 0, b[0] );

  // 字下げ位置に中間行の行頭記号 " * " を追加
  for ( var i = 0, len = a.length; i < len; i ++ ) {
    if ( /^[ \t]*$/.test( a[i] ) ) { a[i] = blanc + ast; }	// 空白行
    else { a[i] = blanc + ast + a[i].slice( b[0] ); }
  }

  var prefix = blanc + p1 + "\n";	// 接頭辞
  var suffix = "\n" + blanc + p2;	// 接尾辞
  return prefix + a.join( "\n" ) + suffix;
}

/**
 * 関数 DeleteComment( arg1, arg2 )
 *
 * 複数のコメントブロックが選択範囲内にある場合、最初のコメントブロックしかアンコメントしない
 * 引数 arg2 は 配列[ "接頭辞" , "接尾辞" , "中間行の行頭記号" ] で、
 *   各要素はあらかじめ JavaScript の正規表現で記述されていないとダメ
 *   arg2 = p = [ "\\/\\*+ ?" , " ?\\*\\/" , " ?(\\*|[・・]) ?" ];
 */
function DeleteComment( str, p ) {
  var a = str.split( "\n" ), len = a.length;
  var hit = false, line0 = 0, line1 = len;

  // コメントの接尾辞と接尾辞を検索・置換する
  for ( var j = 0, reg1; j < 2; j ++ ) {
    // 接頭辞 p[0], 接尾辞 p[1] の検索用
    reg1 = new RegExp( p[j], "" );

    for ( var i = 0; i < len; i ++ ) {
      if ( reg1.test( a[i] ) ) {
        a[i] = a[i].replace( reg1, "" );
        // 接頭辞 p[0] がヒットした行
        if ( j == 0 ) { line0 = i;  hit = true;  break; }
        // 接尾辞 p[1] がヒットした行
        else { line1 = i;  break; }
      }
    }
  }

  // 中間行の行頭記号 p[2] (アスタリスク、中黒)を検索・削除
  // 行頭空白文字の後ろの2文字までを置換対象にする
  var reg2 = new RegExp( "^[ \\t]*" + p[2], "" );	// 行頭空白+行頭記号の検索用
  var reg3 = new RegExp( p[2], "" );	// 行頭記号の置換(削除)用

  // 接頭辞(よりも前)の行と接尾辞(よりも後ろ)の行は、検索・置換の対象外
  for ( var i = line0, limit = Math.min( len, line1 ), id, a1, a2;
  i < limit; i ++ ) {
    // 行頭記号がヒットしたら
    if ( reg2.test( a[i] ) ) {
      // 置換範囲(空白文字数+2文字)
      id = a[i].indexOf( a[i].match( /[^ \t]/ ) ) + 2;
      a1 = a[i].slice( 0, id ).replace( reg3, "" );
      a2 = a[i].slice( id );
      a[i] = a1 + a2;
    }
  }

  // 各行の行末空白文字と、先頭/末尾の空行を削除
  return a.join( "\n" )
          .replace( /[\t  ]*$/gm, "" )	
          .replace( /^[\s ]*\n|\n[\s ]*$/g, "" );
}

/**
 * 関数 GetTag( tagType, tagKey, property )
 * 指定された Tag の値を返す
 */
function GetTag( tagType, tagKey, property ) {
  try {
    var obj = ( typeof tagType === "object" ) ? tagType
            : ( tagType === 1 ) ? editor.ActiveDocument
            : ( tagType === 2 ) ? editor
            : ( tagType === 3 ) ? window
            : window;
    return ( obj.Tag.Exists( tagKey )
    && ( property ? property in obj.Tag( tagKey ) : true ) )
    ? property
      ? obj.Tag( tagKey )[ property ]
      : obj.Tag( tagKey )
    : null;
  } catch( e ) { Status = e;  return null; }
}

/**
 * 関数 SetTag( value, tagType, tagKey, property )
 * 指定された値を Tag に書き込む
 */
function SetTag( value, tagType, tagKey, property ) {
  try {
    var obj = ( typeof tagType === "object" ) ? tagType
            : ( tagType === 1 ) ? editor.ActiveDocument
            : ( tagType === 2 ) ? editor
            : ( tagType === 3 ) ? window
            : window;
    if ( property ) {
      if ( obj.Tag.Exists( tagKey ) ) {
        obj.Tag( tagKey )[ property ] = value;
      }
      else { obj.Tag( tagKey ) = { property: value }; }
    }
    else { obj.Tag( tagKey ) = value; }
  }
  catch( e ) { Status = e; }
  finally { return; }
}

/**
 * 関数 AddStrPrompt( tagType, tagKey )
 * 「任意の文字列」コマンド
 * 入力ダイアログで指定された文字列を返す
 */
function AddStrPrompt( tagType, tagKey ) {
  // 前回使用した文字列があればダイアログの初期値に再利用
  var str = "";
  if ( tagType && ( t = GetTag( tagType, tagKey, "addStr" ) ) ) {
    str = t;
  }
  // 入力ダイアログ
  var msg = "につける文字列:\t"
          + "改行 = \\\\\\n ; タブ = \\\\\\t  (注:¥記号3つ)";
  p = Prompt( "前" + msg, str ).replace( /\\\\\\t/g, "\t" )
                               .replace( /\\\\\\n/g, "\n" );
  if ( p && tagType ) {
    SetTag( p.replace( /\t/g, "\\\\\\t" ).replace( /\n/g, "\\\\\\n" )
    , tagType, tagKey, "addStr" );
  }
  return p;
}

/**
 * 関数 DelStrPrompt( tagType, tagKey )
 * 「任意の文字列を削除」コマンド
 * 入力ダイアログで指定された文字列を返す
 */
function DelStrPrompt( tagType, tagKey ) {
  // 前回使用した文字列があればダイアログの初期値に再利用
  var str = "";
  if ( tagType && ( t = GetTag( tagType, tagKey, "delStr" ) ) ) {
    str = t;
  }
  // 入力ダイアログ
  var msg = "から削除する文字列:\t"
          + "タブ = \\\\\\t  (注:¥記号3つ)";
  p = Prompt( "先頭(行頭)" + msg, str ).replace( /\\\\\\t/g, "\t" );
  if ( p && tagType ) {
    SetTag( p.replace( /\t/g, "\\\\\\t" ), tagType, tagKey, "delStr" );
  }
  return p;
}

/**
 * 関数 ToHalfWidth( strVal )
 * 全角英数記号を半角変換して返す
 */
function ToHalfWidth( strVal ){
  // 半角変換(文字コードをシフト)
  return strVal.replace( /[!-~]/g, function( tmpStr ) {
    return String.fromCharCode( tmpStr.charCodeAt(0) - 0xFEE0 );
  } );
}

/**
 * 関数 DelByNumPrompt()
 * 「任意の文字数を削除」コマンド
 * 入力ダイアログで指定された文字列から
 * 数字を抽出して半角数字にして返す
 */
function DelByNumPrompt() {
  var msg = "から削除する文字数 (数字のみ有効):";
  return ToHalfWidth( Prompt( "先頭(行頭)" + msg, "" )
         ).replace( /\D/g , "" );	// 半角に変換して数字以外の文字は削除
}


/**
 * 関数 MultiFunction( Fn, arg1 )
 * マルチカーソル(複数選択範囲)に対応させる
 * 第1引数: Function; 選択範囲ごとに適用する処理の関数
 * 第2引数以降: Function に渡す引数をまとめた配列
 */
function MultiFunction( Fn, arg ) {
  var d = editor.ActiveDocument;
  var s = d.selection;

  // 矩形選択範囲は行に分ける
  s.Mode = meModeMulti;

  // 選択範囲の座標を取得
  var sCount = s.Count;
  var Sel = [];
  for ( var i = 0; i < sCount; i ++ ) {
    Sel[i] = {
      act: s.GetActivePos( i ),
      anc: s.GetAnchorPos( i )
    };
  }

  // 各選択範囲を処理
  for ( var i = 0, diff = 0, dl; i < sCount; i ++ ) {
    dl = d.TextLength;
    s.SetActivePos( Sel[i].act + diff );
    s.SetAnchorPos( Sel[i].anc + diff );

    Fn( arg );	// LineCommentQuote_Main() 関数

    // Fn() の残した選択範囲(またはキャレット位置)を回収
    Sel[i].act = s.GetActivePos();
    Sel[i].anc = s.GetAnchorPos();
    diff += d.TextLength - dl;	// 文字数の増減量(累積)
  }

  // マルチカーソル(複数選択範囲)を復帰
  for ( var i = 0; i < sCount; i ++ ) {
    s.AddPos( Sel[i].anc, Sel[i].act );
  }
}

謝辞

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

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

スポンサーリンク