前へ 次へ
技術文章qvi

今回は、行内移動コマンドの 0 ^ $ と、次の行・前の行に移動する Enter + - を実装してみる。

0 は行頭に、^ は最初の非空白文字に、$ は行の最後の文字にジャンプするコマンドだ。数値を前置しても無視される。

Enter、+ は次の行の最初の非空白文字に、- は前の行の最初の非空白文字に移動する。 数値を前置して何行先・前に移動するかを指定することもできる。

0 ^ $

移動コマンドなので、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: }

Enter + -

 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: }

Tweet


前へ 次へ
技術文章qvi