ついカッとなってtext objectのi", a", i', a'を実装した

IdeaVIMって実は text object を実装しているんだけど,どうゆうわけかQuote系(", ', `)とタグ(t)が未実装なのだ。タグは(難しそうだから)まあわかるとして,Quote系くらい実装しといたっていいやんか,と思い,ついカッとなって実装してしまった。:-)


ちょっとあやしい...というか詰めが甘いところがあるけど,普段使いに問題ないから,これ以上は深追いしない。ネット不通なところで作業してたんで,パッチじゃなくてコード片のみ残す。
まずは,こんな感じでEditorActionをいくつか用意する(plugin.xml, RegisterActions.javaの変更点は省略)。

package com.maddyhome.idea.vim.action.motion.object;

public class MotionInnerDoubleQuoteAction extends TextObjectAction {
    public MotionInnerDoubleQuoteAction() {
        super(new Handler());
    }

    private static class Handler extends TextObjectActionHandler {
        public TextRange getRange(Editor editor, DataPackage context, int count, int rawCount, Argument argument) {
            return CommandGroups.getInstance().getMotion().getQuoteRange(editor, context, count, false, '"');
        }
    }
}


大事なのは SearchHelper の findQuoteRange() 部分。...その前に MotionGroup(com.maddyhome.idea.vim.group) に getQuoteRange() を新設しとく。

=== SearchHelper.java () ===
  public static TextRange findQuoteRange(Editor editor, char type, int count, boolean isOuter) {
      int pos = editor.getCaretModel().getOffset();
      int line = EditorHelper.getCurrentLogicalLine(editor);
      int lineOffset = EditorHelper.getLineStartOffset(editor, line);

      CharBuffer charBuffer = EditorHelper.getLineBuffer(editor, line);

      int qstart = findQuoteLocation(charBuffer, type, pos - lineOffset, -1);
      if (qstart == -1) return null;

      int qend = findQuoteLocation(charBuffer, type, pos - lineOffset, 1);
      if (qend == -1) return null;

      int start = qstart + lineOffset + (isOuter ? 0 : 1);
      int end = qend + lineOffset;

      return new TextRange(start, end);
  }

  private static int findQuoteLocation(CharBuffer chars, char c, int pos, int dir) {
      while (pos >= 0 && pos < chars.length()) {
          if (chars.charAt(pos) == c) {
              // 見つかった
              if (pos > 0 && chars.charAt(pos - 1) == '\\') {
                  // 前の文字がエスケープ文字だったら,キャンセル
              }
              else {
                  return pos;
              }
          }
          pos += dir;
      }
      return -1;
  }

詰めが甘いのは,以下の2点。

  • カーソル直下の文字がクォート文字の場合,そのクォート文字しか捕捉できない(ロジックがタコだから)。
  • TextRangeの範囲指定がc, dといった編集系とヴィジュアルモード(v)とで終端の解釈にズレがある。

まあ,大した話じゃないので放置。:-P


ps.
ここまで来ると,it, at といったタグオブジェクトも実装したくなるんだけど,こっちはちょーっと手強そうだぬ。:-(