前へ 次へ
技術文章qvi

これまでは、vi コマンドに対応したカーソル移動メソッドを ViEditView が持っていた。 最初は、vi コマンドに依存する処理が、カーソル移動メソッドに含まれていなかったからだ。
しかし今や、h は前の行には移動せず、l は改行には移動しない等、vi コマンドの仕様に依存する処理を含んでしまっている。 特に大規模なシステムでは、依存性除去を行うことが開発効率に後々大きく影響してくる。 なので、vi コマンドに依存するカーソル移動処理は ViEditView から ViEngine の方に移動することにする。

moveCursor()

カーソルオブジェクトはビューが保有するものであり、ViEngine が保有するものではないので、 カーソル移動処理メソッドを ViEngine が保持するのは筋違いである。 よって、moveCursor(QTextCursor&, int moneOperation, int n) : bool という関数を定義し、これを使ってカーソル移動を行うことにする。

 1:     m_editor->moveCursor(mv, repeatCount());

これまで上記のように記述していた部分を下記のように変更する。

 1:     QTextCursor cur = m_editor->textCursor();
 2:     moveCursor(cur, mv, repeatCount());
 3:     m_editor->setTextCursor(cur);

行数の少なさが正義だったはずなのに、行数が増えているじゃないか。しかも何もメリットが無い、と思うかもしれない。 しかし、カーソル移動処理と、移動後のカーソルをビューに反映させる処理を分けたことで、 その間に必要なら別の処理を挟むことができるようになった。
このメリットは、後で cw などの編集+移動コマンド処理を行うときに効いてくるのだ。

 1: bool moveCursor(QTextCursor &cur, int mv, int n)
 2: {
 3:     const int pos = cur.position();
 4:     QTextBlock block = cur.block();
 5:     const int blockPos = block.position();
 6:     const QString blockText = block.text();
 7:     switch( mv ) {
 8:     case ViMoveOperation::Up:
 9:         mv = QTextCursor::Up;
10:         break;
11:     case ViMoveOperation::Down:
12:         mv = QTextCursor::Down;
13:         break;
14:     case ViMoveOperation::Left:
15:         n = qMin(n, pos - blockPos);
16:         mv = QTextCursor::Left;
17:         break;
18:     case ViMoveOperation::Right: {
19:         if( blockText.isEmpty() ) return false;     //  改行 ora EOF オンリー行の場合
20:         const int endpos = blockPos + blockText.length() - 1;
21:         if( pos >= endpos ) return false;
22:         n = qMin(n, endpos - pos);
23:         mv = QTextCursor::Right;
24:         break;
25:     }
26:     .....
27:     }
28:     cur.movePosition(static_cast(mv), QTextCursor::MoveAnchor, n);
29:     return true;
30: }
 1: bool ViEngine::cmdModeKeyPressEvent(QKeyEvent *event)
 2: {
 3:     .....
 4:     QTextCursor cur = m_editor->textCursor();
 5:     bool cursorMoved = false;
 6:     bool toInsertMode = false;
 7:     switch( ch ) {
 8:     case '0':
 9:         cursorMoved = moveCursor(cur, QTextCursor::StartOfBlock);
10:         break;
11:     case '^':
12:         cursorMoved = moveCursor(cur, ViMoveOperation::FirstNonBlankChar);
13:         break;
14:     case '$':
15:         cursorMoved = moveCursor(cur, ViMoveOperation::LastChar);
16:         break;
17:     case 'i':
18:         toInsertMode = true;
19:         break;
20:     case 'A':
21:         cursorMoved = moveCursor(cur, QTextCursor::EndOfBlock);
22:         toInsertMode = true;
23:         break;
24:     .....
25:     }
26:     if( cursorMoved )
27:         m_editor->setTextCursor(cur);
28:     if( toInsertMode )
29:         setMode(INSERT);
30:     .....
31: }

x X コマンド

x X コマンド処理も、ViEditView::doDelete(int n) が vi の仕様に依存していて好ましくないので、 このメソッドは廃止し、代わりに引数の範囲の文字を削除する doDelete(int from, int to) というメソッドを導入する。 これなら汎用的で vi の仕様にはいっさい依存してないぞ。

 1: void ViEditView::doDelete(int from, int to)
 2: {
 3:     QTextCursor cur = textCursor();
 4:     cur.setPosition(from);
 5:     cur.setPosition(to, QTextCursor::KeepAnchor);
 6:     cur.deleteChar();
 7: }
 1: bool ViEngine::cmdModeKeyPressEvent(QKeyEvent *event)
 2: {
 3:     .....
 4:     int delFrom = -1;
 5:     int delTo = -1;
 6:     switch( ch ) {
 7:         .....
 8:     case 'x':
 9:         delFrom = cur.position();
10:         moveCursor(cur, ViMoveOperation::RightForA, repeatCount());
11:         delTo = cur.position();
12:         break;
13:     case 'X':
14:         delTo = cur.position();
15:         moveCursor(cur, ViMoveOperation::Left, repeatCount());
16:         delFrom = cur.position();
17:         break;
18:         .....
19:     }
20:     if( delFrom >= 0 && delFrom < delTo )
21:         m_editor->doDelete(delFrom, delTo);
22:     .....
23: }

Tweet


前へ 次へ
技術文章qvi