「TAB/半角空白 トグル変換」の版間の差分
ナビゲーションに移動
検索に移動
ページを新規作成 |
空行と改行の除去 |
||
| (2人の利用者による、間の7版が非表示) | |||
| 1行目: | 1行目: | ||
実行するごとに「TAB ⇔ 半角空白」の相互変換をします。 | 実行するごとに「TAB ⇔ 半角空白」の相互変換をします。<br> | ||
< | * <span style="color:#0000c0;">'''選択範囲内のみ''' の「TAB ⇔ 半角空白」の相互変換も可。</span><br> | ||
[編集] メニュー >> [選択範囲の変換] | * '''マルチカーソル/複数選択''' からの一括変換可 (Mery Ver 3.0.1 以降) | ||
[編集] メニュー >> [選択範囲の変換] サブメニュー >> のコマンド「[[ヘルプ:編集#タブを空白に変換|タブを空白に変換]]」と「[[ヘルプ:編集#空白をタブに変換|空白をタブに変換]]」は... | |||
# メニューの中をたどってコマンドを見つけるのが面倒 | # メニューの中をたどってコマンドを見つけるのが面倒 | ||
# 選択範囲がないと実行できない | # 選択範囲がないと実行できない | ||
# 選択した範囲を無視して論理行全体が変換される | # 選択した範囲を無視して論理行全体が変換される | ||
# | # トリプルクリックや行番号のクリック(またはドラッグ)で範囲選択した状態から実行すると、ひとつ下の行まで変換されてしまう | ||
などの理由で実際に使用する機会が少なくて、空白文字列を範囲選択してから Space キーや Tab キーを叩くことのほうが多かったりします。 | |||
< | |||
このマクロでは、キャレット位置に TAB | <span style="color:#0000c0;">このマクロでは、キャレット位置に TAB や連続する半角空白があるときは自動的に空白文字列を範囲選択して、選択範囲だけを「TAB ⇔ 半角空白」変換します。</span><br> | ||
<br> | 連続で実行した場合にはトグル式に相互変換しますので、ツールバーアイコンひとつでふたつの機能(+α)を利用できます。 | ||
※ 選択範囲内に「TAB/半角空白」以外の文字列がある場合は、行全体を「TAB ⇔ 半角空白」変換します。 | |||
== 設定項目 == | == 設定項目 == | ||
'''<code>indentSize</code>''' (初期値: '''0''')<br> | |||
「タブの桁数」(半角空白で何コ分の幅か)を数値で指定します。 | |||
: 変更する場合は、[オプション] ダイアログ >> [基本] カテゴリ の「タブの桁数」の設定値で指定してください。 | |||
: ''e.g.'' <code> | |||
* '''<code> | : ''e.g.'' <code>indentSize = 4</code> で「半角空白6コ」の選択範囲を変換すると「TAB1コ+半角空白2コ」になります。 | ||
: Mery | |||
* '''<code>indentSize = 0</code>''' なら「タブの桁数」を Mery から自動取得します。 | |||
<br> | : Mery Ver 3 以降では <code>Document.IndentSize</code> プロパティでの取得を試行します。 <code>Document.IndentSize</code> を取得できなかった場合や Mery Ver 2.x では Mery.ini から「タブの桁数」を読みこみます。 | ||
'''<code>wholeLines</code>''' (初期値: '''true''')<br> | |||
: < | 論理行全体(複数行可)の「TAB ⇔ 半角空白」変換を許可するかしないかのオプションです。 | ||
: <syntaxhighlight lang="javascript" inline>wholeLines = true</syntaxhighlight> のときで、選択範囲がなくキャレット位置に変換対象になる空白文字列がない場合や、選択範囲があっても変換対象になる空白文字列がない(または半角空白の数が「タブの桁数」に満たない)場合、選択範囲に非空白文字(全角空白や改行も)がふくまれている場合は、論理行全体を選択範囲にして「TAB ⇔ 半角空白」の変換を試行します。 | |||
* Mery の標準コマンドの「タブを空白に変換」と「空白をタブに変換」で処理するので複数行の範囲選択状態からでも変換可能ですが、つねに論理行全体、論理行内のすべての TAB とすべての連続する半角空白が対象となります。 | * Mery の標準コマンドの「タブを空白に変換」と「空白をタブに変換」で処理するので複数行の範囲選択状態からでも変換可能ですが、つねに論理行全体、論理行内のすべての TAB とすべての連続する半角空白が対象となります。 | ||
* | * トリプルクリックや行番号のクリックまたはドラッグで「行全体+末尾改行のある範囲選択状態」から実行したとき、ひとつ下の行は変換対象になりません。 | ||
<br> | |||
'''<code>FullToHalf2</code>''' (初期値: '''false''')<br> | |||
全角空白を「空白をタブに変換」の対象に含めるためのオプションです。 | |||
* 設定項目で <syntaxhighlight lang="javascript" inline>FullToHalf2 = true</syntaxhighlight> にすると全角空白を「半角空白×2」とみなして「空白 ⇒ TAB」変換します。 | |||
* さいしょに「全角空白 ⇒ 半角空白×2」変換してから「TAB ⇒ 半角空白」「空白 ⇒ TAB」の順に試行します。 | |||
以下の理由によりあまり役に立つ機能ではなさそうなので、初期値を <syntaxhighlight lang="javascript" inline>false</syntaxhighlight> に設定してあります。 | |||
* 「全角空白 ⇒ 半角空白×2」の変換は一方通行('''不可逆''')です。 | |||
* このマクロの基本プランが「TAB ⇔ 半角空白」の相互変換であるため、「全角空白 ⇒ 半角空白×2」変換は単独的機能として設計していません。 全角空白をふくむ選択範囲から「全角空白 ⇒ 半角空白×2」変換した「連続する半角空白」の先頭が TAB の区切り位置にあれば、「空白 ⇒ TAB」変換します。 | |||
* このマクロは「連続する全角空白」や、「全角空白」と「半角空白や TAB」 と混在する文字列では、空白文字列すべてを自動選択することはできません。 あらかじめ範囲選択してから実行する必要があります。 | |||
: ※ 「[[拡張自動選択]]」マクロか「[[TrueとFalse を切り替える|Yes/No マクロ]]」で「全角空白をふくむ空白文字列」全体を自動選択可。 | |||
* 「行全体」オプションが有効な場合、ソースコード内で全角の <code>" "</code> でなければならない部分が壊れる(半角空白×2に変換される)ことがあります。 | |||
: 設定項目 <syntaxhighlight lang="javascript" inline>var wholeLines = true</syntaxhighlight> で利用する場合、<code>FullToHalf2</code> は無効(<code>false</code>)にすることをお勧めします。 | |||
== 注意事項 == | |||
* このマクロの設定項目 <code>indentSize</code> の値と Mery の [オプション] の「タブの桁数」の設定値(または「[[プラグイン:EditorConfig|EditorConfig プラグイン]]」[https://www.haijin-boys.com/software/mery/mery-2-8-7] の <code>indent_size</code> の値)がことなると、「半角空白 ⇒ TAB」変換の可否判定や変換結果がうまくないかんじになる場合があります。 | |||
: EditorConfig で <code>indent_size</code> をカスタマイズしている場合、このマクロの設定は <syntaxhighlight lang="javascript" inline>var indentSize = 0</syntaxhighlight> にしてください(Mery Ver 3.0.x 以降)。 | |||
: ※「[[プラグイン:EditorConfig|EditorConfig プラグイン]]」は Ver 1.0.2 (2020/03/14) 以降の最新版を使用してください。 | |||
* <s>マルチカーソル/複数選択や</s> <span style="color:#c00;">矩形選択には非対応です。</span><br> 回避処理は入れていませんので、意図したとおりの結果にならなかったときは Undo (Ctrl+Z) してください。<br><span style="color:#0000c0;"> ⇒ マルチカーソル対応版を追加しました</span>が、矩形選択からの実行は考慮していないままです(2020/07/02)。 | |||
; 優先ルール | ; 優先ルール | ||
* | * 「TAB+半角空白」が混在する空白文字列からの実行では、つねに「TAB ⇒ 半角空白」変換になります(再度実行すれば「半角空白 ⇒ TAB」変換できます)。<br> | ||
<br> | |||
* | * 本来、全角空白は「空白をタブに変換」の対象外です。 <syntaxhighlight lang="javascript" inline>FullToHalf2 = false</syntaxhighlight> で全角空白をふくむ空白文字列を範囲選択している場合は、選択範囲をふくむ行全体が変換対象になります。 | ||
: また、選択範囲が複数行にまたがっている場合は、つねに論理行全体が変換対象になります。 | |||
: ※ <syntaxhighlight lang="javascript" inline>wholeLines = false</syntaxhighlight> のときは、行全体の変換なし。 | |||
; キャレット位置または選択範囲内の「TAB ⇔ 半角空白」変換 | ; キャレット位置または選択範囲内の「TAB ⇔ 半角空白」変換 | ||
* < | * <syntaxhighlight lang="javascript" inline>wholeLines = false</syntaxhighlight> のときで、選択範囲内に改行がある場合や、非空白文字(全角空白も含む)がある場合は変換しません。 | ||
* キャレット位置の空白文字列を変換するとき、行頭の TAB インデントから半角空白への変換では後ろの文字列がズレることはないようです<s>が、行の途中の TAB を半角空白に変換した場合や、TAB の区切り位置からズレた連続する半角空白幅を TAB に変換した場合は、うしろの文字列の先頭位置がズレます。</s><br><span style="color:#0000c0;"> ⇒ うしろの非空白文字列がズレないように対策しました</span>(2020/05/29 更新)。 | |||
: ※「連続する半角空白文字列」内の中途半端な位置から範囲選択して「半角空白 ⇒ TAB」変換すると、選択範囲よりうしろの文字列の先頭位置がズレます。 | |||
* | * 行の途中にある TAB ひとつを「TAB ⇒ 半角空白」変換するとき、TAB の見かけ上の幅が「タブの桁数」<code>indentSize</code> より小さいと、「TAB ⇒ 半角空白」したあとの半角空白の数が「タブの桁数」よりも少なくなります。 | ||
<br> | : ※「TAB ⇒ 半角空白」変換後にこのマクロを再実行しても「半角空白 ⇒ TAB」変換にならないことがあります。 | ||
; 行全体の「TAB ⇔ 半角空白」変換 | |||
* 複数行選択の状態から「TAB ⇔ 半角空白」変換したときに、選択範囲内の行のブックマーク設定が維持されるかどうかは Mery 本体のバージョンによってことなります。<br> 最新のベータバージョン(2020/05/29 時点で [https://www.haijin-boys.com/software/mery/mery-3-0-0 Ver 3.0.3])ではブックマーク設定が維持されますが、2020/05/29 時点での <q>正式版</q> Ver 2.6.7 ではブックマーク設定がズレたり消えたりします。 | |||
== ダウンロード == | == ダウンロード == | ||
; 「[[ファイル:TAB/半角空白変換(マルチカーソル対応版).zip]]」(アイコン入り) | |||
; 「[[ファイル:TAB/半角空白変換.zip]] | : ※ マルチカーソル対応版の動作要件:'''Mery Ver 3.0.1''' 以上 | ||
* | ; 「[[ファイル:TAB/半角空白変換.zip]]」(通常版 第2版:アイコン入り) | ||
: ※ Mery Ver 2.x 用 | |||
* TAB/半角空白変換.js | |||
* マテリアルデザインっぽい専用アイコン | * マテリアルデザインっぽい専用アイコン | ||
* (オマケ)タブを空白に変換.js | * (オマケ)タブを空白に変換.js | ||
* (オマケ)空白をタブに変換.js | * (オマケ)空白をタブに変換.js | ||
== | == 変更履歴 == | ||
< | * 2020/07/02: マルチカーソル対応版<br> | ||
* 2020/05/29: 通常版 第2版<br> | |||
* 2020/05/27: 通常版 初版 | |||
== ソースコード(マルチカーソル対応版) == | |||
※ 動作要件:'''Mery Ver 3.0.1''' 以上 | |||
<syntaxhighlight lang="javascript" style="height:100em; overflow:auto;"> | |||
#title = "TAB ⇔ 半角空白 トグル変換" | #title = "TAB ⇔ 半角空白 トグル変換" | ||
#tooltip = "キャレット位置だけ TAB ⇔ 半角空白 の変換をする" | #tooltip = "キャレット位置だけ TAB ⇔ 半角空白 の変換をする" | ||
#icon = "tabify[3].ico" | #icon = "tabify[3].ico" | ||
/** | /** | ||
* --------------------------------------------------------- | * --------------------------------------------------------- | ||
* 「TAB/半角空白変換」マクロ | * 「TAB/半角空白変換」マクロ (マルチカーソル対応版) | ||
* sukemaru, 2020/05/27 | * sukemaru, 2020/05/27 - 2020/07/02 | ||
* --------------------------------------------------------- | * --------------------------------------------------------- | ||
* | * キャレット位置または選択範囲内だけの「TAB ⇔ 半角空白」変換可 | ||
* 2020/05/29: うしろの非空白文字列の開始位置がズレないようコード改編 | |||
* 2020/07/02: マルチカーソル/複数選択に対応(要: Mery Ver 3.0.1 以上) | |||
* ※ 矩形選択からの実行を想定していません。 | |||
* | * | ||
* ※ 優先ルール: タブ+半角空白 | * ※ 優先ルール: タブ+半角空白 からの変換のばあいは「TAB ⇒ 半角空白」変換 | ||
* | * (再度実行すれば「半角空白 ⇒ TAB」変換) | ||
* ※ EditorConfig でタブの桁数をカスタマイズしている場合は、 | |||
* indentSize = 0 にすること。 | |||
*/ | */ | ||
| 72行目: | 112行目: | ||
// ■ タブの桁数 | // ■ タブの桁数 | ||
var | var indentSize = 0; | ||
// ※ Mery から自動取得するなら | |||
// ※ Mery から自動取得するなら indentSize = 0 にする | |||
// (自動取得できなかったときは 4) | |||
// ■ 論理行全体(複数行可)の「TAB ⇔ 半角空白」変換 | |||
var wholeLines = true; // true: する / false: しない | |||
// true: 選択範囲/キャレット位置が変換されなかった場合、行全体を変換する | |||
// false: 行全体の「TAB ⇔ 半角空白」変換はしない | |||
// ※ false の場合、選択範囲に非空白文字があるときは変換なし | |||
// ■「全角空白 ⇒ 半角空白×2」変換(非推奨) | |||
var FullToHalf2 = false; // true: する / false: しない | |||
// | // true なら、「全角空白 ⇒ 半角空白×2」変換後に「空白をタブに変換」する | ||
// ※「半角空白 ⇒ TAB」が優先なので「半角空白 ⇒ 全角空白」はできない | |||
// また、ソースコード内で全角の " " にしている部分が壊れることがある | |||
// ---------- ▲ 設定項目 ▲ ---------- // | // ---------- ▲ 設定項目 ▲ ---------- // | ||
var d = editor.ActiveDocument, s = d.selection; | var d = editor.ActiveDocument, s = d.selection; | ||
var | var sMode = s.Mode; | ||
if ( d.ReadOnly ) { | if ( d.ReadOnly ) { | ||
Status = " ドキュメントは書き換え禁止です。"; | Status = " ドキュメントは書き換え禁止です。"; | ||
} | } | ||
else if ( ! | else if ( ! sMode ) { | ||
Status = " このマクロは Mery Ver 3.0.1 以降でしか使用できません。"; | |||
} | |||
else { | |||
if ( ! indentSize || ! ( indentSize > 0 ) ) { | |||
if ( "IndentSize" in d ) { | |||
indentSize = d.IndentSize; | |||
} | |||
// Mery.ini から「タブの桁数」を取得する | |||
if ( ! indentSize || ! ( indentSize > 0 ) ) { | |||
indentSize = GetIniOptionNum( "TabColumns" ) || 4; | |||
} | |||
} | } | ||
// | |||
/* マルチカーソル対応 */ | |||
var sx = ScrollX, sy = ScrollY; | |||
var dt = d.Text; | |||
var arg = [ indentSize, wholeLines, FullToHalf2 ]; | |||
BeginUndoGroup(); AddUndo(); | |||
// 選択範囲が1つで矩形選択ではないとき | |||
if ( s.Mode == meModeStream ) { | |||
TabifyUntabify( arg ); | |||
} | } | ||
// 複数行または複数選択のとき | |||
else { | |||
MultiFunction( TabifyUntabify, arg ); | |||
} | |||
// テキストの変更なしなら UNDO 履歴から破棄 | |||
EndUndoGroup(); | |||
if ( d.Text === dt ) { | |||
d.Undo(); | |||
} | |||
Status = " タブの桁数: " + indentSize; | |||
ScrollX = sx; ScrollY = sy; | |||
} | } | ||
if ( | /** | ||
s. | * 関数 TabifyUntabify( [ indentSize, wholeLines, FullToHalf2 ] ) | ||
if ( s. | * 「TAB/半角空白変換」マクロ | ||
s.SetActivePos( pos ); | */ | ||
function TabifyUntabify( arg ) { | |||
var indentSize = arg[0]; | |||
var wholeLines = arg[1]; | |||
var FullToHalf2 = arg[2]; | |||
var d = editor.ActiveDocument, s = d.selection; | |||
var sv = d.Saved; | |||
var pos = ( s.IsEmpty ) ? s.GetActivePos() : -1; | |||
if ( pos > -1 ) { | |||
s.SelectWord(); | |||
} | |||
var act = s.GetActivePos(); | |||
var anc = s.GetAnchorPos(); | |||
var ty = s.GetTopPointY( mePosLogical ); | |||
var bx = s.GetBottomPointX( mePosLogical ); | |||
var st = s.Text; | |||
var tmp = d.GetLine( ty, 0 ).slice( 0, bx - 1 ); | |||
// 1行内の選択範囲のとき | |||
if ( st && indentSize > 0 && /^[\t ]+$/.test( st ) ) { | |||
// 全角空白 ⇒ 半角空白×2 | |||
if ( FullToHalf2 && st.indexOf( " " ) > -1 ) { | |||
st = st.replace( /[ ]/g, " " ); | |||
} | |||
// TAB ⇒ 半角空白(キャレット位置または選択範囲内のみ) | |||
if ( st.indexOf( "\t" ) > -1 ) { | |||
// Act1: 行頭から選択範囲の末尾までを新規の行にする | |||
s.Text = "\n" + tmp + "\n"; | |||
s.CharLeft(); s.WordLeft( true ); | |||
// Act2: 「タブを半角空白に変換」して行末の空白文字列を取得 | |||
s.Untabify(); | |||
s.Collapse( meCollapseEnd ); s.WordLeft( true ); | |||
st = s.Text; | |||
// マルチカーソル対応のためコード変更 ◆2020/07/02◆ | |||
s.SetAnchorPos( s.GetAnchorPos() + 1 ); | |||
s.SetActivePoint( mePosLogical, 1, ty, true ); | |||
s.Text = tmp; | |||
s.SetActivePos( anc ); | |||
s.SetActivePos( act, true ); | |||
// 「TAB ⇒ 半角空白」変換終了 | |||
s.Text = st; | |||
s.SetAnchorPos( s.GetActivePos() - st.length ); | |||
return; | |||
} | |||
// 半角空白 ⇒ TAB(キャレット位置または選択範囲内のみ) | |||
else if ( st.indexOf( " " ) > -1 ) { | |||
// Act1: 行頭から選択範囲の末尾までを新規の行にする | |||
s.Text = "\n" + tmp + "\n"; | |||
s.CharLeft(); | |||
s.WordLeft( true ); | |||
// Act2: 「半角空白をタブに変換」して行末の空白文字列を取得 | |||
s.Tabify(); | |||
s.Collapse( meCollapseEnd ); | |||
s.WordLeft( true ); | |||
st = s.Text; | |||
// マルチカーソル対応のためコード変更 ◆2020/07/02◆ | |||
s.SetAnchorPos( s.GetAnchorPos() + 1 ); | |||
s.SetActivePoint( mePosLogical, 1, ty, true ); | |||
s.Text = tmp; | |||
s.SetActivePos( anc ); | |||
s.SetActivePos( act, true ); | |||
// 「半角空白 ⇒ TAB」変換終了 | |||
if ( st !== s.Text ) { | |||
s.Text = st; | |||
s.SetAnchorPos( s.GetActivePos() - st.length ); | |||
return; | |||
} | |||
} | |||
} | |||
// wholeLines = true のとき | |||
if ( st === s.Text && wholeLines ) { | |||
var by = s.GetBottomPointY( mePosLogical ); | |||
// 選択範囲を拡張(末尾改行を含めない) | |||
if ( by !== ty && bx === 1 ) { by --; } | |||
s.SetActivePoint( mePosLogical, 1, ty ); | |||
s.SetAnchorPoint( mePosLogical, d.GetLine( by, 0 ).length + 1, by ); | |||
st = s.Text; | |||
// 全角空白 ⇒ 半角空白×2 | |||
if ( FullToHalf2 && st.indexOf( " " ) > -1 ) { | |||
s.Text = st.replace( /[ ]/g, " " ); | |||
s.SetAnchorPoint( mePosLogical, 1, ty ); | |||
} | |||
// TAB ⇒ 半角空白(行全体) | |||
if ( st.indexOf( "\t" ) > -1 ) { | |||
s.Untabify(); | |||
} | |||
// 半角空白 ⇒ TAB(行全体) | |||
else { | |||
s.Tabify(); | |||
} | |||
// キャレット位置または選択範囲の復帰 | |||
if ( st === s.Text ) { | |||
if ( pos > -1 ) { | |||
s.SetActivePos( pos ); | |||
} | |||
else { | |||
s.SetActivePos( act ); | |||
s.SetAnchorPos( anc ); | |||
} | |||
} | |||
// 行全体を変換したときは論理行全体を範囲選択 | |||
else { | |||
s.SelectLine( true ); | |||
s.CharLeft( true ); | |||
} | |||
} | } | ||
} | } | ||
if ( | /** | ||
* 関数 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 = ""; | |||
if ( ( reg = RegExp( "^" + key + "=(\\d*)$", "m" ) ).test( iniText ) ) | |||
{ value = reg.exec( iniText )[1]; } | |||
return Number( value ); | |||
} | |||
/** | |||
* 関数 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 = d.TextLength; | |||
// | i < sCount; i ++ ) { | ||
s. | // dl = d.TextLength; | ||
s. | s.SetActivePos( Sel[i].act + diff ); | ||
// | s.SetAnchorPos( Sel[i].anc + diff ); | ||
s. | |||
Fn( arg ); // TabifyUntabify() 関数 | |||
// | |||
// Fn() の残した選択範囲(またはキャレット位置)を回収 | |||
Sel[i].act = s.GetActivePos(); | |||
Sel[i].anc = s.GetAnchorPos(); | |||
// diff += d.TextLength - dl; // 文字数の増減量(累積) | |||
diff += - dl + ( dl = d.TextLength ); | |||
} | } | ||
// マルチカーソル(複数選択範囲)を復帰 | |||
for ( var i = 0; i < sCount; i ++ ) { | |||
s.AddPos( Sel[i].anc, Sel[i].act ); | |||
} | |||
} | |||
</syntaxhighlight> | |||
== ソースコード(通常版) == | |||
※ Mery Ver 2.x 用 (マルチカーソル非対応) | |||
<syntaxhighlight lang="javascript" style="height:100em; overflow:auto;"> | |||
#title = "TAB ⇔ 半角空白 トグル変換" | |||
#tooltip = "キャレット位置だけ TAB ⇔ 半角空白 の変換をする" | |||
#icon = "tabify[3].ico" | |||
/** | |||
* --------------------------------------------------------- | |||
* 「TAB/半角空白変換」マクロ | |||
* sukemaru, 2020/05/27 - 2020/05/29 | |||
* --------------------------------------------------------- | |||
* キャレット位置または選択範囲内だけの「TAB ⇔ 半角空白」変換可 | |||
* 2020/05/29: うしろの非空白文字列の開始位置がズレないようコード改編 | |||
* | |||
* ※ 優先ルール: タブ+半角空白 からの変換のばあいは「TAB ⇒ 半角空白」変換 | |||
* (再度実行すれば「半角空白 ⇒ TAB」変換) | |||
* ※ EditorConfig でタブの桁数をカスタマイズしている場合は、 | |||
* indentSize = 0 にすること。 | |||
*/ | |||
// ---------- ▼ 設定項目 ▼ ---------- // | |||
// ■ タブの桁数 | |||
// ※ Mery から自動取得するなら indentSize = 0 にする | |||
var indentSize = 0; | |||
// ■ 論理行全体(複数行可)の「TAB ⇔ 半角空白」変換 | |||
var wholeLines = true; // true: する / false: しない | |||
// ■「全角空白 ⇒ 半角空白×2」変換 | |||
// ※「全角空白 ⇒ 半角空白×2」変換後に「空白をタブに変換」する | |||
var FullToHalf2 = false; // true: する / false: しない | |||
// ---------- ▲ 設定項目 ▲ ---------- // | |||
var d = editor.ActiveDocument, s = d.selection; | |||
var sv = d.Saved; | |||
var isEmpty = s.IsEmpty; | |||
var pos = ( isEmpty ) ? s.GetActivePos() : -1; | |||
if ( d.ReadOnly ) { | |||
Status = " ドキュメントは書き換え禁止です。"; | |||
} | } | ||
else { | |||
if ( ! indentSize || ! ( indentSize > 0 ) ) { | |||
// Mery Ver 3 以降? | |||
if ( "IndentSize" in Document ) { | |||
indentSize = d.IndentSize; | |||
} | |||
// Mery.ini から「タブの桁数」を取得する | |||
if ( ! indentSize || ! ( indentSize > 0 ) ) { | |||
indentSize = GetIniOptionNum( "TabColumns" ) || 4; | |||
} | |||
} | |||
Status = " タブの桁数: " + indentSize; | |||
if ( isEmpty ) { | |||
s.SelectWord(); | |||
} | |||
var act = s.GetActivePos(); | |||
var anc = s.GetAnchorPos(); | |||
var ty = s.GetTopPointY( mePosLogical ); | var ty = s.GetTopPointY( mePosLogical ); | ||
var bx = s.GetBottomPointX( mePosLogical ); | var bx = s.GetBottomPointX( mePosLogical ); | ||
var | var st = s.Text; | ||
if ( | if ( st && indentSize > 0 && /^[\t ]+$/.test( st ) ) { | ||
var ty = s.GetTopPointY( mePosLogical ); | |||
var bx = s.GetBottomPointX( mePosLogical ); | |||
// 全角空白 ⇒ 半角空白×2 | |||
if ( FullToHalf2 && st.indexOf( " " ) > -1 ) { | |||
st = st.replace( /[ ]/g, " " ); | |||
} | |||
// TAB ⇒ 半角空白(キャレット位置または選択範囲内のみ) | |||
if ( st.indexOf( "\t" ) > -1 ) { | |||
s. | // Act1: 行頭から選択範囲の末尾までを新規の行にする | ||
s.Text = "\n" + d.GetLine( ty, 0 ).slice( 0, bx - 1 ) + "\n"; | |||
s.CharLeft(); s.WordLeft( true ); | |||
// Act2: 「タブを半角空白に変換」して行末の空白文字列を取得 | |||
s.Untabify(); | |||
s.Collapse( meCollapseEnd ); s.WordLeft( true ); | |||
st = s.Text; | |||
// Act1, Act2 を巻き戻して「TAB ⇒ 半角空白」変換終了 | |||
d.Undo(); d.Undo(); | |||
s.Text = st; | |||
s.SetAnchorPos( s.GetActivePos() - st.length ); | |||
} | |||
// 半角空白 ⇒ TAB(キャレット位置または選択範囲内のみ) | |||
// else if ( RegExp( "[ ]{" + indentSize + "}", "" ).test( st ) ) { // } | |||
else if ( st.indexOf( " " ) > -1 ) { | |||
// Act1: 行頭から選択範囲の末尾までを新規の行にする | |||
s.Text = "\n" + d.GetLine( ty, 0 ).slice( 0, bx - 1 ) + "\n"; | |||
s.CharLeft(); s.WordLeft( true ); | |||
// Act2: 「半角空白をタブに変換」して行末の空白文字列を取得 | |||
s.Tabify(); | |||
s.Collapse( meCollapseEnd ); s.WordLeft( true ); | |||
st = s.Text; | |||
// Act1, Act2 を巻き戻して「半角空白 ⇒ TAB」変換終了 | |||
d.Undo(); d.Undo(); | |||
if ( st !== s.Text ) { | |||
s.Text = st; | |||
s.SetAnchorPos( s.GetActivePos() - st.length ); | |||
} | |||
} | |||
} | } | ||
// 半角空白 ⇒ TAB(行全体) | // wholeLines = true のとき | ||
if ( st === s.Text && wholeLines ) { | |||
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 ); | |||
st = s.Text; | |||
// TAB ⇒ 半角空白(行全体) | |||
if ( st.indexOf( "\t" ) > -1 ) { | |||
s.Untabify(); | |||
} | |||
// 半角空白 ⇒ TAB(行全体) | |||
else if ( RegExp( "[ ]{" + indentSize + "}", "" ).test( st ) ) { | |||
s.Tabify(); | |||
if ( st === s.Text ) { d.Undo(); d.Saved = sv; } | |||
} | |||
// else { | |||
// s.Tabify(); | |||
// if ( st === s.Text ) { d.Undo(); d.Saved = sv; } | |||
// } | |||
// キャレット位置または選択範囲の復帰 | |||
if ( st === s.Text ) { | |||
if ( pos >= 0 ) { s.SetActivePos( pos ); } | |||
else { s.SetActivePos( act ); s.SetAnchorPos( anc ); } | |||
} | |||
// 行全体を変換したときは論理行全体を範囲選択 | |||
else { s.SelectLine( true ); } | |||
} | } | ||
} | } | ||
| 194行目: | 535行目: | ||
var iniText = iniFile.ReadAll(); | var iniText = iniFile.ReadAll(); | ||
iniFile.Close(); Fso = null; | iniFile.Close(); Fso = null; | ||
var value = RegExp( "^" + key + "=(\\d*)$", "m" ).exec( iniText )[1]; | var value = ""; | ||
if ( ( reg = RegExp( "^" + key + "=(\\d*)$", "m" ) ).test( iniText ) ) | |||
{ value = reg.exec( iniText )[1]; } | |||
return Number( value ); | return Number( value ); | ||
} | } | ||
</ | </syntaxhighlight> | ||
== メモ == | == メモ == | ||
; 2020/07/02: | |||
* 1回の UNDO 操作で元に戻せるようにしたり、テキストに変更なしだったときに UNDO 履歴に残さない対策したりの課題をうまく解消できなかったので、「マルチカーソル対応版」と「通常版 (マルチカーソル非対応)」とでソースコードの共通化ができませんでした。 | |||
2024年9月9日 (月) 10:52時点における最新版
実行するごとに「TAB ⇔ 半角空白」の相互変換をします。
- 選択範囲内のみ の「TAB ⇔ 半角空白」の相互変換も可。
- マルチカーソル/複数選択 からの一括変換可 (Mery Ver 3.0.1 以降)
[編集] メニュー >> [選択範囲の変換] サブメニュー >> のコマンド「タブを空白に変換」と「空白をタブに変換」は...
- メニューの中をたどってコマンドを見つけるのが面倒
- 選択範囲がないと実行できない
- 選択した範囲を無視して論理行全体が変換される
- トリプルクリックや行番号のクリック(またはドラッグ)で範囲選択した状態から実行すると、ひとつ下の行まで変換されてしまう
などの理由で実際に使用する機会が少なくて、空白文字列を範囲選択してから Space キーや Tab キーを叩くことのほうが多かったりします。
このマクロでは、キャレット位置に TAB や連続する半角空白があるときは自動的に空白文字列を範囲選択して、選択範囲だけを「TAB ⇔ 半角空白」変換します。
連続で実行した場合にはトグル式に相互変換しますので、ツールバーアイコンひとつでふたつの機能(+α)を利用できます。
※ 選択範囲内に「TAB/半角空白」以外の文字列がある場合は、行全体を「TAB ⇔ 半角空白」変換します。
設定項目[編集]
indentSize (初期値: 0)
「タブの桁数」(半角空白で何コ分の幅か)を数値で指定します。
- 変更する場合は、[オプション] ダイアログ >> [基本] カテゴリ の「タブの桁数」の設定値で指定してください。
- e.g.
indentSize = 4で「半角空白6コ」の選択範囲を変換すると「TAB1コ+半角空白2コ」になります。
indentSize = 0なら「タブの桁数」を Mery から自動取得します。
- Mery Ver 3 以降では
Document.IndentSizeプロパティでの取得を試行します。Document.IndentSizeを取得できなかった場合や Mery Ver 2.x では Mery.ini から「タブの桁数」を読みこみます。
wholeLines (初期値: true)
論理行全体(複数行可)の「TAB ⇔ 半角空白」変換を許可するかしないかのオプションです。
wholeLines = trueのときで、選択範囲がなくキャレット位置に変換対象になる空白文字列がない場合や、選択範囲があっても変換対象になる空白文字列がない(または半角空白の数が「タブの桁数」に満たない)場合、選択範囲に非空白文字(全角空白や改行も)がふくまれている場合は、論理行全体を選択範囲にして「TAB ⇔ 半角空白」の変換を試行します。
- Mery の標準コマンドの「タブを空白に変換」と「空白をタブに変換」で処理するので複数行の範囲選択状態からでも変換可能ですが、つねに論理行全体、論理行内のすべての TAB とすべての連続する半角空白が対象となります。
- トリプルクリックや行番号のクリックまたはドラッグで「行全体+末尾改行のある範囲選択状態」から実行したとき、ひとつ下の行は変換対象になりません。
FullToHalf2 (初期値: false)
全角空白を「空白をタブに変換」の対象に含めるためのオプションです。
- 設定項目で
FullToHalf2 = trueにすると全角空白を「半角空白×2」とみなして「空白 ⇒ TAB」変換します。 - さいしょに「全角空白 ⇒ 半角空白×2」変換してから「TAB ⇒ 半角空白」「空白 ⇒ TAB」の順に試行します。
以下の理由によりあまり役に立つ機能ではなさそうなので、初期値を false に設定してあります。
- 「全角空白 ⇒ 半角空白×2」の変換は一方通行(不可逆)です。
- このマクロの基本プランが「TAB ⇔ 半角空白」の相互変換であるため、「全角空白 ⇒ 半角空白×2」変換は単独的機能として設計していません。 全角空白をふくむ選択範囲から「全角空白 ⇒ 半角空白×2」変換した「連続する半角空白」の先頭が TAB の区切り位置にあれば、「空白 ⇒ TAB」変換します。
- このマクロは「連続する全角空白」や、「全角空白」と「半角空白や TAB」 と混在する文字列では、空白文字列すべてを自動選択することはできません。 あらかじめ範囲選択してから実行する必要があります。
- ※ 「拡張自動選択」マクロか「Yes/No マクロ」で「全角空白をふくむ空白文字列」全体を自動選択可。
- 「行全体」オプションが有効な場合、ソースコード内で全角の
" "でなければならない部分が壊れる(半角空白×2に変換される)ことがあります。
- 設定項目
var wholeLines = trueで利用する場合、FullToHalf2は無効(false)にすることをお勧めします。
注意事項[編集]
- このマクロの設定項目
indentSizeの値と Mery の [オプション] の「タブの桁数」の設定値(または「EditorConfig プラグイン」[1] のindent_sizeの値)がことなると、「半角空白 ⇒ TAB」変換の可否判定や変換結果がうまくないかんじになる場合があります。
- EditorConfig で
indent_sizeをカスタマイズしている場合、このマクロの設定はvar indentSize = 0にしてください(Mery Ver 3.0.x 以降)。 - ※「EditorConfig プラグイン」は Ver 1.0.2 (2020/03/14) 以降の最新版を使用してください。
マルチカーソル/複数選択や矩形選択には非対応です。
回避処理は入れていませんので、意図したとおりの結果にならなかったときは Undo (Ctrl+Z) してください。
⇒ マルチカーソル対応版を追加しましたが、矩形選択からの実行は考慮していないままです(2020/07/02)。
- 優先ルール
- 「TAB+半角空白」が混在する空白文字列からの実行では、つねに「TAB ⇒ 半角空白」変換になります(再度実行すれば「半角空白 ⇒ TAB」変換できます)。
- 本来、全角空白は「空白をタブに変換」の対象外です。
FullToHalf2 = falseで全角空白をふくむ空白文字列を範囲選択している場合は、選択範囲をふくむ行全体が変換対象になります。
- また、選択範囲が複数行にまたがっている場合は、つねに論理行全体が変換対象になります。
- ※
wholeLines = falseのときは、行全体の変換なし。
- キャレット位置または選択範囲内の「TAB ⇔ 半角空白」変換
wholeLines = falseのときで、選択範囲内に改行がある場合や、非空白文字(全角空白も含む)がある場合は変換しません。
- キャレット位置の空白文字列を変換するとき、行頭の TAB インデントから半角空白への変換では後ろの文字列がズレることはないようです
が、行の途中の TAB を半角空白に変換した場合や、TAB の区切り位置からズレた連続する半角空白幅を TAB に変換した場合は、うしろの文字列の先頭位置がズレます。
⇒ うしろの非空白文字列がズレないように対策しました(2020/05/29 更新)。
- ※「連続する半角空白文字列」内の中途半端な位置から範囲選択して「半角空白 ⇒ TAB」変換すると、選択範囲よりうしろの文字列の先頭位置がズレます。
- 行の途中にある TAB ひとつを「TAB ⇒ 半角空白」変換するとき、TAB の見かけ上の幅が「タブの桁数」
indentSizeより小さいと、「TAB ⇒ 半角空白」したあとの半角空白の数が「タブの桁数」よりも少なくなります。
- ※「TAB ⇒ 半角空白」変換後にこのマクロを再実行しても「半角空白 ⇒ TAB」変換にならないことがあります。
- 行全体の「TAB ⇔ 半角空白」変換
- 複数行選択の状態から「TAB ⇔ 半角空白」変換したときに、選択範囲内の行のブックマーク設定が維持されるかどうかは Mery 本体のバージョンによってことなります。
最新のベータバージョン(2020/05/29 時点で Ver 3.0.3)ではブックマーク設定が維持されますが、2020/05/29 時点での正式版
Ver 2.6.7 ではブックマーク設定がズレたり消えたりします。
ダウンロード[編集]
- 「ファイル:TAB/半角空白変換(マルチカーソル対応版).zip」(アイコン入り)
- ※ マルチカーソル対応版の動作要件:Mery Ver 3.0.1 以上
- 「ファイル:TAB/半角空白変換.zip」(通常版 第2版:アイコン入り)
- ※ Mery Ver 2.x 用
- TAB/半角空白変換.js
- マテリアルデザインっぽい専用アイコン
- (オマケ)タブを空白に変換.js
- (オマケ)空白をタブに変換.js
変更履歴[編集]
- 2020/07/02: マルチカーソル対応版
- 2020/05/29: 通常版 第2版
- 2020/05/27: 通常版 初版
ソースコード(マルチカーソル対応版)[編集]
※ 動作要件:Mery Ver 3.0.1 以上
#title = "TAB ⇔ 半角空白 トグル変換"
#tooltip = "キャレット位置だけ TAB ⇔ 半角空白 の変換をする"
#icon = "tabify[3].ico"
/**
* ---------------------------------------------------------
* 「TAB/半角空白変換」マクロ (マルチカーソル対応版)
* sukemaru, 2020/05/27 - 2020/07/02
* ---------------------------------------------------------
* キャレット位置または選択範囲内だけの「TAB ⇔ 半角空白」変換可
* 2020/05/29: うしろの非空白文字列の開始位置がズレないようコード改編
* 2020/07/02: マルチカーソル/複数選択に対応(要: Mery Ver 3.0.1 以上)
* ※ 矩形選択からの実行を想定していません。
*
* ※ 優先ルール: タブ+半角空白 からの変換のばあいは「TAB ⇒ 半角空白」変換
* (再度実行すれば「半角空白 ⇒ TAB」変換)
* ※ EditorConfig でタブの桁数をカスタマイズしている場合は、
* indentSize = 0 にすること。
*/
// ---------- ▼ 設定項目 ▼ ---------- //
// ■ タブの桁数
var indentSize = 0;
// ※ Mery から自動取得するなら indentSize = 0 にする
// (自動取得できなかったときは 4)
// ■ 論理行全体(複数行可)の「TAB ⇔ 半角空白」変換
var wholeLines = true; // true: する / false: しない
// true: 選択範囲/キャレット位置が変換されなかった場合、行全体を変換する
// false: 行全体の「TAB ⇔ 半角空白」変換はしない
// ※ false の場合、選択範囲に非空白文字があるときは変換なし
// ■「全角空白 ⇒ 半角空白×2」変換(非推奨)
var FullToHalf2 = false; // true: する / false: しない
// true なら、「全角空白 ⇒ 半角空白×2」変換後に「空白をタブに変換」する
// ※「半角空白 ⇒ TAB」が優先なので「半角空白 ⇒ 全角空白」はできない
// また、ソースコード内で全角の " " にしている部分が壊れることがある
// ---------- ▲ 設定項目 ▲ ---------- //
var d = editor.ActiveDocument, s = d.selection;
var sMode = s.Mode;
if ( d.ReadOnly ) {
Status = " ドキュメントは書き換え禁止です。";
}
else if ( ! sMode ) {
Status = " このマクロは Mery Ver 3.0.1 以降でしか使用できません。";
}
else {
if ( ! indentSize || ! ( indentSize > 0 ) ) {
if ( "IndentSize" in d ) {
indentSize = d.IndentSize;
}
// Mery.ini から「タブの桁数」を取得する
if ( ! indentSize || ! ( indentSize > 0 ) ) {
indentSize = GetIniOptionNum( "TabColumns" ) || 4;
}
}
/* マルチカーソル対応 */
var sx = ScrollX, sy = ScrollY;
var dt = d.Text;
var arg = [ indentSize, wholeLines, FullToHalf2 ];
BeginUndoGroup(); AddUndo();
// 選択範囲が1つで矩形選択ではないとき
if ( s.Mode == meModeStream ) {
TabifyUntabify( arg );
}
// 複数行または複数選択のとき
else {
MultiFunction( TabifyUntabify, arg );
}
// テキストの変更なしなら UNDO 履歴から破棄
EndUndoGroup();
if ( d.Text === dt ) {
d.Undo();
}
Status = " タブの桁数: " + indentSize;
ScrollX = sx; ScrollY = sy;
}
/**
* 関数 TabifyUntabify( [ indentSize, wholeLines, FullToHalf2 ] )
* 「TAB/半角空白変換」マクロ
*/
function TabifyUntabify( arg ) {
var indentSize = arg[0];
var wholeLines = arg[1];
var FullToHalf2 = arg[2];
var d = editor.ActiveDocument, s = d.selection;
var sv = d.Saved;
var pos = ( s.IsEmpty ) ? s.GetActivePos() : -1;
if ( pos > -1 ) {
s.SelectWord();
}
var act = s.GetActivePos();
var anc = s.GetAnchorPos();
var ty = s.GetTopPointY( mePosLogical );
var bx = s.GetBottomPointX( mePosLogical );
var st = s.Text;
var tmp = d.GetLine( ty, 0 ).slice( 0, bx - 1 );
// 1行内の選択範囲のとき
if ( st && indentSize > 0 && /^[\t ]+$/.test( st ) ) {
// 全角空白 ⇒ 半角空白×2
if ( FullToHalf2 && st.indexOf( " " ) > -1 ) {
st = st.replace( /[ ]/g, " " );
}
// TAB ⇒ 半角空白(キャレット位置または選択範囲内のみ)
if ( st.indexOf( "\t" ) > -1 ) {
// Act1: 行頭から選択範囲の末尾までを新規の行にする
s.Text = "\n" + tmp + "\n";
s.CharLeft(); s.WordLeft( true );
// Act2: 「タブを半角空白に変換」して行末の空白文字列を取得
s.Untabify();
s.Collapse( meCollapseEnd ); s.WordLeft( true );
st = s.Text;
// マルチカーソル対応のためコード変更 ◆2020/07/02◆
s.SetAnchorPos( s.GetAnchorPos() + 1 );
s.SetActivePoint( mePosLogical, 1, ty, true );
s.Text = tmp;
s.SetActivePos( anc );
s.SetActivePos( act, true );
// 「TAB ⇒ 半角空白」変換終了
s.Text = st;
s.SetAnchorPos( s.GetActivePos() - st.length );
return;
}
// 半角空白 ⇒ TAB(キャレット位置または選択範囲内のみ)
else if ( st.indexOf( " " ) > -1 ) {
// Act1: 行頭から選択範囲の末尾までを新規の行にする
s.Text = "\n" + tmp + "\n";
s.CharLeft();
s.WordLeft( true );
// Act2: 「半角空白をタブに変換」して行末の空白文字列を取得
s.Tabify();
s.Collapse( meCollapseEnd );
s.WordLeft( true );
st = s.Text;
// マルチカーソル対応のためコード変更 ◆2020/07/02◆
s.SetAnchorPos( s.GetAnchorPos() + 1 );
s.SetActivePoint( mePosLogical, 1, ty, true );
s.Text = tmp;
s.SetActivePos( anc );
s.SetActivePos( act, true );
// 「半角空白 ⇒ TAB」変換終了
if ( st !== s.Text ) {
s.Text = st;
s.SetAnchorPos( s.GetActivePos() - st.length );
return;
}
}
}
// wholeLines = true のとき
if ( st === s.Text && wholeLines ) {
var by = s.GetBottomPointY( mePosLogical );
// 選択範囲を拡張(末尾改行を含めない)
if ( by !== ty && bx === 1 ) { by --; }
s.SetActivePoint( mePosLogical, 1, ty );
s.SetAnchorPoint( mePosLogical, d.GetLine( by, 0 ).length + 1, by );
st = s.Text;
// 全角空白 ⇒ 半角空白×2
if ( FullToHalf2 && st.indexOf( " " ) > -1 ) {
s.Text = st.replace( /[ ]/g, " " );
s.SetAnchorPoint( mePosLogical, 1, ty );
}
// TAB ⇒ 半角空白(行全体)
if ( st.indexOf( "\t" ) > -1 ) {
s.Untabify();
}
// 半角空白 ⇒ TAB(行全体)
else {
s.Tabify();
}
// キャレット位置または選択範囲の復帰
if ( st === s.Text ) {
if ( pos > -1 ) {
s.SetActivePos( pos );
}
else {
s.SetActivePos( act );
s.SetAnchorPos( anc );
}
}
// 行全体を変換したときは論理行全体を範囲選択
else {
s.SelectLine( true );
s.CharLeft( 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 = "";
if ( ( reg = RegExp( "^" + key + "=(\\d*)$", "m" ) ).test( iniText ) )
{ value = reg.exec( iniText )[1]; }
return Number( value );
}
/**
* 関数 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 = d.TextLength;
i < sCount; i ++ ) {
// dl = d.TextLength;
s.SetActivePos( Sel[i].act + diff );
s.SetAnchorPos( Sel[i].anc + diff );
Fn( arg ); // TabifyUntabify() 関数
// Fn() の残した選択範囲(またはキャレット位置)を回収
Sel[i].act = s.GetActivePos();
Sel[i].anc = s.GetAnchorPos();
// diff += d.TextLength - dl; // 文字数の増減量(累積)
diff += - dl + ( dl = d.TextLength );
}
// マルチカーソル(複数選択範囲)を復帰
for ( var i = 0; i < sCount; i ++ ) {
s.AddPos( Sel[i].anc, Sel[i].act );
}
}
ソースコード(通常版)[編集]
※ Mery Ver 2.x 用 (マルチカーソル非対応)
#title = "TAB ⇔ 半角空白 トグル変換"
#tooltip = "キャレット位置だけ TAB ⇔ 半角空白 の変換をする"
#icon = "tabify[3].ico"
/**
* ---------------------------------------------------------
* 「TAB/半角空白変換」マクロ
* sukemaru, 2020/05/27 - 2020/05/29
* ---------------------------------------------------------
* キャレット位置または選択範囲内だけの「TAB ⇔ 半角空白」変換可
* 2020/05/29: うしろの非空白文字列の開始位置がズレないようコード改編
*
* ※ 優先ルール: タブ+半角空白 からの変換のばあいは「TAB ⇒ 半角空白」変換
* (再度実行すれば「半角空白 ⇒ TAB」変換)
* ※ EditorConfig でタブの桁数をカスタマイズしている場合は、
* indentSize = 0 にすること。
*/
// ---------- ▼ 設定項目 ▼ ---------- //
// ■ タブの桁数
// ※ Mery から自動取得するなら indentSize = 0 にする
var indentSize = 0;
// ■ 論理行全体(複数行可)の「TAB ⇔ 半角空白」変換
var wholeLines = true; // true: する / false: しない
// ■「全角空白 ⇒ 半角空白×2」変換
// ※「全角空白 ⇒ 半角空白×2」変換後に「空白をタブに変換」する
var FullToHalf2 = false; // true: する / false: しない
// ---------- ▲ 設定項目 ▲ ---------- //
var d = editor.ActiveDocument, s = d.selection;
var sv = d.Saved;
var isEmpty = s.IsEmpty;
var pos = ( isEmpty ) ? s.GetActivePos() : -1;
if ( d.ReadOnly ) {
Status = " ドキュメントは書き換え禁止です。";
}
else {
if ( ! indentSize || ! ( indentSize > 0 ) ) {
// Mery Ver 3 以降?
if ( "IndentSize" in Document ) {
indentSize = d.IndentSize;
}
// Mery.ini から「タブの桁数」を取得する
if ( ! indentSize || ! ( indentSize > 0 ) ) {
indentSize = GetIniOptionNum( "TabColumns" ) || 4;
}
}
Status = " タブの桁数: " + indentSize;
if ( isEmpty ) {
s.SelectWord();
}
var act = s.GetActivePos();
var anc = s.GetAnchorPos();
var ty = s.GetTopPointY( mePosLogical );
var bx = s.GetBottomPointX( mePosLogical );
var st = s.Text;
if ( st && indentSize > 0 && /^[\t ]+$/.test( st ) ) {
var ty = s.GetTopPointY( mePosLogical );
var bx = s.GetBottomPointX( mePosLogical );
// 全角空白 ⇒ 半角空白×2
if ( FullToHalf2 && st.indexOf( " " ) > -1 ) {
st = st.replace( /[ ]/g, " " );
}
// TAB ⇒ 半角空白(キャレット位置または選択範囲内のみ)
if ( st.indexOf( "\t" ) > -1 ) {
// Act1: 行頭から選択範囲の末尾までを新規の行にする
s.Text = "\n" + d.GetLine( ty, 0 ).slice( 0, bx - 1 ) + "\n";
s.CharLeft(); s.WordLeft( true );
// Act2: 「タブを半角空白に変換」して行末の空白文字列を取得
s.Untabify();
s.Collapse( meCollapseEnd ); s.WordLeft( true );
st = s.Text;
// Act1, Act2 を巻き戻して「TAB ⇒ 半角空白」変換終了
d.Undo(); d.Undo();
s.Text = st;
s.SetAnchorPos( s.GetActivePos() - st.length );
}
// 半角空白 ⇒ TAB(キャレット位置または選択範囲内のみ)
// else if ( RegExp( "[ ]{" + indentSize + "}", "" ).test( st ) ) { // }
else if ( st.indexOf( " " ) > -1 ) {
// Act1: 行頭から選択範囲の末尾までを新規の行にする
s.Text = "\n" + d.GetLine( ty, 0 ).slice( 0, bx - 1 ) + "\n";
s.CharLeft(); s.WordLeft( true );
// Act2: 「半角空白をタブに変換」して行末の空白文字列を取得
s.Tabify();
s.Collapse( meCollapseEnd ); s.WordLeft( true );
st = s.Text;
// Act1, Act2 を巻き戻して「半角空白 ⇒ TAB」変換終了
d.Undo(); d.Undo();
if ( st !== s.Text ) {
s.Text = st;
s.SetAnchorPos( s.GetActivePos() - st.length );
}
}
}
// wholeLines = true のとき
if ( st === s.Text && wholeLines ) {
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 );
st = s.Text;
// TAB ⇒ 半角空白(行全体)
if ( st.indexOf( "\t" ) > -1 ) {
s.Untabify();
}
// 半角空白 ⇒ TAB(行全体)
else if ( RegExp( "[ ]{" + indentSize + "}", "" ).test( st ) ) {
s.Tabify();
if ( st === s.Text ) { d.Undo(); d.Saved = sv; }
}
// else {
// s.Tabify();
// if ( st === s.Text ) { d.Undo(); d.Saved = sv; }
// }
// キャレット位置または選択範囲の復帰
if ( st === s.Text ) {
if ( pos >= 0 ) { s.SetActivePos( pos ); }
else { s.SetActivePos( act ); s.SetAnchorPos( anc ); }
}
// 行全体を変換したときは論理行全体を範囲選択
else { s.SelectLine( 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 = "";
if ( ( reg = RegExp( "^" + key + "=(\\d*)$", "m" ) ).test( iniText ) )
{ value = reg.exec( iniText )[1]; }
return Number( value );
}
メモ[編集]
- 2020/07/02
- 1回の UNDO 操作で元に戻せるようにしたり、テキストに変更なしだったときに UNDO 履歴に残さない対策したりの課題をうまく解消できなかったので、「マルチカーソル対応版」と「通常版 (マルチカーソル非対応)」とでソースコードの共通化ができませんでした。
スポンサーリンク