이 페이지에서

구문 강조 표시 예제

구문 강조 표시기 예제는 간단한 구문 강조 표시를 수행하는 방법을 보여줍니다.

구문 강조 기능이 있는 텍스트 편집기

구문 강조 표시 애플리케이션은 사용자 정의 구문 강조 표시를 적용하여 C++ 파일을 표시합니다.

이 예제는 두 개의 클래스로 구성됩니다:

  • Highlighter 클래스는 강조 표시 규칙을 정의하고 적용합니다.
  • MainWindow 위젯은 애플리케이션의 메인 창입니다.

먼저 Highlighter 클래스를 살펴보며 QSyntaxHighlighter 클래스를 사용자의 선호도에 맞게 사용자 정의하는 방법을 확인한 다음, MainWindow 클래스의 관련 부분을 살펴보며 애플리케이션에서 사용자 정의 하이라이터 클래스를 사용하는 방법을 알아보겠습니다.

Highlighter 클래스 정의

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() 함수를 재구현하며, 자신만의 강조 규칙을 정의해야 합니다.

여기서는 프라이빗 구조체(private struct)를 사용하여 강조 표시 규칙을 저장하기로 했습니다. 하나의 규칙은 ` QRegularExpression ` 패턴과 ` QTextCharFormat ` 인스턴스로 구성됩니다. 그런 다음 다양한 규칙들은 ` QList`을 사용하여 저장됩니다.

QTextCharFormat 클래스는 QTextDocument 내의 문자에 대한 서식 정보를 제공하며, 여기에는 텍스트의 시각적 속성과 하이퍼텍스트 문서 내에서의 역할에 대한 정보가 포함됩니다. 이 예제에서는 QTextCharFormat::setFontWeight() 및 QTextCharFormat::setForeground() 함수를 사용하여 글꼴 굵기와 색상만 정의할 것입니다.

Highlighter 클래스 구현

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);
    }

keywordFormat 먼저 가장 일반적인 C++ 키워드를 인식하는 키워드 규칙을 정의합니다. 키워드에는 굵은 진한 파란색 글꼴을 적용합니다. 각 키워드에 대해, 해당 키워드와 지정된 서식을 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++ 언어에는 두 가지 형태의 주석이 있습니다: 단일 행 주석(//)과 다중 행 주석(/*...*/ )입니다. 단일 행 주석은 앞서 설명한 것과 유사한 강조 표시 규칙을 통해 쉽게 정의할 수 있습니다. 하지만 다중 행 주석은 ` 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++의 다중 행 주석)을 처리하려면, 이전 텍스트 블록의 종료 상태(예: "주석 내")를 알아야 합니다. highlightBlock() 구현 내부에서는 QSyntaxHighlighter::previousBlockState() 함수를 사용하여 이전 텍스트 블록의 종료 상태를 조회할 수 있습니다. 블록을 파싱한 후에는 QSyntaxHighlighter::setCurrentBlockState()을 사용하여 마지막 상태를 저장할 수 있습니다.

previousBlockState() 함수는 int 값을 반환합니다. 상태가 설정되어 있지 않으면 반환 값은 -1입니다. setCurrentBlockState() 함수를 사용하여 주어진 상태를 식별하기 위해 임의의 다른 값을 지정할 수 있습니다. 상태가 설정되면 QTextBlock 는 해당 값이 다시 설정되거나 해당 텍스트 단락이 삭제될 때까지 그 값을 유지합니다.

이 예제에서는 “주석이 아님(not in comment)” 상태를 나타내기 위해 0을, “주석 상태(in comment)”를 나타내기 위해 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 인스턴스에 대한 포인터를 선언하며, 이 인스턴스는 나중에 비공개 함수인 ` setupEditor() `에서 초기화될 것입니다.

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 객체는 한 번에 하나의 문서에만 설치할 수 있지만, QSyntaxHighlighter::setDocument() 함수를 사용하여 다른 문서에 하이라이터를 쉽게 다시 설치할 수 있습니다. QSyntaxHighlighter 클래스는 현재 설정된 문서를 반환하는 document() 함수도 제공합니다.

예제 프로젝트 @ code.qt.io

© 2026 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.