ブロックコメント これまでのドキュメントで、構文を認識し、行コメント、キーワード、HTMLタグ、HTMLタグ内キーワード、ダブルクォート、シングルクォートを認識し、折り返し表示することが可能なビューワのコードを示して来た。ひとつひとつの処理はそれほど複雑ではないのだが、全部の機能を実装するとコードはそれなりに複雑になった。読者はちゃんと理解し、ついてこれているだろうか。ちょっと心配だ。だが、構文強調表示機能として残っている課題はブロックコメント、<? ... ?>、<script>...</script> の3つだけになった。あと少しだ。 ■ ブロックコメント C/C++ では "/*" と "*/" で囲まれる範囲は、コメントとして取り扱われる(本稿ではこのような形式のコメントをブロックコメントと呼ぶ)。これは論理行に制約されることなく行われるので、ある行がブロックコメント内にあるかどうかは、その行を見るだけでは判断できない。折り返しサポート下での行コメント処理でも同様の問題があったが、常に論理行の先頭からスキャンすることで問題を解決した。ブロックコメントの場合もそれと同様にエディットバッファの最初からスキャンする必要がある。しかし、表示のたびにエディットバッファ先頭からスキャンするのは処理時間が無駄と考えられるので、各論理行に行頭がブロックコメント内であるかどうかを示すフラグを導入することにする。 ## column #define EBLF_BLOCK_COMMENT 0x01 // // エディットバッファでの1行構造体 // struct SEditBufLine { uchar m_flags; CString m_text; public: SEditBufLine() {}; SEditBufLine(const CString &text, uchar flag = 0) { m_text = text; m_flags = 0; }; SEditBufLine(const SEditBufLine &src) { m_text = src.m_text; m_flags = src.m_flags; }; SEditBufLine &operator=(const SEditBufLine &src) { m_text = src.m_text; m_flags = src.m_flags; return *this; }; }; // // CArray を利用するエディットエンジン // class CEditEngine { CArray m_editBuf; ..... }; ## endcolumn 上記リストはそのためのヘッダファイル修正個所である。実装も以下のように、m_editBuf への引数を text から CEditEngine(text) に修正する。 ## column void CEditEngine::addText(const CString &text) { m_editBuf.Add(SEditBufLine(text)); } ## endcolumn この修正を行っても、CViewView の方は1バイトも修正する必要がない。 ■ フラグの設定 ブロックコメントの設定を行うメソッドを void CEditEngine::doScan() とすると、以下のように実装できる。 ## column void skipDblQuote(cchar *&srcptr, cchar *endptr, bool bsesc) { int dq = 2; while( dq > 0 && srcptr < endptr ) { uchar uch = *srcptr++; if( bsesc && uch == '\\' && srcptr < endptr ) srcptr += 1; else if( uch == '"' ) dq -= 1; else if( IsDBCSLeadByte(uch) && srcptr < endptr ) srcptr += 1; } } bool isSingleQuote(cchar *srcptr, int srclen, int &len) { if( *srcptr == '\'' ) { if( srclen >= 3 && srcptr[1] != '\\' && !IsDBCSLeadByte((uchar)srcptr[1]) && srcptr[2] == '\'' ) { len = 3; return true; } if( srclen >= 4 && srcptr[1] == '\\' && !IsDBCSLeadByte((uchar)srcptr[2]) && srcptr[3] == '\'' ) { len = 4; return true; } if( srclen >= 4 && IsDBCSLeadByte((uchar)srcptr[1]) && srcptr[3] == '\'' ) { len = 4; return true; } } return false; } void CEditEngine::doScan(uchar mask, cchar *start, cchar *term, bool bsEsc) { bool inside = false; for(int line = 0; line < getLineCount(); ++line) { SEditBufLine tmp = m_editBuf.GetAt(line); if( inside ) tmp.m_flags |= mask; else tmp.m_flags &= ~mask; m_editBuf.SetAt(line, tmp); int startlen = strlen(start); cchar *text = (LPCSTR)tmp.m_text; cchar *endptr = text + tmp.m_text.GetLength(); cchar *ptr; int len; while( text < endptr ) { if( inside ) { if( (ptr = strstr(text, term)) == NULL ) break; text = ptr + strlen(term); inside = false; } else { while( text < endptr ) { if( !strncmp(text, start, startlen) ) { inside = true; break; } else if( *text == '"' ) { skipDblQuote(text, endptr, true); } else if( isSingleQuote(text, endptr - text, len) ) text += len; else text += 1; } } } } } ## endcolumn このメソッドは、ファイルをオープンした直後にコールする。 ## column BOOL CViewDoc::OnOpenDocument(LPCTSTR lpszPathName) { ..... m_ee.doScan(EBLF_BLOCK_COMMENT, VW_START_COMMENT, VW_TERM_COMMENT, true); return TRUE; } ## endcolumn ■ OnDraw() のブロックコメント対応 以上で前準備が調ったので、後はブロックコメント状態を判断しながら表示を行うだけだ。 ## column void CViewView::OnDraw(CDC* pDC) { ..... bool blockComment = false; // ブロックコメント中かどうか for(; line < m_vwLineMgr->getLineCountEx() && py < bottom; ++line, py += VW_LINE_HEIGHT) { ..... if( m_vwLineMgr->isLgFirstLine(line) ) { blockComment = (m_vwLineMgr->getLgLineFlags(line) & EBLF_BLOCK_COMMENT) ? true : false; ..... if( lineComment ) { // コメント状態 ..... } else if( blockComment ) { // コメント状態 textColor = RGB(0, 0x80, 0); // 文字色は黒緑 int termlen = strlen(VW_TERM_COMMENT); while( ptr < eolptr && *ptr != '\t' ) { if( endptr - ptr >= termlen && !strncmp(ptr, VW_TERM_COMMENT, termlen) ) { ptr += termlen; blockComment = false; break; } ptr += 1; } } ..... int startlen = strlen(VW_START_COMMENT); while( ptr < eolptr && *ptr != '\t' ) { if( endptr - ptr >= startlen && !strncmp(ptr, VW_START_COMMENT, startlen) ) { blockComment = true; break; } ..... } ..... } ## endcolumn 上記プログラムの実行結果を ここ に示す。