7#include "highlightingdata_p.hpp"
8#include "ksyntaxhighlighting_logging.h"
11#include <QXmlStreamReader>
16template<
class Data,
class... Args>
17static void initRuleData(Data &data, Args &&...args)
19 new (&data) Data{std::move(args)...};
27static 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) {
221 rule.common.contextName = attrs.value(
QLatin1String(
"context")).toString();
222 rule.common.beginRegionName = attrs.value(
QLatin1String(
"beginRegion")).toString();
223 rule.common.endRegionName = attrs.value(
QLatin1String(
"endRegion")).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) {
228 rule.common.attributeName = attrs.value(
QLatin1String(
"attribute")).toString();
231 rule.common.column = attrs.value(
QLatin1String(
"column")).toInt(&colOk);
233 rule.common.column = -1;
240template<
class Data1,
class Data2,
class Visitor>
241static 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:
297HighlightingContextData::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));
309HighlightingContextData::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);
319HighlightingContextData::Rule::~Rule()
321 dataRuleVisit(type, data, data, [](
auto &data,
auto &) {
322 using Data = std::remove_reference_t<
decltype(data)>;
343 while (!reader.
atEnd()) {
346 auto &rule = rules.emplace_back();
347 if (!loadRule(defName, rule, reader)) {
Represents the raw xml data of a context and its rules.
QString attribute
attribute name, to lookup our format
Type type(const QSqlDatabase &db)
QString name(StandardAction id)
Syntax highlighting engine for Kate syntax definitions.
char32_t toLower(char32_t ucs4)
char32_t toUpper(char32_t ucs4)
bool isEmpty() const const
qsizetype size() const const
QString toString() const const
QStringView value(QAnyStringView namespaceUri, QAnyStringView name) const const
QXmlStreamAttributes attributes() const const
qint64 lineNumber() const const
QStringView name() const const
void skipCurrentElement()
TokenType tokenType() const const