TAB/半角空白 トグル変換

提供:MeryWiki
2020年5月27日 (水) 18:39時点におけるSukemaru (トーク | 投稿記録)による版 (ページを新規作成)
(差分) ← 古い版 | 最新版 (差分) | 新しい版 → (差分)
ナビゲーションに移動 検索に移動

実行するごとに「TAB ⇔ 半角空白」の相互変換をします。

[編集] メニュー >> [選択範囲の変換] サブメニューのコマンド「タブを空白に変換」と「空白をタブに変換」は

  1. メニューの中をたどってコマンドを見つけるのが面倒
  2. 選択範囲がないと実行できない
  3. 選択した範囲を無視して論理行全体が変換される
  4. トリプルクリックや行番号のクリック(ドラッグ)で範囲選択した状態から実行すると、ひとつ下の行まで変換されてしまう

などいろいろと使いづらい部分があります。

このマクロでは、キャレット位置に TAB や連続する半角空白があるときは自動的に空白文字列を範囲選択して「TAB ⇔ 半角空白」変換します。
連続実行した場合にはトグル式に相互変換しますので、ツールバーアイコンひとつでふたつの機能(+α)を利用できます。

設定項目


tabColumns

「タブの桁数」(半角空白で何コ分の幅か)を数値で指定します。
[オプション] ダイアログの [基本] カテゴリの「タブの桁数」の設定値で指定してください。

e.g. tabColumns = 4 で「半角空白6コ」の選択範囲を変換すると「TAB1コ+半角空白2コ」になります。
  • tabColumns = 0 を指定すると「タブの桁数」を Mery から自動取得します。
Mery ver 3 以降では Document.IndentSize プロパティでの取得を試行します。 取得できなかった場合や Mery ver 2.x では Mery.ini から読み込みます。
EditorConfigindent_size をカスタマイズしている場合は、indentSize = 0 に設定してください。


wholeLinesEnable
論理行全体の「TAB ⇔ 半角空白」変換を許可するかしないかの設定です。
wholeLinesEnable = true のとき、選択範囲がなくキャレット位置に変換対象になる空白文字列がない場合や、選択範囲があっても変換対象になる空白文字列がない場合、は、論理行全体を選択範囲にして「TAB ⇔ 半角空白」の変換を試行します。
  • Mery の標準コマンドの「タブを空白に変換」と「空白をタブに変換」で処理するので複数行の範囲選択状態からでも変換可能ですが、つねに論理行全体、論理行内のすべての TAB とすべての連続する半角空白が対象となります。
  • 事前処理コードを入れてあるので、トリプルクリックや行番号のクリックまたはドラッグで行全体+末尾改行のある範囲選択状態から実行したときに、ひとつ下の行は変換対象になりません。


使用上の注意


優先ルール
  • 「TAB+半角空白」からの実行では、つねに「TAB ⇒ 半角空白」変換になります(再度実行すれば「半角空白 ⇒ TAB」変換になります)。


  • 選択範囲が複数行にまたがっている場合は、つねに論理行全体が変換対象になります。
  • このマクロの設定項目 tabColumns の値と Mery の [オプション] の「タブの桁数」の設定値がことなると、行全体の「半角空白 ⇒ TAB」変換の可否判定や変換結果がうまくないかんじになる場合があります。
  • マルチカーソル複数選択や矩形選択には非対応です。 回避処理は入れていませんので、意図したとおりの結果にならなかったときは Undo (Ctrl+Z) してください。


キャレット位置または選択範囲内の「TAB ⇔ 半角空白」変換
  • wholeLines = false のとき、選択範囲内に改行がある場合や、非空白文字(全角空白も含む)がある場合は変換しません。
  • キャレット位置の空白文字列を変換するとき、行頭の TAB インデントから半角空白への変換では後ろの文字列がズレることはないようですが、行の途中の TAB を半角空白に変換した場合や、TAB の区切り位置からズレた連続する半角空白幅を TAB に変換した場合は、後ろの文字列の先頭位置がズレます。


ダウンロード


ファイル:TAB/半角空白変換.zip」(アイコン入り)
  • 半角空白変換.js
  • マテリアルデザインっぽい専用アイコン
  • (オマケ)タブを空白に変換.js
  • (オマケ)空白をタブに変換.js


ソースコード

#title = "TAB ⇔ 半角空白 トグル変換"
#tooltip = "キャレット位置だけ TAB ⇔ 半角空白 の変換をする"
#icon = "tabify[3].ico"
/**
 * ---------------------------------------------------------
 * 「TAB/半角空白変換」マクロ
 * sukemaru, 2020/05/27
 * ---------------------------------------------------------
 * キャレット位置または選択範囲内だけの TAB ⇔ 半角空白 変換可能
 * 
 * ※ 優先ルール: タブ+半角空白 からの変換のばあいは TAB ⇒ 半角空白 変換
 *  (再度実行すれば 半角空白 ⇒ TAB 変換)
 */

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

// ■ タブの桁数
var tabColumns = 0;
  // ※ Mery から自動取得するなら tabColumns = 0 にする

// ■ 論理行全体(複数行可)の TAB ⇔ 半角空白 もおこなう
var wholeLinesEnable = true;	// true: する / false: しない

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

var d = editor.ActiveDocument,  s = d.selection;
var sv = d.Saved;
var isEmpty = s.IsEmpty;
var pos = ( isEmpty ) ? s.GetActivePos() : -1;
var ay = s.GetActivePointY( mePosLogical );

if ( d.ReadOnly ) {
  Status = " ドキュメントは書き換え禁止です。";
}
else if ( ! tabColumns || ! ( tabColumns > 0 ) ) {
  // Mery ver 3 以降?
  if ( "IndentSize" in window.Document ) {
    tabColumns = d.IndentSize;
  }
  // Mery.ini から「タブの桁数」を取得する
  if ( ! tabColumns || ! ( tabColumns > 0 ) ) {
    tabColumns = GetIniOptionNum( "TabColumns" ) || 8;
  }
}

if ( isEmpty ) {
  s.SelectWord();
  if ( s.GetActivePointY( mePosLogical ) != ay ) {
    s.SetActivePos( pos );
  }
}
var act = s.GetActivePos(),  anc = s.GetAnchorPos();
var bp = Math.min( act, anc );
var str = tmp = s.Text;

if ( str && tabColumns > 0 && /^[\t ]+$/.test( str ) ) {

  // TAB ⇒ 半角空白(キャレット位置または選択範囲内のみ)
  if ( str.indexOf( "\t" ) > -1 ) {
    // Act1: 選択範囲を新規の空白行にして範囲選択
    s.Text = "\n" + str + "\n";
    s.CharLeft();  s.SetAnchorPos( bp + 1 );
    // Act2: 「タブを半角空白に変換」した文字列を取得
    s.Untabify();
    tmp = s.Text;
    // Act1, Act2 を巻き戻して TAB ⇒ 半角空白 変換終了
    d.Undo();  d.Undo();
    s.Text = tmp;
    s.SetAnchorPos( s.GetActivePos() - tmp.length );
  }

  // 半角空白 ⇒ TAB(キャレット位置または選択範囲内のみ)
  else if ( RegExp( "[ ]{" + tabColumns + "}", "" ).test( str ) ) {
    // Act1: 選択範囲を新規の空白行にして範囲選択
    s.Text = "\n" + str + "\n";
    s.CharLeft();  s.SetAnchorPos( bp + 1 );
    // Act2: 「半角空白をタブに変換」した文字列を取得
    s.Tabify();
    tmp = s.Text;
    // Act1, Act2 を巻き戻して 半角空白 ⇒ TAB 変換終了
    d.Undo();  d.Undo();
    s.Text = tmp;
    s.SetAnchorPos( s.GetActivePos() - tmp.length );
  }
}

// wholeLinesEnable = true のとき
if ( str === s.Text && wholeLinesEnable ) {
  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 );
  str = s.Text;

  // TAB ⇒ 半角空白(行全体)
  if ( str.indexOf( "\t" ) > -1 ) {
    s.Untabify();
  }

  // 半角空白 ⇒ TAB(行全体)
  else if ( RegExp( "[ ]{2}", "" ).test( str ) ) {
    s.Tabify();
    if ( str === s.Text ) { d.Undo();  d.Saved = sv;  }
  }
  // else {
  //   s.Tabify();
  //   if ( str === s.Text ) { d.Undo();  d.Saved = sv;  }
  // }

  if ( str === s.Text ) {
    if ( pos >= 0 ) { s.SetActivePos( pos ); }
    else { s.SetActivePos( act );  s.SetAnchorPos( anc ); }
  }
  else { s.CharRight( true ); }
}

/**
 * 関数 GetIniOptionNum( key )
 * 引数で指定された設定項目の「値」を返す(※数値のみ)
 */
function GetIniOptionNum( key ) {
  var Fso = new ActiveXObject( "Scripting.FileSystemObject" );
  // Mery.ini を探す
  var meryPath = editor.FullName;
  var mery     = Fso.GetBaseName( meryPath );
  var iniPath  = meryPath.replace( /\.exe$/i, ".ini" );
  if ( ! Fso.FileExists( iniPath ) ) {
    iniPath = new ActiveXObject( "WScript.Shell" )
              .ExpandEnvironmentStrings( "%APPDATA%" )
              + "\\Mery\\" + mery + ".ini";
  }
  // Mery.ini を読み込んで項目 key の値を取得する
  var iniFile = Fso.OpenTextFile( iniPath, 1 );
  var iniText = iniFile.ReadAll();
  iniFile.Close();  Fso = null;
  var value = RegExp( "^" + key + "=(\\d*)$", "m" ).exec( iniText )[1];
  return Number( value );
}


メモ

前半部分は、行頭インデントの「ハード ⇔ ソフト」変換を目的として「Yes/No マクロ」への自家用追加コードとしてつくった簡易的な処理なので、行の途中にある空白文字列の「TAB ⇔ 半角空白」に使うには適していないとおもいます。
Alert(Document.IndentSize);で -1 が返ってくるファイルもあって、なんかモヤっとしてます...。

スポンサーリンク