今回は、行内移動コマンドの 0 ^ $ と、次の行・前の行に移動する Enter + - を実装してみる。
0 は行頭に、^ は最初の非空白文字に、$ は行の最後の文字にジャンプするコマンドだ。数値を前置しても無視される。
Enter、+ は次の行の最初の非空白文字に、- は前の行の最初の非空白文字に移動する。 数値を前置して何行先・前に移動するかを指定することもできる。
移動コマンドなので、ViEngine::cmdModeKeyPressEvent() でコマンドを認識し、ViEditView::moveCursor() をコールするだけだ。 これまでは QTextCursor::MoveOperation で間に合っていたのだが、行の最初の非空白文字にジャンプする ^ コマンドは 対応するものが無いので、ViEditView::moveCursor() の第1引数を int に変更し、 enum ViMoveOperation を導入し、無いものはそこで宣言することにする。
1: namespace ViMoveOperation {
2: enum {
3: FirstNonBlankChar = 100, // ^
4: LastChar, // $
5: NextLine, // + Enter
6: PrevLine, // -
7: };
8: }
1: bool ViEngine::cmdModeKeyPressEvent(QKeyEvent *event)
2: {
3: .....
4: switch( ch ) {
5: case '0':
6: m_editor->moveCursor(QTextCursor::StartOfBlock);
7: break;
8: case '^':
9: m_editor->moveCursor(ViMoveOperation::FirstNonBlankChar);
10: break;
11: case '$':
12: m_editor->moveCursor(ViMoveOperation::LastChar);
13: break;
14: case '\r': // Enter
15: case '+':
16: m_editor->moveCursor(ViMoveOperation::NextLine);
17: break;
18: case '-':
19: m_editor->moveCursor(ViMoveOperation::PrevLine);
20: break;
21: .....
22: }
23: .....
24: }
1: void ViEditView::moveCursor(int mv, int n)
2: {
3: QTextCursor cur = textCursor();
4: const int pos = cur.position();
5: QTextBlock block = cur.block();
6: const int blockPos = block.position();
7: const QString blockText = block.text();
8: bool moved = false;
9: switch( mv ) {
10: .....
11: case ViMoveOperation::FirstNonBlankChar:
12: cur.setPosition(blockPos + firstNonBlankCharPos(blockText));
13: moved = true;
14: break;
15: case ViMoveOperation::LastChar: {
16: int ix = blockText.length();
17: if( ix != 0 ) --ix;
18: cur.setPosition(blockPos + ix);
19: moved = true;
20: break;
21: }
22: }
23: if( !moved )
24: cur.movePosition(static_cast<QTextCursor::MoveOperation>(mv), QTextCursor::MoveAnchor, n);
25: setTextCursor(cur);
26: }
1: inline bool isTabOrSpace(const QChar ch)
2: {
3: return ch == '\t' || ch == ' ';
4: }
5: int firstNonBlankCharPos(const QString &text)
6: {
7: int ix = 0;
8: while( ix < text.length() && isTabOrSpace(text[ix]) )
9: ++ix;
10: return ix;
11: }
1: void ViEditView::moveCursor(int mv, int n)
2: {
3: .....
4: switch( mv ) {
5: .....
6: case ViMoveOperation::NextLine:
7: cur.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, n);
8: moveToFirstNonBlankChar(cur);
9: moved = true;
10: break;
11: case ViMoveOperation::PrevLine:
12: cur.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor, n);
13: moveToFirstNonBlankChar(cur);
14: moved = true;
15: break;
16: }
17: .....
18: }
1: void moveToFirstNonBlankChar(QTextCursor &cur)
2: {
3: QTextBlock block = cur.block();
4: const int blockPos = block.position();
5: const QString blockText = block.text();
6: if( !blockText.isEmpty() )
7: cur.setPosition(blockPos + firstNonBlankCharPos(blockText));
8: }