// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only

/*!
    \example richtext/syntaxhighlighter
    \title Syntax Highlighter Example
    \examplecategory {User Interface Components}
    \ingroup examples-richtext
    \brief The Syntax Highlighter example shows how to perform
    simple syntax highlighting.

    \brief The Syntax Highlighter example shows how to perform simple syntax
    highlighting by subclassing the QSyntaxHighlighter class.

    \image syntaxhighlighter-example.png
           {Text editor with syntax highlighting}

    The Syntax Highlighter application displays C++ files with custom
    syntax highlighting.

    The example consists of two classes:

    \list
        \li The \c Highlighter class defines and applies the
           highlighting rules.
        \li The \c MainWindow widget is the application's main window.
    \endlist

    We will first review the \c Highlighter class to see how you can
    customize the QSyntaxHighlighter class to fit your preferences,
    then we will take a look at the relevant parts of the \c
    MainWindow class to see how you can use your custom highlighter
    class in an application.

    \section1 Highlighter Class Definition

    \snippet richtext/syntaxhighlighter/highlighter.h 0

    To provide your own syntax highlighting, you must subclass
    QSyntaxHighlighter, reimplement the \l
    {QSyntaxHighlighter::highlightBlock()}{highlightBlock()} function,
    and define your own highlighting rules.

    We have chosen to store our highlighting rules using a private
    struct: A rule consists of a QRegularExpression pattern and a
    QTextCharFormat instance. The various rules are then stored using a
    QList.

    The QTextCharFormat class provides formatting information for
    characters in a QTextDocument specifying the visual properties of
    the text, as well as information about its role in a hypertext
    document. In this example, we will only define the font weight and
    color using the QTextCharFormat::setFontWeight() and
    QTextCharFormat::setForeground() functions.

    \section1 Highlighter Class Implementation

    When subclassing the QSyntaxHighlighter class you must pass the
    parent parameter to the base class constructor. The parent is the
    text document upon which the syntax highlighting will be
    applied. In this example, we have also chosen to define our
    highlighting rules in the constructor:

    \snippet richtext/syntaxhighlighter/highlighter.cpp 0
    \snippet richtext/syntaxhighlighter/highlighter.cpp 1

    First we define a keyword rule which recognizes the most common
    C++ keywords. We give the \c keywordFormat a bold, dark blue
    font. For each keyword, we assign the keyword and the specified
    format to a HighlightingRule object and append the object to our
    list of rules.

    \snippet richtext/syntaxhighlighter/highlighter.cpp 2
    \codeline
    \snippet richtext/syntaxhighlighter/highlighter.cpp 4
    \codeline
    \snippet richtext/syntaxhighlighter/highlighter.cpp 5

    Then we create a format that we will apply to Qt class names. The
    class names will be rendered with a dark magenta color and a bold
    style. We specify a string pattern that is actually a regular
    expression capturing all Qt class names. Then we assign the
    regular expression and the specified format to a HighlightingRule
    object and append the object to our list of rules.

    We also define highlighting rules for quotations and functions
    using the same approach: The patterns have the form of regular
    expressions and are stored in HighlightingRule objects with the
    associated format.

    \snippet richtext/syntaxhighlighter/highlighter.cpp 3
    \codeline
    \snippet richtext/syntaxhighlighter/highlighter.cpp 6

    The C++ language has two variations of comments: The single line
    comment (\c //) and the multiline comment (\c{/*...*}\c{/}). The single
    line comment can easily be defined through a highlighting rule
    similar to the previous ones. But the multiline comment needs
    special care due to the design of the QSyntaxHighlighter class.

    After a QSyntaxHighlighter object is created, its \l
    {QSyntaxHighlighter::highlightBlock()}{highlightBlock()} function
    will be called automatically whenever it is necessary by the rich
    text engine, highlighting the given text block. The problem
    appears when a comment spans several text blocks. We will take a
    closer look at how this problem can be solved when reviewing the
    implementation of the \c Highlighter::highlightBlock()
    function. At this point we only specify the multiline comment's
    color.

    \snippet richtext/syntaxhighlighter/highlighter.cpp 7

    The highlightBlock() function is called automatically whenever it
    is necessary by the rich text engine, i.e. when there are text
    blocks that have changed.

    First we apply the syntax highlighting rules that we stored in the
    \c highlightingRules list. For each rule (i.e. for each
    HighlightingRule object) we search for the pattern in the given
    text block using the QString::indexOf() function. When the first
    occurrence of the pattern is found, we use the
    QRegularExpressionMatch::capturedLength() function to determine the string
    that will be formatted. QRegularExpressionMatch::capturedLength() returns
    the length of the last matched string, or 0 if there was no match.

    To perform the actual formatting the QSyntaxHighlighter class
    provides the \l {QSyntaxHighlighter::setFormat()}{setFormat()}
    function. This function operates on the text block that is passed
    as argument to the \c highlightBlock() function. The specified
    format is applied to the text from the given start position for
    the given length. The formatting properties set in the given
    format are merged at display time with the formatting information
    stored directly in the document. Note that the document itself
    remains unmodified by the format set through this function.

    This process is repeated until the last occurrence of the pattern
    in the current text block is found.

    \snippet richtext/syntaxhighlighter/highlighter.cpp 8

    To deal with constructs that can span several text blocks (like
    the C++ multiline comment), it is necessary to know the end state
    of the previous text block (e.g. "in comment"). Inside your \c
    highlightBlock() implementation you can query the end state of the
    previous text block using the
    QSyntaxHighlighter::previousBlockState() function. After parsing
    the block you can save the last state using
    QSyntaxHighlighter::setCurrentBlockState().

    The \l
    {QSyntaxHighlighter::previousBlockState()}{previousBlockState()}
    function return an int value. If no state is set, the returned
    value is -1. You can designate any other value to identify any
    given state using the \l
    {QSyntaxHighlighter::setCurrentBlockState()}{setCurrentBlockState()}
    function. Once the state is set, the QTextBlock keeps that value
    until it is set again or until the corresponding paragraph of text
    is deleted.

    In this example we have chosen to use 0 to represent the "not in
    comment" state, and 1 for the "in comment" state. When the stored
    syntax highlighting rules are applied we initialize the current
    block state to 0.

    \snippet richtext/syntaxhighlighter/highlighter.cpp 9

    If the previous block state was "in comment" (\c
    {previousBlockState() == 1}), we start the search for an end
    expression at the beginning of the text block. If the
    previousBlockState() returns 0, we start the search at the
    location of the first occurrence of a start expression.

    \snippet richtext/syntaxhighlighter/highlighter.cpp 10
    \snippet richtext/syntaxhighlighter/highlighter.cpp 11

    When an end expression is found, we calculate the length of the
    comment and apply the multiline comment format. Then we search for
    the next occurrence of the start expression and repeat the
    process.  If no end expression can be found in the current text
    block we set the current block state to 1, i.e. "in comment".

    This completes the \c Highlighter class implementation; it is now
    ready for use.

    \section1 MainWindow Class Definition

    Using a QSyntaxHighlighter subclass is simple; just provide your
    application with an instance of the class and pass it the document
    upon which you want the highlighting to be applied.

    \snippet richtext/syntaxhighlighter/mainwindow.h 0

    In this example we declare a pointer to a \c Highlighter instance
    which we later will initialize in the private \c setupEditor()
    function.

    \section1 MainWindow Class Implementation

    The constructor of the main window is straight forward. We first
    set up the menus, then we initialize the editor and make it the
    central widget of the application. Finally we set the main
    window's title.

    \snippet richtext/syntaxhighlighter/mainwindow.cpp 0

    We initialize and install the \c Highlighter object in the private
    setupEditor() convenience function:

    \snippet richtext/syntaxhighlighter/mainwindow.cpp 1

    First we create the font we want to use in the editor, then we
    create the editor itself which is an instance of the QTextEdit
    class. Before we initialize the editor with the \c MainWindow
    class definition file, we create a \c Highlighter instance passing
    the editor's document as argument. This is the document that the
    highlighting will be applied to. Then we are done.

    A QSyntaxHighlighter object can only be installed on one document
    at the time, but you can easily reinstall the highlighter on
    another document using the QSyntaxHighlighter::setDocument()
    function. The QSyntaxHighlighter class also provides the \l
    {QSyntaxHighlighter::document()}{document()} function which
    returns the currently set document.
*/
