7 #include "highlightingdata_p.hpp"
8 #include "ksyntaxhighlighting_logging.h"
11 #include <QXmlStreamReader>
12 #include <QStringView>
16 template<
class Data,
class... Args>
17 static void initRuleData(Data &data, Args &&...args)
19 new (&data) Data{std::move(args)...};
27 static HighlightingContextData::Rule::WordDelimiters loadAdditionalWordDelimiters(
QXmlStreamReader &reader)
29 return HighlightingContextData::Rule::WordDelimiters{
41 qCWarning(
Log) << defName <<
"at line" << reader.
lineNumber() <<
": " << attrName <<
"attribute is empty";
47 if (str.
size() == 1) {
51 qCWarning(
Log) << defName <<
"at line" << reader.
lineNumber() <<
": " << attrName <<
"attribute must contain exactly 1 character";
57 using Rule = HighlightingContextData::Rule;
61 bool isIncludeRules =
false;
65 if (!checkIsChar(s,
"char", defName, reader)) {
68 const QChar c = s.at(0);
69 const bool dynamic = Xml::attrToBool(attrs.value(
QLatin1String(
"dynamic")));
71 initRuleData(
rule.data.detectChar, c, dynamic);
72 rule.type = Rule::Type::DetectChar;
75 if (!checkIsNotEmpty(pattern,
"String", defName, reader)) {
79 const auto isCaseInsensitive = attrToCaseSensitivity(attrs.value(
QLatin1String(
"insensitive")));
80 const auto isMinimal = Xml::attrToBool(attrs.value(
QLatin1String(
"minimal")));
81 const auto dynamic = Xml::attrToBool(attrs.value(
QLatin1String(
"dynamic")));
83 initRuleData(
rule.data.regExpr,
pattern.toString(), isCaseInsensitive, isMinimal, dynamic);
84 rule.type = Rule::Type::RegExpr;
87 if (!checkIsNotEmpty(context,
"context", defName, reader)) {
90 const bool includeAttribute = Xml::attrToBool(attrs.value(
QLatin1String(
"includeAttrib")));
92 initRuleData(
rule.data.includeRules, context.toString(), includeAttribute);
93 rule.type = Rule::Type::IncludeRules;
94 isIncludeRules =
true;
98 if (!checkIsChar(s1,
"char", defName, reader)) {
101 if (!checkIsChar(s2,
"char1", defName, reader)) {
105 initRuleData(
rule.data.detect2Chars, s1.at(0), s2.at(0));
106 rule.type = Rule::Type::Detect2Chars;
109 if (!checkIsNotEmpty(s,
"String", defName, reader)) {
113 bool hasCaseSensitivityOverride =
false;
120 hasCaseSensitivityOverride =
true;
121 caseSensitivityOverride = attrToCaseSensitivity(attrs.value(
QLatin1String(
"insensitive")));
124 initRuleData(
rule.data.keyword, s.toString(), loadAdditionalWordDelimiters(reader), caseSensitivityOverride, hasCaseSensitivityOverride);
125 rule.type = Rule::Type::Keyword;
127 rule.type = Rule::Type::DetectSpaces;
130 if (!checkIsNotEmpty(
string,
"String", defName, reader)) {
133 const auto caseSensitivity = attrToCaseSensitivity(attrs.value(
QLatin1String(
"insensitive")));
134 const auto dynamic = Xml::attrToBool(attrs.value(
QLatin1String(
"dynamic")));
138 if (!dynamic &&
string.size() == 1) {
139 QChar c =
string.at(0);
141 initRuleData(
rule.data.detectChar, c, dynamic);
142 rule.type = Rule::Type::DetectChar;
145 rule.type = Rule::Type::AnyChar;
149 else if (isSensitive && !dynamic &&
string.size() == 2) {
150 initRuleData(
rule.data.detect2Chars,
string.at(0),
string.at(1));
151 rule.type = Rule::Type::Detect2Chars;
153 initRuleData(
rule.data.stringDetect,
string.toString(), caseSensitivity, dynamic);
154 rule.type = Rule::Type::StringDetect;
158 if (!checkIsNotEmpty(word,
"String", defName, reader)) {
161 const auto caseSensitivity = attrToCaseSensitivity(attrs.value(
QLatin1String(
"insensitive")));
163 initRuleData(
rule.data.wordDetect, word.toString(), loadAdditionalWordDelimiters(reader), caseSensitivity);
164 rule.type = Rule::Type::WordDetect;
167 if (!checkIsNotEmpty(chars,
"String", defName, reader)) {
172 if (chars.size() == 1) {
173 initRuleData(
rule.data.detectChar, chars.at(0),
false);
174 rule.type = Rule::Type::DetectChar;
176 initRuleData(
rule.data.anyChar, chars.toString());
177 rule.type = Rule::Type::AnyChar;
180 rule.type = Rule::Type::DetectIdentifier;
185 initRuleData(
rule.data.lineContinue, c);
186 rule.type = Rule::Type::LineContinue;
188 initRuleData(
rule.data.detectInt, loadAdditionalWordDelimiters(reader));
189 rule.type = Rule::Type::Int;
191 initRuleData(
rule.data.detectFloat, loadAdditionalWordDelimiters(reader));
192 rule.type = Rule::Type::Float;
194 rule.type = Rule::Type::HlCStringChar;
198 if (!checkIsChar(s1,
"char", defName, reader)) {
201 if (!checkIsChar(s2,
"char1", defName, reader)) {
205 initRuleData(
rule.data.rangeDetect, s1.at(0), s2.at(0));
206 rule.type = Rule::Type::RangeDetect;
208 initRuleData(
rule.data.hlCHex, loadAdditionalWordDelimiters(reader));
209 rule.type = Rule::Type::HlCHex;
211 rule.type = Rule::Type::HlCChar;
213 initRuleData(
rule.data.hlCOct, loadAdditionalWordDelimiters(reader));
214 rule.type = Rule::Type::HlCOct;
216 qCWarning(
Log) <<
"Unknown rule type:" <<
name;
220 if (!isIncludeRules) {
222 rule.common.beginRegionName = attrs.value(
QLatin1String(
"beginRegion")).toString();
224 rule.common.firstNonSpace = Xml::attrToBool(attrs.value(
QLatin1String(
"firstNonSpace")));
225 rule.common.lookAhead = Xml::attrToBool(attrs.value(
QLatin1String(
"lookAhead")));
227 if (!
rule.common.lookAhead) {
233 rule.common.column = -1;
240 template<
class Data1,
class Data2,
class Visitor>
241 static void dataRuleVisit(HighlightingContextData::Rule::Type type, Data1 &&data1, Data2 &&data2, Visitor &&visitor)
243 using Rule = HighlightingContextData::Rule;
244 using Type = Rule::Type;
247 visitor(data1.anyChar, data2.anyChar);
249 case Type::DetectChar:
250 visitor(data1.detectChar, data2.detectChar);
252 case Type::Detect2Chars:
253 visitor(data1.detect2Chars, data2.detect2Chars);
256 visitor(data1.hlCOct, data2.hlCOct);
258 case Type::IncludeRules:
259 visitor(data1.includeRules, data2.includeRules);
262 visitor(data1.detectInt, data2.detectInt);
265 visitor(data1.keyword, data2.keyword);
267 case Type::LineContinue:
268 visitor(data1.lineContinue, data2.lineContinue);
270 case Type::RangeDetect:
271 visitor(data1.rangeDetect, data2.rangeDetect);
274 visitor(data1.regExpr, data2.regExpr);
276 case Type::StringDetect:
277 visitor(data1.stringDetect, data2.stringDetect);
279 case Type::WordDetect:
280 visitor(data1.wordDetect, data2.wordDetect);
283 visitor(data1.detectFloat, data2.detectFloat);
286 visitor(data1.hlCHex, data2.hlCHex);
289 case Type::HlCStringChar:
290 case Type::DetectIdentifier:
291 case Type::DetectSpaces:
297 HighlightingContextData::Rule::Rule() noexcept = default;
300 : common(
std::move(other.common))
302 dataRuleVisit(other.type, data, other.data, [](
auto &data1,
auto &data2) {
303 using Data = std::remove_reference_t<decltype(data1)>;
304 new (&data1) Data(std::move(data2));
309 HighlightingContextData::Rule::Rule(
const Rule &other)
310 : common(other.common)
312 dataRuleVisit(other.type, data, other.data, [](
auto &data1,
auto &data2) {
313 using Data = std::remove_reference_t<decltype(data1)>;
314 new (&data1) Data(data2);
319 HighlightingContextData::Rule::~Rule()
321 dataRuleVisit(type, data, data, [](
auto &data,
auto &) {
322 using Data = std::remove_reference_t<decltype(data)>;
327 HighlightingContextData::ContextSwitch::ContextSwitch(
QStringView str)
329 if (str.
isEmpty() || str == QStringLiteral(
"#stay")) {
333 while (str.
startsWith(QStringLiteral(
"#pop"))) {
346 m_contextAndDefName = str.
toString();
347 m_defNameIndex = str.
indexOf(QStringLiteral(
"##"));
350 bool HighlightingContextData::ContextSwitch::isStay()
const
352 return m_popCount == -1 && m_contextAndDefName.isEmpty();
355 QStringView HighlightingContextData::ContextSwitch::contextName()
const
357 if (m_defNameIndex == -1) {
358 return m_contextAndDefName;
363 QStringView HighlightingContextData::ContextSwitch::defName()
const
365 if (m_defNameIndex == -1) {
386 while (!reader.
atEnd()) {
389 auto &
rule = rules.emplace_back();
390 if (!loadRule(defName,
rule, reader)) {