シンタックスハイライトの例
Syntax Highlighter の例では、簡単なシンタックスハイライトの実行方法を紹介します。
Syntax Highlighterアプリケーションは、カスタム・シンタックス・ハイライトでC++ファイルを表示します。
この例は 2 つのクラスで構成されています:
Highlighter
クラスは、ハイライト・ルールを定義し、適用します。MainWindow
ウィジェットはアプリケーションのメイン・ウィンドウです。
まず、Highlighter
クラスを確認し、QSyntaxHighlighter クラスを好みに合わせてカスタマイズする方法を確認します。次に、MainWindow
クラスの関連部分を確認し、カスタム・ハイライト・クラスをアプリケーションで使用する方法を確認します。
ハイライト・クラスの定義
class Highlighter : public QSyntaxHighlighter { Q_OBJECT public: Highlighter(QTextDocument *parent = nullptr); protected: void highlightBlock(const QString &text) override; private: struct HighlightingRule { QRegularExpression pattern; QTextCharFormat format; }; QList<HighlightingRule> highlightingRules; QRegularExpression commentStartExpression; QRegularExpression commentEndExpression; QTextCharFormat keywordFormat; QTextCharFormat classFormat; QTextCharFormat singleLineCommentFormat; QTextCharFormat multiLineCommentFormat; QTextCharFormat quotationFormat; QTextCharFormat functionFormat; };
独自の構文強調表示を行うには、QSyntaxHighlighter をサブクラス化し、highlightBlock() 関数を再実装して、独自の強調表示ルールを定義する必要があります。
ここでは、プライベート構造体を使用してハイライト・ルールを格納することにしました:ルールはQRegularExpression パターンとQTextCharFormat インスタンスで構成されます。ルールはQList パターンと インスタンスで構成されます。
QTextCharFormat クラスは、QTextDocument 内の文字に対す る 組版情報を提供 し 、 テ キ ス ト の視覚的特性を指定す る と と も に、 ハ イ パーテ キ ス ト 文書内の役割に関す る 情報 も 提供 し ます。こ の例では、QTextCharFormat::setFontWeight () とQTextCharFormat::setForeground () 関数を使っ て、 文字の太 さ と 色のみを定義 し ます。
蛍光ペンクラスの実装
QSyntaxHighlighter クラスをサブクラス化する際には、ベースクラスのコンストラクタに parent パラメータを渡す必要があります。parent は、構文強調表示が適用されるテキスト文書です。この例では、コンストラクタの中でハイライト・ルールを定義します:
Highlighter::Highlighter(QTextDocument *parent) : QSyntaxHighlighter(parent) { HighlightingRule rule; keywordFormat.setForeground(Qt::darkBlue); keywordFormat.setFontWeight(QFont::Bold); const QString keywordPatterns[] = { QStringLiteral("\\bchar\\b"), QStringLiteral("\\bclass\\b"), QStringLiteral("\\bconst\\b"), QStringLiteral("\\bdouble\\b"), QStringLiteral("\\benum\\b"), QStringLiteral("\\bexplicit\\b"), QStringLiteral("\\bfriend\\b"), QStringLiteral("\\binline\\b"), QStringLiteral("\\bint\\b"), QStringLiteral("\\blong\\b"), QStringLiteral("\\bnamespace\\b"), QStringLiteral("\\boperator\\b"), QStringLiteral("\\bprivate\\b"), QStringLiteral("\\bprotected\\b"), QStringLiteral("\\bpublic\\b"), QStringLiteral("\\bshort\\b"), QStringLiteral("\\bsignals\\b"), QStringLiteral("\\bsigned\\b"), QStringLiteral("\\bslots\\b"), QStringLiteral("\\bstatic\\b"), QStringLiteral("\\bstruct\\b"), QStringLiteral("\\btemplate\\b"), QStringLiteral("\\btypedef\\b"), QStringLiteral("\\btypename\\b"), QStringLiteral("\\bunion\\b"), QStringLiteral("\\bunsigned\\b"), QStringLiteral("\\bvirtual\\b"), QStringLiteral("\\bvoid\\b"), QStringLiteral("\\bvolatile\\b"), QStringLiteral("\\bbool\\b") }; for (const QString &pattern : keywordPatterns) { rule.pattern = QRegularExpression(pattern); rule.format = keywordFormat; highlightingRules.append(rule); }
まず、最も一般的なC++キーワードを認識するキーワード・ルールを定義します。keywordFormat
には太字で濃い青のフォントを使用します。キーワードごとに、そのキーワードと指定された書式をHighlightingRuleオブジェクトに代入し、そのオブジェクトをルールのリストに追加します。
classFormat.setFontWeight(QFont::Bold); classFormat.setForeground(Qt::darkMagenta); rule.pattern = QRegularExpression(QStringLiteral("\\bQ[A-Za-z]+\\b")); rule.format = classFormat; highlightingRules.append(rule); quotationFormat.setForeground(Qt::darkGreen); rule.pattern = QRegularExpression(QStringLiteral("\".*\"")); rule.format = quotationFormat; highlightingRules.append(rule); functionFormat.setFontItalic(true); functionFormat.setForeground(Qt::blue); rule.pattern = QRegularExpression(QStringLiteral("\\b[A-Za-z0-9_]+(?=\\()")); rule.format = functionFormat; highlightingRules.append(rule);
次に、Qtのクラス名に適用するフォーマットを作成します。クラス名は、濃いマゼンタ色と太字のスタイルでレンダリングされます。実際には、すべてのQtクラス名をキャプチャする正規表現である文字列パターンを指定します。次に、正規表現と指定された書式をHighlightingRuleオブジェクトに代入し、そのオブジェクトをルールのリストに追加します。
同じ方法で、引用符や関数のハイライト・ルールも定義します:パターンは正規表現の形式を持ち、関連する書式とともにHighlightingRuleオブジェクトに格納されます。
singleLineCommentFormat.setForeground(Qt::red); rule.pattern = QRegularExpression(QStringLiteral("//[^\n]*")); rule.format = singleLineCommentFormat; highlightingRules.append(rule); multiLineCommentFormat.setForeground(Qt::red); commentStartExpression = QRegularExpression(QStringLiteral("/\\*")); commentEndExpression = QRegularExpression(QStringLiteral("\\*/")); }
C++言語には2種類のコメントがあります:一行コメント (//
) と複数行コメント (/*...*
/
) です。単一行コメントは、前述のものと同様のハイライト・ルールで簡単に定義できます。しかし、複数行コメントはQSyntaxHighlighter クラスの設計上、特別な注意が必要です。
QSyntaxHighlighter オブジェクトが作成されると、そのhighlightBlock() 関数がリッチテキストエンジンによって必要なときに自動的に呼び出され、指定されたテキストブロックが強調表示されます。問題は、コメントが複数のテキストブロックにまたがる場合に現れます。この問題をどう解決するかは、Highlighter::highlightBlock()
関数の実装を見直すときに詳しく見ていきます。この時点では、複数行のコメントの色を指定するだけです。
void Highlighter::highlightBlock(const QString &text) { for (const HighlightingRule &rule : std::as_const(highlightingRules)) { QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text); while (matchIterator.hasNext()) { QRegularExpressionMatch match = matchIterator.next(); setFormat(match.capturedStart(), match.capturedLength(), rule.format); } }
highlightBlock() 関数は、 リ ッ チテ キ ス ト エ ン ジ ンに よ っ て必要な と き に、 すなわち変更 さ れたテ キ ス ト ブ ロ ッ ク があ る と き に、 自動的に呼び出 さ れます。
まず、highlightingRules
リストに格納した構文強調表示ルールを適用します。各ルールについて(つまり各HighlightingRuleオブジェクトについて)、QString::indexOf ()関数を使用して、指定されたテキストブロック内のパターンを検索します。パターンが最初に見つかったら、QRegularExpressionMatch::capturedLength() 関数を使用して、フォーマットされる文字列を決定します。QRegularExpressionMatch::capturedLength() は最後にマッチした文字列の長さを返し、マッチしなかった場合は 0 を返します。
実際の組版を行うために、QSyntaxHighlighter クラスはsetFormat() 関数を提供しています。この関数は、highlightBlock()
関数の引数として渡されたテキスト・ブロックを操作します。指定された書式が、指定された長さの指定された開始位置からのテキストに適用されます。指定 さ れた書式で設定 さ れた組版プ ロ パテ ィ は、 表示時に、 文書内に直接格納 さ れてい る 組版情報 と 結合 さ れます。文書自体は、この関数で設定された書式によって変更されないままであることに注意してください。
この処理は、現在のテキストブロック内でパターンの最後の出現が見つかるまで繰り返されます。
setCurrentBlockState(0);
複数のテ キ ス ト ブ ロ ッ ク に ま たが る 構造体を扱 う には (C++ の複数行 コ メ ン ト の よ う に)、 直前のテ キ ス ト ブ ロ ッ ク の終了状態を知 る 必要があ り ます (た と えば 「in comment」)。highlightBlock()
実装の内部では、QSyntaxHighlighter::previousBlockState() 関数を使って、前のテキスト・ブロックの終了状態を問い合わせることができます。ブロックをパースした後、QSyntaxHighlighter::setCurrentBlockState() を使って最後の状態を保存できます。
previousBlockState() 関数は int 値を返します。状態が設定されていない場合、返される値は -1 です。setCurrentBlockState() 関数を使用して、任意の状態を識別するために他の値を指定することができる。一旦状態が設定されると、QTextBlock は、再度設定されるか、対応するテキストの段落が削除されるまで、その値を保持します。
この例では、「コメントなし」の状態を表すために「0」を、「コメントあり」の状態を表すために「1」を使用することにしました。保存された構文の強調表示ルールが適用されると、現在のブロック状態を 0 に初期化します。
int startIndex = 0; if (previousBlockState() != 1) startIndex = text.indexOf(commentStartExpression);
直前のブロック状態が「コメント中」 (previousBlockState() == 1
) であった場合、テキスト・ブロックの先頭で終了式の検索を開始します。previousBlockState() が 0 を返した場合は、開始表現が最初に出現する位置から検索を開始します。
while (startIndex >= 0) { QRegularExpressionMatch match = commentEndExpression.match(text, startIndex); int endIndex = match.capturedStart(); int commentLength = 0; if (endIndex == -1) { setCurrentBlockState(1); commentLength = text.length() - startIndex; } else { commentLength = endIndex - startIndex + match.capturedLength(); } setFormat(startIndex, commentLength, multiLineCommentFormat); startIndex = text.indexOf(commentStartExpression, startIndex + commentLength); } }
終了表現が見つかると、コメントの長さを計算し、複数行コメント形式を適用します。次に、開始表現が次に出現する場所を検索し、この処理を繰り返します。現在のテキスト・ブロックに終了表現が見つからない場合は、現在のブロックの状態を1、つまり「コメント中」に設定します。
これでHighlighter
クラスの実装は完了です。
MainWindowクラスの定義
QSyntaxHighlighter アプリケーションにクラスのインスタンスを提供し、ハイライトを適用したいドキュメントを渡すだけです。
class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); public slots: void about(); void newFile(); void openFile(const QString &path = QString()); private: void setupEditor(); void setupFileMenu(); void setupHelpMenu(); QTextEdit *editor; Highlighter *highlighter; };
この例では、Highlighter
インスタンスへのポインタを宣言し、後で privatesetupEditor()
関数で初期化します。
MainWindowクラスの実装
メインウィンドウのコンストラクタは単純明快です。最初にメニューを設定し、次にエディタを初期化してアプリケーションの中心ウィジェットにします。最後にメインウィンドウのタイトルを設定します。
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { setupFileMenu(); setupHelpMenu(); setupEditor(); setCentralWidget(editor); setWindowTitle(tr("Syntax Highlighter")); }
setupEditor()関数でHighlighter
:
void MainWindow::setupEditor() { QFont font; font.setFamily("Courier"); font.setFixedPitch(true); font.setPointSize(10); editor = new QTextEdit; editor->setFont(font); highlighter = new Highlighter(editor->document()); QFile file("mainwindow.h"); if (file.open(QFile::ReadOnly | QFile::Text)) editor->setPlainText(file.readAll()); }
まず、エディターで使用したいフォントを作成し、次にエディター自体をQTextEdit クラスのインスタンスとして作成します。エディタをMainWindow
クラス定義ファイルで初期化する前に、Highlighter
インスタンスを作成し、引数としてエディタのドキュメントを渡します。これはハイライトが適用されるドキュメントです。これで完了です。
QSyntaxHighlighter オブジェクトは一度に1つのドキュメントにしかインストールできませんが、QSyntaxHighlighter::setDocument() 関数を使えば簡単に別のドキュメントにハイライトを再インストールできます。QSyntaxHighlighter クラスは、現在設定されているドキュメントを返すdocument() 関数も提供しています。
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.