diff --git a/share/qtcreator/styles/creator-dark.xml b/share/qtcreator/styles/creator-dark.xml
index 85add5cfac2..088feb7a2ed 100644
--- a/share/qtcreator/styles/creator-dark.xml
+++ b/share/qtcreator/styles/creator-dark.xml
@@ -29,6 +29,7 @@
+
diff --git a/share/qtcreator/styles/dark.xml b/share/qtcreator/styles/dark.xml
index 3a2e2c59dda..db26e1467ef 100644
--- a/share/qtcreator/styles/dark.xml
+++ b/share/qtcreator/styles/dark.xml
@@ -19,6 +19,7 @@
+
diff --git a/share/qtcreator/styles/default_classic.xml b/share/qtcreator/styles/default_classic.xml
index 08d9fd0e812..3d7f8f04a65 100644
--- a/share/qtcreator/styles/default_classic.xml
+++ b/share/qtcreator/styles/default_classic.xml
@@ -25,6 +25,7 @@
+
diff --git a/share/qtcreator/styles/grayscale.xml b/share/qtcreator/styles/grayscale.xml
index 8d7cbfb6cd2..3d0e41936a5 100644
--- a/share/qtcreator/styles/grayscale.xml
+++ b/share/qtcreator/styles/grayscale.xml
@@ -17,6 +17,7 @@
+
diff --git a/share/qtcreator/styles/inkpot.xml b/share/qtcreator/styles/inkpot.xml
index 1f7332857f0..c934812c5dc 100644
--- a/share/qtcreator/styles/inkpot.xml
+++ b/share/qtcreator/styles/inkpot.xml
@@ -23,6 +23,7 @@
+
diff --git a/share/qtcreator/styles/intellij.xml b/share/qtcreator/styles/intellij.xml
index aedb2df64f9..d229781cf83 100644
--- a/share/qtcreator/styles/intellij.xml
+++ b/share/qtcreator/styles/intellij.xml
@@ -11,6 +11,7 @@
+
diff --git a/share/qtcreator/styles/modnokai_night_shift_v2.xml b/share/qtcreator/styles/modnokai_night_shift_v2.xml
index c274ebe52d4..923cc887728 100644
--- a/share/qtcreator/styles/modnokai_night_shift_v2.xml
+++ b/share/qtcreator/styles/modnokai_night_shift_v2.xml
@@ -35,6 +35,7 @@
+
diff --git a/share/qtcreator/styles/solarized-dark.xml b/share/qtcreator/styles/solarized-dark.xml
index 07d97ad858d..ade9f227d07 100644
--- a/share/qtcreator/styles/solarized-dark.xml
+++ b/share/qtcreator/styles/solarized-dark.xml
@@ -35,6 +35,7 @@
+
diff --git a/share/qtcreator/styles/solarized-light.xml b/share/qtcreator/styles/solarized-light.xml
index ede48e7d0bc..348d89f4453 100644
--- a/share/qtcreator/styles/solarized-light.xml
+++ b/share/qtcreator/styles/solarized-light.xml
@@ -35,6 +35,7 @@
+
diff --git a/src/plugins/cppeditor/cpphighlighter.cpp b/src/plugins/cppeditor/cpphighlighter.cpp
index 66fb796dedb..cf41b6fbf01 100644
--- a/src/plugins/cppeditor/cpphighlighter.cpp
+++ b/src/plugins/cppeditor/cpphighlighter.cpp
@@ -33,6 +33,15 @@ using namespace CPlusPlus;
namespace CppEditor {
using namespace Internal;
+union AttributeState {
+ struct {
+ quint8 lbrackets: 1;
+ quint8 rbrackets: 1;
+ quint8 opened: 6;
+ };
+ quint8 state;
+};
+
CppHighlighter::CppHighlighter(QTextDocument *document) :
SyntaxHighlighter(document)
{
@@ -79,6 +88,8 @@ void CppHighlighter::highlightBlock(const QString &text)
userData->setFoldingStartIncluded(false);
userData->setFoldingEndIncluded(false);
}
+ AttributeState attrState;
+ attrState.state = TextDocumentLayout::attributeState(prevBlock);
if (tokens.isEmpty()) {
setCurrentBlockState((braceDepth << 8) | lexerState);
@@ -93,6 +104,7 @@ void CppHighlighter::highlightBlock(const QString &text)
}
TextDocumentLayout::setFoldingIndent(currentBlock(), foldingIndent);
TextDocumentLayout::setExpectedRawStringSuffix(currentBlock(), inheritedRawStringSuffix);
+ TextDocumentLayout::setAttributeState(currentBlock(), attrState.state);
qCDebug(highlighterLog) << "no tokens, storing brace depth" << braceDepth << "and foldingIndent"
<< foldingIndent;
return;
@@ -185,6 +197,25 @@ void CppHighlighter::highlightBlock(const QString &text)
if (onlyHighlightComments && !tk.isComment())
continue;
+ // Handle attributes, i.e. identifiers in pairs of "[[" and "]]".
+ if (tk.is(T_LBRACKET) && !tk.isOperator()) {
+ attrState.lbrackets = !attrState.lbrackets;
+ if (attrState.lbrackets == 0)
+ ++attrState.opened;
+ continue;
+ }
+ if (tk.is(T_RBRACKET) && !tk.isOperator()) {
+ attrState.rbrackets = !attrState.rbrackets;
+ if (attrState.rbrackets == 0 && attrState.opened > 0)
+ --attrState.opened;
+ continue;
+ }
+ attrState.lbrackets = attrState.rbrackets = 0;
+ if (attrState.opened && (tk.is(T_IDENTIFIER) || (tk.isKeyword() && !tk.is(T_USING)))) {
+ setFormat(tk.utf16charsBegin(), tk.utf16chars(), formatForCategory(C_ATTRIBUTE));
+ continue;
+ }
+
if (i == 0 && tk.is(T_POUND)) {
setFormatWithSpaces(text, tk.utf16charsBegin(), tk.utf16chars(),
formatForCategory(C_PREPROCESSOR));
@@ -297,6 +328,7 @@ void CppHighlighter::highlightBlock(const QString &text)
}
TextDocumentLayout::setParentheses(currentBlock(), parentheses);
+ TextDocumentLayout::setAttributeState(currentBlock(), attrState.state);
TextDocumentLayout::setFoldingIndent(currentBlock(), foldingIndent);
setCurrentBlockState(rehighlightNextBlock | (braceDepth << 8) | tokenize.state());
@@ -655,6 +687,46 @@ private slots:
<< 73 << 17 << 73 << 18 << C_STRING;
QTest::newRow("wide char literal with user-defined suffix (suffix)")
<< 73 << 20 << 73 << 22 << C_OVERLOADED_OPERATOR;
+ QTest::newRow("separate attributes specs, 1/4, namespace")
+ << 75 << 3 << 72 << 5 << C_ATTRIBUTE;
+ QTest::newRow("separate attributes specs, 1/4, attr")
+ << 75 << 8 << 72 << 20 << C_ATTRIBUTE;
+ QTest::newRow("separate attributes specs, 2/4, namespace")
+ << 75 << 26 << 72 << 28 << C_ATTRIBUTE;
+ QTest::newRow("separate attributes specs, 2/4, attr")
+ << 75 << 31 << 72 << 33 << C_ATTRIBUTE;
+ QTest::newRow("separate attributes specs, 3/4, namespace")
+ << 75 << 39 << 72 << 41 << C_ATTRIBUTE;
+ QTest::newRow("separate attributes specs, 3/4, attr")
+ << 75 << 44 << 72 << 48 << C_ATTRIBUTE;
+ QTest::newRow("separate attributes specs, 4/4, attr")
+ << 75 << 54 << 72 << 62 << C_ATTRIBUTE;
+ QTest::newRow("single attributes spec, 1/4, namespace")
+ << 76 << 3 << 73 << 5 << C_ATTRIBUTE;
+ QTest::newRow("single attributes spec, 1/4, attr")
+ << 76 << 8 << 73 << 20 << C_ATTRIBUTE;
+ QTest::newRow("single attributes spec, 2/4, namespace")
+ << 76 << 23 << 73 << 25 << C_ATTRIBUTE;
+ QTest::newRow("single attributes spec, 2/4, attr")
+ << 76 << 28 << 73 << 32 << C_ATTRIBUTE;
+ QTest::newRow("single attributes spec, 3/4, namespace")
+ << 76 << 35 << 73 << 37 << C_ATTRIBUTE;
+ QTest::newRow("single attributes spec, 3/4, attr")
+ << 76 << 40 << 73 << 42 << C_ATTRIBUTE;
+ QTest::newRow("single attributes spec, 4/4, attr")
+ << 76 << 45 << 73 << 53 << C_ATTRIBUTE;
+ QTest::newRow("attributes with using, namespace")
+ << 77 << 9 << 74 << 11 << C_ATTRIBUTE;
+ QTest::newRow("attributes with using, attr 1/3")
+ << 77 << 15 << 74 << 19 << C_ATTRIBUTE;
+ QTest::newRow("attributes with using, attr 2/3")
+ << 77 << 22 << 74 << 34 << C_ATTRIBUTE;
+ QTest::newRow("attributes with using, attr 3/3")
+ << 77 << 37 << 74 << 39 << C_ATTRIBUTE;
+ QTest::newRow("attribute with line split, namespace")
+ << 79 << 9 << 76 << 11 << C_ATTRIBUTE;
+ QTest::newRow("attribute with line split, attr")
+ << 79 << 14 << 76 << 26 << C_ATTRIBUTE;
}
void test()
diff --git a/src/plugins/cppeditor/testcases/highlightingtestcase.cpp b/src/plugins/cppeditor/testcases/highlightingtestcase.cpp
index 049bc42ecfb..3211c0ecf1b 100644
--- a/src/plugins/cppeditor/testcases/highlightingtestcase.cpp
+++ b/src/plugins/cppeditor/testcases/highlightingtestcase.cpp
@@ -71,3 +71,10 @@ static void parenTest2()
wchar_t operator ""_wc(const wchar_t c) { return c; }
const auto c = L'c'_wc;
+
+[[gnu::always_inline]] [[gnu::hot]] [[gnu::const]] [[nodiscard]] int attr1();
+[[gnu::always_inline, gnu::const, gnu::hot, nodiscard]] int attr2();
+[[using gnu : const, always_inline, hot]] int attr3[
+ [
+ gnu::always_inline]
+]();
diff --git a/src/plugins/texteditor/textdocumentlayout.cpp b/src/plugins/texteditor/textdocumentlayout.cpp
index 1eb18c51f51..6804cacfdd9 100644
--- a/src/plugins/texteditor/textdocumentlayout.cpp
+++ b/src/plugins/texteditor/textdocumentlayout.cpp
@@ -543,6 +543,21 @@ TextSuggestion *TextDocumentLayout::suggestion(const QTextBlock &block)
return nullptr;
}
+void TextDocumentLayout::setAttributeState(const QTextBlock &block, quint8 attrState)
+{
+ if (TextBlockUserData * const data = textUserData(block))
+ data->setAttrState(attrState);
+ else if (attrState)
+ userData(block)->setAttrState(attrState);
+}
+
+quint8 TextDocumentLayout::attributeState(const QTextBlock &block)
+{
+ if (TextBlockUserData *userData = textUserData(block))
+ return userData->attrState();
+ return 0;
+}
+
void TextDocumentLayout::updateSuggestionFormats(const QTextBlock &block,
const FontSettings &fontSettings)
{
diff --git a/src/plugins/texteditor/textdocumentlayout.h b/src/plugins/texteditor/textdocumentlayout.h
index 427500a8326..c484d0591e8 100644
--- a/src/plugins/texteditor/textdocumentlayout.h
+++ b/src/plugins/texteditor/textdocumentlayout.h
@@ -153,6 +153,9 @@ public:
TextSuggestion *suggestion() const;
void clearSuggestion();
+ void setAttrState(quint8 state) { m_attrState = state; }
+ quint8 attrState() const { return m_attrState; }
+
private:
TextMarks m_marks;
int m_foldingIndent : 16;
@@ -168,6 +171,7 @@ private:
QByteArray m_expectedRawStringSuffix; // A bit C++-specific, but let's be pragmatic.
std::unique_ptr m_replacement;
std::unique_ptr m_suggestion;
+ quint8 m_attrState = 0;
};
class TEXTEDITOR_EXPORT TextDocumentLayout : public QPlainTextDocumentLayout
@@ -201,6 +205,8 @@ public:
static void setExpectedRawStringSuffix(const QTextBlock &block, const QByteArray &suffix);
static QByteArray expectedRawStringSuffix(const QTextBlock &block);
static TextSuggestion *suggestion(const QTextBlock &block);
+ static void setAttributeState(const QTextBlock &block, quint8 attrState);
+ static quint8 attributeState(const QTextBlock &block);
static void updateSuggestionFormats(const QTextBlock &block,
const FontSettings &fontSettings);
static bool updateSuggestion(const QTextBlock &block,
diff --git a/src/plugins/texteditor/texteditorconstants.cpp b/src/plugins/texteditor/texteditorconstants.cpp
index 689669a91a6..32e0be34271 100644
--- a/src/plugins/texteditor/texteditorconstants.cpp
+++ b/src/plugins/texteditor/texteditorconstants.cpp
@@ -52,6 +52,7 @@ const char *nameForStyle(TextStyle style)
case C_PREPROCESSOR: return "Preprocessor";
case C_MACRO: return "Macro";
case C_LABEL: return "Label";
+ case C_ATTRIBUTE: return "Attribute";
case C_COMMENT: return "Comment";
case C_DOXYGEN_COMMENT: return "Doxygen.Comment";
case C_DOXYGEN_TAG: return "Doxygen.Tag";
diff --git a/src/plugins/texteditor/texteditorconstants.h b/src/plugins/texteditor/texteditorconstants.h
index e32cd64bd3a..cbb91c46516 100644
--- a/src/plugins/texteditor/texteditorconstants.h
+++ b/src/plugins/texteditor/texteditorconstants.h
@@ -50,6 +50,7 @@ enum TextStyle : quint8 {
C_PREPROCESSOR,
C_MACRO,
C_LABEL,
+ C_ATTRIBUTE,
C_COMMENT,
C_DOXYGEN_COMMENT,
C_DOXYGEN_TAG,
diff --git a/src/plugins/texteditor/texteditorsettings.cpp b/src/plugins/texteditor/texteditorsettings.cpp
index 356ae76fcd1..4b7b1f3d7c2 100644
--- a/src/plugins/texteditor/texteditorsettings.cpp
+++ b/src/plugins/texteditor/texteditorsettings.cpp
@@ -251,6 +251,8 @@ FormatDescriptions TextEditorSettingsPrivate::initialFormats()
Tr::tr("Macros."), functionFormat);
formatDescr.emplace_back(C_LABEL, Tr::tr("Label"), Tr::tr("Labels for goto statements."),
Qt::darkRed);
+ formatDescr.emplace_back(C_ATTRIBUTE, Tr::tr("Attribute"), Tr::tr("Attributes."),
+ Qt::darkYellow);
formatDescr.emplace_back(C_COMMENT, Tr::tr("Comment"),
Tr::tr("All style of comments except Doxygen comments."),
Qt::darkGreen);