Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: gil9red от Апрель 02, 2013, 15:31



Название: QSyntaxHighlighter. Подсветка пар скобок
Отправлено: gil9red от Апрель 02, 2013, 15:31
Здравствуйте!
Делаю многоязыковую подсветку синтаксиса, в качестве основы взял qt-шный пример
Можно ли в наследнике QSyntaxHighlighter написать код подсветки пар скобок: (), {}, [], <>?
Если можно то как и где :)

Спасибо :)


Название: Re: QSyntaxHighlighter. Подсветка пар скобок
Отправлено: _OLEGator_ от Апрель 02, 2013, 16:03
А в чем собственно проблема, разве в примерах этого нет?


Название: Re: QSyntaxHighlighter. Подсветка пар скобок
Отправлено: gil9red от Апрель 02, 2013, 16:32
да можно это сделать, вот только нужно делать подсветку, когда текстовый курсор будет рядом с скобкой :)


Название: Re: QSyntaxHighlighter. Подсветка пар скобок
Отправлено: gil9red от Апрель 03, 2013, 17:02
Что никто не знает? :)
Такая подсветка пар скобок есть например в Qt Creator'е, NotePad++ :)
Хотелось бы определять подсветку пар скобок именно в классах подсветки синтаксиса - для гибкость подсветки)

Приведу тогда код, может станет яснее)

Есть класс USyntaxHighlighter, наследуемый от QSyntaxHighlighter.
Класс USyntaxHighlighter содержит только алгоритмы подсветки и все:

h.
Код:
#include <QtGui>
#include <QtCore>

struct HighlightingRule
{
    QRegExp pattern;
    QTextCharFormat format;
};

// добавить границу слова
static void addWordBoundary(QStringList *list)
{
    list->replaceInStrings(QRegExp("^"), "\\b");
    list->replaceInStrings(QRegExp("$"), "\\b");
}

class USyntaxHighlighter: public QSyntaxHighlighter
{
    Q_OBJECT

public:
    USyntaxHighlighter(QTextDocument *parent = 0);
    void addRule(HighlightingRule hRule);
    inline virtual QString language(){ return ""; }
    inline QStringList keywordList()
    {
        return keywordPatterns.replaceInStrings("\\b", "");
    }

protected:
    QVector <HighlightingRule> highlightingRules;

    QRegExp commentStartExpression;
    QRegExp commentEndExpression;

    QTextCharFormat keywordFormat;
    QTextCharFormat classFormat;
    QTextCharFormat singleLineCommentFormat;
    QTextCharFormat multiLineCommentFormat;
    QTextCharFormat quotationFormat;
    QTextCharFormat functionFormat;

    QStringList keywordPatterns;

protected:
    void highlightBlock(const QString &text);
};

.срр
Код:
/// PUBLIC
USyntaxHighlighter::USyntaxHighlighter(QTextDocument *parent):
    QSyntaxHighlighter(parent)
{

}

void USyntaxHighlighter::addRule(HighlightingRule hRule)
{
    highlightingRules.append(hRule);
}

/// PROTECTED
void USyntaxHighlighter::highlightBlock(const QString &text)
{
    foreach (const HighlightingRule &rule, highlightingRules)
    {
        QRegExp expression(rule.pattern);
        int index = expression.indexIn(text);
        while (index >= 0)
        {
            int length = expression.matchedLength();
            setFormat(index, length, rule.format);
            index = expression.indexIn(text, index + length);
        }
    }   
    if(commentStartExpression.isEmpty()
            || commentEndExpression.isEmpty())
        return;

    setCurrentBlockState(0);

    int startIndex = 0;
    if (previousBlockState() != 1)
        startIndex = commentStartExpression.indexIn(text);

    while (startIndex >= 0)
    {
        int endIndex = commentEndExpression.indexIn(text, startIndex);
        int commentLength;
        if (endIndex == -1)
        {
            setCurrentBlockState(1);
            commentLength = text.length() - startIndex;
        }else
            commentLength = endIndex - startIndex
                            + commentEndExpression.matchedLength();

        setFormat(startIndex, commentLength, multiLineCommentFormat);
        startIndex = commentStartExpression.indexIn(text, startIndex + commentLength);
    }
}

А тут пример класса, наследуемого от USyntaxHighlighter и определяемого синтаксис:
Код:
class USyntaxHighlighter_Python: public USyntaxHighlighter
{
    Q_OBJECT

public:
    inline USyntaxHighlighter_Python(QTextDocument *parent = 0):
        USyntaxHighlighter(parent)
    {
        HighlightingRule rule;

        keywordFormat.setForeground(Qt::darkCyan);
        keywordFormat.setFontWeight(QFont::Bold);                   

        keywordPatterns << "False" << "class" << "finally"
                        << "is" << "return" << "None"
                        << "continue" << "for" << "lambda"
                        << "try" << "True" << "def"
                        << "from" << "nonlocal" << "while"
                        << "and" << "del" << "global"
                        << "not" << "with" << "as"
                        << "elif" << "if" << "or"
                        << "yield" << "assert" << "else"
                        << "import" << "pass" << "reak"
                        << "except" << "in" << "raise"
                        << "print" << "type";

        addWordBoundary(&keywordPatterns);

        for(int i = 0; i < keywordPatterns.count(); i++)
        {
            QString pattern = keywordPatterns.at(i);
            rule.pattern = QRegExp(pattern);
            rule.format = keywordFormat;
            highlightingRules.append(rule);
        }

        singleLineCommentFormat.setForeground(Qt::darkGreen);
        rule.pattern = QRegExp("#[^\n]*");
        rule.format = singleLineCommentFormat;
        highlightingRules.append(rule);

        multiLineCommentFormat.setForeground(Qt::darkGreen);

        quotationFormat.setForeground(Qt::darkGray);
        rule.pattern = QRegExp("\".*\"");
        rule.format = quotationFormat;
        highlightingRules.append(rule);

        rule.pattern = QRegExp("\'.*\'");
        rule.format = quotationFormat;
        highlightingRules.append(rule);

        commentStartExpression = QRegExp("");
        commentEndExpression = QRegExp("");
    }

    inline QString language(){ return "Python"; }
};



Название: Re: QSyntaxHighlighter. Подсветка пар скобок
Отправлено: gil9red от Апрель 03, 2013, 23:34
Похоже я тут сам с собой общаюсь...
Нашел решение, осталось малость - сделать его :D
скину решение, когда готово будет :)


Название: Re: QSyntaxHighlighter. Подсветка пар скобок
Отправлено: _OLEGator_ от Апрель 04, 2013, 09:12
Как в Creator'e реализовано смотрел?


Название: Re: QSyntaxHighlighter. Подсветка пар скобок
Отправлено: gil9red от Апрель 04, 2013, 11:21
Да, сначало искал в гите креаторе, но надоело искать :)
и нашел исходники какого то редактора кода написанного на qt, но его подсветка не полностью соответствовала тому что мне нужно было, поэтому буду переделывать :)


Название: Re: QSyntaxHighlighter. Подсветка пар скобок
Отправлено: _OLEGator_ от Апрель 04, 2013, 11:29
Можно повеситься на сигнал QTextEdit::cursorPositionChanged(), запоминать позицию и при необходимости обновлять подсветку.
Код
C++ (Qt)
void QSyntaxHighlighter::rehighlightBlock ( const QTextBlock & block ) [slot]
void QSyntaxHighlighter::rehighlight () [slot]
Конечно я бы все-таки глянул, как реализовано в Creator'e.


Название: Re: QSyntaxHighlighter. Подсветка пар скобок
Отправлено: kai666_73 от Апрель 04, 2013, 17:23
И все-таки в креаторе это сделано смачно )
Могу, так-сказать, задать направление
  basetexteditor.cpp ->
    BaseTextEditorWidget::slotCursorPositionChanged() -> BaseTextEditorWidget::updateHighlights() -> ...


Название: Re: QSyntaxHighlighter. Подсветка пар скобок
Отправлено: gil9red от Апрель 04, 2013, 21:58
Раз обещал скинуть, слово нужно выполнять :)

Нам нужно будет сделать наследника QTextEdit и в него добавить слоты matchBrackets, matchLeftBrackets, matchRightBrackets , createBracketsSelection.
Слот matchBrackets подключаем к сигналу cursorPositionChanged:
Код:
UTextEdit::UTextEdit(QWidget *parent):
    QTextEdit(parent)
{
    connect(this, SIGNAL(cursorPositionChanged()),
             this, SLOT(matchBrackets()));
}

Код:
static bool isLeftBrackets(QChar symbol)
{
    return symbol == '('
            || symbol == '{'
            || symbol == '[';
}

static bool isRightBrackets(QChar symbol)
{
    return symbol == ')'
            || symbol == '}'
            || symbol == ']';
}

struct UBracketInfo
{
    QChar character;
    int position;
};


class UTextBlockData: public QTextBlockUserData
{
public:
    QVector <UBracketInfo *> brackets()
    { return m_brackets; }

    void insert(UBracketInfo *info)
    {
        int i = 0;

        while(i < m_brackets.size()
              && info->position > m_brackets.at(i)->position)
            i++;

        m_brackets.insert(i, info);
    }   

private:
    QVector <UBracketInfo *> m_brackets;
};

void UTextEdit::matchBrackets()
{
    QList <QTextEdit::ExtraSelection> selections;
    setExtraSelections(selections);

    QTextBlock textBlock = textCursor().block();

    UTextBlockData *data = static_cast <UTextBlockData *> (textBlock.userData());

    if(data)
    {
        QVector <UBracketInfo *> brackets = data->brackets();
        int position = textCursor().block().position();

        for(int i = 0; i < brackets.size(); i++)
        {
            UBracketInfo *bracket = brackets.at(i);
            int currentPosition = textCursor().position() - textBlock.position();

            // Clicked on a left brackets?
            if (bracket->position == currentPosition - 1
                 && isLeftBrackets(bracket->character))
            {
                if (matchLeftBrackets(textBlock, i + 1, 0))
                    createBracketsSelection(position + bracket->position);
            }

            // Clicked on a right brackets?
            if (bracket->position == currentPosition - 1
                 && isRightBrackets(bracket->character))
            {
                if (matchRightBrackets(textBlock, i - 1, 0))
                    createBracketsSelection(position + bracket->position);
            }
        }
    }
}

/** Test left brackets match **/
bool UTextEdit::matchLeftBrackets(QTextBlock currentBlock, int index, int numberLeftBracket)
{
    UTextBlockData *data = static_cast <UTextBlockData *> (currentBlock.userData());

    QVector<UBracketInfo *> brackets = data->brackets();

    int positionInDocument = currentBlock.position();

    // Match in same line?
    for (; index < brackets.count(); index++)
    {
        UBracketInfo *bracket = brackets.at(index);

        if (isLeftBrackets(bracket->character))
        {
            ++numberLeftBracket;
            continue;
        }

        if (isRightBrackets(bracket->character)
             && numberLeftBracket == 0)
        {
            createBracketsSelection(positionInDocument + bracket->position);
            return true;
        }else
            --numberLeftBracket;
    }

    // No match yet? Then try next block
    currentBlock = currentBlock.next();
    if (currentBlock.isValid())
        return matchLeftBrackets(currentBlock, 0, numberLeftBracket);

    // No match at all
    return false;
}

/** Test right brackets match **/
bool UTextEdit::matchRightBrackets(QTextBlock currentBlock, int index, int numberRightBracket)
{
    UTextBlockData *data = static_cast <UTextBlockData *> (currentBlock.userData());

    QVector<UBracketInfo *> brackets = data->brackets();
    int positionInDocument = currentBlock.position();

    // Match in same line?
    for (int i = index; i >= 0; i--)
    {
        UBracketInfo *bracket = brackets.at(i);

        if (isRightBrackets(bracket->character))
        {
            ++numberRightBracket;
            continue;
        }

        if (isLeftBrackets(bracket->character)
             && numberRightBracket == 0)
        {
            createBracketsSelection(positionInDocument + bracket->position);
            return true;
        } else
            --numberRightBracket;
    }

    // No match yet? Then try previous block
    currentBlock = currentBlock.previous();
    if (currentBlock.isValid())
    {

        // Recalculate correct index first
        UTextBlockData *data = static_cast <UTextBlockData *> (currentBlock.userData());

        QVector <UBracketInfo *> brackets = data->brackets();

        return matchRightBrackets(currentBlock, brackets.count() - 1, numberRightBracket);
    }

    // No match at all
    return false;
}

/** Set brackets highlighter at pos **/
void UTextEdit::createBracketsSelection(int position)
{
    QList <QTextEdit::ExtraSelection> listSelections = extraSelections();

    QTextEdit::ExtraSelection selection;

    QTextCharFormat format = selection.format;
    format.setForeground(Qt::red);
    selection.format = format;

    QTextCursor cursor = textCursor();
    cursor.setPosition(position);
    cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);

    selection.cursor = cursor;

    listSelections.append(selection);

    setExtraSelections(listSelections);
}

А также создадим наследника QSyntaxHighlighter:
Код:
class USyntaxHighlighter: public QSyntaxHighlighter
{
    Q_OBJECT

public:
    inline USyntaxHighlighter(QTextDocument *document = 0):
        QSyntaxHighlighter(document)
    {       
    }

    inline void addRule(UHighlightingRule hRule)
    { highlightingRules.append(hRule); }

    inline virtual QString language(){ return ""; }

    inline void setTextEdit(QTextEdit *textEdit)
    { setDocument(textEdit->document()); }

    inline QStringList keywordList()
    { return keywordPatterns.replaceInStrings("\\b", ""); }

protected:
    QVector <UHighlightingRule> highlightingRules;

    QRegExp commentStartExpression;
    QRegExp commentEndExpression;

    QTextCharFormat keywordFormat;
    QTextCharFormat classFormat;
    QTextCharFormat singleLineCommentFormat;
    QTextCharFormat multiLineCommentFormat;
    QTextCharFormat quotationFormat;
    QTextCharFormat functionFormat;

    QStringList keywordPatterns;   

    enum UBrackets { RoundBrackets, CurlyBraces, SquareBrackets };

protected:
    void highlightBlock(const QString &text);
    void insertBrackets(QChar leftChar, QChar rightChar,
                        UTextBlockData *data, QString text);
    void insertBrackets(UBrackets brackets,
                        UTextBlockData *data, QString text);
};

/// PROTECTED
void USyntaxHighlighter::highlightBlock(const QString &text)
{
    for(int i = 0; i < highlightingRules.count(); i++)
    {
        UHighlightingRule rule = highlightingRules.at(i);
        QRegExp expression(rule.pattern);
        int index = expression.indexIn(text);

        while(index >= 0)
        {
            int length = expression.matchedLength();
            setFormat(index, length, rule.format);
            index = expression.indexIn(text, index + length);
        }
    }

    UTextBlockData *data = new UTextBlockData();

    /// скобки
    insertBrackets(RoundBrackets, data, text);
    insertBrackets(CurlyBraces, data, text);
    insertBrackets(SquareBrackets, data, text);
    /// скобки


    setCurrentBlockUserData(data);


    if(commentStartExpression.isEmpty()
            || commentEndExpression.isEmpty())
        return;

    setCurrentBlockState(0);

    int startIndex = 0;
    if(previousBlockState() != 1)
        startIndex = commentStartExpression.indexIn(text);

    while (startIndex >= 0)
    {
        int endIndex = commentEndExpression.indexIn(text, startIndex);
        int commentLength;

        if (endIndex == -1)
        {
            setCurrentBlockState(1);
            commentLength = text.length() - startIndex;
        }else
            commentLength = endIndex - startIndex
                    + commentEndExpression.matchedLength();

        setFormat(startIndex, commentLength, multiLineCommentFormat);
        startIndex = commentStartExpression.indexIn(text, startIndex + commentLength);
    }
}

void USyntaxHighlighter::insertBrackets(QChar leftChar, QChar rightChar,
                                        UTextBlockData *data, QString text)
{
    int leftPosition = text.indexOf(leftChar);

    while(leftPosition != -1)
    {
        UBracketInfo *info = new UBracketInfo();
        info->character = leftChar;
        info->position = leftPosition;

        data->insert(info);
        leftPosition = text.indexOf(leftChar, leftPosition + 1);
    }


    int rightPosition = text.indexOf(rightChar);

    while(rightPosition != -1)
    {
        UBracketInfo *info = new UBracketInfo();
        info->character = rightChar;
        info->position = rightPosition;

        data->insert(info);
        rightPosition = text.indexOf(rightChar, rightPosition + 1);
    }
}

void USyntaxHighlighter::insertBrackets(UBrackets brackets,
                                        UTextBlockData *data, QString text)
{
    QChar leftChar = '0';
    QChar rightChar = '0';

    switch(brackets)
    {
    case RoundBrackets:
        leftChar = '('; rightChar = ')';
        break;

    case CurlyBraces:
        leftChar = '{'; rightChar = '}';
        break;

    case SquareBrackets:
        leftChar = '['; rightChar = ']';
        break;
    }

    insertBrackets(leftChar, rightChar, data, text);
}


Код еще не закончен, но рабочий :)

Во вложении код UTextEdit, USyntaxHighlighter и его наследника с поддержкой подсветки синтаксиса с++ USyntaxHighlighter_Сpp :)


Название: Re: QSyntaxHighlighter. Подсветка пар скобок
Отправлено: krish от Август 09, 2013, 12:49
А можно весь проект Qt ? а то интересно посмотреть как это выглядит в деле. Я пытался по исходникам оживить код, но кроме синтаксиса ничего не выделяет. Ни скобки, ничего другого.


Название: Re: QSyntaxHighlighter. Подсветка пар скобок
Отправлено: gil9red от Август 09, 2013, 13:57
Можно :D
Вот только не для всех языков мне хватило энтузиазма и терпения закончить настройку файлов правил (rules) :)