「字下げ・字上げ」の版間の差分
細 更新履歴の書式を変更 |
→sukemaru 版: 更新(マルチカーソル対応) |
||
| 80行目: | 80行目: | ||
<br> | <br> | ||
'''ダウンロード:''' 「[[ファイル:字下げ/字上げ.zip]]」(アイコン入り)<br> | '''ダウンロード:''' 「[[ファイル:字下げ/字上げ.zip]]」(アイコン入り)<br> | ||
最終更新: | 最終更新: 2020/06/29 '''マルチカーソル''' & '''ブックマーク''' に対応 | ||
* [[#字下げ(スペース×2 追加)|スペース×2 追加]].js | * [[#字下げ(スペース×2 追加)|スペース×2 追加]].js | ||
* [[#字上げ(スペース×2 削除)|スペース×2 削除]].js | * [[#字上げ(スペース×2 削除)|スペース×2 削除]].js | ||
| 87行目: | 87行目: | ||
: それぞれ専用の「マテリアルデザインっぽいアイコン」を同梱 | : それぞれ専用の「マテリアルデザインっぽいアイコン」を同梱 | ||
<br> | <br> | ||
* マルチカーソル/複数選択範囲に対応('''Mery ver 3.0.1 以上''') | |||
: | |||
* ブックマーク対応の推奨要件: '''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: | <source lang="javascript" style="height:60em; overflow:auto;"> | ||
#title = "字下げ" | #title = "字下げ" | ||
#tooltip = "スペース×2 追加" | #tooltip = "スペース×2 追加" | ||
| 112行目: | 114行目: | ||
* --------------------------------------------------------- | * --------------------------------------------------------- | ||
* 「字下げ(スペース×2 追加)」マクロ | * 「字下げ(スペース×2 追加)」マクロ | ||
* sukemaru, 2018/11/01 - | * sukemaru, 2018/11/01 - 2020/06/29 | ||
* --------------------------------------------------------- | * --------------------------------------------------------- | ||
* 最初に選択範囲がなかった場合は「タブ文字」を追加、 | * 最初に選択範囲がなかった場合は「タブ文字」を追加、 | ||
| 118行目: | 120行目: | ||
* 選択範囲があった場合は、つねに「半角スペース×2」を行頭に追加する(空行は無視)。 | * 選択範囲があった場合は、つねに「半角スペース×2」を行頭に追加する(空行は無視)。 | ||
* | * | ||
* | * 2020/06/29: マルチカーソル/複数選択範囲に対応 | ||
* | * ブックマーク行を字下げしたときのみブックマークの復元をするように変更 | ||
* ブックマークの収集範囲を選択範囲に限定 | |||
* ブックマークの収集/復元コードを関数化 | |||
* 行頭空白追加の処理をループ関数から正規表現置換に変更 | |||
*/ | */ | ||
// ---------- ▼ 設定項目 ▼ ---------- | // ---------- ▼ 設定項目 ▼ ---------- | ||
// ■ | // ■ 選択範囲なしのとき | ||
var tabEnable = 0; | var tabEnable = 0; | ||
/** | /** | ||
| 140行目: | 145行目: | ||
var tab = "\t"; | var tab = "\t"; | ||
var d = editor.ActiveDocument; | var d = editor.ActiveDocument; | ||
if ( d.ReadOnly ) { | if ( d.ReadOnly ) { | ||
Status = " ドキュメントは書き換え禁止です。"; | 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( /\./, ". " ) | |||
+ " 秒 ]"; | |||
} | } | ||
/** | |||
var | * 関数 AddSpace2_Main( [ tabEnable, blank ] ) | ||
if ( | * 「字上げ(スペース×2 追加)」マクロ | ||
var | */ | ||
s. | function AddSpace2_Main( arg ) { | ||
if ( s. | var tabEnable = arg[0]; | ||
var blank = arg[1]; | |||
s. | 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() ) { | |||
s. | 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 ) | |||
* 指定された範囲のブックマークを復元 | |||
if ( | */ | ||
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 | function MultiFunction( Fn, arg ) { | ||
var | var d = editor.ActiveDocument; | ||
for ( var i = 0, | 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 ); | |||
} | } | ||
} | } | ||
</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: | <source lang="javascript" style="height:60em; overflow:auto;"> | ||
#title = "字上げ" | #title = "字上げ" | ||
#tooltip = "スペース×2 削除" | #tooltip = "スペース×2 削除" | ||
| 271行目: | 363行目: | ||
* --------------------------------------------------------- | * --------------------------------------------------------- | ||
* 「字上げ(スペース×2 削除)」マクロ | * 「字上げ(スペース×2 削除)」マクロ | ||
* sukemaru, 2018/11/01 - | * sukemaru, 2018/11/01 - 2020/06/29 | ||
* --------------------------------------------------------- | * --------------------------------------------------------- | ||
* 行頭の「空白文字」を削除、またはキャレット位置の前後にある「タブ文字」を削除する。 | * 行頭の「空白文字」を削除、またはキャレット位置の前後にある「タブ文字」を削除する。 | ||
| 278行目: | 370行目: | ||
* 配列 blank で定義する。 | * 配列 blank で定義する。 | ||
* | * | ||
* | * 2020/06/29: マルチカーソル/複数選択範囲に対応 | ||
* | * ブックマーク行を字下げしたときのみブックマークの復元をするように変更 | ||
* ブックマークの収集範囲を選択範囲に限定 | |||
* ブックマークの収集/復元コードを関数化 | |||
* 行頭空白削除の処理をループ関数から正規表現置換に変更 | |||
*/ | */ | ||
| 308行目: | 403行目: | ||
// ---------- ▲ 設定項目 ▲ ---------- | // ---------- ▲ 設定項目 ▲ ---------- | ||
var d = editor.ActiveDocument; | var d = editor.ActiveDocument; | ||
if ( d.ReadOnly ) { | if ( d.ReadOnly ) { | ||
Status = " ドキュメントは書き換え禁止です。"; | Status = " ドキュメントは書き換え禁止です。"; | ||
} | } | ||
else { | |||
/ | var s = d.selection; | ||
var sx = ScrollX, sy = ScrollY; | |||
if ( | |||
// マルチカーソル対応 | |||
var arg = [ deleteNearestTab, blank ]; | |||
// 選択範囲が1つで矩形選択ではないとき | |||
if ( ! s.Mode || s.Mode === meModeStream ) { | |||
DeleteSpace2_Main( arg ); | |||
} | } | ||
else | // 複数行または複数選択のとき | ||
else { | |||
BeginUndoGroup(); | |||
AddUndo(); | |||
MultiFunction( DeleteSpace2_Main, arg ); | |||
EndUndoGroup(); | |||
} | } | ||
ScrollX = sx; ScrollY = sy; | |||
var elapsedSec = ( ( new Date() - start ) / 1000 ).toFixed( 3 ); | |||
Status = " 「スペース×2 削除」マクロ [ " | |||
+ elapsedSec.replace( /\./, ". " ) | |||
+ " 秒 ]"; | |||
} | } | ||
if ( | |||
/** | |||
* 関数 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, | s.SetActivePoint( mePosLogical, d.GetLine( by, 0 ).length + 1, by ); | ||
s.SetAnchorPoint( mePosLogical, 1, ty ); | s.SetAnchorPoint( mePosLogical, 1, ty ); | ||
/* 行頭の空白文字を削除 */ | /* 行頭の空白文字を削除 */ | ||
var st = s.Text; | var st = s.Text; | ||
var | var tp = s.GetAnchorPos(); | ||
if ( | for ( var i = 0, len = blank.length; i < len; i ++ ) { | ||
s.Text = | 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 ) { | // 範囲選択を残さない | ||
if ( st.length - | 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 - | s.SetActivePos( pos - ( st.length - tmp.length ) ); | ||
} | } | ||
} | } | ||
// 範囲選択を残す(論理行単位) | |||
s.SetActivePos( | else { | ||
s. | 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 | function MultiFunction( Fn, arg ) { | ||
var | var d = editor.ActiveDocument; | ||
for ( var i = 0 | 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 ); | |||
} | } | ||
} | } | ||
</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: | <source lang="javascript" style="height:60em; overflow:auto;"> | ||
#title = "インデント" | #title = "インデント" | ||
#tooltip = "選択範囲がなくてもインデント" | #tooltip = "選択範囲がなくてもインデント" | ||
| 448行目: | 626行目: | ||
* --------------------------------------------------------- | * --------------------------------------------------------- | ||
* 「インデント」マクロ | * 「インデント」マクロ | ||
* sukemaru, 2018/11/15 - | * sukemaru, 2018/11/15 - 2020/06/29 | ||
* --------------------------------------------------------- | * --------------------------------------------------------- | ||
* ・選択範囲なしでも、論理行全体をタブインデント(行頭)して範囲選択する | * ・選択範囲なしでも、論理行全体をタブインデント(行頭)して範囲選択する | ||
| 457行目: | 635行目: | ||
* ツールバーアイコンにするなら tabEnable = 0 | * ツールバーアイコンにするなら tabEnable = 0 | ||
* | * | ||
* | * 2020/06/29: マルチカーソル/複数選択範囲に対応 | ||
* | * ブックマーク行を字下げしたときのみブックマークの復元をするように変更 | ||
* ブックマークの収集範囲を選択範囲に限定 | |||
* ブックマークの収集/復元コードを関数化 | |||
*/ | */ | ||
| 494行目: | 674行目: | ||
// ---------- ▲ 設定項目 ▲ ---------- | // ---------- ▲ 設定項目 ▲ ---------- | ||
var d = editor.ActiveDocument; | var d = editor.ActiveDocument; | ||
if ( d.ReadOnly ) { | if ( d.ReadOnly ) { | ||
| 505行目: | 682行目: | ||
else { | else { | ||
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 ( | 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, | s.SetActivePoint( mePosLogical, d.GetLine( yy, 0 ).length + 1, yy ); | ||
s.SetAnchorPoint( mePosLogical, 1, ty ); | s.SetAnchorPoint( mePosLogical, 1, ty ); | ||
// ブックマークを保存 | /* インデント */ | ||
var bmArray = []; | 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 ); | s.SetActivePoint( mePosLogical, 1, 2 ); | ||
if ( s.PreviousBookmark() ) { | if ( s.PreviousBookmark() ) { | ||
bmY = s.GetActivePointY( mePosLogical ); | |||
if ( bmY >= ty && bmY <= by ) { | |||
bmArray.push( bmY ); | |||
} | |||
} | } | ||
s.SetActivePos( 0 ); | 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 ) | |||
s. | * 指定された範囲のブックマークを復元 | ||
s. | */ | ||
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( | 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 ); | |||
} | |||
} | } | ||
</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: | <source lang="javascript" style="height:60em; overflow:auto;"> | ||
#title = "逆インデント" | #title = "逆インデント" | ||
#tooltip = "選択範囲がなくてもアンインデント" | #tooltip = "選択範囲がなくてもアンインデント" | ||
| 625行目: | 902行目: | ||
* --------------------------------------------------------- | * --------------------------------------------------------- | ||
* 「逆インデント」マクロ | * 「逆インデント」マクロ | ||
* sukemaru, 2018/11/15 - | * sukemaru, 2018/11/15 - 2020/06/29 | ||
* --------------------------------------------------------- | * --------------------------------------------------------- | ||
* 選択範囲なしでも、行頭またはカーソル付近の「タブ文字×1」か、 | * 選択範囲なしでも、行頭またはカーソル付近の「タブ文字×1」か、 | ||
| 633行目: | 910行目: | ||
* ・選択範囲ありなら論理行全体をアンインデントして範囲選択する | * ・選択範囲ありなら論理行全体をアンインデントして範囲選択する | ||
* | * | ||
* | * 2020/06/29: マルチカーソル/複数選択範囲に対応 | ||
* | * ブックマーク行を字下げしたときのみブックマークの復元をするように変更 | ||
* ブックマークの収集範囲を選択範囲に限定 | |||
* ブックマークの収集/復元コードを関数化 | |||
*/ | */ | ||
// ---------- ▼ 設定項目 ▼ ---------- | // ---------- ▼ 設定項目 ▼ ---------- | ||
// | // ■ 選択範囲なしのときに、カーソル位置の前後にある「タブ文字」を優先的に削除するか? | ||
// ※ 選択範囲が1行内で、先頭に「タブ文字」がある場合も優先的に削除する | // ※ 選択範囲が1行内で、先頭に「タブ文字」がある場合も優先的に削除する | ||
var deleteNearestTab = true; | var deleteNearestTab = true; | ||
| 649行目: | 928行目: | ||
// | // ■ 行頭の「半角スペース」や「全角スペース」も削除するか? | ||
var deleteSpace = 1; | var deleteSpace = 1; | ||
/** | /** | ||
| 661行目: | 940行目: | ||
// ■ deleteSpace が 3 以上のとき、削除できる「全角スペース」の最大数 | |||
var roundDecimal = false; | |||
/** | |||
* ※ deleteSpace が奇数のときの小数点以下の処理 | |||
* false にすると ( deleteSpace / 2 ) の小数点以下を切り捨て | |||
* true にすると ( deleteSpace / 2 ) の小数点以下を切り上げ(四捨五入) | |||
*/ | |||
// ---------- ▲ 設定項目 ▲ ---------- | // ---------- ▲ 設定項目 ▲ ---------- | ||
var d = editor.ActiveDocument; | var d = editor.ActiveDocument; | ||
if ( d.ReadOnly ) { | if ( d.ReadOnly ) { | ||
Status = " ドキュメントは書き換え禁止です。"; | Status = " ドキュメントは書き換え禁止です。"; | ||
} | } | ||
else { | |||
var s = d.selection; | |||
var sx = ScrollX, sy = ScrollY; | |||
/ | // マルチカーソル/複数選択に対応 | ||
var arg = [ deleteNearestTab, deleteSpace, roundDecimal ]; | |||
if ( | // 選択範囲が1つで矩形選択ではないとき | ||
if ( ! s.Mode || s.Mode === 1 ) { | |||
Unindent_Main( arg ); | |||
} | } | ||
else | // 矩形選択または複数選択のとき | ||
else { | |||
BeginUndoGroup(); | |||
AddUndo(); | |||
MultiFunction( Unindent_Main, arg ); | |||
EndUndoGroup(); | |||
} | } | ||
ScrollX = sx; ScrollY = sy; | |||
var elapsedSec = ( ( new Date() - start ) / 1000 ).toFixed( 3 ); | |||
Status = " 「逆インデント」マクロ [ " | |||
+ elapsedSec.replace( /\./, ". " ) | |||
+ " 秒 ]"; | |||
} | } | ||
if ( | /** | ||
* 関数 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, | s.SetActivePoint( mePosLogical, d.GetLine( yy, 0 ).length + 1, yy ); | ||
s.SetAnchorPoint( mePosLogical, 1, ty ); | s.SetAnchorPoint( mePosLogical, 1, ty ); | ||
/* 逆インデント */ | /* 逆インデント */ | ||
| 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 */ | : /* else */ "^\\t"; | ||
st = s.Text; | st = s.Text; | ||
var | var tp = s.GetAnchorPos(); | ||
if ( | var tmp = st.replace( new RegExp( indent , "gm" ) , "" ); | ||
s.Text = | if ( tmp !== st ) { | ||
// ブックマークを保存 | |||
var bmArray = GetBookmark( ty, yy ); | |||
// 逆インデント完了 | |||
s.Text = tmp; | |||
// ブックマークを復元 | // ブックマークを復元 | ||
RestoreBookmark( ty, yy, bmArray ); | |||
} | } | ||
// 選択範囲が1行だったときは、位置関係を維持して選択範囲を復元する | // 選択範囲が1行だったときは、位置関係を維持して選択範囲を復元する | ||
if ( ty == by ) { | if ( ty === by ) { | ||
var gap = st.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() | s.SetActivePos( tp + tmp.length + 1 ); | ||
s. | 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 ); | |||
} | |||
} | } | ||
</source> | </source> | ||
| 806行目: | 1,181行目: | ||
== メモ == | == メモ == | ||
'''2019/04/07''' (sukemaru) | |||
:ソースコードから Quit() メソッドを削除 | :ソースコードから Quit() メソッドを削除 | ||
---- | ---- | ||
'''2019/11/10''' (sukemaru) | |||
:Mery 2.8.6 以上でブックマークを保持 | :Mery 2.8.6 以上でブックマークを保持 | ||
:※ Mery 2.8.5 以前ではブックマーク機能の仕様が不安定なので、個別対応はあきらめた…。 | :※ Mery 2.8.5 以前ではブックマーク機能の仕様が不安定なので、個別対応はあきらめた…。 | ||
---- | ---- | ||
'''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 | :※ Mery 2.7.0 - 2.8.5 ではブックマーク機能の仕様が不安定なので、1行のみの「字下げ/字上げ (インデント/逆インデント)」をしたときにブックマークがおかしくなることがある。<br> 現時点での最新の正式版と最新ベータ版で正常に機能するように調整した。 | ||
'''2019/11/18''' (sukemaru) | |||
:「スペース×2 削除.js」のソースコードで設定項目部分がダブっていたのを修正 | :「スペース×2 削除.js」のソースコードで設定項目部分がダブっていたのを修正 | ||
---- | ---- | ||
'''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 = n で 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)
- マルチカーソル/複数選択範囲に対応
- ブックマークの収集/復元コードを関数化
- (ブックマークの収集を選択範囲内の行に限定し、ブックマーク行を「字下げ/字上げ」したときのみ復元処理をするように変更)
