「字下げ・字上げ」の版間の差分

提供: MeryWiki
ナビゲーションに移動 検索に移動
Masme (トーク | 投稿記録)
更新履歴の書式を変更
Sukemaru (トーク | 投稿記録)
sukemaru 版: 更新(マルチカーソル対応)
80行目: 80行目:
<br>
<br>
'''ダウンロード:''' 「[[ファイル:字下げ/字上げ.zip]]」(アイコン入り)<br>
'''ダウンロード:''' 「[[ファイル:字下げ/字上げ.zip]]」(アイコン入り)<br>
最終更新: 2019/12/01 '''ブックマーク対応版'''
最終更新: 2020/06/29 '''マルチカーソル''' & '''ブックマーク''' に対応
* [[#字下げ(スペース×2 追加)|スペース×2 追加]].js
* [[#字下げ(スペース×2 追加)|スペース×2 追加]].js
* [[#字上げ(スペース×2 削除)|スペース×2 削除]].js
* [[#字上げ(スペース×2 削除)|スペース×2 削除]].js
87行目: 87行目:
: それぞれ専用の「マテリアルデザインっぽいアイコン」を同梱
: それぞれ専用の「マテリアルデザインっぽいアイコン」を同梱
<br>
<br>
※ ブックマーク対応の '''2019/12/01 版'''の推奨要件: '''Mery ver 2.8.6 以上''' <br><span style="color:#0000c0;">  Mery ver 2.6.7 と ver 2.8.6 以上でブックマークを正常に保持できることを確認しています。</span>
* マルチカーソル/複数選択範囲に対応('''Mery ver 3.0.1 以上'''
:* Mery ver 2.7.0 - ver 2.8.5 では、Mery 本体側のブックマーク機能の仕様が安定していないので、ブックマークを正しく復元できないことがあります。
 
:* Mery ver 2.7.0 以降では、文書全体が1行のみで1行目をブックマークしている状態のばあい、「1行目のブックマーク状態」を取得することができないのですが、ver 2.8.6 であればブックマークを正常に保持できるはず。
* ブックマーク対応の推奨要件: '''Mery ver 2.8.6 以上'''
:※ ブックマーク行が 数十件~ 大量にある文書で実行すると動作がモタつくことがあります。
: <span style="color:#0000c0;">  Mery ver 2.6.7 と ver 2.8.6 以上でブックマークを正常に保持できることを確認しています。</span>
:Mery ver 2.7.0 - ver 2.8.5 では、Mery 本体側のブックマーク機能の仕様が安定していないので、ブックマークを正しく復元できないことがあります。




100行目: 101行目:
* 行頭に「半角スペース×2」を追加する場合は、空行を無視します。
* 行頭に「半角スペース×2」を追加する場合は、空行を無視します。
* 選択範囲が末尾に改行を含んでいた場合は、最後の改行を無視します。
* 選択範囲が末尾に改行を含んでいた場合は、最後の改行を無視します。
* <span style="color:#0000c0;">マルチカーソル/複数選択範囲に対応(Mery ver 3.0.1 以上)</span>


<source lang="javascript" style="height:40em; overflow:auto;">
<source lang="javascript" style="height:60em; overflow:auto;">
#title = "字下げ"
#title = "字下げ"
#tooltip = "スペース×2 追加"
#tooltip = "スペース×2 追加"
112行目: 114行目:
  * ---------------------------------------------------------
  * ---------------------------------------------------------
  * 「字下げ(スペース×2 追加)」マクロ
  * 「字下げ(スペース×2 追加)」マクロ
  *  sukemaru, 2018/11/01 - 2019/12/01
  *  sukemaru, 2018/11/01 - 2020/06/29
  * ---------------------------------------------------------
  * ---------------------------------------------------------
  * 最初に選択範囲がなかった場合は「タブ文字」を追加、
  * 最初に選択範囲がなかった場合は「タブ文字」を追加、
118行目: 120行目:
  * 選択範囲があった場合は、つねに「半角スペース×2」を行頭に追加する(空行は無視)。
  * 選択範囲があった場合は、つねに「半角スペース×2」を行頭に追加する(空行は無視)。
  *  
  *  
  * 2019/11/17: ブックマークの保守方法を変更
  * 2020/06/29: マルチカーソル/複数選択範囲に対応
  * 2019/12/01: 複数行選択のときのみブックマークの保存/復元するように変更
  *             ブックマーク行を字下げしたときのみブックマークの復元をするように変更
*            ブックマークの収集範囲を選択範囲に限定
*            ブックマークの収集/復元コードを関数化
*            行頭空白追加の処理をループ関数から正規表現置換に変更
  */
  */


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


// ■ 選択範囲なしのときには「タブ文字×1」を追加するか?
// ■ 選択範囲なしのとき
var tabEnable = 0;
var tabEnable = 0;
   /**
   /**
140行目: 145行目:
var tab = "\t";
var tab = "\t";


Redraw = false;
var sx = ScrollX,  sy = ScrollY;
var d = editor.ActiveDocument;
var d = editor.ActiveDocument;
var s = d.selection;
// "選択範囲なし" のフラグ
var pos = s.IsEmpty ? s.GetActivePos() : "";
if ( d.ReadOnly ) {
if ( d.ReadOnly ) {
   Status = " ドキュメントは書き換え禁止です。";
   Status = " ドキュメントは書き換え禁止です。";
}
}
else {
  var s = d.selection;
  var sx = ScrollX,  sy = ScrollY;


/* キャレット位置に「タブ文字×1」を挿入するパターン */
  // マルチカーソル対応
else if ( tabEnable == 2 && pos ) {
  var arg = [ tabEnable, blank ];
   s.Text = tab;
  // 選択範囲が1つで矩形選択ではないとき
  if ( ! s.Mode || s.Mode === meModeStream ) {
    AddSpace2_Main( arg );
   }
  // 複数行または複数選択のとき
  else {
    BeginUndoGroup();
    AddUndo();
    MultiFunction( AddSpace2_Main, arg );
    EndUndoGroup();
  }
  ScrollX = sx;  ScrollY = sy;
 
  var elapsedSec = ( ( new Date() - start ) / 1000 ).toFixed( 3 );
  Status = " 「スペース×2 追加」マクロ  [ "
        + elapsedSec.replace( /\./, ". " )
        + " 秒 ]";
}
}


else {
  // 選択範囲を取得
  var ty = s.GetTopPointY( mePosLogical );
  var bx = s.GetBottomPointX( mePosLogical );
  var by = s.GetBottomPointY( mePosLogical );
  // 選択範囲を拡張
  if ( by != ty && bx == 1 ) { by -= 1; }
  s.SetActivePoint( mePosLogical, 1, by );
  s.EndOfLine( false, mePosLogical );
  s.SetAnchorPoint( mePosLogical, 1, ty );


  // ブックマークを保存
/**
   var bmArray = [];
* 関数 AddSpace2_Main( [ tabEnable, blank ] )
   if ( ty < by ) {
* 「字上げ(スペース×2 追加)」マクロ
     var tp = s.GetAnchorPos(),  bp = s.GetActivePos();
*/
     s.SetActivePoint( mePosLogical, 1, 2 );
function AddSpace2_Main( arg ) {
     if ( s.PreviousBookmark() ) {
   var tabEnable = arg[0];
      bmArray.push( s.GetActivePointY( mePosLogical ) );
  var blank    = arg[1];
     }
 
     s.SetActivePos( 0 );
  var d = editor.ActiveDocument;
     while ( s.NextBookmark() ) {
  var s = d.selection;
       bmArray.push( s.GetActivePointY( mePosLogical ) );
 
  // "選択範囲なし" のフラグ
  var pos = s.IsEmpty ? s.GetActivePos() : -1;
 
  /* キャレット位置に「タブ文字×1」を挿入するパターン */
   if ( tabEnable === 2 && pos > -1 ) {
    s.Text = tab;
  }
 
  else {
     var ty = s.GetTopPointY( mePosLogical );
    var bx = s.GetBottomPointX( mePosLogical );
     var by = s.GetBottomPointY( mePosLogical );
     if ( by !== ty && bx === 1 ) { by -= 1; }
    s.SetActivePoint( mePosLogical, d.GetLine( by, 0 ).length + 1, by );
    s.SetAnchorPoint( mePosLogical, 1, ty );
 
     // 選択範囲の文字列を取得
     var st = s.Text;
     var tp = s.GetAnchorPos();
 
    /* 最初に選択範囲がなかった場合は、行頭に「タブ文字×1」を追加するパターン */
    if ( tabEnable === 1 && pos > -1 ) {
       s.Text = tab + st;
      s.SetActivePos( pos + tab.length ); // 範囲選択を残さない
     }
     }
    s.SetAnchorPos( tp );  s.SetActivePos( bp, true );
    ScrollX = sx;  ScrollY = sy;
  }
  var bmCount = bmArray.length;


  // 選択範囲の文字列を取得
    /* 選択範囲があった場合は、行頭に「半角スペース×2」を追加(空行は無視) */
  var st = s.Text;
    else {
      var tmp = st.replace( /^(?!$)/gm, blank );
      if ( tmp !== st ) {
        // ブックマークを保存
        var bmArray = GetBookmark( ty, by );
        // 字下げ完了
        s.Text = tmp;
        // ブックマークを復元
        RestoreBookmark( ty, by, bmArray );
      }


  /* 最初に選択範囲がなかった場合は、行頭に「タブ文字×1」を追加するパターン */
      // 範囲選択を残さない
  if ( tabEnable == 1 && pos ) {
      if ( pos > -1 ) {
    s.Text = InsertSpace( st, tab );
        s.SetActivePos( pos + ( tmp.length - st.length ) );
    s.SetActivePos( pos + tab.length ); // 範囲選択を残さない
      }
      // 範囲選択を残す(論理行単位)
      else {
        s.SetActivePos( tp + tmp.length + 1 );
        s.SetAnchorPoint( mePosLogical, 1, ty );
      }
    }
   }
   }
}


  /* 選択範囲があった場合は、行頭に「半角スペース×2」を追加(空行は無視) */
/**
  else { // ( tabEnable == 0 || ! pos )
* 関数 GetBookmark( ty, by )
    var t = InsertSpace( st, blank );
* 指定された範囲のブックマークを収集
    if ( t != st ) {
*/
      s.Text = t;
function GetBookmark( ty, by ) {
  var bmArray = [], bmY;
  var s = editor.ActiveDocument.selection;
  if ( ty < by ) {
    var anc = s.GetAnchorPos(),  act = s.GetActivePos();


      // ブックマークを復元
    if ( ty === 1 ) {
      if ( bmCount ) {
      s.SetActivePoint( mePosLogical, 1, 2 );
        var ep = s.GetActivePos();
      if ( s.PreviousBookmark() ) {
         editor.ExecuteCommandByID( 2129 ); // ブックマークをクリア
         bmY = s.GetActivePointY( mePosLogical );
         for ( var i = 0; i < bmCount; i ++ ) {
         if ( bmY >= ty && bmY <= by ) {
           s.SetActivePoint( mePosLogical, 1, bmArray[i], false );
           bmArray.push( bmY );
          editor.ExecuteCommandByID( 2126 ); // ブックマークを設定
         }
         }
        s.SetActivePos( ep );
       }
       }
      s.SetActivePos( 0 );
     }
     }
    else { s.SetActivePoint( mePosLogical, 1, ty - 1 ); }


     if ( pos ) { // 範囲選択を残さない
     while ( s.NextBookmark() ) {
       s.SetActivePos( pos + ( t.length - st.length ) );
       bmY = s.GetActivePointY( mePosLogical );
    }
      if ( bmY >= ty && bmY <= by ) {
    else { // 範囲選択を残す(論理行単位)
        bmArray.push( bmY );
      s.SetActivePos( s.GetActivePos() + 1 );
       }
       s.SetAnchorPoint( mePosLogical, 1, ty );
      else if ( bmY > by ) { break; }
     }
     }
    s.SetActivePos( act );  s.SetAnchorPos( anc );
   }
   }
  return bmArray;
}
}


ScrollX = sx; ScrollY = sy;
/**
Redraw = true;
* 関数 RestoreBookmark( ty, by, bmArray )
 
* 指定された範囲のブックマークを復元
if ( ! d.ReadOnly ) {
  */
  var elapsedSec = ( ( new Date() - start ) / 1000 ).toFixed( 3 );
function RestoreBookmark( ty, by, bmArray, sel ) {
  Status = " 「スペース×2 追加」マクロ  [ "
  var bmCount = bmArray.length;
        + elapsedSec.replace( /\./, ". " )
  if ( bmCount ) {
        + " 秒 ]";
    var s = editor.ActiveDocument.selection;
    var anc = s.GetAnchorPos(),  act = s.GetActivePos();
    s.SetActivePoint( mePosLogical, 1, ty );
    s.ClearBookmark();
    for ( var i = 0; i < bmCount, bmArray[i] <= by; i ++ ) {
      if ( bmArray[i] < ty ) { continue; }
      s.SetActivePoint( mePosLogical, 1, bmArray[i], false );
      s.SetBookmark();
    }
    if ( sel ) {
      s.SetActivePos( act );  s.SetAnchorPos( anc );
    }
  }
}
}


/**
/**
  * 関数 InsertSpace( str, blank )
  * 関数 MultiFunction( Fn, arg1 )
  * str の各行の先頭に blank を挿入する
  * マルチカーソル(複数選択範囲)に対応させる
* 第1引数: Function; 選択範囲ごとに適用する処理の関数
* 第2引数: Function に渡す引数をまとめた配列
  */
  */
function InsertSpace( str, blank ) {
function MultiFunction( Fn, arg ) {
   var a = str.split( "\n" );
   var d = editor.ActiveDocument;
   for ( var i = 0, len = a.length; i < len; i ++ ) {
  var s = d.selection;
     if ( a[i].length || blank == "\t" ) {
 
      a[i] = blank + a[i];
  // 矩形選択範囲は行に分ける
     }
  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 ); // AddSpace2_Main() 関数
 
    // Fn() の残した選択範囲(またはキャレット位置)を回収
    Sel[i].act = s.GetActivePos();
    Sel[i].anc = s.GetAnchorPos();
    diff += d.TextLength - dl; // 文字数の増減量(累積)
  }
 
  // マルチカーソル(複数選択範囲)を復帰
  s.SetActivePos( Sel[0].anc );
  for ( var i = 0; i < sCount; i ++ ) {
     s.AddPos( Sel[i].anc, Sel[i].act );
   }
   }
  return a.join( "\n" );
}
}
</source>
</source>
256行目: 347行目:
: ''e.g.'' 「半角スペース×2」「半角スペース×1」「全角スペース×1」「タブ文字×1」
: ''e.g.'' 「半角スペース×2」「半角スペース×1」「全角スペース×1」「タブ文字×1」
* 選択範囲が末尾に改行を含んでいた場合は、最後の改行を無視します。
* 選択範囲が末尾に改行を含んでいた場合は、最後の改行を無視します。
* <span style="color:#0000c0;">マルチカーソル/複数選択範囲に対応(Mery ver 3.0.1 以上)</span>


※ sukemaru 版の「字上げ」と「逆インデント」マクロを並存させるメリットは少ないとおもいますので、どちらかお好みで選んでください。 <br>  
※ sukemaru 版の「字上げ」と「逆インデント」マクロを並存させるメリットは少ないとおもいますので、どちらかお好みで選んでください。 <br>  
「字上げ」マクロの場合、削除対象に「空白文字」以外の任意の "文字列" や "記号" なども追加できます。
「字上げ」マクロの場合、削除対象に「空白文字」以外の任意の "文字列" や "記号" なども追加できます。


<source lang="javascript" style="height:40em; overflow:auto;">
<source lang="javascript" style="height:60em; overflow:auto;">
#title = "字上げ"
#title = "字上げ"
#tooltip = "スペース×2 削除"
#tooltip = "スペース×2 削除"
271行目: 363行目:
  * ---------------------------------------------------------
  * ---------------------------------------------------------
  * 「字上げ(スペース×2 削除)」マクロ
  * 「字上げ(スペース×2 削除)」マクロ
  *  sukemaru, 2018/11/01 - 2019/12/01
  *  sukemaru, 2018/11/01 - 2020/06/29
  * ---------------------------------------------------------
  * ---------------------------------------------------------
  * 行頭の「空白文字」を削除、またはキャレット位置の前後にある「タブ文字」を削除する。
  * 行頭の「空白文字」を削除、またはキャレット位置の前後にある「タブ文字」を削除する。
278行目: 370行目:
  * 配列 blank で定義する。
  * 配列 blank で定義する。
  *  
  *  
  * 2019/11/17: ブックマークの保守方法を変更
  * 2020/06/29: マルチカーソル/複数選択範囲に対応
  * 2019/12/01: 複数行選択のときのみブックマークの保存/復元するように変更
  *             ブックマーク行を字下げしたときのみブックマークの復元をするように変更
*            ブックマークの収集範囲を選択範囲に限定
*            ブックマークの収集/復元コードを関数化
*            行頭空白削除の処理をループ関数から正規表現置換に変更
  */
  */


308行目: 403行目:
// ---------- ▲ 設定項目 ▲ ----------
// ---------- ▲ 設定項目 ▲ ----------


Redraw = false;
 
var sx = ScrollX,  sy = ScrollY;
var d = editor.ActiveDocument;
var d = editor.ActiveDocument;
var s = d.selection;
var pos = s.IsEmpty ? s.GetActivePos() : ""; // "選択範囲なし" のフラグ
var isDone = false; // Quit() メソッドの代用フラグ
if ( d.ReadOnly ) {
if ( d.ReadOnly ) {
   Status = " ドキュメントは書き換え禁止です。";
   Status = " ドキュメントは書き換え禁止です。";
  isDone = true;
}
}
 
else {
/* カーソル付近の「タブ文字×1」を削除するパターン(左側優先) */
  var s = d.selection;
else if ( pos && deleteNearestTab ) {
  var sx = ScrollX,  sy = ScrollY;
   if ( d.Text.charAt( pos - 1 ) == "\t" ) {
 
     s.DeleteLeft();
  // マルチカーソル対応
    isDone = true;
  var arg = [ deleteNearestTab, blank ];
  // 選択範囲が1つで矩形選択ではないとき
   if ( ! s.Mode || s.Mode === meModeStream ) {
     DeleteSpace2_Main( arg );
   }
   }
   else if ( d.Text.charAt( pos ) == "\t" ) {
  // 複数行または複数選択のとき
     s.Delete();
   else {
     isDone = true;
    BeginUndoGroup();
    AddUndo();
     MultiFunction( DeleteSpace2_Main, arg );
     EndUndoGroup();
   }
   }
  ScrollX = sx;  ScrollY = sy;
  var elapsedSec = ( ( new Date() - start ) / 1000 ).toFixed( 3 );
  Status = " 「スペース×2 削除」マクロ  [ "
        + elapsedSec.replace( /\./, ". " )
        + " 秒 ]";
}
}


if ( ! isDone ) {
 
/**
* 関数 DeleteSpace2_Main( [ deleteNearestTab, blank ] )
* 「字上げ(スペース×2 削除)」マクロ
*/
function DeleteSpace2_Main( arg ) {
  var deleteNearestTab = arg[0];
  var blank = arg[1];
 
  var d = editor.ActiveDocument;
  var s = d.selection;
 
  // "選択範囲なし" のフラグ
  var pos = s.IsEmpty ? s.GetActivePos() : -1;
 
  /* カーソル付近の「タブ文字×1」を削除するパターン(左側優先) */
  if ( pos > -1 && deleteNearestTab ) {
    if ( d.Text.charAt( pos - 1 ) === "\t" ) {
      s.DeleteLeft();  return;
    }
    else if ( d.Text.charAt( pos ) === "\t" ) {
      s.Delete();  return;
    }
  }
 
   // 選択範囲を取得
   // 選択範囲を取得
   var tx = s.GetTopPointX( mePosLogical );
   var tx = s.GetTopPointX( mePosLogical );
340行目: 464行目:
   var by = s.GetBottomPointY( mePosLogical );
   var by = s.GetBottomPointY( mePosLogical );
   // 選択範囲を拡張
   // 選択範囲を拡張
   if ( by != ty && bx == 1 ) { by -= 1; }
   if ( by !== ty && bx === 1 ) { by -= 1; }
   s.SetActivePoint( mePosLogical, 1, by );
   s.SetActivePoint( mePosLogical, d.GetLine( by, 0 ).length + 1, by );
  s.EndOfLine( false, mePosLogical );
   s.SetAnchorPoint( mePosLogical, 1, ty );
   s.SetAnchorPoint( mePosLogical, 1, ty );
  // ブックマークを保存
  var bmArray = [];
  if ( ty < by ) {
    var tp = s.GetAnchorPos(),  bp = s.GetActivePos();
    s.SetActivePoint( mePosLogical, 1, 2 );
    if ( s.PreviousBookmark() ) {
      bmArray.push( s.GetActivePointY( mePosLogical ) );
    }
    s.SetActivePos( 0 );
    while ( s.NextBookmark() ) {
      bmArray.push( s.GetActivePointY( mePosLogical ) );
    }
    s.SetAnchorPos( tp );  s.SetActivePos( bp, true );
    ScrollX = sx;  ScrollY = sy;
  }
  var bmCount = bmArray.length;


   /* 行頭の空白文字を削除 */
   /* 行頭の空白文字を削除 */
   var st = s.Text;
   var st = s.Text;
   var t = DeleteSpace( st, blank );
   var tp = s.GetAnchorPos();
   if ( t != st ) {
  for ( var i = 0, len = blank.length; i < len; i ++ ) {
     s.Text = t;
    blank[i].replace( /\W/g, "\\$&" );
 
  }
  var reg = RegExp( "^(?:" + blank.join( "|" ) + ")", "gm" );
  var tmp = st.replace( reg, "" );
   if ( tmp !== st ) {
    // ブックマークを保存
    var bmArray = GetBookmark( ty, by );
    // 字上げ完了
     s.Text = tmp;
     // ブックマークを復元
     // ブックマークを復元
     if ( bmCount ) {
     RestoreBookmark( ty, by, bmArray );
      var ep = s.GetActivePos();
      editor.ExecuteCommandByID( 2129 ); // ブックマークをクリア
      for ( var i = 0; i < bmCount; i ++ ) {
        s.SetActivePoint( mePosLogical, 1, bmArray[i], false );
        editor.ExecuteCommandByID( 2126 ); // ブックマークを設定
      }
      s.SetActivePos( ep );
    }
   }
   }


   if ( pos ) { // 範囲選択を残さない
  // 範囲選択を残さない
     if ( st.length - t.length >= tx ) {
   if ( pos > -1 ) {
     if ( st.length - tmp.length >= tx ) {
       s.SetActivePoint( mePosLogical, 1, ty );
       s.SetActivePoint( mePosLogical, 1, ty );
     }
     }
     else {
     else {
       s.SetActivePos( pos - ( st.length - t.length ) );
       s.SetActivePos( pos - ( st.length - tmp.length ) );
     }
     }
   }
   }
   else { // 範囲選択を残す(論理行単位)
   // 範囲選択を残す(論理行単位)
     s.SetActivePos( s.GetActivePos() + 1 );
  else {
     s.SetAnchorPoint( mePosLogical, 1, ty );
     s.SetActivePos( tp + tmp.length + 1 );
     s.SetAnchorPos( tp );
   }
   }
}
}


ScrollX = sxScrollY = sy;
/**
Redraw = true;
* 関数 GetBookmark( ty, by )
* 指定された範囲のブックマークを収集
*/
function GetBookmark( ty, by ) {
  var bmArray = [],  bmY;
  var s = editor.ActiveDocument.selection;
  if ( ty < by ) {
    var anc = s.GetAnchorPos(), act = s.GetActivePos();
 
    if ( ty === 1 ) {
      s.SetActivePoint( mePosLogical, 1, 2 );
      if ( s.PreviousBookmark() ) {
        bmY = s.GetActivePointY( mePosLogical );
        if ( bmY >= ty && bmY <= by ) {
          bmArray.push( bmY );
        }
      }
      s.SetActivePos( 0 );
    }
    else { s.SetActivePoint( mePosLogical, 1, ty - 1 ); }


if ( ! d.ReadOnly ) {
    while ( s.NextBookmark() ) {
  var elapsedSec = ( ( new Date() - start ) / 1000 ).toFixed( 3 );
      bmY = s.GetActivePointY( mePosLogical );
  Status = " 「スペース×2 削除」マクロ  [ "
      if ( bmY >= ty && bmY <= by ) {
        + elapsedSec.replace( /\./, ". " )
        bmArray.push( bmY );
        + " 秒 ]";
      }
      else if ( bmY > by ) { break; }
    }
    s.SetActivePos( act );  s.SetAnchorPos( anc );
  }
  return bmArray;
}
}


/**
* 関数 RestoreBookmark( ty, by, bmArray )
* 指定された範囲のブックマークを復元
*/
function RestoreBookmark( ty, by, bmArray, sel ) {
  var bmCount = bmArray.length;
  if ( bmCount ) {
    var s = editor.ActiveDocument.selection;
    var anc = s.GetAnchorPos(),  act = s.GetActivePos();
    s.SetActivePoint( mePosLogical, 1, ty );
    s.ClearBookmark();
    for ( var i = 0; i < bmCount, bmArray[i] <= by; i ++ ) {
      if ( bmArray[i] < ty ) { continue; }
      s.SetActivePoint( mePosLogical, 1, bmArray[i], false );
      s.SetBookmark();
    }
    if ( sel ) {
      s.SetActivePos( act );  s.SetAnchorPos( anc );
    }
  }
}


/**
/**
  * 関数 DeleteSpace( str, blank )
  * 関数 MultiFunction( Fn, arg1 )
  * str の各行の先頭から blank を削除する
  * マルチカーソル(複数選択範囲)に対応させる
  * ※ blank は空白文字列のパターンを収録した配列
  * 第1引数: Function; 選択範囲ごとに適用する処理の関数
* 第2引数: Function に渡す引数をまとめた配列
  */
  */
function DeleteSpace( str, blank ) {
function MultiFunction( Fn, arg ) {
   var a = str.split( "\n" );
   var d = editor.ActiveDocument;
   for ( var i = 0, aLen = a.length; i < aLen; i ++ ) {
  var s = d.selection;
     if ( ! a[i] ) { continue; }
 
    for ( var j = 0, bLen = blank.length; j < bLen; j ++ ) {
  // 矩形選択範囲は行に分ける
      if ( blank[j] && a[i].substr( 0, blank[j].length ) == blank[j] ) {
  s.Mode = meModeMulti;
        a[i] = a[i].substr( blank[j].length );
 
        break;
  // 選択範囲の座標を取得
      }
  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 ); // DeleteSpace2_Main() 関数
 
    // Fn() の残した選択範囲(またはキャレット位置)を回収
    Sel[i].act = s.GetActivePos();
    Sel[i].anc = s.GetAnchorPos();
    diff += d.TextLength - dl; // 文字数の増減量(累積)
  }
 
  // マルチカーソル(複数選択範囲)を復帰
  s.SetActivePos( Sel[0].anc );
  for ( var i = 0; i < sCount; i ++ ) {
     s.AddPos( Sel[i].anc, Sel[i].act );
   }
   }
  return a.join( "\n" );
}
}
</source>
</source>
431行目: 608行目:
* またはカーソル位置にタブ文字を挿入します。
* またはカーソル位置にタブ文字を挿入します。
* 選択範囲が末尾に改行を含んでいた場合は、最後の改行を無視します。
* 選択範囲が末尾に改行を含んでいた場合は、最後の改行を無視します。
* <span style="color:#0000c0;">マルチカーソル/複数選択範囲に対応(Mery ver 3.0.1 以上)</span>




437行目: 615行目:
ツールバーアイコンにするなら <syntaxhighlight lang="javascript" inline>var tabEnable = 0</syntaxhighlight>
ツールバーアイコンにするなら <syntaxhighlight lang="javascript" inline>var tabEnable = 0</syntaxhighlight>


<source lang="javascript" style="height:40em; overflow:auto;">
<source lang="javascript" style="height:60em; overflow:auto;">
#title = "インデント"
#title = "インデント"
#tooltip = "選択範囲がなくてもインデント"
#tooltip = "選択範囲がなくてもインデント"
448行目: 626行目:
  * ---------------------------------------------------------
  * ---------------------------------------------------------
  * 「インデント」マクロ
  * 「インデント」マクロ
  *  sukemaru, 2018/11/15 - 2019/12/01
  *  sukemaru, 2018/11/15 - 2020/06/29
  * ---------------------------------------------------------
  * ---------------------------------------------------------
  * ・選択範囲なしでも、論理行全体をタブインデント(行頭)して範囲選択する
  * ・選択範囲なしでも、論理行全体をタブインデント(行頭)して範囲選択する
457行目: 635行目:
  *              ツールバーアイコンにするなら tabEnable = 0
  *              ツールバーアイコンにするなら tabEnable = 0
  *  
  *  
  * 2019/11/17: ブックマークの保守方法を変更
  * 2020/06/29: マルチカーソル/複数選択範囲に対応
  * 2019/12/01: 複数行選択のときのみブックマークの保存/復元するように変更
  *             ブックマーク行を字下げしたときのみブックマークの復元をするように変更
*            ブックマークの収集範囲を選択範囲に限定
*            ブックマークの収集/復元コードを関数化
  */
  */


494行目: 674行目:


// ---------- ▲ 設定項目 ▲ ----------
// ---------- ▲ 設定項目 ▲ ----------
var tab = "\t";


var d = editor.ActiveDocument;
var d = editor.ActiveDocument;
var s = d.selection;


if ( d.ReadOnly ) {
if ( d.ReadOnly ) {
505行目: 682行目:


else {
else {
   Redraw = false;
   var s = d.selection;
   var sx = ScrollX,  sy = ScrollY;
   var sx = ScrollX,  sy = ScrollY;
  var tab = "\t";
  // マルチカーソル/複数選択に対応
  var arg = [ tabEnable, tab ];
  // 選択範囲が1つで矩形選択ではないとき
  if ( ! s.Mode || s.Mode === 1 ) {
    Indent_Main( arg );
  }
  // 矩形選択または複数選択のとき
  else {
    BeginUndoGroup();
    AddUndo();
    MultiFunction( Indent_Main, arg );
    EndUndoGroup();
  }
  ScrollX = sx;  ScrollY = sy;
  var elapsedSec = ( ( new Date() - start ) / 1000 ).toFixed( 3 );
  Status = " 「インデント」マクロ  [ "
        + elapsedSec.replace( /\./, ". " )
        + " 秒 ]";
}
/**
* 関数 Indent_Main( [ tabEnable, tab ] )
* 「インデント」マクロ
*/
function Indent_Main( arg ) {
  var tabEnable = arg[0];
  var tab      = arg[1];


   // 選択範囲
   // 選択範囲
515行目: 723行目:
   var bx = s.GetBottomPointX( mePosLogical );
   var bx = s.GetBottomPointX( mePosLogical );
   var by = yy = s.GetBottomPointY( mePosLogical );
   var by = yy = s.GetBottomPointY( mePosLogical );
 
   var inLine = ( ty === by && act !== anc );
   var inLine = ( ty == by && act != anc );


   /* 行の途中の選択範囲を字下げするパターン */
   /* 行の途中の選択範囲を字下げするパターン */
   if ( inLine && tabEnable == 4 ) {
   if ( tabEnable === 4 && inLine ) {
     s.Text = tab + s.Text;
     s.Text = tab + s.Text;
     s.SetAnchorPoint( mePosLogical, tx, ty );
     s.SetAnchorPoint( mePosLogical, tx, ty );
525行目: 732行目:


   /* カーソル位置にタブを挿入するパターン */
   /* カーソル位置にタブを挿入するパターン */
   else if ( ( tabEnable == 1 && s.IsEmpty )
   else if ( ( tabEnable === 1 && s.IsEmpty )
         || ( tabEnable == 2 && ty == by )
         || ( tabEnable === 2 && ty === by )
         ||  tabEnable == 3 ) {
         ||  tabEnable === 3 ) {
     s.Text = tab;
     s.Text = tab;
   }
   }
533行目: 740行目:
   else {
   else {
     // 選択範囲を拡張
     // 選択範囲を拡張
     if ( ty != by && bx == 1 ) { yy --; }
     if ( ty !== by && bx === 1 ) { yy --; }
     s.SetActivePoint( mePosLogical, 1, yy );
     s.SetActivePoint( mePosLogical, d.GetLine( yy, 0 ).length + 1, yy );
    s.EndOfLine( false, mePosLogical );
     s.SetAnchorPoint( mePosLogical, 1, ty );
     s.SetAnchorPoint( mePosLogical, 1, ty );


     // ブックマークを保存
     /* インデント */
     var bmArray = [];
    var tp = s.GetAnchorPos();
    if ( ty < by ) {
    var st = s.Text;
      var tp = s.GetAnchorPos(),  bp = s.GetActivePos();
    var tmp = st.replace( /^/gm , tab );
    if ( tmp !== st ) {
      // ブックマークを保存
      var bmArray = GetBookmark( ty, by );
      // インデント完了
      s.Text = tmp;
      // ブックマークを復元
      RestoreBookmark( ty, by, bmArray );
    }
 
    // 選択範囲を復元
     if ( ty === by ) {
      s.SetActivePos( act + tab.length );
      s.SetAnchorPos( anc + tab.length );
    }
    else {
      s.SetActivePos( tp + tmp.length + 1 );
      s.SetAnchorPos( tp );
    }
  }
}
 
/**
* 関数 GetBookmark( ty, by )
* 指定された範囲のブックマークを収集
*/
function GetBookmark( ty, by ) {
  var bmArray = [],  bmY;
  var s = editor.ActiveDocument.selection;
  if ( ty < by ) {
    var anc = s.GetAnchorPos(),  act = s.GetActivePos();
 
    if ( ty === 1 ) {
       s.SetActivePoint( mePosLogical, 1, 2 );
       s.SetActivePoint( mePosLogical, 1, 2 );
       if ( s.PreviousBookmark() ) {
       if ( s.PreviousBookmark() ) {
         bmArray.push( s.GetActivePointY( mePosLogical ) );
         bmY = s.GetActivePointY( mePosLogical );
        if ( bmY >= ty && bmY <= by ) {
          bmArray.push( bmY );
        }
       }
       }
       s.SetActivePos( 0 );
       s.SetActivePos( 0 );
      while ( s.NextBookmark() ) {
        bmArray.push( s.GetActivePointY( mePosLogical ) );
      }
      s.SetAnchorPos( tp );  s.SetActivePos( bp, true );
      ScrollX = sx;  ScrollY = sy;
     }
     }
     var bmCount = bmArray.length;
     else { s.SetActivePoint( mePosLogical, 1, ty - 1 ); }


     // インデント
     while ( s.NextBookmark() ) {
    var st = s.Text;
       bmY = s.GetActivePointY( mePosLogical );
    var t = st.replace( /^/gm , tab );
      if ( bmY >= ty && bmY <= by ) {
    if ( t != st ) {
        bmArray.push( bmY );
       s.Text = t;
 
      // ブックマークを復元
      if ( bmCount ) {
        var ep = s.GetActivePos();
        editor.ExecuteCommandByID( 2129 ); // ブックマークをクリア
        for ( var i = 0; i < bmCount; i ++ ) {
          s.SetActivePoint( mePosLogical, 1, bmArray[i], false );
          editor.ExecuteCommandByID( 2126 ); // ブックマークを設定
        }
        s.SetActivePos( ep );
       }
       }
      else if ( bmY > by ) { break; }
     }
     }
    s.SetActivePos( act );  s.SetAnchorPos( anc );
  }
  return bmArray;
}


    // 選択範囲を復元
/**
     if ( ty == by ) {
* 関数 RestoreBookmark( ty, by, bmArray )
       s.SetActivePos( act + tab.length );
* 指定された範囲のブックマークを復元
       s.SetAnchorPos( anc + tab.length );
*/
function RestoreBookmark( ty, by, bmArray, sel ) {
  var bmCount = bmArray.length;
  if ( bmCount ) {
    var s = editor.ActiveDocument.selection;
     var anc = s.GetAnchorPos(),  act = s.GetActivePos();
    s.SetActivePoint( mePosLogical, 1, ty );
    s.ClearBookmark();
    for ( var i = 0; i < bmCount, bmArray[i] <= by; i ++ ) {
      if ( bmArray[i] < ty ) { continue; }
       s.SetActivePoint( mePosLogical, 1, bmArray[i], false );
       s.SetBookmark();
     }
     }
     else {
     if ( sel ) {
       s.SetActivePos( s.GetActivePos() + 1 );
       s.SetActivePos( act ); s.SetAnchorPos( anc );
      s.SetAnchorPoint( mePosLogical, 1, ty );
     }
     }
  }
}
/**
* 関数 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 )
    };
   }
   }


   ScrollX = sx; ScrollY = sy;
   // 各選択範囲を処理
  Redraw = true;
  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 ); // Indent_Main() 関数
 
    // Fn() の残した選択範囲(またはキャレット位置)を回収
    Sel[i].act = s.GetActivePos();
    Sel[i].anc = s.GetAnchorPos();
    diff += d.TextLength - dl; // 文字数の増減量(累積)
  }


   var elapsedSec = ( ( new Date() - start ) / 1000 ).toFixed( 3 );
   // マルチカーソル(複数選択範囲)を復帰
   Status = " 「インデント」マクロ  [ "
  s.SetActivePos( Sel[0].anc );
        + elapsedSec.replace( /\./, ". " )
   for ( var i = 0; i < sCount; i ++ ) {
        + " 秒 ]";
    s.AddPos( Sel[i].anc, Sel[i].act );
  }
}
}
</source>
</source>
604行目: 880行目:
* 選択範囲ありなら論理行全体をアンインデント(行頭)して範囲選択します。
* 選択範囲ありなら論理行全体をアンインデント(行頭)して範囲選択します。
* 選択範囲が末尾に改行を含んでいた場合は、最後の改行を無視します。
* 選択範囲が末尾に改行を含んでいた場合は、最後の改行を無視します。
* <span style="color:#0000c0;">マルチカーソル/複数選択範囲に対応(Mery ver 3.0.1 以上)</span>




614行目: 891行目:
※ sukemaru 版の「逆インデント」と「字上げ(スペース×2 削除)」マクロを並存させるメリットは特にないとおもいますので、どちらかお好みで選んでください。
※ sukemaru 版の「逆インデント」と「字上げ(スペース×2 削除)」マクロを並存させるメリットは特にないとおもいますので、どちらかお好みで選んでください。


<source lang="javascript" style="height:40em; overflow:auto;">
<source lang="javascript" style="height:60em; overflow:auto;">
#title = "逆インデント"
#title = "逆インデント"
#tooltip = "選択範囲がなくてもアンインデント"
#tooltip = "選択範囲がなくてもアンインデント"
625行目: 902行目:
  * ---------------------------------------------------------
  * ---------------------------------------------------------
  * 「逆インデント」マクロ
  * 「逆インデント」マクロ
  *  sukemaru, 2018/11/15 - 2019/12/01
  *  sukemaru, 2018/11/15 - 2020/06/29
  * ---------------------------------------------------------
  * ---------------------------------------------------------
  * 選択範囲なしでも、行頭またはカーソル付近の「タブ文字×1」か、
  * 選択範囲なしでも、行頭またはカーソル付近の「タブ文字×1」か、
633行目: 910行目:
  * ・選択範囲ありなら論理行全体をアンインデントして範囲選択する
  * ・選択範囲ありなら論理行全体をアンインデントして範囲選択する
  *  
  *  
  * 2019/11/17: ブックマークの保守方法を変更
  * 2020/06/29: マルチカーソル/複数選択範囲に対応
  * 2019/12/01: 複数行選択のときのみブックマークの保存/復元するように変更
  *             ブックマーク行を字下げしたときのみブックマークの復元をするように変更
*            ブックマークの収集範囲を選択範囲に限定
*            ブックマークの収集/復元コードを関数化
  */
  */


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


// ■選択範囲なしのときに、カーソル位置の前後にある「タブ文字」を優先的に削除するか?
// ■ 選択範囲なしのときに、カーソル位置の前後にある「タブ文字」を優先的に削除するか?
// ※ 選択範囲が1行内で、先頭に「タブ文字」がある場合も優先的に削除する
// ※ 選択範囲が1行内で、先頭に「タブ文字」がある場合も優先的に削除する
var deleteNearestTab = true;
var deleteNearestTab = true;
649行目: 928行目:




// ■行頭の「半角スペース」や「全角スペース」も削除するか?
// ■ 行頭の「半角スペース」や「全角スペース」も削除するか?
var deleteSpace = 1;
var deleteSpace = 1;
   /**
   /**
661行目: 940行目:




    // ■deleteSpace が 3 以上のとき、削除できる「全角スペース」の最大数
  // ■ deleteSpace が 3 以上のとき、削除できる「全角スペース」の最大数
    var roundDecimal = false;
  var roundDecimal = false;
      /**
    /**
      * ※ deleteSpace が奇数のときの小数点以下の処理
    * ※ deleteSpace が奇数のときの小数点以下の処理
      * false にすると ( deleteSpace / 2 ) の小数点以下を切り捨て
    * false にすると ( deleteSpace / 2 ) の小数点以下を切り捨て
      * true にすると  ( deleteSpace / 2 ) の小数点以下を切り上げ(四捨五入)
    * true にすると  ( deleteSpace / 2 ) の小数点以下を切り上げ(四捨五入)
      */
    */


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


Redraw = false;
var sx = ScrollX,  sy = ScrollY;
var d = editor.ActiveDocument;
var d = editor.ActiveDocument;
var s = d.selection;
var pos = s.IsEmpty ? s.GetActivePos() : ""; // "選択範囲なし" のフラグ
var isDone = false; // Quit() メソッドの代用フラグ


if ( d.ReadOnly ) {
if ( d.ReadOnly ) {
   Status = " ドキュメントは書き換え禁止です。";
   Status = " ドキュメントは書き換え禁止です。";
  isDone = true;
}
}
else {
  var s = d.selection;
  var sx = ScrollX,  sy = ScrollY;


/* カーソル付近の「タブ文字×1」を削除するパターン(左側優先) */
  // マルチカーソル/複数選択に対応
else if ( pos && deleteNearestTab ) {
  var arg = [ deleteNearestTab, deleteSpace, roundDecimal ];
   if ( d.Text.charAt( pos - 1 ) == "\t" ) {
  // 選択範囲が1つで矩形選択ではないとき
     s.DeleteLeft();
   if ( ! s.Mode || s.Mode === 1 ) {
    isDone = true;
     Unindent_Main( arg );
   }
   }
   else if ( d.Text.charAt( pos ) == "\t" ) {
  // 矩形選択または複数選択のとき
     s.Delete();
   else {
     isDone = true;
    BeginUndoGroup();
    AddUndo();
     MultiFunction( Unindent_Main, arg );
     EndUndoGroup();
   }
   }
  ScrollX = sx;  ScrollY = sy;
  var elapsedSec = ( ( new Date() - start ) / 1000 ).toFixed( 3 );
  Status = " 「逆インデント」マクロ  [ "
        + elapsedSec.replace( /\./, ". " )
        + " 秒 ]";
}
}


if ( ! isDone ) {
/**
* 関数 Unindent_Main( [ deleteNearestTab, deleteSpace, roundDecimal ] )
* 「逆インデント」マクロ
*/
function Unindent_Main( arg ) {
  var deleteNearestTab = arg[0];
  var deleteSpace      = arg[1];
  var roundDecimal    = arg[2];
 
  var d = editor.ActiveDocument;
  var s = editor.ActiveDocument.selection;
 
  // "選択範囲なし" のフラグ
  var pos = s.IsEmpty ? s.GetActivePos() : -1;
 
  /* カーソル付近の「タブ文字×1」を削除するパターン(左側優先) */
  if ( pos > -1 && deleteNearestTab ) {
    if ( d.Text.charAt( pos - 1 ) === "\t" ) {
      s.DeleteLeft();  return;
    }
    else if ( d.Text.charAt( pos ) === "\t" ) {
      s.Delete();  return;
    }
  }
 
   // 選択範囲
   // 選択範囲
   var act = s.GetActivePos();
   var act = s.GetActivePos();
704行目: 1,012行目:
   var bx = s.GetBottomPointX( mePosLogical );
   var bx = s.GetBottomPointX( mePosLogical );
   var by = yy = s.GetBottomPointY( mePosLogical );
   var by = yy = s.GetBottomPointY( mePosLogical );
   var inLine = ( ty == by && act != anc );
   var inLine = ( ty === by && act !== anc );
   var st = s.Text;
   var st = s.Text;


   /* 選択範囲が1行内のときに、先頭の「タブ文字」を優先的に削除するパターン */
   /* 選択範囲が1行内のときに、先頭の「タブ文字」を優先的に削除するパターン */
   if ( inLine && deleteNearestTab && st.charAt( 0 ) == "\t" ) {
   if ( inLine && deleteNearestTab && st.charAt( 0 ) === "\t" ) {
     s.Text = st.replace( /^\t/, "" );
     s.Text = st.replace( /^\t/, "" );
     s.SetAnchorPoint( mePosLogical, tx, ty );
     s.SetAnchorPoint( mePosLogical, tx, ty );
715行目: 1,023行目:
   else {
   else {
     // 選択範囲を拡張
     // 選択範囲を拡張
     if ( ty != by && bx == 1 ) { yy --; }
     if ( ty !== by && bx === 1 ) { yy --; }
     s.SetActivePoint( mePosLogical, 1, yy );
     s.SetActivePoint( mePosLogical, d.GetLine( yy, 0 ).length + 1, yy );
    s.EndOfLine( false, mePosLogical );
     s.SetAnchorPoint( mePosLogical, 1, ty );
     s.SetAnchorPoint( mePosLogical, 1, ty );
    // ブックマークを保存
    var bmArray = [];
    if ( ty < by ) {
      var tp = s.GetAnchorPos(),  bp = s.GetActivePos();
      s.SetActivePoint( mePosLogical, 1, 2 );
      if ( s.PreviousBookmark() ) {
        bmArray.push( s.GetActivePointY( mePosLogical ) );
      }
      s.SetActivePos( 0 );
      while ( s.NextBookmark() ) {
        bmArray.push( s.GetActivePointY( mePosLogical ) );
      }
      s.SetAnchorPos( tp );  s.SetActivePos( bp, true );
      ScrollX = sx;  ScrollY = sy;
    }
    var bmCount = bmArray.length;


     /* 逆インデント */
     /* 逆インデント */
744行目: 1,034行目:
                                 : Math.floor( deleteSpace / 2 ) ) // 切り捨て
                                 : Math.floor( deleteSpace / 2 ) ) // 切り捨て
                 + "}";
                 + "}";
     var indent = ( deleteSpace == 1 ) ? regStr1
     var indent = ( deleteSpace === 1 ) ? regStr1
               : ( deleteSpace == 2 ) ? regStr2
               : ( deleteSpace === 2 ) ? regStr2
               : ( deleteSpace >= 3 ) ? regStr3
               : ( deleteSpace >= 3 ) ? regStr3
               : /*    else      */   "^\\t";
               : /*    else      */   "^\\t";


     st = s.Text;
     st = s.Text;
     var u = st.replace( new RegExp( indent , "gm" ) , "" );
     var tp = s.GetAnchorPos();
     if ( u != st ) {
    var tmp = st.replace( new RegExp( indent , "gm" ) , "" );
       s.Text = u;
     if ( tmp !== st ) {
 
      // ブックマークを保存
      var bmArray = GetBookmark( ty, yy );
      // 逆インデント完了
       s.Text = tmp;
       // ブックマークを復元
       // ブックマークを復元
       if ( bmCount ) {
       RestoreBookmark( ty, yy, bmArray );
        var ep = s.GetActivePos();
        editor.ExecuteCommandByID( 2129 ); // ブックマークをクリア
        for ( var i = 0; i < bmCount; i ++ ) {
          s.SetActivePoint( mePosLogical, 1, bmArray[i], false );
          editor.ExecuteCommandByID( 2126 ); // ブックマークを設定/解除(設定)
        }
        s.SetActivePos( ep );
      }
     }
     }


     // 選択範囲が1行だったときは、位置関係を維持して選択範囲を復元する
     // 選択範囲が1行だったときは、位置関係を維持して選択範囲を復元する
     if ( ty == by ) {
     if ( ty === by ) {
       var gap = st.length - u.length;
       var gap = st.length - tmp.length;
       if ( bx <= gap ) {
       if ( bx <= gap ) {
         s.SetActivePoint( mePosLogical, 1, ty );
         s.SetActivePoint( mePosLogical, 1, ty );
787行目: 1,072行目:
     // 選択範囲が複数行だったときは、拡張した選択範囲を復元する
     // 選択範囲が複数行だったときは、拡張した選択範囲を復元する
     else {
     else {
       s.SetActivePos( s.GetActivePos() + 1 );
       s.SetActivePos( tp + tmp.length + 1 );
       s.SetAnchorPoint( mePosLogical, 1, ty );
      s.SetAnchorPos( tp );
    }
  }
}
 
/**
* 関数 GetBookmark( ty, by )
* 指定された範囲のブックマークを収集
*/
function GetBookmark( ty, by ) {
  var bmArray = [],  bmY;
  var s = editor.ActiveDocument.selection;
  if ( ty < by ) {
    var anc = s.GetAnchorPos(),  act = s.GetActivePos();
 
    if ( ty === 1 ) {
      s.SetActivePoint( mePosLogical, 1, 2 );
      if ( s.PreviousBookmark() ) {
        bmY = s.GetActivePointY( mePosLogical );
        if ( bmY >= ty && bmY <= by ) {
          bmArray.push( bmY );
        }
      }
      s.SetActivePos( 0 );
    }
    else { s.SetActivePoint( mePosLogical, 1, ty - 1 ); }
 
    while ( s.NextBookmark() ) {
      bmY = s.GetActivePointY( mePosLogical );
      if ( bmY >= ty && bmY <= by ) {
        bmArray.push( bmY );
      }
       else if ( bmY > by ) { break; }
    }
    s.SetActivePos( act );  s.SetAnchorPos( anc );
  }
  return bmArray;
}
 
/**
* 関数 RestoreBookmark( ty, by, bmArray )
* 指定された範囲のブックマークを復元
*/
function RestoreBookmark( ty, by, bmArray, sel ) {
  var bmCount = bmArray.length;
  if ( bmCount ) {
    var s = editor.ActiveDocument.selection;
    var anc = s.GetAnchorPos(),  act = s.GetActivePos();
    s.SetActivePoint( mePosLogical, 1, ty );
    s.ClearBookmark();
    for ( var i = 0; i < bmCount, bmArray[i] <= by; i ++ ) {
      if ( bmArray[i] < ty ) { continue; }
      s.SetActivePoint( mePosLogical, 1, bmArray[i], false );
      s.SetBookmark();
    }
    if ( sel ) {
      s.SetActivePos( act );  s.SetAnchorPos( anc );
     }
     }
   }
   }
}
}


ScrollX = sx; ScrollY = sy;
/**
Redraw = true;
* 関数 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 ); // Unindent_Main() 関数


if ( ! d.ReadOnly ) {
    // Fn() の残した選択範囲(またはキャレット位置)を回収
  var elapsedSec = ( ( new Date() - start ) / 1000 ).toFixed( 3 );
    Sel[i].act = s.GetActivePos();
   Status = " 「逆インデント」マクロ  [ "
    Sel[i].anc = s.GetAnchorPos();
        + elapsedSec.replace( /\./, ". " )
    diff += d.TextLength - dl; // 文字数の増減量(累積)
        + " 秒 ]";
  }
 
  // マルチカーソル(複数選択範囲)を復帰
  s.SetActivePos( Sel[0].anc );
   for ( var i = 0; i < sCount; i ++ ) {
    s.AddPos( Sel[i].anc, Sel[i].act );
  }
}
}
</source>
</source>
806行目: 1,181行目:


== メモ ==
== メモ ==
*(2019/04/07 sukemaru)
'''2019/04/07''' (sukemaru)
:ソースコードから Quit() メソッドを削除
:ソースコードから Quit() メソッドを削除
----
----
*(2019/11/10 sukemaru)
'''2019/11/10''' (sukemaru)
:Mery 2.8.6 以上でブックマークを保持
:Mery 2.8.6 以上でブックマークを保持
:※ Mery 2.8.5 以前ではブックマーク機能の仕様が不安定なので、個別対応はあきらめた…。
:※ Mery 2.8.5 以前ではブックマーク機能の仕様が不安定なので、個別対応はあきらめた…。
----
----
*(2019/11/17 sukemaru)
'''2019/11/17''' (sukemaru)
:Mery 2.8.6 以上、または Mery 2.6.7 でブックマークを保持
:Mery 2.8.6 以上、または Mery 2.6.7 でブックマークを保持
:※ Mery 2.7.0 - 2.8.5 ではブックマーク機能の仕様が不安定なので、1行のみの字下げ/字上げ(インデント/逆インデント)をしたときにブックマークがおかしくなることがある。 現時点での最新の正式版と最新ベータ版で正常に機能するように調整した。
:※ Mery 2.7.0 - 2.8.5 ではブックマーク機能の仕様が不安定なので、1行のみの「字下げ/字上げ (インデント/逆インデント)」をしたときにブックマークがおかしくなることがある。<br> 現時点での最新の正式版と最新ベータ版で正常に機能するように調整した。
*(2019/11/18 sukemaru)
'''2019/11/18''' (sukemaru)
:「スペース×2 削除.js」のソースコードで設定項目部分がダブっていたのを修正
:「スペース×2 削除.js」のソースコードで設定項目部分がダブっていたのを修正
----
----
*(2019/12/01 sukemaru)
'''2019/12/01''' (sukemaru)
: 複数行選択のときのみブックマークの保存/復元するように変更
: 複数行選択のときのみブックマークの保存/復元するように変更
:(ブックマークが大量にある文書で、範囲選択1行内で実行したときの速度を改善)
:(ブックマークが大量にある文書で、範囲選択1行内で実行したときの速度を改善)
----
'''2020/06/29''' (sukemaru)
: マルチカーソル/複数選択範囲に対応
: ブックマークの収集/復元コードを関数化
: (ブックマークの収集を選択範囲内の行に限定し、ブックマーク行を「字下げ/字上げ」したときのみ復元処理をするように変更)
----
----

2020年6月29日 (月) 21:08時点における版

論理行単位で字下げ・字上げします。
インデントには任意の文字列を設定できます。※初期設定は半角空白2個です。

masme 版

更新履歴

2019/06/01
  • 字下げ・字上げした次の行に変更行の印がつかないようにした。
    末尾の改行(次の行頭)を範囲に含めず書き換えるように変更。
  • with 文を使用せず変数化した(他の自作マクロに合わせた)。
2019/04/12
  • (字下げ) Quit() → break ラベル文に変更。
2015/02/14
  • (字上げ) 「インデントの定義」に正規表現の特殊文字があると誤動作する不具合を修正。
  • 矩形選択(始点or終点が左下かつ論理行頭)時、下端の行が範囲から漏れる不具合を修正。
2014/02/09
  • 初版公開。

字下げ (インデント)

  • 複数行選択時 (選択範囲が2行以上) は字下げします。
    単一行選択時 (選択範囲が1行以内) または非選択時はタブ文字を挿入します。
    ※選択範囲の行数はステータスバーの「xx文字 (xx行) 選択」の部分で判断してください。
  • 空行 (改行のみの行) は初期状態では字下げしません。
  • 「Tab」キーなどに割り当ててご利用ください。
//■字下げ(インデント)
// 2014/02/09-2019/06/01
//・論理行単位で字下げする。空行は字下げしない。非選択時/単行選択時はタブ挿入。

//■インデントの定義 ●初期値="  "
var INDENT = "  ";

quit: {
var Sel = Document.Selection;
if (Sel.GetTopPointY(mePosView)===Sel.GetBottomPointY(mePosView)) {
  Sel.Text = "\t"; break quit; //非選択時/単行選択時はタブ挿入して終了
}
var ty = Sel.GetTopPointY(mePosLogical);
var by = Sel.GetBottomPointY(mePosLogical);
var bx = Sel.GetBottomPointX(mePosLogical);
var nn =(Sel.Text.match(/\n/g)||[]).length;
if (bx===1 && nn<=by-ty && nn) by--; //末尾改行対策
Sel.SetActivePoint(mePosLogical, 1, by);
Sel.EndOfLine(false, mePosLogical);
Sel.SetAnchorPoint(mePosLogical, 1, ty);
Sel.Text = Sel.Text.replace(/^(?!$)/gm,INDENT);
Sel.SetActivePoint(mePosLogical, 1, by+1);
Sel.SetAnchorPoint(mePosLogical, 1, ty);
}

字上げ (アンインデント)

  • 単一行選択時 (選択範囲が1行以内) または非選択時でも字上げします。
  • 「Shift + Tab」キーなどに割り当ててご利用ください。
//■字上げ(アンインデント)
// 2014/02/09-2019/06/01
//・論理行単位で字上げする。非選択時/単行選択時でも字上げする。

//■インデントの定義 ●初期値="  "
var INDENT = "  ";

var Sel = Document.Selection;
var ty = Sel.GetTopPointY(mePosLogical);
var by = Sel.GetBottomPointY(mePosLogical);
var bx = Sel.GetBottomPointX(mePosLogical);
var nn =(Sel.Text.match(/\n/g)||[]).length;
if (bx===1 && nn<=by-ty && nn) by--; //末尾改行対策
Sel.SetActivePoint(mePosLogical, 1, by);
Sel.EndOfLine(false, mePosLogical);
Sel.SetAnchorPoint(mePosLogical, 1, ty);
var pat = "^"+ INDENT.replace(/[$()*+.?\[\\\]^{|}]/g,"\\$&");
Sel.Text = Sel.Text.replace(new RegExp(pat,"gm"),"");
Sel.SetActivePoint(mePosLogical, 1, by+1);
Sel.SetAnchorPoint(mePosLogical, 1, ty);

sukemaru 版


ダウンロード:ファイル:字下げ/字上げ.zip」(アイコン入り)
最終更新: 2020/06/29 マルチカーソルブックマーク に対応

それぞれ専用の「マテリアルデザインっぽいアイコン」を同梱


  • マルチカーソル/複数選択範囲に対応(Mery ver 3.0.1 以上
  • ブックマーク対応の推奨要件: Mery ver 2.8.6 以上
  Mery ver 2.6.7 と ver 2.8.6 以上でブックマークを正常に保持できることを確認しています。
※ Mery ver 2.7.0 - ver 2.8.5 では、Mery 本体側のブックマーク機能の仕様が安定していないので、ブックマークを正しく復元できないことがあります。


字下げ(スペース×2 追加)

スペース×2 追加.js

キャレットまたは選択範囲をふくむ論理行の各先頭に「半角スペース×2」を追加します。

  • 選択範囲がなかった場合は「タブ文字」を追加、または「半角スペース×2」を行頭に追加します。
  • 選択範囲があった場合は、つねに「半角スペース×2」を行頭に追加します。
  • 行頭に「半角スペース×2」を追加する場合は、空行を無視します。
  • 選択範囲が末尾に改行を含んでいた場合は、最後の改行を無視します。
  • マルチカーソル/複数選択範囲に対応(Mery ver 3.0.1 以上)
#title = "字下げ"
#tooltip = "スペース×2 追加"
// #icon = "space_x2[1].ico"
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",247

var start = new Date();

/**
 * ---------------------------------------------------------
 * 「字下げ(スペース×2 追加)」マクロ
 *   sukemaru, 2018/11/01 - 2020/06/29
 * ---------------------------------------------------------
 * 最初に選択範囲がなかった場合は「タブ文字」を追加、
 * または「半角スペース×2」を行頭に追加する。
 * 選択範囲があった場合は、つねに「半角スペース×2」を行頭に追加する(空行は無視)。
 * 
 * 2020/06/29: マルチカーソル/複数選択範囲に対応
 *             ブックマーク行を字下げしたときのみブックマークの復元をするように変更
 *             ブックマークの収集範囲を選択範囲に限定
 *             ブックマークの収集/復元コードを関数化
 *             行頭空白追加の処理をループ関数から正規表現置換に変更
 */

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

// ■ 選択範囲なしのとき
var tabEnable = 0;
  /**
   * 0: 「半角空白×2」を行頭に追加する(空行は無視)
   * 1: 選択範囲なしのときは「タブ文字×1」を行頭に追加
   * 2: 選択範囲なしのときは「タブ文字×1」をキャレット位置に追加
   */


// ■ 行頭に追加する空白文字の定義(初期値:「半角空白×2」)
var blank = "  ";		// 半角空白×2

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

var tab = "\t";

var d = editor.ActiveDocument;
if ( d.ReadOnly ) {
  Status = " ドキュメントは書き換え禁止です。";
}
else {
  var s = d.selection;
  var sx = ScrollX,  sy = ScrollY;

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

  var elapsedSec = ( ( new Date() - start ) / 1000 ).toFixed( 3 );
  Status = " 「スペース×2 追加」マクロ  [ "
         + elapsedSec.replace( /\./, ". " )
         + " 秒 ]";
}


/**
 * 関数 AddSpace2_Main( [ tabEnable, blank ] )
 * 「字上げ(スペース×2 追加)」マクロ
 */
function AddSpace2_Main( arg ) {
  var tabEnable = arg[0];
  var blank     = arg[1];

  var d = editor.ActiveDocument;
  var s = d.selection;

  // "選択範囲なし" のフラグ
  var pos = s.IsEmpty ? s.GetActivePos() : -1;

  /* キャレット位置に「タブ文字×1」を挿入するパターン */
  if ( tabEnable === 2 && pos > -1 ) {
    s.Text = tab;
  }

  else {
    var ty = s.GetTopPointY( mePosLogical );
    var bx = s.GetBottomPointX( mePosLogical );
    var by = s.GetBottomPointY( mePosLogical );
    if ( by !== ty && bx === 1 ) { by -= 1; }
    s.SetActivePoint( mePosLogical, d.GetLine( by, 0 ).length + 1, by );
    s.SetAnchorPoint( mePosLogical, 1, ty );

    // 選択範囲の文字列を取得
    var st = s.Text;
    var tp = s.GetAnchorPos();

    /* 最初に選択範囲がなかった場合は、行頭に「タブ文字×1」を追加するパターン */
    if ( tabEnable === 1 && pos > -1 ) {
      s.Text = tab + st;
      s.SetActivePos( pos + tab.length );	// 範囲選択を残さない
    }

    /* 選択範囲があった場合は、行頭に「半角スペース×2」を追加(空行は無視) */
    else {
      var tmp = st.replace( /^(?!$)/gm, blank );
      if ( tmp !== st ) {
        // ブックマークを保存
        var bmArray = GetBookmark( ty, by );
        // 字下げ完了
        s.Text = tmp;
        // ブックマークを復元
        RestoreBookmark( ty, by, bmArray );
      }

      // 範囲選択を残さない
      if ( pos > -1 ) {
        s.SetActivePos( pos + ( tmp.length - st.length ) );
      }
      // 範囲選択を残す(論理行単位)
      else {
        s.SetActivePos( tp + tmp.length + 1 );
        s.SetAnchorPoint( mePosLogical, 1, ty );
      }
    }
  }
}

/**
 * 関数 GetBookmark( ty, by )
 * 指定された範囲のブックマークを収集
 */
function GetBookmark( ty, by ) {
  var bmArray = [],  bmY;
  var s = editor.ActiveDocument.selection;
  if ( ty < by ) {
    var anc = s.GetAnchorPos(),  act = s.GetActivePos();

    if ( ty === 1 ) {
      s.SetActivePoint( mePosLogical, 1, 2 );
      if ( s.PreviousBookmark() ) {
        bmY = s.GetActivePointY( mePosLogical );
        if ( bmY >= ty && bmY <= by ) {
          bmArray.push( bmY );
        }
      }
      s.SetActivePos( 0 );
    }
    else { s.SetActivePoint( mePosLogical, 1, ty - 1 ); }

    while ( s.NextBookmark() ) {
      bmY = s.GetActivePointY( mePosLogical );
      if ( bmY >= ty && bmY <= by ) {
        bmArray.push( bmY );
      }
      else if ( bmY > by ) { break; }
    }
    s.SetActivePos( act );  s.SetAnchorPos( anc );
  }
  return bmArray;
}

/**
 * 関数 RestoreBookmark( ty, by, bmArray )
 * 指定された範囲のブックマークを復元
 */
function RestoreBookmark( ty, by, bmArray, sel ) {
  var bmCount = bmArray.length;
  if ( bmCount ) {
    var s = editor.ActiveDocument.selection;
    var anc = s.GetAnchorPos(),  act = s.GetActivePos();
    s.SetActivePoint( mePosLogical, 1, ty );
    s.ClearBookmark();
    for ( var i = 0; i < bmCount, bmArray[i] <= by; i ++ ) {
      if ( bmArray[i] < ty ) { continue; }
      s.SetActivePoint( mePosLogical, 1, bmArray[i], false );
      s.SetBookmark();
    }
    if ( sel ) {
      s.SetActivePos( act );  s.SetAnchorPos( anc );
    }
  }
}

/**
 * 関数 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 );	// AddSpace2_Main() 関数

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

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


字上げ(スペース×2 削除)

スペース×2 削除.js

キャレットまたは選択範囲をふくむ論理行の各先頭から「半角スペース×2」を削除します。

  • 行頭の「空白文字」を削除、またはキャレット位置の前後にある「タブ文字」を削除します。
  • 選択範囲があった場合は、つねに行頭の「空白文字」だけを削除します。
  • 削除対象の行頭の「空白文字」には「半角スペース×2」以外の空白文字もふくめます。
e.g. 「半角スペース×2」「半角スペース×1」「全角スペース×1」「タブ文字×1」
  • 選択範囲が末尾に改行を含んでいた場合は、最後の改行を無視します。
  • マルチカーソル/複数選択範囲に対応(Mery ver 3.0.1 以上)

※ sukemaru 版の「字上げ」と「逆インデント」マクロを並存させるメリットは少ないとおもいますので、どちらかお好みで選んでください。
「字上げ」マクロの場合、削除対象に「空白文字」以外の任意の "文字列" や "記号" なども追加できます。

#title = "字上げ"
#tooltip = "スペース×2 削除"
// #icon = "space_x2[2].ico"
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",249

var start = new Date();

/**
 * ---------------------------------------------------------
 * 「字上げ(スペース×2 削除)」マクロ
 *   sukemaru, 2018/11/01 - 2020/06/29
 * ---------------------------------------------------------
 * 行頭の「空白文字」を削除、またはキャレット位置の前後にある「タブ文字」を削除する。
 * 選択範囲があった場合は、つねに行頭の「空白文字」だけを削除する。
 * 削除対象の「空白文字」は、「半角スペース×2」以外の空白文字もふくめて
 * 配列 blank で定義する。
 * 
 * 2020/06/29: マルチカーソル/複数選択範囲に対応
 *             ブックマーク行を字下げしたときのみブックマークの復元をするように変更
 *             ブックマークの収集範囲を選択範囲に限定
 *             ブックマークの収集/復元コードを関数化
 *             行頭空白削除の処理をループ関数から正規表現置換に変更
 */

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

// ■ 選択範囲なしのときに、キャレット位置の前後にある「タブ文字」を優先的に削除するか?
var deleteNearestTab = true;
  /**
   * false にすると、つねに行頭の空白文字のみを削除
   * true にすると、キャレット付近の「タブ文字×1」を優先的に削除
   *    (タブ文字がなければ行頭の空白を削除)
   */


// ■ 削除対象(削除の単位)にする行頭の空白文字の定義
var blank = [ "  " , " " , " " , "\t" ];
  /**
   * 初期値: [ "  " , " " , " " , "\t" ]
   * i.e. 「半角空白×2」「半角空白×1」「全角空白×1」「タブ文字×1」
   * ※ 前に置いたもののほうが優先順位が高くなる。
   *    e.g. 行頭に半角空白が3つある行で連続で実行した場合、
   *   1回目では「半角空白×2」を削除し、2回目で「半角空白×1」を削除する。
   * ※ 削除対象を「半角空白×4 ~ 1」「全角空白×2 ~ 1」「タブ文字×1」にするなら
   *   blank = [ "    " , "   " , "  " , " " , "  " , " " , "\t" ];
   * ※ 空白文字以外の "文字列" を [ ... ] のなかに追加するのも可。
   */

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


var d = editor.ActiveDocument;
if ( d.ReadOnly ) {
  Status = " ドキュメントは書き換え禁止です。";
}
else {
  var s = d.selection;
  var sx = ScrollX,  sy = ScrollY;
  
  // マルチカーソル対応
  var arg = [ deleteNearestTab, blank ];
  // 選択範囲が1つで矩形選択ではないとき
  if ( ! s.Mode || s.Mode === meModeStream ) {
    DeleteSpace2_Main( arg );
  }
  // 複数行または複数選択のとき
  else {
    BeginUndoGroup();
    AddUndo();
    MultiFunction( DeleteSpace2_Main, arg );
    EndUndoGroup();
  }
  ScrollX = sx;  ScrollY = sy;

  var elapsedSec = ( ( new Date() - start ) / 1000 ).toFixed( 3 );
  Status = " 「スペース×2 削除」マクロ  [ "
         + elapsedSec.replace( /\./, ". " )
         + " 秒 ]";
}


/**
 * 関数 DeleteSpace2_Main( [ deleteNearestTab, blank ] )
 * 「字上げ(スペース×2 削除)」マクロ
 */
function DeleteSpace2_Main( arg ) {
  var deleteNearestTab = arg[0];
  var blank = arg[1];

  var d = editor.ActiveDocument;
  var s = d.selection;

  // "選択範囲なし" のフラグ
  var pos = s.IsEmpty ? s.GetActivePos() : -1;

  /* カーソル付近の「タブ文字×1」を削除するパターン(左側優先) */
  if ( pos > -1 && deleteNearestTab ) {
    if ( d.Text.charAt( pos - 1 ) === "\t" ) {
      s.DeleteLeft();  return;
    }
    else if ( d.Text.charAt( pos ) === "\t" ) {
      s.Delete();  return;
    }
  }

  // 選択範囲を取得
  var tx = s.GetTopPointX( mePosLogical );
  var ty = s.GetTopPointY( mePosLogical );
  var bx = s.GetBottomPointX( mePosLogical );
  var by = s.GetBottomPointY( mePosLogical );
  // 選択範囲を拡張
  if ( by !== ty && bx === 1 ) { by -= 1; }
  s.SetActivePoint( mePosLogical, d.GetLine( by, 0 ).length + 1, by );
  s.SetAnchorPoint( mePosLogical, 1, ty );

  /* 行頭の空白文字を削除 */
  var st = s.Text;
  var tp = s.GetAnchorPos();
  for ( var i = 0, len = blank.length; i < len; i ++ ) {
    blank[i].replace( /\W/g, "\\$&" );
  }
  var reg = RegExp( "^(?:" + blank.join( "|" ) + ")", "gm" );
  var tmp = st.replace( reg, "" );
  if ( tmp !== st ) {
    // ブックマークを保存
    var bmArray = GetBookmark( ty, by );
    // 字上げ完了
    s.Text = tmp;
    // ブックマークを復元
    RestoreBookmark( ty, by, bmArray );
  }

  // 範囲選択を残さない
  if ( pos > -1 ) {
    if ( st.length - tmp.length >= tx ) {
      s.SetActivePoint( mePosLogical, 1, ty );
    }
    else {
      s.SetActivePos( pos - ( st.length - tmp.length ) );
    }
  }
  // 範囲選択を残す(論理行単位)
  else {
    s.SetActivePos( tp + tmp.length + 1 );
    s.SetAnchorPos( tp );
  }
}

/**
 * 関数 GetBookmark( ty, by )
 * 指定された範囲のブックマークを収集
 */
function GetBookmark( ty, by ) {
  var bmArray = [],  bmY;
  var s = editor.ActiveDocument.selection;
  if ( ty < by ) {
    var anc = s.GetAnchorPos(),  act = s.GetActivePos();

    if ( ty === 1 ) {
      s.SetActivePoint( mePosLogical, 1, 2 );
      if ( s.PreviousBookmark() ) {
        bmY = s.GetActivePointY( mePosLogical );
        if ( bmY >= ty && bmY <= by ) {
          bmArray.push( bmY );
        }
      }
      s.SetActivePos( 0 );
    }
    else { s.SetActivePoint( mePosLogical, 1, ty - 1 ); }

    while ( s.NextBookmark() ) {
      bmY = s.GetActivePointY( mePosLogical );
      if ( bmY >= ty && bmY <= by ) {
        bmArray.push( bmY );
      }
      else if ( bmY > by ) { break; }
    }
    s.SetActivePos( act );  s.SetAnchorPos( anc );
  }
  return bmArray;
}

/**
 * 関数 RestoreBookmark( ty, by, bmArray )
 * 指定された範囲のブックマークを復元
 */
function RestoreBookmark( ty, by, bmArray, sel ) {
  var bmCount = bmArray.length;
  if ( bmCount ) {
    var s = editor.ActiveDocument.selection;
    var anc = s.GetAnchorPos(),  act = s.GetActivePos();
    s.SetActivePoint( mePosLogical, 1, ty );
    s.ClearBookmark();
    for ( var i = 0; i < bmCount, bmArray[i] <= by; i ++ ) {
      if ( bmArray[i] < ty ) { continue; }
      s.SetActivePoint( mePosLogical, 1, bmArray[i], false );
      s.SetBookmark();
    }
    if ( sel ) {
      s.SetActivePos( act );  s.SetAnchorPos( anc );
    }
  }
}

/**
 * 関数 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 );	// DeleteSpace2_Main() 関数

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

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

インデント

インデント.js

既存のツールバーアイコンのアイテム「インデント」から置きかえて使う目的のマクロで、選択範囲がなくてもタブインデントします。
ソースコードの内容は「字上げ(スペース×2 追加)」マクロとほぼ同じですが、こちらの「インデント」マクロの場合は、挿入する空白は「タブ文字」のみで空白行にたいしてもインデントを追加します。

  • 論理行全体をタブインデント(行頭)して範囲選択します。
    選択範囲が1行内ならもとの選択範囲を維持し、複数行なら対象の論理行全体を範囲選択します。
  • またはカーソル位置にタブ文字を挿入します。
  • 選択範囲が末尾に改行を含んでいた場合は、最後の改行を無視します。
  • マルチカーソル/複数選択範囲に対応(Mery ver 3.0.1 以上)


【推奨設定】
Tab キーに割りあてるなら var tabEnable = 2,
ツールバーアイコンにするなら var tabEnable = 0

#title = "インデント"
#tooltip = "選択範囲がなくてもインデント"
// #icon = "indent_right.ico"
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",260

var start = new Date();

/**
 * ---------------------------------------------------------
 * 「インデント」マクロ
 *   sukemaru, 2018/11/15 - 2020/06/29
 * ---------------------------------------------------------
 * ・選択範囲なしでも、論理行全体をタブインデント(行頭)して範囲選択する
 *   選択範囲が1行内ならもとの選択範囲を維持し、
 *   複数行なら対象の論理行全体を範囲選択する
 * ・またはカーソル位置にタブ文字を挿入する
 * 【推奨設定】 Tab キーに割りあてるなら		tabEnable = 2
 *              ツールバーアイコンにするなら	tabEnable = 0
 * 
 * 2020/06/29: マルチカーソル/複数選択範囲に対応
 *             ブックマーク行を字下げしたときのみブックマークの復元をするように変更
 *             ブックマークの収集範囲を選択範囲に限定
 *             ブックマークの収集/復元コードを関数化
 */

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

// ■選択範囲があるときに、選択範囲を「タブ文字」にするか? 行全体をタブインデントするか?
var tabEnable = 0;
  /**
   * 0: 選択範囲がなくてもつねに行全体をタブインデント(行頭)
   * 1: 選択範囲があれば行全体をタブインデント、
   *    なければキャレット位置に「タブ文字」を挿入
   *  (※ツールバーアイコンの「インデント」に似た動作)
   * 2: 選択範囲が1行内(論理行)のときは選択範囲を「タブ文字」にする
   *    選択範囲がないときはキャレット位置に「タブ文字」を挿入
   *    選択範囲が複数行(論理行)のときはタブインデントする
   *  (※Tab キーの通常動作にちかい ← Mery 標準機能では "表示行" で1行内)
   * 3: つねに選択範囲を「タブ文字」にする(※インデントしない)
   * 4: 選択範囲があり、1行内(論理行)のときは選択範囲の先頭に「タブ文字」を追加し、
   *    それ以外の場合には 0 と同じ動作(行全体をタブインデント)をする
   * 
   * ※ 0 の場合、「タブ文字」を挿入することができないので、
   *       ツールバーアイコン向けの設定。
   * ※ 1 の場合、選択範囲を「タブ文字」にすることができないので、
   *      Tab キーにマクロを割りあてると少し不便になるかも?
   *      範囲選択 → 削除 (Delete / BackSpace) → 「インデント」マクロ (Tab)
   * ※ 2 の場合に1行だけインデントさせるなら、行頭でクリックするか、
   *      行内でトリプルクリック、または行番号のクリックで行全体を選択範囲にして
   *    「インデント」マクロを実行する (末尾改行まで含める必要あり)。
   *     [EOF] マークのある文書末行(さいごに改行がない)だけの1行選択では
   *      インデントできない。
   *      ⇔ Tab キーの通常動作では末尾改行を含める必要はない
   * ※ 3 は、わざわざマクロでやる意味もないかと。
   * ※ 4 は、0 と 1 の動作の統合型(1 の動作条件を反転させたもの)。
   */

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

var d = editor.ActiveDocument;

if ( d.ReadOnly ) {
  Status = " ドキュメントは書き換え禁止です。";
}

else {
  var s = d.selection;
  var sx = ScrollX,  sy = ScrollY;
  var tab = "\t";

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

  var elapsedSec = ( ( new Date() - start ) / 1000 ).toFixed( 3 );
  Status = " 「インデント」マクロ  [ "
         + elapsedSec.replace( /\./, ". " )
         + " 秒 ]";
}


/**
 * 関数 Indent_Main( [ tabEnable, tab ] )
 * 「インデント」マクロ
 */
function Indent_Main( arg ) {
  var tabEnable = arg[0];
  var tab       = arg[1];

  // 選択範囲
  var act = s.GetActivePos();
  var anc = s.GetAnchorPos();
  var tx = s.GetTopPointX( mePosLogical );
  var ty = s.GetTopPointY( mePosLogical );
  var bx = s.GetBottomPointX( mePosLogical );
  var by = yy = s.GetBottomPointY( mePosLogical );
  var inLine = ( ty === by && act !== anc );

  /* 行の途中の選択範囲を字下げするパターン */
  if ( tabEnable === 4 && inLine ) {
    s.Text = tab + s.Text;
    s.SetAnchorPoint( mePosLogical, tx, ty );
  }

  /* カーソル位置にタブを挿入するパターン */
  else if ( ( tabEnable === 1 && s.IsEmpty )
         || ( tabEnable === 2 && ty === by )
         ||   tabEnable === 3 ) {
    s.Text = tab;
  }

  else {
    // 選択範囲を拡張
    if ( ty !== by && bx === 1 ) { yy --; }
    s.SetActivePoint( mePosLogical, d.GetLine( yy, 0 ).length + 1, yy );
    s.SetAnchorPoint( mePosLogical, 1, ty );

    /* インデント */
    var tp = s.GetAnchorPos();
    var st = s.Text;
    var tmp = st.replace( /^/gm , tab );
    if ( tmp !== st ) {
      // ブックマークを保存
      var bmArray = GetBookmark( ty, by );
      // インデント完了
      s.Text = tmp;
      // ブックマークを復元
      RestoreBookmark( ty, by, bmArray );
    }

    // 選択範囲を復元
    if ( ty === by ) {
      s.SetActivePos( act + tab.length );
      s.SetAnchorPos( anc + tab.length );
    }
    else {
      s.SetActivePos( tp + tmp.length + 1 );
      s.SetAnchorPos( tp );
    }
  }
}

/**
 * 関数 GetBookmark( ty, by )
 * 指定された範囲のブックマークを収集
 */
function GetBookmark( ty, by ) {
  var bmArray = [],  bmY;
  var s = editor.ActiveDocument.selection;
  if ( ty < by ) {
    var anc = s.GetAnchorPos(),  act = s.GetActivePos();

    if ( ty === 1 ) {
      s.SetActivePoint( mePosLogical, 1, 2 );
      if ( s.PreviousBookmark() ) {
        bmY = s.GetActivePointY( mePosLogical );
        if ( bmY >= ty && bmY <= by ) {
          bmArray.push( bmY );
        }
      }
      s.SetActivePos( 0 );
    }
    else { s.SetActivePoint( mePosLogical, 1, ty - 1 ); }

    while ( s.NextBookmark() ) {
      bmY = s.GetActivePointY( mePosLogical );
      if ( bmY >= ty && bmY <= by ) {
        bmArray.push( bmY );
      }
      else if ( bmY > by ) { break; }
    }
    s.SetActivePos( act );  s.SetAnchorPos( anc );
  }
  return bmArray;
}

/**
 * 関数 RestoreBookmark( ty, by, bmArray )
 * 指定された範囲のブックマークを復元
 */
function RestoreBookmark( ty, by, bmArray, sel ) {
  var bmCount = bmArray.length;
  if ( bmCount ) {
    var s = editor.ActiveDocument.selection;
    var anc = s.GetAnchorPos(),  act = s.GetActivePos();
    s.SetActivePoint( mePosLogical, 1, ty );
    s.ClearBookmark();
    for ( var i = 0; i < bmCount, bmArray[i] <= by; i ++ ) {
      if ( bmArray[i] < ty ) { continue; }
      s.SetActivePoint( mePosLogical, 1, bmArray[i], false );
      s.SetBookmark();
    }
    if ( sel ) {
      s.SetActivePos( act );  s.SetAnchorPos( anc );
    }
  }
}

/**
 * 関数 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 );	// Indent_Main() 関数

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

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


逆インデント

アンインデント.js

既存のツールバーアイコンのアイテム「逆インデント」から置きかえて使う目的のマクロで、選択範囲がなくてもアンインデントします。
機能的には「字上げ(スペース×2 削除)」マクロとほぼ同じですが、削除できる「半角スペース×n」の最大数をかんたんに設定できるようにしてあります
( "␣␣␣␣", "␣␣␣", "␣␣", "␣" なら var deleteSpace = 4 と指定するだけ)。

  • 行頭またはカーソル付近の「タブ文字×1」か、行頭の「半角スペース×n」を削除します(半角スペース×n は行頭だけが対象です)。
  • 選択範囲なしならカーソル付近のタブを削除するか、またはカーソルのある1行をアンインデントして、カーソル位置を復帰します。
  • 選択範囲ありなら論理行全体をアンインデント(行頭)して範囲選択します。
  • 選択範囲が末尾に改行を含んでいた場合は、最後の改行を無視します。
  • マルチカーソル/複数選択範囲に対応(Mery ver 3.0.1 以上)


【推奨設定】
「字上げ」マクロと並存させるなら var deleteSpace = 0 ,
「字上げ」マクロなしで Shift+Tab キーに割りあてるなら var deleteSpace 0 ( 1 以上の値)

※ 設定用変数 var deleteSpace = 3 以上の数値を指定するさいは、Mery のオプション設定の「タブの桁数」を目安にしてください(大きな数字や非整数値を入れての動作検証はしていません)。

※ sukemaru 版の「逆インデント」と「字上げ(スペース×2 削除)」マクロを並存させるメリットは特にないとおもいますので、どちらかお好みで選んでください。

#title = "逆インデント"
#tooltip = "選択範囲がなくてもアンインデント"
// #icon = "indent_left.ico"
// #icon = "Mery用 マテリアルデザインっぽいアイコン.icl",261

var start = new Date();

/**
 * ---------------------------------------------------------
 * 「逆インデント」マクロ
 *   sukemaru, 2018/11/15 - 2020/06/29
 * ---------------------------------------------------------
 * 選択範囲なしでも、行頭またはカーソル付近の「タブ文字×1」か、
 * 行頭の「半角スペース×2」を削除する
 * ・選択範囲なしならカーソル付近のタブを削除するか、
 *   またはカーソルのある1行をアンインデントして、カーソル位置を復帰する
 * ・選択範囲ありなら論理行全体をアンインデントして範囲選択する
 * 
 * 2020/06/29: マルチカーソル/複数選択範囲に対応
 *             ブックマーク行を字下げしたときのみブックマークの復元をするように変更
 *             ブックマークの収集範囲を選択範囲に限定
 *             ブックマークの収集/復元コードを関数化
 */

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

// ■ 選択範囲なしのときに、カーソル位置の前後にある「タブ文字」を優先的に削除するか?
// ※ 選択範囲が1行内で、先頭に「タブ文字」がある場合も優先的に削除する
var deleteNearestTab = true;
  /**
   * false にすると、つねに行頭の空白文字のみを削除
   * true にすると、カーソル付近の「タブ文字×1」を優先的に削除
   * (タブ文字がなければ行頭の空白を削除)
   */


// ■ 行頭の「半角スペース」や「全角スペース」も削除するか?
var deleteSpace = 1;
  /**
   * ※ 正の整数値で指定してください
   * 0: 行頭の「タブ文字×1」だけを削除
   * 1: 行頭の「タブ文字×1」と「半角スペース×1~2」も削除
   * 2: 行頭の「タブ文字×1」と「半角スペース×1~2」と「全角スペース×1」も削除
   * 3: 1回の逆インデントで削除できる「半角スペース×n」の最大数が増加
   *  ※ 削除できる「全角スペース」の最大数は n / 2 ( → roundDecimal 変数で設定 )
   */


  // ■ deleteSpace が 3 以上のとき、削除できる「全角スペース」の最大数
  var roundDecimal = false;
    /**
     * ※ deleteSpace が奇数のときの小数点以下の処理
     * false にすると ( deleteSpace / 2 ) の小数点以下を切り捨て
     * true にすると  ( deleteSpace / 2 ) の小数点以下を切り上げ(四捨五入)
     */

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

var d = editor.ActiveDocument;

if ( d.ReadOnly ) {
  Status = " ドキュメントは書き換え禁止です。";
}
else {
  var s = d.selection;
  var sx = ScrollX,  sy = ScrollY;

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

  var elapsedSec = ( ( new Date() - start ) / 1000 ).toFixed( 3 );
  Status = " 「逆インデント」マクロ  [ "
         + elapsedSec.replace( /\./, ". " )
         + " 秒 ]";
}

/**
 * 関数 Unindent_Main( [ deleteNearestTab, deleteSpace, roundDecimal ] )
 * 「逆インデント」マクロ
 */
function Unindent_Main( arg ) {
  var deleteNearestTab = arg[0];
  var deleteSpace      = arg[1];
  var roundDecimal     = arg[2];

  var d = editor.ActiveDocument;
  var s = editor.ActiveDocument.selection;

  // "選択範囲なし" のフラグ
  var pos = s.IsEmpty ? s.GetActivePos() : -1;

  /* カーソル付近の「タブ文字×1」を削除するパターン(左側優先) */
  if ( pos > -1 && deleteNearestTab ) {
    if ( d.Text.charAt( pos - 1 ) === "\t" ) {
      s.DeleteLeft();  return;
    }
    else if ( d.Text.charAt( pos ) === "\t" ) {
      s.Delete();  return;
    }
  }

  // 選択範囲
  var act = s.GetActivePos();
  var anc = s.GetAnchorPos();
  var tx = s.GetTopPointX( mePosLogical );
  var ty = s.GetTopPointY( mePosLogical );
  var bx = s.GetBottomPointX( mePosLogical );
  var by = yy = s.GetBottomPointY( mePosLogical );
  var inLine = ( ty === by && act !== anc );
  var st = s.Text;

  /* 選択範囲が1行内のときに、先頭の「タブ文字」を優先的に削除するパターン */
  if ( inLine && deleteNearestTab && st.charAt( 0 ) === "\t" ) {
    s.Text = st.replace( /^\t/, "" );
    s.SetAnchorPoint( mePosLogical, tx, ty );
  }

  else {
    // 選択範囲を拡張
    if ( ty !== by && bx === 1 ) { yy --; }
    s.SetActivePoint( mePosLogical, d.GetLine( yy, 0 ).length + 1, yy );
    s.SetAnchorPoint( mePosLogical, 1, ty );

    /* 逆インデント */
    var regStr1 = "^\\t|^[ ]{1,2}";
    var regStr2 = "^\\t|^[ ]{1,2}|^[ ]";
    var regStr3 = "^\\t|^[ ]{1," + deleteSpace + "}|^[ ]{1,"
                + ( roundDecimal ? Math.round( deleteSpace / 2 )	// 切り上げ
                                 : Math.floor( deleteSpace / 2 ) )	// 切り捨て
                + "}";
    var indent = ( deleteSpace === 1 ) ? regStr1
               : ( deleteSpace === 2 ) ? regStr2
               : ( deleteSpace >= 3 )  ? regStr3
               : /*     else       */    "^\\t";

    st = s.Text;
    var tp = s.GetAnchorPos();
    var tmp = st.replace( new RegExp( indent , "gm" ) , "" );
    if ( tmp !== st ) {
      // ブックマークを保存
      var bmArray = GetBookmark( ty, yy );
      // 逆インデント完了
      s.Text = tmp;
      // ブックマークを復元
      RestoreBookmark( ty, yy, bmArray );
    }

    // 選択範囲が1行だったときは、位置関係を維持して選択範囲を復元する
    if ( ty === by ) {
      var gap = st.length - tmp.length;
      if ( bx <= gap ) {
        s.SetActivePoint( mePosLogical, 1, ty );
      }
      else if ( anc < act && tx <= gap ) {
        s.SetActivePos( act - gap );
        s.SetAnchorPoint( mePosLogical, 1, ty );
      }
      else if ( tx <= gap ) {
        s.SetActivePoint( mePosLogical, 1, ty );
        s.SetAnchorPos( anc - gap );
      }
      else {
        s.SetActivePos( act - gap );
        s.SetAnchorPos( anc - gap );
      }
    }
    // 選択範囲が複数行だったときは、拡張した選択範囲を復元する
    else {
      s.SetActivePos( tp + tmp.length + 1 );
      s.SetAnchorPos( tp );
    }
  }
}

/**
 * 関数 GetBookmark( ty, by )
 * 指定された範囲のブックマークを収集
 */
function GetBookmark( ty, by ) {
  var bmArray = [],  bmY;
  var s = editor.ActiveDocument.selection;
  if ( ty < by ) {
    var anc = s.GetAnchorPos(),  act = s.GetActivePos();

    if ( ty === 1 ) {
      s.SetActivePoint( mePosLogical, 1, 2 );
      if ( s.PreviousBookmark() ) {
        bmY = s.GetActivePointY( mePosLogical );
        if ( bmY >= ty && bmY <= by ) {
          bmArray.push( bmY );
        }
      }
      s.SetActivePos( 0 );
    }
    else { s.SetActivePoint( mePosLogical, 1, ty - 1 ); }

    while ( s.NextBookmark() ) {
      bmY = s.GetActivePointY( mePosLogical );
      if ( bmY >= ty && bmY <= by ) {
        bmArray.push( bmY );
      }
      else if ( bmY > by ) { break; }
    }
    s.SetActivePos( act );  s.SetAnchorPos( anc );
  }
  return bmArray;
}

/**
 * 関数 RestoreBookmark( ty, by, bmArray )
 * 指定された範囲のブックマークを復元
 */
function RestoreBookmark( ty, by, bmArray, sel ) {
  var bmCount = bmArray.length;
  if ( bmCount ) {
    var s = editor.ActiveDocument.selection;
    var anc = s.GetAnchorPos(),  act = s.GetActivePos();
    s.SetActivePoint( mePosLogical, 1, ty );
    s.ClearBookmark();
    for ( var i = 0; i < bmCount, bmArray[i] <= by; i ++ ) {
      if ( bmArray[i] < ty ) { continue; }
      s.SetActivePoint( mePosLogical, 1, bmArray[i], false );
      s.SetBookmark();
    }
    if ( sel ) {
      s.SetActivePos( act );  s.SetAnchorPos( anc );
    }
  }
}

/**
 * 関数 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 );	// Unindent_Main() 関数

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

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


メモ

2019/04/07 (sukemaru)

ソースコードから Quit() メソッドを削除

2019/11/10 (sukemaru)

Mery 2.8.6 以上でブックマークを保持
※ Mery 2.8.5 以前ではブックマーク機能の仕様が不安定なので、個別対応はあきらめた…。

2019/11/17 (sukemaru)

Mery 2.8.6 以上、または Mery 2.6.7 でブックマークを保持
※ Mery 2.7.0 - 2.8.5 ではブックマーク機能の仕様が不安定なので、1行のみの「字下げ/字上げ (インデント/逆インデント)」をしたときにブックマークがおかしくなることがある。
現時点での最新の正式版と最新ベータ版で正常に機能するように調整した。

2019/11/18 (sukemaru)

「スペース×2 削除.js」のソースコードで設定項目部分がダブっていたのを修正

2019/12/01 (sukemaru)

複数行選択のときのみブックマークの保存/復元するように変更
(ブックマークが大量にある文書で、範囲選択1行内で実行したときの速度を改善)

2020/06/29 (sukemaru)

マルチカーソル/複数選択範囲に対応
ブックマークの収集/復元コードを関数化
(ブックマークの収集を選択範囲内の行に限定し、ブックマーク行を「字下げ/字上げ」したときのみ復元処理をするように変更)

スポンサーリンク