選択範囲を広げる
ナビゲーションに移動
検索に移動
選択範囲を広げる
選択範囲を徐々に広げます。VSCode や IntelliJ っぽいやつです。
私は Sublime Text に倣い、Ctrl+Shift+Space に割り当てて使っています。
未選択状態での動作:
- カーソルの左、もしくは右に括弧記号がある場合は括弧の中全体を選択
- それ以外はカーソル位置の単語を選択
選択状態での動作:
- 選択状態によって選択範囲を広げる
- 選択範囲の開始位置に括弧の開始記号がある場合は、括弧全体に選択範囲を広げる
- 上記の条件に当てはまらず、選択範囲末尾の右側に括弧の終了記号がある場合は、括弧全体に選択範囲を広げる
- 上記の条件に当てはまらず、選択範囲末尾の右側にドットがある場合には、そのドット連なりの一番右側の単語まで選択範囲を広げる
- 上記の条件に当てはまらず、選択範囲末尾の右側に括弧の開始記号がある場合には、その括弧を全て選択する
- 上記の動作によって選択範囲が広がらなかった場合には、選択範囲を左右それぞれ1単語ずつ広げる
ソースコード:
#title="選択範囲を広げる"
BeginUndoGroup();
Redraw = false;
//■括弧の定義(0+2n:開き/1+2n:閉じ)
var BRACKET = '()<>[]{}「」『』【】()';
var sel = document.selection;
var txt = Document.Text;
if (sel.Mode === meModeMulti) {
// マルチカーソル動作中の場合は SelectWord しか行わない
sel.SelectWord();
} else if (sel.IsEmpty) {
var selectStartPos = selectBottomPos = sel.GetActivePos();
if (leftCharIsStartBracket(selectStartPos)
|| rightCharIsEndBracket(selectBottomPos)) {
// カーソルの左右いずれかに開き括弧があった場合
moveToBracketEnd(selectStartPos, selectBottomPos);
} else {
// 単語を選択
sel.SelectWord();
}
} else {
expandSelection();
}
function expandSelection() {
var EXTEND_ON = true;
var EXTEND_OFF = false;
// 選択範囲取得
var topX = sel.GetTopPointX(mePosLogical);
var topY = sel.GetTopPointY(mePosLogical);
var bottomX = sel.GetBottomPointX(mePosLogical);
var bottomY = sel.GetBottomPointY(mePosLogical);
// 選択終了位置のポジション取得
sel.SetActivePoint(mePosLogical, bottomX, bottomY, EXTEND_OFF);
var selectBottomPos = sel.GetActivePos();
// 選択開始位置に移動
sel.SetActivePoint(mePosLogical, topX, topY, EXTEND_OFF);
var selectTopPos = sel.GetActivePos();
var selectStartPos, selectEndPos;
if (selectTopPos <= selectBottomPos) {
selectStartPos = selectTopPos;
selectEndPos = selectBottomPos;
} else {
selectStartPos = selectBottomPos;
selectEndPos = selectTopPos;
}
if (bothSideCharIsBracket(selectStartPos, selectEndPos)) {
// 両脇が括弧だった場合
sel.CharLeft(EXTEND_OFF);
sel.SetActivePos(selectEndPos, EXTEND_ON);
sel.CharRight(EXTEND_ON);
} else if (txt.charAt(selectEndPos) === '.') {
// 選択範囲末尾右側がドットだった場合
selectRightSideWordsOfDot(selectStartPos, selectEndPos);
} else if (leftCharIsStartBracket(selectEndPos + 1)) {
// 選択範囲右側が開始括弧だった場合
selectRightSideWordsOfBracket(selectStartPos, selectEndPos);
} else if (
leftCharIsStartBracket(selectStartPos) ||
rightCharIsEndBracket(selectEndPos)
) {
// 左右どちらかが括弧だった場合
moveToBracketEnd(selectStartPos, selectEndPos);
} else {
// 選択範囲を拡大
sel.WordLeft(EXTEND_OFF);
sel.SetActivePos(selectEndPos, EXTEND_ON);
sel.WordRight(EXTEND_ON);
}
// 選択範囲拡大後の選択範囲取得
var expandedTopX = sel.GetTopPointX(mePosLogical);
var expandedTopY = sel.GetTopPointY(mePosLogical);
var expandedBottomX = sel.GetBottomPointX(mePosLogical);
var expandedBottomY = sel.GetBottomPointY(mePosLogical);
// 選択範囲が変わっていない場合は、左右1単語ずつ拡大する
if (
topX === expandedTopX &&
topY === expandedTopY &&
bottomX === expandedBottomX &&
bottomY === expandedBottomY
) {
sel.SetActivePos(selectStartPos);
sel.WordLeft(EXTEND_OFF);
sel.SetActivePos(selectEndPos, EXTEND_ON);
sel.WordRight(EXTEND_ON);
}
}
function selectRightSideWordsOfDot(selectStartPos, selectEndPos) {
if (txt.charAt(selectEndPos) !== '.') {
return;
}
var EXTEND_ON = true;
sel.SetActivePos(selectStartPos);
sel.SetActivePos(selectEndPos, EXTEND_ON);
sel.CharRight(EXTEND_ON);
sel.WordRight(EXTEND_ON);
selectRightSideWordsOfDot(selectStartPos, sel.GetActivePos());
}
function selectRightSideWordsOfBracket(selectStartPos, selectEndPos) {
if (!leftCharIsStartBracket(selectEndPos + 1)) {
return;
}
moveToBracketEnd(selectEndPos + 1, selectEndPos + 1);
var movedEndPos = sel.GetActivePos() + 1;
var EXTEND_ON = true;
sel.SetActivePos(selectStartPos);
sel.SetActivePos(movedEndPos, EXTEND_ON);
}
function leftCharIsStartBracket(selectStartPos) {
var startPos;
if (selectStartPos) {
startPos = selectStartPos;
} else {
startPos = Document.Selection.GetActivePos();
}
var startChar = txt.charAt(startPos - 1); //カーソル左側の文字を取得
var startBrcIdx = BRACKET.indexOf(startChar);
return startBrcIdx !== -1 && startChar !== '' && startBrcIdx % 2 === 0;
}
function rightCharIsEndBracket(selectEndPos) {
var endPos;
if (selectEndPos) {
endPos = selectEndPos;
} else {
endPos = Document.Selection.GetActivePos();
}
var endChar = txt.charAt(endPos); //カーソル右側の文字を取得
var endBrcIdx = BRACKET.indexOf(endChar);
return endBrcIdx !== -1 && endChar !== '' && endBrcIdx % 2 === 1;
}
function bothSideCharIsBracket(selectStartPos, selectEndPos) {
var txt = Document.Text;
var sPos = selectStartPos; //カーソル始点
var ePos = selectEndPos; //カーソル終点
var sBrc = txt.charAt(sPos - 1); //カーソル始点左側の文字を取得
var eBrc = txt.charAt(ePos); //カーソル終点右側の文字を取得
var isBrc = BRACKET.indexOf(sBrc);
var ieBrc = BRACKET.indexOf(eBrc);
return (
!(isBrc === -1 || sBrc === '') &&
!(ieBrc === -1 || eBrc === '') &&
ieBrc - isBrc === 1
);
}
function moveToBracketEnd(selectStartPos, selectEndPos) {
//■範囲選択(true:する/false:しない)
var SHIFT = true;
quit: {
var startPos = selectStartPos;
var endPos = selectEndPos;
var startChar = txt.charAt(startPos - 1); //選択範囲開始左側の文字を取得
var startBrcIdx = BRACKET.indexOf(startChar);
var endChar = txt.charAt(selectEndPos); //選択範囲末尾右側の文字を取得
var endBrcIdx = BRACKET.indexOf(endChar);
var nest = 1;
var s;
var e;
var pairChar;
// if (startBrcIdx !== -1 && startChar !== '' && startBrcIdx % 2 === 0) {
if (leftCharIsStartBracket(selectStartPos)) {
//◆開き括弧の場合、末尾方向へ探す
pairChar = BRACKET.charAt(startBrcIdx + 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;
}
}
// } else if (endBrcIdx !== -1 && endChar !== '' && endBrcIdx % 2 === 1) {
} else if (rightCharIsEndBracket(selectEndPos)) {
//◆閉じ括弧の場合、先頭方向へ探す
pairChar = BRACKET.charAt(endBrcIdx - 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;
}
}
} else {
break quit;
}
sel.SetActivePos(startPos);
sel.SetActivePos(endPos, SHIFT);
}
}
Mery Ver.3.0.1 以降に対応するマルチカーソル対応実装 (試験実装メソッドを利用):
#title="選択範囲を広げる"
BeginUndoGroup();
Redraw = false;
//■括弧の定義(0+2n:開き/1+2n:閉じ)
var BRACKET = '()<>[]{}「」『』【】()';
var sel = document.selection;
var txt = Document.Text;
if (sel.Mode === meModeMulti) {
doMultiAction(main);
} else {
main();
}
function main() {
if (sel.IsEmpty) {
var selectStartPos = selectBottomPos = sel.GetActivePos();
if (leftCharIsStartBracket(selectStartPos)
|| rightCharIsEndBracket(selectBottomPos)) {
// カーソルの左右いずれかに開き括弧があった場合
moveToBracketEnd(selectStartPos, selectBottomPos);
} else {
// 単語を選択
sel.SelectWord();
}
} else {
expandSelection();
}
}
function expandSelection() {
var EXTEND_ON = true;
var EXTEND_OFF = false;
// 選択範囲取得
var topX = sel.GetTopPointX(mePosLogical);
var topY = sel.GetTopPointY(mePosLogical);
var bottomX = sel.GetBottomPointX(mePosLogical);
var bottomY = sel.GetBottomPointY(mePosLogical);
// 選択終了位置のポジション取得
sel.SetActivePoint(mePosLogical, bottomX, bottomY, EXTEND_OFF);
var selectBottomPos = sel.GetActivePos();
// 選択開始位置に移動
sel.SetActivePoint(mePosLogical, topX, topY, EXTEND_OFF);
var selectTopPos = sel.GetActivePos();
var selectStartPos, selectEndPos;
if (selectTopPos <= selectBottomPos) {
selectStartPos = selectTopPos;
selectEndPos = selectBottomPos;
} else {
selectStartPos = selectBottomPos;
selectEndPos = selectTopPos;
}
if (bothSideCharIsBracket(selectStartPos, selectEndPos)) {
// 両脇が括弧だった場合
sel.CharLeft(EXTEND_OFF);
sel.SetActivePos(selectEndPos, EXTEND_ON);
sel.CharRight(EXTEND_ON);
} else if (txt.charAt(selectEndPos) === '.') {
// 選択範囲末尾右側がドットだった場合
selectRightSideWordsOfDot(selectStartPos, selectEndPos);
} else if (leftCharIsStartBracket(selectEndPos + 1)) {
// 選択範囲右側が開始括弧だった場合
selectRightSideWordsOfBracket(selectStartPos, selectEndPos);
} else if (
leftCharIsStartBracket(selectStartPos) ||
rightCharIsEndBracket(selectEndPos)
) {
// 左右どちらかが括弧だった場合
moveToBracketEnd(selectStartPos, selectEndPos);
} else {
// 選択範囲を拡大
sel.WordLeft(EXTEND_OFF);
sel.SetActivePos(selectEndPos, EXTEND_ON);
sel.WordRight(EXTEND_ON);
}
// 選択範囲拡大後の選択範囲取得
var expandedTopX = sel.GetTopPointX(mePosLogical);
var expandedTopY = sel.GetTopPointY(mePosLogical);
var expandedBottomX = sel.GetBottomPointX(mePosLogical);
var expandedBottomY = sel.GetBottomPointY(mePosLogical);
// 選択範囲が変わっていない場合は、左右1単語ずつ拡大する
if (
topX === expandedTopX &&
topY === expandedTopY &&
bottomX === expandedBottomX &&
bottomY === expandedBottomY
) {
sel.SetActivePos(selectStartPos);
sel.WordLeft(EXTEND_OFF);
sel.SetActivePos(selectEndPos, EXTEND_ON);
sel.WordRight(EXTEND_ON);
}
}
function selectRightSideWordsOfDot(selectStartPos, selectEndPos) {
if (txt.charAt(selectEndPos) !== '.') {
return;
}
var EXTEND_ON = true;
sel.SetActivePos(selectStartPos);
sel.SetActivePos(selectEndPos, EXTEND_ON);
sel.CharRight(EXTEND_ON);
sel.WordRight(EXTEND_ON);
selectRightSideWordsOfDot(selectStartPos, sel.GetActivePos());
}
function selectRightSideWordsOfBracket(selectStartPos, selectEndPos) {
if (!leftCharIsStartBracket(selectEndPos + 1)) {
return;
}
moveToBracketEnd(selectEndPos + 1, selectEndPos + 1);
var movedEndPos = sel.GetActivePos() + 1;
var EXTEND_ON = true;
sel.SetActivePos(selectStartPos);
sel.SetActivePos(movedEndPos, EXTEND_ON);
}
function leftCharIsStartBracket(selectStartPos) {
var startPos;
if (selectStartPos) {
startPos = selectStartPos;
} else {
startPos = Document.Selection.GetActivePos();
}
var startChar = txt.charAt(startPos - 1); //カーソル左側の文字を取得
var startBrcIdx = BRACKET.indexOf(startChar);
return startBrcIdx !== -1 && startChar !== '' && startBrcIdx % 2 === 0;
}
function rightCharIsEndBracket(selectEndPos) {
var endPos;
if (selectEndPos) {
endPos = selectEndPos;
} else {
endPos = Document.Selection.GetActivePos();
}
var endChar = txt.charAt(endPos); //カーソル右側の文字を取得
var endBrcIdx = BRACKET.indexOf(endChar);
return endBrcIdx !== -1 && endChar !== '' && endBrcIdx % 2 === 1;
}
function bothSideCharIsBracket(selectStartPos, selectEndPos) {
var txt = Document.Text;
var sPos = selectStartPos; //カーソル始点
var ePos = selectEndPos; //カーソル終点
var sBrc = txt.charAt(sPos - 1); //カーソル始点左側の文字を取得
var eBrc = txt.charAt(ePos); //カーソル終点右側の文字を取得
var isBrc = BRACKET.indexOf(sBrc);
var ieBrc = BRACKET.indexOf(eBrc);
return (
!(isBrc === -1 || sBrc === '') &&
!(ieBrc === -1 || eBrc === '') &&
ieBrc - isBrc === 1
);
}
function moveToBracketEnd(selectStartPos, selectEndPos) {
//■範囲選択(true:する/false:しない)
var SHIFT = true;
quit: {
var startPos = selectStartPos;
var endPos = selectEndPos;
var startChar = txt.charAt(startPos - 1); //選択範囲開始左側の文字を取得
var startBrcIdx = BRACKET.indexOf(startChar);
var endChar = txt.charAt(selectEndPos); //選択範囲末尾右側の文字を取得
var endBrcIdx = BRACKET.indexOf(endChar);
var nest = 1;
var s;
var e;
var pairChar;
if (leftCharIsStartBracket(selectStartPos)) {
//◆開き括弧の場合、末尾方向へ探す
pairChar = BRACKET.charAt(startBrcIdx + 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;
}
}
} else if (rightCharIsEndBracket(selectEndPos)) {
//◆閉じ括弧の場合、先頭方向へ探す
pairChar = BRACKET.charAt(endBrcIdx - 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;
}
}
} else {
break quit;
}
sel.SetActivePos(startPos);
sel.SetActivePos(endPos, SHIFT);
}
}
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);
}
更新履歴
2020/03/15 マルチカーソル時の動作を考慮し処理を追加 2020/03/22 Mery Ver.3.0.1 マルチカーソル対応化の試験実装メソッドを使った実装を追加
スポンサーリンク