前へ 次へ
技術文章qvi

本稿では、行頭が '{' で始まる行を検索する ']]' '[[' コマンドを実装する。

たまに C/C++ の関数・メソッド定義を下記のように記述する人を見かけるが、vi 的には正しくない。

 1: int main() {
 2:     .....
 3: }

必ず下記の様に { は行頭に書かなくてはいけない。そうすれば ]] [[ コマンドでカーソル移動することができる。

 1: int main()
 2: {
 3:     .....
 4: }

このコマンドも数値を前置することで繰り返し回数を指定することができる。 { で始まる行が無かった場合は、1行目または最終行にカーソル移動する。

実装

処理の流れは他のカーソル移動コマンドと同じなのだが、']]' '[[' コマンドは複数ストロークなので、 そのための機構を新たに導入する。

 1: namespace ViMoveOperation {
 2:     enum {
 3:         Left = 100,             //  h
 4:         .....
 5:         NextBeginBlock,         //  ]]
 6:         PrevBeginBlock,         //  [[
 7:     };
 8: }
 1: bool moveCursor(QTextCursor &cur, int mv, int n)
 2: {
 3:     .....
 4:     switch( mv ) {
 5:         .....
 6:     case ViMoveOperation::NextBeginBlock:
 7:         return gotoBeginBlock(cur, /*forward=*/true, n);
 8:     case ViMoveOperation::PrevBeginBlock:
 9:         return gotoBeginBlock(cur, /*forward=*/false, n);
10:     }
11:     .....
12: }
 1: bool gotoBeginBlock(QTextCursor &cur, bool forward, int n)
 2: {
 3:     QTextBlock block = cur.block();
 4:     if( !block.isValid() ) return false;
 5:     int lastPos = block.position();
 6:     while( --n >= 0 ) {
 7:         for(;;) {
 8:             block = forward ? block.next() : block.previous();
 9:             if( !block.isValid() ) {
10:                 cur.setPosition(lastPos);
11:                 return true;
12:             }
13:             lastPos = block.position();
14:             const QString text = block.text();
15:             if( !text.isEmpty() && text[0] == '{' )
16:                 break;
17:         }
18:     }
19:     cur.setPosition(lastPos);
20:     return true;
21: }

2ストロークコマンドを実現するために、ushort m_cmdPrefix をメンバ変数に加える。

m_cmdPrefix は 0 に初期化され、複数ストロークコマンドの場合は最初の文字のUnicode値を格納する。
コマンド解析部では、m_cmdPrefix が 0 でなければ、ch を次の文字と解釈する。

2ストロークコマンドに対応したコマンド解析部のコードは下記の様になる。

 1: bool ViEngine::doViCommand(const QChar &qch)
 2: {
 3:     .....
 4:     if( m_cmdPrefix != 0 ) {
 5:         switch( m_cmdPrefix ) {
 6:         case ']':
 7:             if( ch == ']' )
 8:                 cursorMoved = moveCursor(cur, ViMoveOperation::NextBeginBlock, repeatCount());
 9:             break;
10:         case '[':
11:             if( ch == '[' )
12:                 cursorMoved = moveCursor(cur, ViMoveOperation::PrevBeginBlock, repeatCount());
13:             break;
14:         }
15:     } else {
16:         switch( ch ) {
17:         case ']':
18:         case '[':
19:             m_cmdPrefix = ch;
20:             return true;
21:         .....
22:         }
23:     }
24:     m_cmdPrefix = 0;
25:     .....
26: }

Tweet


前へ 次へ
技術文章qvi