選択範囲を広げる

提供: MeryWiki
2020年2月24日 (月) 09:02時点におけるYuko (トーク | 投稿記録)による版 (新規投稿)
(差分) ← 古い版 | 最新版 (差分) | 新しい版 → (差分)
ナビゲーションに移動 検索に移動

選択範囲を徐々に広げます。VSCode や IntelliJ っぽいやつです。

私は Sublime Text に倣い、Ctrl+Shift+Space に割り当てて使っています。

未選択状態での動作:

  • カーソルの左、もしくは右に括弧記号がある場合は括弧の中全体を選択
  • それ以外はカーソル位置の単語を選択

選択状態での動作:

  • 選択状態によって選択範囲を広げる
    • 選択範囲の開始位置に括弧の開始記号がある場合は、括弧全体に選択範囲を広げる
    • 上記の条件に当てはまらず、選択範囲の終了位置に括弧の終了記号がある場合は、括弧全体に選択範囲を広げる
    • 上記の動作によって選択範囲が広がらなかった場合には、選択範囲を左右それぞれ1単語ずつ広げる
#title="選択範囲を広げる"
BeginUndoGroup();
Redraw = false;

//■括弧の定義(0+2n:開き/1+2n:閉じ)
var BRACKET = '()<>[]{}「」『』【】()';

var sel = document.selection;

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 (
    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(selectTopPos);
    sel.WordLeft(EXTEND_OFF);
    sel.SetActivePos(selectEndPos, EXTEND_ON);
    sel.WordRight(EXTEND_ON);
  }
}

function leftCharIsStartBracket(selectStartPos) {
  var txt = Document.Text;
  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 txt = Document.Text;
  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 sel = Document.Selection;
    var txt = Document.Text;

    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);
  }
}
スポンサーリンク