選択範囲を広げる
ナビゲーションに移動
検索に移動
選択範囲を徐々に広げる[編集]
選択範囲を徐々に広げます。VSCode や IntelliJ っぽいやつです。
私は Sublime Text に倣い、Ctrl+Shift+Space に割り当てて使っています。
未選択状態での動作[編集]
- 選択範囲が空の場合、カーソル位置の単語を選択します。
- カーソル位置が括弧だった場合は、括弧の中身も選択します。
- ドットを検出した場合は、ドットの1つ右の単語も選択します。
- ハイフンを検出した場合は、ハイフンで区切られた1つ右の単語も選択します。
選択状態での動作[編集]
- 選択範囲がある場合、選択範囲の前後にある括弧を検出し、その中身を選択します。
- ドットを検出した場合は、ドットの1つ左または右の単語まで拡張します。
- ハイフンを検出した場合は、ハイフンで区切られた1つ右の単語まで拡張します。
- これらの記号を検出しなかった場合は、単純な選択範囲の拡張を行います。
ソースコード[編集]
#title = "選択範囲を広げる"
/**
* 選択範囲を広げるためのマクロです。
*
* 選択範囲が空の場合、カーソル位置の単語を選択します。
* カーソル位置が括弧だった場合は、括弧の中身も選択します。
* ドットを検出した場合は、ドットの1つ右の単語も選択します。
* ハイフンを検出した場合は、ハイフンで区切られた1つ右の単語も選択します。
*
* 選択範囲がある場合、選択範囲の前後にある括弧を検出し、その中身を選択します。
* ドットを検出した場合は、ドットの1つ左または右の単語まで拡張します。
* ハイフンを検出した場合は、ハイフンで区切られた1つ右の単語まで拡張します。
* これらの記号を検出しなかった場合は、単純な選択範囲の拡張を行います。
*/
BeginUndoGroup();
Redraw = false;
//■括弧の定義(0+2n:開き/1+2n:閉じ)
var BRACKET = '()<>[]{}「」『』【】()[]{}〈〉《》〔〕〘〙〚〛〖〗‘’“”';
var doc = document;
var sel = document.selection;
var txt = document.Text;
function main() {
if (sel.IsEmpty) {
// 未選択の場合
var beforePos = sel.GetActivePos();
sel.SelectWord();
var selChar = sel.Text;
var selCharIndex = BRACKET.indexOf(selChar);
if (selChar.length === 1 && selCharIndex !== -1) {
// 選択文字が括弧記号1文字の場合、括弧の中身を選択
if (selCharIndex % 2 === 1) {
// 閉じ括弧の場合
sel.CharLeft();
var topX = sel.GetTopPointX(mePosLogical);
var topY = sel.GetTopPointY(mePosLogical);
var bottomX = sel.GetBottomPointX(mePosLogical);
var bottomY = sel.GetBottomPointY(mePosLogical);
selectToBracketStart(topX, topY, bottomX, bottomY, selChar);
} else if (selCharIndex % 2 === 0) {
// 開き括弧の場合
var topX = sel.GetTopPointX(mePosLogical);
var topY = sel.GetTopPointY(mePosLogical);
var bottomX = sel.GetBottomPointX(mePosLogical);
var bottomY = sel.GetBottomPointY(mePosLogical);
var activePos = sel.GetActivePos();
selectToBracketEnd(topX, topY, bottomX, bottomY, selChar);
if (beforePos === activePos) {
// 行末で先頭方向に向かって選択していた場合は、先頭の開き括弧の選択を外す
sel.SetAnchorPos(beforePos);
} else if (activePos !== sel.GetActivePos()) {
// 括弧の中身が選択された場合、閉じ括弧も選択する
sel.SetActivePos(sel.GetActivePos() + 1, true);
}
}
} else if (selChar === ".") {
// ドットの場合
// ドットの1つ右の単語まで拡張
selectFuncNameToChild(sel.GetActivePos() - 1);
} else if (selChar === "-") {
// ドットの場合
// ドットの1つ右の単語まで拡張
selectHyphenSeparatedWord(sel.GetActivePos() - 1);
}
} else {
// 選択済みの場合
// 現在の選択範囲を取得
var topX = sel.GetTopPointX(mePosLogical);
var topY = sel.GetTopPointY(mePosLogical);
var bottomX = sel.GetBottomPointX(mePosLogical);
var bottomY = sel.GetBottomPointY(mePosLogical);
// 選択範囲の左右の文字を取得
var leftChar = "dummy";
if (topX - 2 >= 0) leftChar = doc.GetLine(topY, meGetLineWithNewLines).charAt(topX - 2);
var rightChar = doc.GetLine(bottomY, meGetLineWithNewLines).charAt(bottomX - 1);
var rightIndex = BRACKET.indexOf(rightChar);
var leftIndex = BRACKET.indexOf(leftChar);
if (rightChar === ".") {
// 選択範囲の右にドットがある場合
// 末尾方向にドットを検索
sel.SetActivePoint(mePosLogical, bottomX, bottomY, false);
selectFuncNameToChild(sel.GetActivePos());
sel.SetAnchorPoint(mePosLogical, topX, topY);
} else if (rightChar === "-") {
// 選択範囲の右にハイフンがある場合
sel.SetActivePoint(mePosLogical, bottomX, bottomY, false);
selectHyphenSeparatedWord(sel.GetActivePos());
sel.SetAnchorPoint(mePosLogical, topX, topY);
} else if (rightIndex % 2 === 0) {
// 右位置に開き括弧があるかをチェック
// 選択範囲を広げる
selectToBracketEnd(topX, topY, bottomX + 1, bottomY, rightChar);
// 閉じ括弧も選択する
sel.CharRight(true);
} else if (leftChar === ".") {
// 選択範囲の左にドットがある場合
// 先頭方向にドットを検索
sel.SetActivePoint(mePosLogical, topX, topY, false);
selectFuncNameToParent(sel.GetActivePos() - 2);
sel.SetAnchorPoint(mePosLogical, bottomX, bottomY);
} else if (rightIndex % 2 === 1) {
// 右位置に閉じ括弧があるかをチェック
// 選択範囲を広げる
selectToBracketStart(topX, topY, bottomX, bottomY, rightChar);
} else if (leftIndex % 2 === 0) {
// 左位置に括弧があるかをチェック
// 選択範囲を広げる
selectToBracketEnd(topX, topY, bottomX, bottomY, leftChar);
} else {
// いずれでもない場合は、選択開始位置と終了位置の両方で単語選択を行う
simpleExpand(topX, topY, bottomX, bottomY, 0);
}
var afterTopX = sel.GetTopPointX(mePosLogical);
var afterTopY = sel.GetTopPointY(mePosLogical);
var afterBottomX = sel.GetBottomPointX(mePosLogical);
var afterBottomY = sel.GetBottomPointY(mePosLogical);
// 選択範囲を広げる処理の後も同じ位置の場合、通常の選択範囲を広げる処理を行う
if (topX === afterTopX
&& topY === afterTopY
&& bottomX === afterBottomX
&& bottomY === afterBottomY) {
simpleExpand(afterTopX, afterTopY, afterBottomX, afterBottomY, 1);
}
}
}
function selectFuncNameToChild(pos) {
// "$" はEOFへの対応
var movePos = txt.slice(pos + 1).search(/[^a-z0-9_-]|$/i) + pos + 1;
sel.SetActivePos(movePos, true);
}
function selectFuncNameToParent(pos) {
var toSeparator1 = txt.lastIndexOf(".", pos);
var toSeparator2 = txt.lastIndexOf("\n", pos);
var toSeparator3 = txt.lastIndexOf(" ", pos);
var toSeparator4 = txt.lastIndexOf("\t", pos);
var toSeparator5 = txt.lastIndexOf(" ", pos);
var maxIndex = Math.max(toSeparator1, toSeparator2, toSeparator3, toSeparator4, toSeparator5);
sel.SetActivePos(maxIndex + 1, true);
}
function selectHyphenSeparatedWord(pos) {
// "$" はEOFへの対応
var movePos = txt.slice(pos + 1).search(/[^a-z0-9_]|$/i) + pos + 1;
sel.SetActivePos(movePos, true);
}
// 単純な選択範囲の拡大
function simpleExpand(topX, topY, bottomX, bottomY, offset) {
// 拡大範囲は行を跨がないようにする
// 左側の拡大後の位置を取得
var simpleExpandTopX = sel.GetTopPointX(mePosLogical);
var simpleExpandTopY = sel.GetTopPointY(mePosLogical);
sel.SetActivePoint(mePosLogical, topX, topY, false);
var leftChar = txt.charAt(sel.GetActivePos() - 1);
if (leftChar !== "\n" && leftChar !== "") {
// 行頭以外のときだけ、左側を拡大
sel.CharLeft(false, 1 + offset);
sel.SelectWord();
simpleExpandTopX = sel.GetTopPointX(mePosLogical);
simpleExpandTopY = sel.GetTopPointY(mePosLogical);
}
// 右側の拡大後の位置を取得
sel.SetActivePoint(mePosLogical, bottomX, bottomY, false);
var simpleExpandBottomX = sel.GetBottomPointX(mePosLogical);
var simpleExpandBottomY = sel.GetBottomPointY(mePosLogical);
var rightChar = txt.charAt(sel.GetActivePos());
if (rightChar !== "\n" && rightChar !== "") {
// 行末以外のときだけ、右側を拡大
if (offset > 0) {
sel.CharRight(false, offset);
}
sel.SelectWord();
simpleExpandBottomX = sel.GetBottomPointX(mePosLogical);
simpleExpandBottomY = sel.GetBottomPointY(mePosLogical);
}
// 選択範囲を広げる
sel.SetActivePoint(mePosLogical, simpleExpandTopX, simpleExpandTopY, false);
sel.SetActivePoint(mePosLogical, simpleExpandBottomX, simpleExpandBottomY, true);
}
// 先頭方向の開き括弧まで選択範囲を広げる
function selectToBracketStart(topX, topY, bottomX, bottomY, endChar) {
var endBracketIndex = BRACKET.indexOf(endChar);
sel.SetActivePoint(mePosLogical, topX, topY, false);
var startPos = sel.GetActivePos();
sel.SetActivePoint(mePosLogical, bottomX, bottomY, false);
var endPos = sel.GetActivePos();
var nest = 1;
var s;
var e;
var pairChar;
pairChar = BRACKET.charAt(endBracketIndex - 1); //対応する開き括弧を取得
e = txt.lastIndexOf(endChar, endPos);
while (nest) {
s = txt.lastIndexOf(pairChar, e - 1);
e = txt.lastIndexOf(endChar, e - 1);
if (s === -1 || startPos <= 0) break;
if (s < e && s !== -1) {
nest++;
s = e;
} else {
nest--;
e = s;
}
if (s !== -1 && startPos > s) {
startPos = s + 1;
}
}
sel.SetActivePos(startPos);
sel.SetActivePos(endPos, true);
}
// 末尾方向の閉じ括弧まで選択範囲を広げる
function selectToBracketEnd(topX, topY, bottomX, bottomY, startChar) {
var startBracketIndex = BRACKET.indexOf(startChar);
sel.SetActivePoint(mePosLogical, topX, topY, false);
var startPos = sel.GetActivePos();
sel.SetActivePoint(mePosLogical, bottomX, bottomY, false);
var endPos = sel.GetActivePos();
var nest = 1;
var s;
var e;
var pairChar;
pairChar = BRACKET.charAt(startBracketIndex + 1); //対応する閉じ括弧を取得
s = txt.indexOf(startChar, startPos - 1);
while (nest) {
e = txt.indexOf(pairChar, s + 1);
s = txt.indexOf(startChar, s + 1);
if (e === -1) break;
if (s < e && s !== -1) {
nest++;
e = s;
} else {
nest--;
s = e;
}
if (e !== -1 && endPos < e) {
endPos = e;
}
}
sel.SetActivePos(startPos);
sel.SetActivePos(endPos, true);
}
function doMultiAction(fn) {
var d = document, s = d.selection;
s.Mode = meModeMulti;
// ① まず、GetActivePos と GetAnchorPos で複数選択のリストを作成します
var selections = [{ s: s.GetAnchorPos(), e: s.GetActivePos() }];
if (s.Count > 0) {
selections = [];
for (var i = 0; i < s.Count; i++)
selections.push({ s: s.GetActivePos(i), e: s.GetAnchorPos(i) });
}
var p = 0;
var q = 0;
for (var i = 0; i < selections.length; i++) {
// ② そのリストを上から順に SetActivePos で、「シングルカーソル」で範囲選択します。この段階で複数選択は解除され、通常のマクロ操作が可能となります
s.SetActivePos(selections[i].s + p);
s.SetActivePos(selections[i].e + p, true);
q = d.TextLength;
// ③ <ここで通常のマクロの機能を使って普通に編集などを行います>
fn();
p += d.TextLength - q;
// ④ 処理を行った後の選択範囲をリストに反映し、これをリストの最後まで繰り返します
selections[i] = { s: s.GetAnchorPos(), e: s.GetActivePos() };
}
// ⑤ そのリストを上から順に document.selection.AddPos(StartPos, EndPos) で複数選択として復元します
for (var i = 0; i < selections.length; i++)
s.AddPos(selections[i].s, selections[i].e);
}
if (sel.Mode === meModeMulti) {
doMultiAction(main);
} else {
main();
}
更新履歴[編集]
- 2023/07/21
- 処理を見直し、より適切な範囲に拡張するよう調整しました。
- 2020/03/22
- Mery Ver.3.0.1 マルチカーソル対応化の試験実装メソッドを使った実装を追加しました。
- 2020/03/15
- マルチカーソル時の動作を考慮し処理を追加しました。
スポンサーリンク