2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2019 The Qt Company Ltd.
|
2022-12-21 10:12:09 +01:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2018-11-08 11:39:48 +01:00
|
|
|
|
|
|
|
|
#include "highlighter.h"
|
|
|
|
|
|
2019-05-03 08:48:25 +02:00
|
|
|
#include "tabsettings.h"
|
2021-06-21 08:46:58 +02:00
|
|
|
#include "textdocumentlayout.h"
|
2018-11-08 11:39:48 +01:00
|
|
|
|
2019-11-04 10:03:27 +01:00
|
|
|
#include <coreplugin/editormanager/documentmodel.h>
|
2018-11-08 11:39:48 +01:00
|
|
|
#include <coreplugin/icore.h>
|
|
|
|
|
#include <coreplugin/messagemanager.h>
|
2023-01-17 18:02:43 +01:00
|
|
|
|
2022-02-23 17:11:20 +01:00
|
|
|
#include <utils/mimeutils.h>
|
2019-06-21 14:16:54 +02:00
|
|
|
#include <utils/qtcassert.h>
|
2021-06-21 08:46:58 +02:00
|
|
|
#include <utils/stylehelper.h>
|
2018-11-08 11:39:48 +01:00
|
|
|
|
2023-11-01 10:08:31 +01:00
|
|
|
#include <KSyntaxHighlighting/Definition>
|
2022-05-18 18:03:22 +03:00
|
|
|
#include <KSyntaxHighlighting/DefinitionDownloader>
|
|
|
|
|
#include <KSyntaxHighlighting/FoldingRegion>
|
|
|
|
|
#include <KSyntaxHighlighting/Format>
|
|
|
|
|
#include <KSyntaxHighlighting/Repository>
|
|
|
|
|
#include <KSyntaxHighlighting/SyntaxHighlighter>
|
2018-11-08 11:39:48 +01:00
|
|
|
|
2021-06-21 08:46:58 +02:00
|
|
|
#include <QLoggingCategory>
|
2018-11-08 11:39:48 +01:00
|
|
|
#include <QMetaEnum>
|
|
|
|
|
|
2021-06-29 18:00:43 +02:00
|
|
|
using namespace Utils;
|
|
|
|
|
|
|
|
|
|
namespace TextEditor {
|
2018-11-08 11:39:48 +01:00
|
|
|
|
2021-06-21 08:46:58 +02:00
|
|
|
static Q_LOGGING_CATEGORY(highlighterLog, "qtc.editor.highlighter", QtWarningMsg)
|
|
|
|
|
|
2018-11-08 11:39:48 +01:00
|
|
|
TextStyle categoryForTextStyle(int style)
|
|
|
|
|
{
|
|
|
|
|
switch (style) {
|
|
|
|
|
case KSyntaxHighlighting::Theme::Normal: return C_TEXT;
|
|
|
|
|
case KSyntaxHighlighting::Theme::Keyword: return C_KEYWORD;
|
|
|
|
|
case KSyntaxHighlighting::Theme::Function: return C_FUNCTION;
|
|
|
|
|
case KSyntaxHighlighting::Theme::Variable: return C_LOCAL;
|
|
|
|
|
case KSyntaxHighlighting::Theme::ControlFlow: return C_KEYWORD;
|
|
|
|
|
case KSyntaxHighlighting::Theme::Operator: return C_OPERATOR;
|
|
|
|
|
case KSyntaxHighlighting::Theme::BuiltIn: return C_PRIMITIVE_TYPE;
|
|
|
|
|
case KSyntaxHighlighting::Theme::Extension: return C_GLOBAL;
|
|
|
|
|
case KSyntaxHighlighting::Theme::Preprocessor: return C_PREPROCESSOR;
|
|
|
|
|
case KSyntaxHighlighting::Theme::Attribute: return C_LOCAL;
|
|
|
|
|
case KSyntaxHighlighting::Theme::Char: return C_STRING;
|
|
|
|
|
case KSyntaxHighlighting::Theme::SpecialChar: return C_STRING;
|
|
|
|
|
case KSyntaxHighlighting::Theme::String: return C_STRING;
|
|
|
|
|
case KSyntaxHighlighting::Theme::VerbatimString: return C_STRING;
|
|
|
|
|
case KSyntaxHighlighting::Theme::SpecialString: return C_STRING;
|
|
|
|
|
case KSyntaxHighlighting::Theme::Import: return C_PREPROCESSOR;
|
|
|
|
|
case KSyntaxHighlighting::Theme::DataType: return C_TYPE;
|
|
|
|
|
case KSyntaxHighlighting::Theme::DecVal: return C_NUMBER;
|
|
|
|
|
case KSyntaxHighlighting::Theme::BaseN: return C_NUMBER;
|
|
|
|
|
case KSyntaxHighlighting::Theme::Float: return C_NUMBER;
|
|
|
|
|
case KSyntaxHighlighting::Theme::Constant: return C_KEYWORD;
|
|
|
|
|
case KSyntaxHighlighting::Theme::Comment: return C_COMMENT;
|
|
|
|
|
case KSyntaxHighlighting::Theme::Documentation: return C_DOXYGEN_COMMENT;
|
|
|
|
|
case KSyntaxHighlighting::Theme::Annotation: return C_DOXYGEN_TAG;
|
|
|
|
|
case KSyntaxHighlighting::Theme::CommentVar: return C_DOXYGEN_TAG;
|
|
|
|
|
case KSyntaxHighlighting::Theme::RegionMarker: return C_PREPROCESSOR;
|
|
|
|
|
case KSyntaxHighlighting::Theme::Information: return C_WARNING;
|
|
|
|
|
case KSyntaxHighlighting::Theme::Warning: return C_WARNING;
|
|
|
|
|
case KSyntaxHighlighting::Theme::Alert: return C_ERROR;
|
|
|
|
|
case KSyntaxHighlighting::Theme::Error: return C_ERROR;
|
|
|
|
|
case KSyntaxHighlighting::Theme::Others: return C_TEXT;
|
|
|
|
|
}
|
|
|
|
|
return C_TEXT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Highlighter::Highlighter()
|
|
|
|
|
{
|
|
|
|
|
setTextFormatCategories(QMetaEnum::fromType<KSyntaxHighlighting::Theme::TextStyle>().keyCount(),
|
|
|
|
|
&categoryForTextStyle);
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-07 10:03:09 +01:00
|
|
|
static bool isOpeningParenthesis(QChar c)
|
|
|
|
|
{
|
|
|
|
|
return c == QLatin1Char('{') || c == QLatin1Char('[') || c == QLatin1Char('(');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool isClosingParenthesis(QChar c)
|
|
|
|
|
{
|
|
|
|
|
return c == QLatin1Char('}') || c == QLatin1Char(']') || c == QLatin1Char(')');
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-08 11:39:48 +01:00
|
|
|
void Highlighter::highlightBlock(const QString &text)
|
|
|
|
|
{
|
2020-01-07 12:05:16 +01:00
|
|
|
if (!definition().isValid()) {
|
|
|
|
|
formatSpaces(text);
|
2018-11-08 11:39:48 +01:00
|
|
|
return;
|
2020-01-07 12:05:16 +01:00
|
|
|
}
|
2019-06-05 09:26:12 +02:00
|
|
|
QTextBlock block = currentBlock();
|
2023-05-24 15:38:04 +02:00
|
|
|
const QTextBlock previousBlock = block.previous();
|
|
|
|
|
TextDocumentLayout::setBraceDepth(block, TextDocumentLayout::braceDepth(previousBlock));
|
|
|
|
|
KSyntaxHighlighting::State previousLineState;
|
|
|
|
|
if (TextBlockUserData *data = TextDocumentLayout::textUserData(previousBlock))
|
|
|
|
|
previousLineState = data->syntaxState();
|
|
|
|
|
KSyntaxHighlighting::State oldState;
|
2019-12-04 08:19:53 +01:00
|
|
|
if (TextBlockUserData *data = TextDocumentLayout::textUserData(block)) {
|
2023-05-24 15:38:04 +02:00
|
|
|
oldState = data->syntaxState();
|
2019-05-03 08:48:25 +02:00
|
|
|
data->setFoldingStartIncluded(false);
|
|
|
|
|
data->setFoldingEndIncluded(false);
|
|
|
|
|
}
|
2023-05-24 15:38:04 +02:00
|
|
|
KSyntaxHighlighting::State state = highlightLine(text, previousLineState);
|
|
|
|
|
if (oldState != state) {
|
|
|
|
|
TextBlockUserData *data = TextDocumentLayout::userData(block);
|
|
|
|
|
data->setSyntaxState(state);
|
|
|
|
|
// Toggles the LSB of current block's userState. It forces rehighlight of next block.
|
|
|
|
|
setCurrentBlockState(currentBlockState() ^ 1);
|
|
|
|
|
}
|
2019-03-07 10:03:09 +01:00
|
|
|
|
|
|
|
|
Parentheses parentheses;
|
|
|
|
|
int pos = 0;
|
|
|
|
|
for (const QChar &c : text) {
|
|
|
|
|
if (isOpeningParenthesis(c))
|
|
|
|
|
parentheses.push_back(Parenthesis(Parenthesis::Opened, c, pos));
|
|
|
|
|
else if (isClosingParenthesis(c))
|
|
|
|
|
parentheses.push_back(Parenthesis(Parenthesis::Closed, c, pos));
|
|
|
|
|
pos++;
|
|
|
|
|
}
|
|
|
|
|
TextDocumentLayout::setParentheses(currentBlock(), parentheses);
|
|
|
|
|
|
2023-05-24 15:38:04 +02:00
|
|
|
const QTextBlock nextBlock = block.next();
|
2019-05-03 08:48:25 +02:00
|
|
|
if (nextBlock.isValid()) {
|
|
|
|
|
TextBlockUserData *data = TextDocumentLayout::userData(nextBlock);
|
2019-06-05 09:26:12 +02:00
|
|
|
data->setFoldingIndent(TextDocumentLayout::braceDepth(block));
|
2019-05-03 08:48:25 +02:00
|
|
|
}
|
|
|
|
|
|
2019-03-14 10:04:33 +01:00
|
|
|
formatSpaces(text);
|
2018-11-08 11:39:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Highlighter::applyFormat(int offset, int length, const KSyntaxHighlighting::Format &format)
|
|
|
|
|
{
|
2019-07-02 13:04:46 +02:00
|
|
|
const KSyntaxHighlighting::Theme defaultTheme;
|
|
|
|
|
QTextCharFormat qformat = formatForCategory(format.textStyle());
|
|
|
|
|
|
|
|
|
|
if (format.hasTextColor(defaultTheme)) {
|
|
|
|
|
const QColor textColor = format.textColor(defaultTheme);
|
|
|
|
|
if (format.hasBackgroundColor(defaultTheme)) {
|
|
|
|
|
const QColor backgroundColor = format.hasBackgroundColor(defaultTheme);
|
2021-06-29 18:00:43 +02:00
|
|
|
if (StyleHelper::isReadableOn(backgroundColor, textColor)) {
|
2019-07-02 13:04:46 +02:00
|
|
|
qformat.setForeground(textColor);
|
|
|
|
|
qformat.setBackground(backgroundColor);
|
2021-06-29 18:00:43 +02:00
|
|
|
} else if (StyleHelper::isReadableOn(qformat.background().color(), textColor)) {
|
2019-07-02 13:04:46 +02:00
|
|
|
qformat.setForeground(textColor);
|
|
|
|
|
}
|
2021-06-29 18:00:43 +02:00
|
|
|
} else if (StyleHelper::isReadableOn(qformat.background().color(), textColor)) {
|
2019-07-02 13:04:46 +02:00
|
|
|
qformat.setForeground(textColor);
|
|
|
|
|
}
|
|
|
|
|
} else if (format.hasBackgroundColor(defaultTheme)) {
|
|
|
|
|
const QColor backgroundColor = format.hasBackgroundColor(defaultTheme);
|
2021-06-29 18:00:43 +02:00
|
|
|
if (StyleHelper::isReadableOn(backgroundColor, qformat.foreground().color()))
|
2019-07-02 13:04:46 +02:00
|
|
|
qformat.setBackground(backgroundColor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (format.isBold(defaultTheme))
|
|
|
|
|
qformat.setFontWeight(QFont::Bold);
|
|
|
|
|
|
|
|
|
|
if (format.isItalic(defaultTheme))
|
|
|
|
|
qformat.setFontItalic(true);
|
|
|
|
|
|
|
|
|
|
if (format.isUnderline(defaultTheme))
|
|
|
|
|
qformat.setFontUnderline(true);
|
|
|
|
|
|
|
|
|
|
if (format.isStrikeThrough(defaultTheme))
|
|
|
|
|
qformat.setFontStrikeOut(true);
|
|
|
|
|
setFormat(offset, length, qformat);
|
2018-11-08 11:39:48 +01:00
|
|
|
}
|
2019-05-03 08:48:25 +02:00
|
|
|
|
|
|
|
|
void Highlighter::applyFolding(int offset,
|
|
|
|
|
int length,
|
|
|
|
|
KSyntaxHighlighting::FoldingRegion region)
|
|
|
|
|
{
|
|
|
|
|
if (!region.isValid())
|
|
|
|
|
return;
|
2019-06-05 09:26:12 +02:00
|
|
|
QTextBlock block = currentBlock();
|
2019-05-03 08:48:25 +02:00
|
|
|
const QString &text = block.text();
|
|
|
|
|
TextBlockUserData *data = TextDocumentLayout::userData(currentBlock());
|
|
|
|
|
const bool fromStart = TabSettings::firstNonSpace(text) == offset;
|
|
|
|
|
const bool toEnd = (offset + length) == (text.length() - TabSettings::trailingWhitespaces(text));
|
|
|
|
|
if (region.type() == KSyntaxHighlighting::FoldingRegion::Begin) {
|
2021-06-21 08:46:58 +02:00
|
|
|
const int newBraceDepth = TextDocumentLayout::braceDepth(block) + 1;
|
|
|
|
|
TextDocumentLayout::setBraceDepth(block, newBraceDepth);
|
|
|
|
|
qCDebug(highlighterLog) << "Found folding start from '" << offset << "' to '" << length
|
|
|
|
|
<< "' resulting in the bracedepth '" << newBraceDepth << "' in :";
|
|
|
|
|
qCDebug(highlighterLog) << text;
|
|
|
|
|
// if there is only a folding begin marker in the line move the current block into the fold
|
|
|
|
|
if (fromStart && toEnd && length <= 1) {
|
2019-06-05 09:26:12 +02:00
|
|
|
data->setFoldingIndent(TextDocumentLayout::braceDepth(block));
|
2019-05-03 08:48:25 +02:00
|
|
|
data->setFoldingStartIncluded(true);
|
|
|
|
|
}
|
|
|
|
|
} else if (region.type() == KSyntaxHighlighting::FoldingRegion::End) {
|
2021-06-21 08:46:58 +02:00
|
|
|
const int newBraceDepth = qMax(0, TextDocumentLayout::braceDepth(block) - 1);
|
|
|
|
|
qCDebug(highlighterLog) << "Found folding end from '" << offset << "' to '" << length
|
|
|
|
|
<< "' resulting in the bracedepth '" << newBraceDepth << "' in :";
|
|
|
|
|
qCDebug(highlighterLog) << text;
|
|
|
|
|
TextDocumentLayout::setBraceDepth(block, newBraceDepth);
|
2019-05-03 08:48:25 +02:00
|
|
|
// if the folding end is at the end of the line move the current block into the fold
|
|
|
|
|
if (toEnd)
|
|
|
|
|
data->setFoldingEndIncluded(true);
|
|
|
|
|
else
|
2019-06-05 09:26:12 +02:00
|
|
|
data->setFoldingIndent(TextDocumentLayout::braceDepth(block));
|
2019-05-03 08:48:25 +02:00
|
|
|
}
|
|
|
|
|
}
|
2021-06-29 18:00:43 +02:00
|
|
|
|
|
|
|
|
} // TextEditor
|