マルチカーソル選択範囲の並べ替え

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



Mery ver 3.0.1 以降の マルチカーソル/複数選択範囲 の配置を「昇順/降順」または「逆順」で並べ替えます。
⇒ 2020/07/06:「後ろ送り (右・下にひとつずらす)」と「逆送り (左・上にひとつずらす)」を追加

  • 単語のみの選択範囲 でも、複数行の選択範囲 でも「ひとつの選択範囲」として扱います。
⇒ 2020/07/04:「各選択範囲の末尾調整」(末尾改行を除外する) 用の設定項目 chompEnable を追加 (初期値:false
※ 「各選択範囲の末尾調整」を有効にした場合、並べ替え後の各選択範囲からも末尾改行が除外されたままになります。


  • 矩形選択範囲は行に分けてから並べ替えをおこないます(実行後は矩形選択状態を復帰しません)。
※ 同一論理行内で完結している矩形選択範囲では並べ替えをおこないません
(折り返し位置より後ろの文字列は矩形選択範囲内の文字列と見做しません。 また、折り返しよりも後ろの文字列から下方向に矩形選択して実行することを考慮していません)。


動作モード[編集]

設定項目 var mutiSelMode = 0; で設定します (初期値:0)。
※ いずれの動作モードでも 選択範囲が2つ のときは「入れ替え

  • 0昇順/降順 で並べ替え
  • 1逆順 で並べ替え
  • 2後ろ送り (右・下にひとつずらす)
  • 3逆送り (左・上にひとつずらす)


連続で実行したときの動作例
  • 選択範囲が2つなら「選択範囲を入れ替え」
"HOGE" , "FUGA" , "PIYO" ⇒ "FUGA" , "PIYO" , "HOGE" ⇒ "HOGE" , "FUGA" , "PIYO"


選択範囲が3つ以上なら...
  • 「昇順/降順 で並べ替え」
multiSelMode = 0(初期値)
"HOGE" , "FUGA" , "PIYO" ⇒ "FUGA" , "HOGE" , "PIYO" ⇒ "PIYO" , "HOGE" , "FUGA"
※ もともとの並び順が昇順または降順でないときは、連続実行(昇順/降順トグル)しても元の並び順にはなりません。
⇒ 元の並び順に戻すばあいは UNDO (Ctrl+Z) 連打で。


  • 「逆順で並べ替え」
multiSelMode = 1
"HOGE" , "FUGA" , "PIYO" ⇒ "PIYO" , "FUGA" , "HOGE" ⇒ "HOGE" , "FUGA" , "PIYO"


  • 「後ろ送り (右・下にひとつずらす)」
multiSelMode = 2
"HOGE" , "FUGA" , "PIYO" ⇒ "PIYO" , "HOGE" , "FUGA" ⇒ "FUGA" , "PIYO" , "HOGE"


  • 「逆送り (左・上にひとつずらす)」
multiSelMode = 3
"HOGE" , "FUGA" , "PIYO" ⇒ "FUGA" , "PIYO" , "HOGE" ⇒ "PIYO" , "HOGE" , "FUGA"



「昇順/降順 または 逆順 で並べ替え」マクロとの比較[編集]

「昇順/降順 トグル」を例に、動作方式を比較

  • 選択範囲の並べ替え」マクロの「昇順/降順 トグル」 (このページのマクロ)
各選択範囲内のテキストは並べ替えず、選択範囲の配置だけを並べ替えます。
単語だけを複数選択した場合も並べ替えをおこないます。
※ 画像は、「各選択範囲の末尾調整」の設定項目を有効にした状態

Mery 選択範囲の並べ替え 20200704.png


それぞれの選択範囲内の行を並べ替え、または、すべての選択範囲を行に分割してから一括で並べ替えます。

Mery 昇順/降順マクロ(0) 20200704.png

Mery 昇順/降順マクロ(1) 20200704.png

※ 「マルチカーソル用:『昇順/降順』または『逆順』で並べ替え」マクロには、「ひとつずらす (後ろ送り/逆送り)」の動作モードはありません。
論理行単位で「ひとつずらす」の並べ替え操作をしたい場合は、各行内での Ctrl+トリプルクリック か行番号の Ctrl+クリック で複数選択範囲にしてからこのマクロを実行してください (または、選択範囲の ドラッグ・アンド・ドロップ 操作か「行を上下に移動」マクロを使用してください ← 複数選択範囲には非対応)。


GetKeyState.exe による動作モードの切り替え[編集]

GetKeyState.exe(キー状態取得実行ファイル)」を導入している場合、設定項目 var gksEnable = 0; の値を変更すると、実行時の修飾キー (CtrlShift キー) の押し下げ状態で動作モードを変更できます。

  • 0: 動作モードを変更しない (GetKeyState.exe を使用しない)
  • 1: Ctrl キーで 「昇順/降順」⇔「逆順」 または 「後ろ送り」⇔「逆送り」
  • 2: Shift キーで 「昇順/降順」⇔「後ろ送り」 または 「逆順」⇔「逆送り」
  • 3: CtrlShift キーで 「昇順/降順」⇔「逆送り」 または 「逆順」⇔「後ろ送り」


※ 1, 2 では1回、3 では2回、GetKeyState.exe を起動します (処理開始までのタイムラグが増えます)。
※ 1 ~ 3 でも、選択範囲が2つだけのときは GetKeyState.exe を起動しません。

※ GetKeyState.exe を導入してショートカットキーからこのマクロを使用する場合、[オプション] の [キーボード設定] に「Ctrl/Shift キーの あり/なし」の組み合わせで複数のショートカットキーのパターンを登録する必要があります。
※ GetKeyState.exe を導入していない場合、使用したい動作モードごとにマクロの実体ファイルを別名で保存し、それぞれ [マクロのカスタマイズ] に登録する必要があります(ZIP 書庫内に4種類のアイコンを同梱してあります)。


ダウンロード[編集]

ファイル:選択範囲の並べ替え.zip」(最終更新:2020/07/06)

アイコン見本画像

「上/下」「左/右」「ぐるぐる」の矢印アイコンを 17 コ入れてあります。
動作モードにあわせて、お好みで選んでご利用ください。

ソースコード[編集]

#title = "選択範囲の並べ替え"
#tooltip = "マルチカーソル選択範囲の並べ替え または 入れ替え"
#icon = "arrows_swap_horaizontal[7].ico"

// #title = "選択範囲の並べ替え(昇順/降順)"

// #title = "選択範囲の並べ替え(反転)"

// #title = "選択範囲の並べ替え(後ろ送り)"

// #title = "選択範囲の並べ替え(逆送り)"


/**
 * ---------------------------------------------------------
 * 「選択範囲の並べ替え」マクロ
 * sukemaru, 2020/06/29 - 2020/07/06
 * ---------------------------------------------------------
 * ・マルチカーソル選択範囲を「昇順/降順」または「逆順 (反転)」で並べ替え
 *   または選択範囲の並び順を「ひとつずらす (後ろ送り/逆送り)」
 * ・選択範囲が2つなら「入れ替え」
 * ・連続実行でトグル変換
 * 
 * 2020/07/04: 「逆順で並べ替え (並び順を反転)」モードを追加 
 *             「各選択範囲の末尾調整」を追加
 * 2020/07/06: 「後ろ送り(右・下にひとつずらす)」モードと
 *             「逆送り(左・上にひとつずらす)」モードを追加 
 *             GetKeyState.exe による4モード切り替え処理を追加
 * 
 * 【仕様上の制限】
 * ・動作要件:Mery ver 3.0.1 以降
 * ・元々の並び順が昇順または降順でなかった場合
 *   連続実行で「昇順/降順トグル」しても元の並び順にはなりません。
 *   ⇒ 元々の並び順に戻す場合は「元に戻す (Ctrl+Z)」を連打
 * ・矩形選択範囲は行に分けてから並べ替えます。 ⇒ 実行後は複数選択状態
 * (※ 元の矩形選択のかたちが歪だった場合に、矩形で復帰できないため)
 * ・選択範囲復帰後の各キャレット位置は、各選択範囲の先頭になります。
 * (※ 行単位で並べ替えたときに、キャレットが末尾だと分かりづらいため)
 * ・ブックマークを考慮しません。
 * ・並べ替え処理中に選択範囲(ゼロ幅も含めて)の数が変わってしまったり
 *   文書全体の文字数が変わってしまう場合は、並べ替えの処理ををキャンセルします。
 * (※ 選択範囲が結合されてしまう場合もキャンセル
 *   ※ 失敗した試行の結果は「やり直し (Ctrl+Y)」×2回すると確認できます)
 */

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

// ■ 「昇順/降順」で、半角数字を数値で評価して並べ替える
// (各選択範囲内の最初の半角数字部分を数値として扱う ...たぶん小数も可)
var sortByNum = false;
  /**
   * true: 数値順
   * 	1  <  002  <  3  <  10  <  020
   * 	-2.5  <  -2  <  -1  <  0  <  1  <  1.2  <  1.2.30  <  1.2.4
   * 
   * false: 文字順 @ unicode
   * 	002  <  020  <  1  <  10  <  3
   * 	-1  <  -2  <  -2.5  <  0  <  1  <  1.2  <  1.2.30  <  1.2.4
   * 
   * ※ true の場合でも「1.2.30」や「1.2.4」などは
   *    「1.2」までを数値(小数)として評価し
   *    それ以降の「.30」や「.4」は文字列として評価するので
   *    ビルド番号や IP アドレスなどは数値順にならない
   *    また「ほげ 1 ふが 2 ぴよ 3」のように2ヵ所以上数字が出現する場合
   *    2つめ以降の数字はすべて文字列として評価する
   */

// ■ 各選択範囲の末尾調整をする
var chompEnable = false;
  /**
   * true:  各選択範囲の末尾が改行のとき、末尾改行を選択範囲から除外する
   * false: 選択範囲の末尾調整をしない
   * 
   * ※ true の場合、並べ替え後も末尾改行は除外したまま
   */

// ■ 「並べ替え」の動作モード
var multiSelMode = 0;
  /**                    凡例:     元の文字列        1回目          2回目          3回目
   * 0: 昇順/降順 で並べ替え      1, 2, 3, 0  ⇒  0, 1, 2, 3  ⇒  3, 2, 1, 0  ⇒  0, 1, 2, 3
   * 1: 逆順で並べ替え (反転)      1, 2, 3, 0  ⇒  0, 3, 2, 1  ⇒  1, 2, 3, 0  ⇒  0, 3, 2, 1
   * 2: 後ろ送り(右・下にずらす)  1, 2, 3, 0  ⇒  0, 1, 2, 3  ⇒  3, 0, 1, 2  ⇒  2, 3, 0, 1
   * 3: 逆送り  (左・上にずらす)  1, 2, 3, 0  ⇒  2, 3, 0, 1  ⇒  3, 0, 1, 2  ⇒  0, 1, 2, 3
   * 
   * ※ 凡例は、各数字1文字ずつ複数選択(4ヶ所)したときの動作パターン
   */

// ■ 選択範囲が3つ以上のとき Ctrl/Shift キーで動作モードを切り替える
var gksEnable = 0;
  /**
   * 0: 動作モードの切り替えをしない(GetKeyState.exe を使用しない)
   * 1: Ctrl  キーで「昇順/降順」⇔「逆順」または「後ろ送り」⇔「逆送り」
   * 2: Shift キーで「昇順/降順」⇔「後ろ送り」または「逆順」⇔「逆送り」
   * 3: Ctrl+Shift キーで「昇順/降順」⇔「逆送り」または「逆順」⇔「後ろ送り」
   * 
   * ※ 3 の場合、GetKeyState.exe を2回起動する
   * ※ 1 ~ 3 でも、選択範囲が2つなら GetKeyState.exe を起動しない(選択範囲の入れ替え)
   * 
   * ※ 要:「GetKeyState.exe(キー状態取得実行ファイル)」
   * https://www.haijin-boys.com/wiki/GetKeyState.exe(%E3%82%AD%E3%83%BC%E7%8A%B6%E6%85%8B%E5%8F%96%E5%BE%97%E5%AE%9F%E8%A1%8C%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB)
   */

// ■ GetKeyState.exe のフルパスを指定する場合
var gksPath = "";
    /**
     * 未指定 "" なら、Mery インストールフォルダの Macros\GetKeyState.exe
     * ※ GetKeyState.exe なしのときも "" にすること。
     * ※ パス指定での区切り文字の \ 記号はふたつがさね「\\」で記述すること。
     */

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


// 実行
SortByBlock( sortByNum, multiSelMode, chompEnable, gksEnable, gksPath );


/**
 * 関数 SortByBlock()
 * マルチカーソル複数選択範囲の入れ替え
 * ・ マルチカーソル選択範囲を「昇順/降順」で並べ替え、または「反転 (逆転)」
 * 
 * 2020/07/06: 第ニ引数を reverseMode (true/false)から multiSelMode (0~3) に変更し
 *             動作モード「後ろ送り (右・下方向にずらす )」「逆送り (左・上方向にずらす )」を追加
 *             GetKeyState.exe による4モード切り替えを追加
 */
function SortByBlock( sortByNum, multiSelMode, chompEnable, gksEnable, gksPath ) {
  var d = editor.ActiveDocument;
  var s = d.selection;
  var ty = s.GetTopPointY( mePosLogical );
  var by = s.GetBottomPointY( mePosLogical );
  var sMode = s.Mode || 1;

  if ( d.ReadOnly ) {
    Status = " このドキュメントは書き換え禁止です。";
    return;
  }
  if ( s.IsEmpty
  || sMode == meModeStream
  || ( sMode == meModeBox && ty === by ) ) {
    Status = " 並べ替えられる選択範囲がありません。";
    return;
  }

  var sx = ScrollX,  sy = ScrollY;
  BeginUndoGroup();  AddUndo();

//   /* 「昇順/降順」マクロ専用: 「すべての選択範囲を行に分割して並べ替え」 */
//   // 末尾改行の調整
//   if ( chompe0 ) {
//     MultiFunction( PseudoChomp );
//     editor.ExecuteCommandByID( 2254 );	// 「選択範囲を行に分ける」
//   }

  // 矩形選択を「行に分ける」(論理行)
  s.Mode = meModeMulti;

  // 末尾改行の調整
  if ( sMode == meModeMulti && chompEnable ) {
    MultiFunction( PseudoChomp );
  }

  // 各選択範囲の先頭/末尾の座標と文字列を取得
  var dt = d.Text;
  var sCount = s.Count;
  var Sel = [],  Str = [];
  var act, anc, tp, bp;
  for ( var i = 0; i < sCount; i ++ ) {
    act = s.GetActivePos( i );
    anc = s.GetAnchorPos( i );
    tp  = Math.min( act, anc );
    bp  = Math.max( act, anc );
    Sel[i] = { tp: tp,  bp: bp };
    Str[i] = dt.slice( tp, bp );
  }
  var tmp = Str.toString();

  /* 「選択範囲の並べ替え」マクロ用 */
  // 選択範囲が3つ以上のとき、GetKeyState.exe で動作モードを切り替え
  GetKeyState:
  if ( gksEnable && sCount > 2 ) {
    var gks = gksPath
    || editor.FullName.replace( /[^\\]+$/, "Macros\\GetKeyState.exe" );
    gksEnable = new ActiveXObject( "Scripting.FileSystemObject" ).FileExists( gks )
              ? gksEnable : 0;
    if ( ! gksEnable ) { break GetKeyState; }
    var WshShell = new ActiveXObject( "WScript.Shell" );
    // ● Ctrl キーあり
    if ( gksEnable % 2
    && WshShell.Run( '"' + gks + '" Ctrl', 0, true ) === 1 ) {
      // 「昇順/降順」⇔「逆順」 または 「後ろ送り」⇔「逆送り」
      if ( multiSelMode % 2 ) { multiSelMode --; }
      else { multiSelMode ++; }
    }
    // ● Shift キーあり
    if ( gksEnable > 1
    && WshShell.Run( '"' + gks + '" Shift', 0, true ) === 1 ) {
      // 「昇順/降順」⇔「後ろ送り」 または 「逆順」⇔「逆送り」
      if ( multiSelMode > 1 ) { multiSelMode -= 2; }
      else { multiSelMode += 2; }
    }
  }

  // 逆順で並べ替え(反転)
  if ( sCount === 2 || multiSelMode == 1 ) {
    $status = " 逆順で並べ替え (反転)";
    Str.reverse();
  }

  else if ( multiSelMode == 0 ) {
    // 昇順で並べ替え
    $status = " 昇順で並べ替え";
    Str.sort( Sort );

    // すでに昇順だった場合は降順で並べ替え(反転)
    if ( Str.toString() === tmp ) {
      $status = " 降順で並べ替え";
      Str.reverse();
    }
  }

  else if ( multiSelMode === 2 ) {
    // 後ろ送り(右・下方向にひとつずらす)
    $status = " 後ろ送り (右・下にひとつずらす)";
    Str.unshift( Str.pop() );
  }

  else if ( multiSelMode === 3 ) {
    // 逆送り(左・上方向にひとつずらす)
    $status = " 逆送り (左・上にひとつずらす)";
    Str.push( Str.shift() );
  }

  // 変更なしなら UNDO 履歴を残さない
  if ( Str.toString() === tmp ) {
    EndUndoGroup();  d.Undo();
    Status = " 並べ替えなし";
    return;
  }

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

    // 並べ替えを適用
    s.Text = Str[i];

    // 復帰用選択範囲の先頭/末尾
    Sel[i].bp = s.GetActivePos();
    Sel[i].tp = Sel[i].bp - Str[i].length;
    // 文字数の増減量(累積)
    // diff += d.TextLength - dl;
    diff += ( - dl ) + ( dl = d.TextLength );
  }

  // マルチカーソル/複数選択範囲を復帰
  s.SetActivePos( Sel[ sCount - 1 ].tp, true );
  for ( var i = 0; i < sCount; i ++ ) {
    // キャレットは各選択範囲の先頭
    s.AddPos( Sel[i].bp, Sel[i].tp );
  }
  EndUndoGroup();

  // 選択範囲の数が変わってしまう場合(マージ)や
  // 文書全体の文字数が変わってしまう場合は失敗 
  if ( sMode == meModeMulti
  && s.Count !== sCount || diff ) {
    AddUndo();  d.Undo();  d.Undo();
    Status = " 並べ替え失敗";
  }
  else {
    Status = ( sCount === 2 ) ? " 選択範囲を入れ替え"
                              : $status;
  }
  ScrollX = sx;  ScrollY = sy;
}

/**
 * 関数 Sort( a, b )
 * 昇順で並べ替え
 * 半角数字部分は「数値」または「文字列」で評価
 * ※ sortByNum はグローバルスコープの設定変数
 */
function Sort( a, b ) {
  var reg = /^(.*?)(-?\d+(?:\.\d+)?)/;
  var aa = reg.exec( a ),  a1 = aa ? aa[1] : "";
  var bb = reg.exec( b ),  b1 = bb ? bb[1] : "";
  var a2 = a,  b2 = b;
  if ( sortByNum && aa && bb && a1 === b1 ) {
    a2 = Number( aa[2] );
    b2 = Number( bb[2] );
  }
  return ( a2 !== a && a2 !== b2 ) ? a2 - b2
       : ( a < b ) ? -1
       : ( a > b ) ?  1
       :/* else */    0;
}

/**
 * 関数 PseudoChomp()
 * 選択範囲から末尾改行を除去する
 * ※ 選択範囲の先頭位置は変更しない
 */ 
function PseudoChomp() { 
  var d = editor.ActiveDocument;
  var s = d.selection;
  if ( s.Text.slice( -1 ) === "\n" ) {
    var ty = s.GetTopPointY( mePosLogical );
    var tx = s.GetTopPointX( mePosLogical );
    var by = s.GetBottomPointY( mePosLogical );
    by --;
    s.SetActivePoint( mePosLogical, tx, ty );
    s.SetAnchorPoint( mePosLogical, d.GetLine( by, 0 ).length + 1, by );
  }
}

/**
 * 関数 MultiFunction( Fn, arg )
 * マルチカーソル(複数選択範囲)に対応させる
 * マルチカーソル(複数選択範囲)に対応させる
 * 第1引数: Function; 選択範囲ごとに適用する処理の関数
 * 第2引数: Function に渡す引数をまとめた配列
 * 
 * ※ 並べ替えマクロ用カスタマイズ: 
 * ⇒ PseudoChomp() 関数にしか使わないので、文字数の増減量をチェックしない
 */
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.SetAnchorPos( Sel[i].anc + diff );
    s.SetActivePos( Sel[i].act, true + diff );

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

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


メモ[編集]

2020/06/30 (sukemaru)

  • 書き替え禁止のタブで実行したときに選択範囲のハイライト位置だけが移動してしまう不具合を修正
  • 同一論理行内の折り返しによる「見かけ上の矩形選択範囲」でのエラー回避処理を追加
※ 折り返し位置よりも後ろの文字列を矩形選択して並べ替える必要がある場合は、「折り返しなし」表示にしてから実行するか、複数選択範囲状態から実行しなければならない
  • 並べ替え後のマルチカーソル選択範囲の各キャレット位置を、各選択範囲の先頭に変更
  • 小数と負の数値の処理を変更
  • ゼロ幅選択範囲をふくむ並べ替えの不具合を修正(ゼロ幅選択範囲があるときは並べ替えなし)
※ 現状のコードでは、ゼロ幅選択範囲があると実行後に選択範囲を正常に復帰に復帰できないことや、元の選択範囲周辺の文字列が消えてしまうことがあるため

2020/07/04 (sukemaru)

  • 「並び順を反転 (逆順で並べ替え)」モードを追加 し、マクロの #title から「昇順/降順トグル」の語句をはずした
  • 「各選択範囲の末尾調整」の設定項目を追加
  • ゼロ幅選択範囲をふくむ並べ替えを許容
※ ただし、処理中に選択範囲が結合されてしまったり文字数の増減が発生する場合は、処理をキャンセルして実行前の状態にもどす

2020/07/06 (sukemaru)

  • 動作モードに「後ろ送り (後方にシフト=右・下にひとつずらす)」と「逆送り (前方にシフト=左・上にひとつずらす)」を追加
  • GetKeyState.exe(キー状態取得実行ファイル)」による動作モード切り替え用の設定項目を追加
  • 「ぐるぐる」アイコンと、アイコンの見本画像を追加
スポンサーリンク