拡張自動選択

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

通常の「カーソル位置の単語の選択 (ダブルクリック)」を拡張したかんじの自動選択機能を提供します。
※ マクロを "ダブルクリック" に割りあてることはできません

Mery ver 3.0.1 以降の マルチカーソル/複数選択範囲 に対応


ちょっと便利な自動選択拡張機能[編集]

「選択範囲がない」状態からこのマクロを実行すると「拡張自動選択」モードになります。

1. 「キャレット位置の単語」の自動選択 (通常のダブルクリックと同等)

2. 「同一記号の繰り返し部分」の自動選択
半角記号 #####、全角記号 ※※※※※、全角空白       など、おなじ記号が連続する部分を範囲選択します。
3. 「全角空白を含む空白文字列」の自動選択
通常のダブルクリックでも「タブと半角空白が混在した空白文字列」の部分を自動選択できますが、このマクロでは全角空白を含めて「タブと全角/半角空白が混在した空白文字列」全体を自動選択できます。
4. 「日付」の自動選択
キャレットの右側が「日付」の数字部分なら2001年1月1日2002-2-22003/3/32020.05.31 のような「日付」の文字列全体を自動選択します。
5. 「行全体を選択」 (改行を含めない)
2. - 4. に該当しなかったときに「行全体」を自動選択します(設定項目で表示行か論理行かを指定)。
行番号のクリックとはことなり、末尾改行を選択範囲に含めません。
※ Mery ver 3.0.1 以降でマルチカーソルが有効のばあい、矩形選択範囲は行に分割して各行を範囲選択します。
⇒ 範囲選択したらマクロ終了


35 の機能は、ソースコード内の設定項目の値を変更すると利用できます(初期値:無効)。


【注意】
  • 「同一記号の繰り返し」の自動選択機能は、たまに "ダブルクリックで同じ記号2文字選択できちゃった" が発生するのと同じレベルの頻度で失敗することがあります。
  • URL 文字列上(Mery の標準機能で下線のついた URL 部分)で実行した場合は、URL が範囲選択され、URL を勝手にブラウザで開かれることがあります。


今日の日付に変換[編集]

「日付」の文字列を範囲選択した状態からこのマクロを実行すると「今日の日付」モードになります。

2001年1月1日2002-2-22003/3/32020.05.31 のような「日付」の文字列を「今日の日付」に変換しますので、このマクロを 連続実行 することで キャレット位置の「日付を自動選択」 ⇒ 「今日の日付に変換」 ができます。


「日付」の判定基準
  • 「年」は、 1900 - 2099 の範囲内の4ケタの半角数字
  • 「月」は、 1 - 12 または 01 - 09 の範囲内の半角数字
  • 「日」は、 1 - 31 または 01 - 09 の範囲内の半角数字 ("小の月" を考慮しない)
  • 年月日の数字の区切りは、- . / のいずれかの記号×2回、または


2020年01.011900/1-5 のように区切り文字の形式が混在した文字列からは変換しません。
※ 和暦の年号や全角数字で記述された日付、20200601 のような区切り文字のない半角8ケタの数値は対象外です。
2001-02-31 のような実在しない日付でも「今日の日付」に変換されます。


変換後の「日付」の形式
「区切り文字」の部分は変換前とおなじままになりますが、月と日の数字部分はつねに 2020/06/01 のようにゼロ埋めした2ケタに変換されます。


1行コメントアウト/アンコメント[編集]

末尾改行を含めずに「論理行全体」(1行) を範囲選択した状態からこのマクロを実行すると「1行コメントアウト/アンコメント」モードを利用できます(ソースコード内の設定項目の初期値:無効 commentOut = false )。

行頭に > のようなコメントマークを付けたりはずしたりできますので、このマクロを 連続実行 することで 「論理行全体を自動選択」 ⇒ 「1行コメントアウト/アンコメント」 ができます。
※ 設定項目 selectLine = 0 のとき


※ 「1行コメントアウト」は、範囲選択なしの空行でも実行できます(コメントマーク挿入)。
※「コメントマーク付け外し」マクロなどから「編集モードによるコメントマークの定義切替」のコードを移植することもできます。


ソースコード[編集]


ダウンロード >> 「ファイル:ちょっと便利な拡張自動選択.zip」(アイコン入り)

(最終更新:2020/07/01)

#title = "拡張自動選択"
#tooltip = "ちょっと便利な自動選択拡張機能 + α"
#icon = "select_ex.ico"

/**
 * ---------------------------------------------------------
 * 「拡張自動選択」マクロ
 * sukemaru, 2020/05/31 - 2020/07/01
 * ---------------------------------------------------------
 * 通常の「カーソル位置の単語の選択」を拡張した自動選択機能
 * ※ ダブルクリックにマクロを割りあてることはできません
 * ※「選択範囲がある」状態からマクロを実行したときは「今日の日付」か
 *   「1行コメントアウト/アンコメント」機能のみ
 * 
 * ■ マルチカーソル/複数選択範囲に対応 (2020/07/01)
 * ※ 矩形選択範囲から実行したときは、行に分けてからマルチ処理
 * 
 * 「選択範囲がない」状態からマクロを実行すると「拡張自動選択」モード
 * 
 *   1.「キャレット位置の単語」の自動選択(通常のダブルクリックと同等)
 *   2.「同一記号の繰り返し」の自動選択	#####	※※※※※	     
 *   3.「全角空白を含む空白文字列」の自動選択	     	    	
 *   4.「日付け」の自動選択(キャレットの右側が数字のとき)
 *      ○ 2001年1月1日, 2002-2-2, 2003/3/03, 2020.05.31 / × 2020/05.31
 *   5.「行全体を選択」(改行を含めない)
 * ⇒ 範囲選択したらマクロ終了
 * 
 * ※ 3, 5 は設定項目で可否を選択(初期値:無効)
 * ※ fullWSpc = false の設定でも、キャレットの右側に全角空白があれば
 *   「連続する全角空白」の部分を自動選択できる(2 の機能)
 * ※ 5 は「タブと半角空白のみの空白文字列」上からでも実行可
 * 
 * マクロ実行時の選択範囲の文字列が「日付」なら「今日の日付」に変換する
 * 
 * マクロ実行時の選択範囲が論理行全体なら「1行コメントアウト/アンコメント」可
 * ※ 「1行コメントアウト」は空行上でも実行可
 */

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

// ■ 「空白文字列の自動選択」に「全角空白」を含める
var fullWSpc = false;	// true 含める / false 含めない (タブと半角空白のみ)

// ■ 説明欄の 1 - 4 に該当しなかったときには「行全体を選択」する(改行を含めない)
var selectLine = 0;		// 0 しない / 1 表示行を選択 / 2 論理行を選択

// ■ 選択範囲が論理行全体のとき「1行コメントアウト/アンコメント」する
var commentOut = false;	// true する / false しない

var commentMark = "> ";	// コメントマーク

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

var d = editor.ActiveDocument,  s = d.selection;
var sx = ScrollX,  sy = ScrollY;

/* マルチカーソル/複数選択範囲に対応 */
var dt = d.Text;
var sMode = s.Mode || 1;
var arg = [ fullWSpc, selectLine, commentOut, commentMark ];

// 選択範囲が1つで矩形選択ではないとき
if ( sMode == 1 ) {
  SelectEx_Main( arg );
}
// 矩形選択または複数選択のとき
else {
  BeginUndoGroup();
  if ( ! d.ReadOnly ) { AddUndo(); }
  MultiFunction( SelectEx_Main, arg );
  EndUndoGroup();
}

/* 無駄な UNDO 履歴が残らないよう対策 */
// テキストの変更なしなら一度 UNDO して、拡張自動選択だけ再試行する
if ( sMode > 1 && d.Text === dt ) {
  if ( ! d.ReadOnly ) { d.Undo(); }
  MultiFunction( SelectWordEx, arg );
}

ScrollX = sx;  ScrollY = sy;

/**
 * 関数  SelectEx_Main()
 * 「拡張自動選択」マクロ
 */
function SelectEx_Main( arg ) {
  var fullWSpc    = arg[0];
  var selectLine  = arg[1];
  var commentOut  = arg[2];
  var commentMark = arg[3];

  var d = editor.ActiveDocument,  s = d.selection;
  var st = s.Text;
  var readOnly = d.ReadOnly;

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

  // 選択範囲の文字列が日付なら「今日の日付」に変換して終了
  else if ( st ) {
    var today = DateOfToday( st );
    if ( s.Text !== today ) {
      s.Text = today;
      return;
    }
  }

  // 「キャレット位置の単語」の自動選択 または
  // 「同一記号の繰り返し」/「全角空白を含む空白文字列」/「日付」の自動選択
  if ( ! st && SelectWordEx( arg ) ) {
    return;
  }

  // 「1行コメントアウト/アンコメント」
  if ( ! readOnly && commentOut && commentMark
  && s.Text === d.GetLine( s.GetActivePointY( mePosLogical ), 0 )
  && CommentOut( s.Text, commentMark ) ) {
    return;
  }
}

/**
 * 関数 DateOfToday( str )
 * 選択範囲の文字列が日付なら今日の日付に変換する
 * 
 * 引数の条件(日付の判定基準)
 * ・「年」は 19 か 20 ではじまる4ケタの半角数字
 * ・「月」は 1 - 12 または 01 - 09 の範囲内の半角数字
 * ・「日」は 1 - 31 または 01 - 09 の範囲内の半角数字(小の月を考慮しない)
 * ・年月日の数字の区切りは
 *   「-」「.」「/」のいずれか(同一の記号2回の場合のみ)
 *   または「年月日」
 * ・和暦の年号や全角数字、区切り文字のない半角8ケタの数値は不可
 * 
 * 戻り値の「月日」の数字はつねにゼロ埋め
 */
function DateOfToday( str ) {
  var reg1 = /^(?:19|20)\d{2}([-.\/])(?:0?[1-9]|1[0-2])(\1)(?:0?[1-9]|[12][0-9]|3[01])(\b)$/;
  var reg2 = /^(?:19|20)\d{2}(年)(?:0?[1-9]|1[0-2])(月)(?:0?[1-9]|[12][0-9]|3[01])(日)$/;
  if ( str.match( reg1 ) || str.match( reg2 ) ) {
    var sep1 = RegExp["$1"], sep2 = RegExp["$2"], sep3 = RegExp["$3"];
    var Pad = function( tmp ) { return tmp < 10 ? "0" + tmp : tmp; }
    var dt = new Date();
    var yyyy = dt.getFullYear() + sep1;
    var mm = Pad( dt.getMonth() + 1 ) + sep2;
    var dd = Pad( dt.getDate() ) + sep3;
    str = yyyy + mm + dd;
  }
  return str;
}

/**
 * 関数 SelectWordEx( [ fullWSpc, selectLine ] )
 * ※ 引数: 「全角空白を含む空白文字列」の自動選択の許可フラグ
 * 拡張自動選択
 * 1.「キャレット位置の単語」の自動選択(通常のダブルクリックと同等)
 * 2.「同一記号の繰り返し」の自動選択	#####	※※※※※
 * 3.「全角空白を含む空白文字列」の自動選択	    	    	
 * 4.「日付け」の自動選択
 *    ○ 2001年1月1日, 2002-2-2, 2003/3/03, 2020.05.31 / × 2020/05.31
 * ※ 「日付け」の自動選択は、キャレットの右側が数字のときのみ
 */
function SelectWordEx( arg ) {
  var fullWSpc   = arg[0];
  var selectLine = arg[1];

  var d = editor.ActiveDocument,  s = d.selection;
  if ( s.IsEmpty ) { s.SelectWord(); }	// キャレットは選択範囲の末尾
  var st = s.Text;  if ( ! st ) { return false; }
  var ax = s.GetActivePointX( mePosLogical );
  var ay = s.GetActivePointY( mePosLogical );
  var line = d.GetLine( ay, 0 );
  var str1 = line.slice( 0, ax - 1 );	// キャレットより前の文字列
  var str2 = line.slice( ax - 1 );		// キャレットより後ろの文字列

  // ※ 全角空白をふくむ空白文字列全体を自動選択
  if ( fullWSpc && /^[\t  ]+$/.test( st ) ) {
    var sig1 = String( str1.match( /[\t  ]+$/ ) || "" );
    var sig2 = String( str2.match( /^[\t  ]+/ ) || "" );
    if ( sig1 || sig2 ) {
      s.SetAnchorPoint( mePosLogical, ax - sig1.length, ay );
      s.SetActivePoint( mePosLogical, ax + sig2.length, ay, true );
    }
    if ( s.Text !== st ) { return true; }
  }

  // ※ 同一記号の繰り返し文字列の自動選択
  if ( st.length === 1 ) {
    var sig1 = String( str1.match( new RegExp( "\\" + st + "+$", "" ) ) || "" );
    var sig2 = String( str2.match( new RegExp( "^\\" + st + "+", "" ) ) || "" );
    if ( sig1 || sig2 ) {
      s.SetAnchorPoint( mePosLogical, ax - sig1.length, ay );
      s.SetActivePoint( mePosLogical, ax + sig2.length, ay, true );
    }
    if ( s.Text !== st ) { return true; }
  }

  // ※ 日付の自動選択
  if ( /^\d{4}$/.test( st )	// yyyy
  && ( mmdd = str2.match( /^年(?:0?[1-9]|1[0-2])月(?:0?[1-9]|[12][0-9]|3[01])日/ ) )
  || ( mmdd = str2.match( /^([-.\/])(?:0?[1-9]|1[0-2])\1(?:0?[1-9]|[12][0-9]|3[01])\b/ ) )
  ) {
    s.SetActivePoint( mePosLogical, ax + String( mmdd[0] ).length, ay, true );
    return true;
  }
  var reg1 = /^(?:19|20)\d{2}([-.\/])(?:0?[1-9]|1[0-2])\1(?:0?[1-9]|[12][0-9]|3[01])$/;
  var reg2 = /^(?:19|20)\d{2}年(?:0?[1-9]|1[0-2])月(?:0?[1-9]|[12][0-9]|3[01])日$/;
  if ( /^(?:0?[1-9]|1[0-2])$/.test( st )	// mm
  && ( yy = str1.slice( 0, - st.length ).match( /\b\d{4}[-.\/年]$/ ) )
  && ( dd = str2.match( /^[-.\/月](?:0?[1-9]|[12][0-9]|3[01])日?(?=$|\D)/ ) )
  && ( reg1.test( yy + st + dd ) || reg2.test( yy + st + dd ) ) ) {
    s.SetAnchorPoint( mePosLogical, ax - ( yy + st ).length, ay );
    s.SetActivePoint( mePosLogical, ax + String( dd ).length, ay, true );
    return true;
  }
  if ( /^(?:0?[1-9]|[12][0-9]|3[01])$/.test( st )	// dd
  && ( yymm = str1.slice( 0, - st.length ).match( /\b\d{4}[-.\/年](?:0?[1-9]|1[0-2])[-.\/月]$/ ) )
  && ( dd = str2.match( /^日?(?=$|\D)/ ) )
  && ( reg1.test( yymm + st + dd ) || reg2.test( yymm + st + dd ) )
  ) {
    s.SetAnchorPoint( mePosLogical, ax - ( yymm + st ).length, ay );
    s.SetActivePoint( mePosLogical, ax + String( dd ).length, ay, true );
    return true;
  }

  // ※ 行の自動選択
  if ( selectLine ) {
    s.StartOfLine( false, selectLine );
    s.EndOfLine( true, selectLine );
    if ( s.Text ) { return true; }
  }
  return false;
}

/**
 * 関数 CommentOut( str, commentMark )
 * 1行コメントアウト/アンコメント
 */
function CommentOut( line, commentMark ) {
  var s = editor.ActiveDocument.selection;
  // s.EndOfLine( false, mePosLogical );
  // s.StartOfLine( true, mePosLogical );
  // var line = s.Text;
  var comBlank = commentMark.match( /\s+$/ ) || "";
  var comEscaped = commentMark.replace( /\s+$/g, "" ).replace( /\W/g, "\\$&" );
  var regUnComment = new RegExp(
  "^(\\s*)(" + comEscaped + ( comBlank ? comBlank : "" ) + ")"
  , "" );

  if ( regUnComment.test( line ) ) {
    s.Text = line.replace( regUnComment, "$1" );
  }
  else {
    s.Text = line.replace( /^(\s*)(\S*)/, "$1" + commentMark + "$2" );
  }
  s.StartOfLine( true, mePosLogical );
  return true;
}

/**
 * 関数 MultiFunction( Fn, arg )
 * マルチカーソル(複数選択範囲)に対応させる
 * 第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 );	// SelectEX_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 );
  }
}


メモ[編集]

2020/05/31 ➀
  • このマクロは、「TAB/半角空白 トグル変換」マクロとおなじく「Yes/No マクロ」への自家用追加コードとしてつくったものを単独マクロとして切出したものです。 現在自分が使用している「同一記号の繰り返し部分の自動選択」と「日付の自動選択」⇒「今日の日付に変換」機能以外は、公開・配布用のソースコードでも初期値を「無効」に設定してあります。
  • ZIP には既製の「マテリアルデザインっぽいアイコン」からちょっといじって詰めておきましたが、いい感じではなくて申しわけないです。
2020/05/31 ➁
  • 正規表現のミスを修正。
2020/07/01
  • マルチカーソル/複数選択範囲に対応。
  • 「日付の自動選択」の誤爆を減らす修正。
  • 「全角空白を含む空白文字列」の自動選択の不具合を修正。
  • 「行全体を選択」と「1行コメントアウト/アンコメント」以外の機能は「Yes/No マクロ」に移植済み。
スポンサーリンク