File indexing completed on 2024-05-12 15:50:04
0001 /* 0002 SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org> 0003 SPDX-FileCopyrightText: 2018 Dominik Haumann <dhaumann@kde.org> 0004 SPDX-FileCopyrightText: 2018 Christoph Cullmann <cullmann@kde.org> 0005 SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com> 0006 0007 SPDX-License-Identifier: MIT 0008 */ 0009 0010 #include "definition.h" 0011 #include "definition_p.h" 0012 #include "definitionref_p.h" 0013 0014 #include "context_p.h" 0015 #include "format.h" 0016 #include "format_p.h" 0017 #include "highlightingdata_p.hpp" 0018 #include "ksyntaxhighlighting_logging.h" 0019 #include "ksyntaxhighlighting_version.h" 0020 #include "repository.h" 0021 #include "repository_p.h" 0022 #include "rule_p.h" 0023 #include "worddelimiters_p.h" 0024 #include "xml_p.h" 0025 0026 #include <QCborMap> 0027 #include <QCoreApplication> 0028 #include <QFile> 0029 #include <QHash> 0030 #include <QStringList> 0031 #include <QXmlStreamReader> 0032 0033 #include <algorithm> 0034 #include <atomic> 0035 0036 using namespace KSyntaxHighlighting; 0037 0038 DefinitionData::DefinitionData() 0039 : wordDelimiters() 0040 , wordWrapDelimiters(wordDelimiters) 0041 { 0042 } 0043 0044 DefinitionData::~DefinitionData() = default; 0045 0046 DefinitionData *DefinitionData::get(const Definition &def) 0047 { 0048 return def.d.get(); 0049 } 0050 0051 Definition::Definition() 0052 : d(new DefinitionData) 0053 { 0054 d->q = *this; 0055 } 0056 0057 Definition::Definition(Definition &&other) noexcept = default; 0058 Definition::Definition(const Definition &) = default; 0059 Definition::~Definition() = default; 0060 Definition &Definition::operator=(Definition &&other) noexcept = default; 0061 Definition &Definition::operator=(const Definition &) = default; 0062 0063 Definition::Definition(std::shared_ptr<DefinitionData> &&dd) 0064 : d(std::move(dd)) 0065 { 0066 } 0067 0068 bool Definition::operator==(const Definition &other) const 0069 { 0070 return d->fileName == other.d->fileName; 0071 } 0072 0073 bool Definition::operator!=(const Definition &other) const 0074 { 0075 return d->fileName != other.d->fileName; 0076 } 0077 0078 bool Definition::isValid() const 0079 { 0080 return d->repo && !d->fileName.isEmpty() && !d->name.isEmpty(); 0081 } 0082 0083 QString Definition::filePath() const 0084 { 0085 return d->fileName; 0086 } 0087 0088 QString Definition::name() const 0089 { 0090 return d->name; 0091 } 0092 0093 QString Definition::translatedName() const 0094 { 0095 return QCoreApplication::instance()->translate("Language", d->name.toUtf8().constData()); 0096 } 0097 0098 QString Definition::section() const 0099 { 0100 return d->section; 0101 } 0102 0103 QString Definition::translatedSection() const 0104 { 0105 return QCoreApplication::instance()->translate("Language Section", d->section.toUtf8().constData()); 0106 } 0107 0108 QVector<QString> Definition::mimeTypes() const 0109 { 0110 return d->mimetypes; 0111 } 0112 0113 QVector<QString> Definition::extensions() const 0114 { 0115 return d->extensions; 0116 } 0117 0118 int Definition::version() const 0119 { 0120 return d->version; 0121 } 0122 0123 int Definition::priority() const 0124 { 0125 return d->priority; 0126 } 0127 0128 bool Definition::isHidden() const 0129 { 0130 return d->hidden; 0131 } 0132 0133 QString Definition::style() const 0134 { 0135 return d->style; 0136 } 0137 0138 QString Definition::indenter() const 0139 { 0140 return d->indenter; 0141 } 0142 0143 QString Definition::author() const 0144 { 0145 return d->author; 0146 } 0147 0148 QString Definition::license() const 0149 { 0150 return d->license; 0151 } 0152 0153 bool Definition::isWordDelimiter(QChar c) const 0154 { 0155 d->load(); 0156 return d->wordDelimiters.contains(c); 0157 } 0158 0159 bool Definition::isWordWrapDelimiter(QChar c) const 0160 { 0161 d->load(); 0162 return d->wordWrapDelimiters.contains(c); 0163 } 0164 0165 bool Definition::foldingEnabled() const 0166 { 0167 d->load(); 0168 if (d->hasFoldingRegions || indentationBasedFoldingEnabled()) { 0169 return true; 0170 } 0171 0172 // check included definitions 0173 const auto defs = includedDefinitions(); 0174 for (const auto &def : defs) { 0175 if (def.foldingEnabled()) { 0176 d->hasFoldingRegions = true; 0177 break; 0178 } 0179 } 0180 0181 return d->hasFoldingRegions; 0182 } 0183 0184 bool Definition::indentationBasedFoldingEnabled() const 0185 { 0186 d->load(); 0187 return d->indentationBasedFolding; 0188 } 0189 0190 QStringList Definition::foldingIgnoreList() const 0191 { 0192 d->load(); 0193 return d->foldingIgnoreList; 0194 } 0195 0196 QStringList Definition::keywordLists() const 0197 { 0198 d->load(DefinitionData::OnlyKeywords(true)); 0199 return d->keywordLists.keys(); 0200 } 0201 0202 QStringList Definition::keywordList(const QString &name) const 0203 { 0204 d->load(DefinitionData::OnlyKeywords(true)); 0205 const auto list = d->keywordList(name); 0206 return list ? list->keywords() : QStringList(); 0207 } 0208 0209 bool Definition::setKeywordList(const QString &name, const QStringList &content) 0210 { 0211 d->load(DefinitionData::OnlyKeywords(true)); 0212 KeywordList *list = d->keywordList(name); 0213 if (list) { 0214 list->setKeywordList(content); 0215 return true; 0216 } else { 0217 return false; 0218 } 0219 } 0220 0221 QVector<Format> Definition::formats() const 0222 { 0223 d->load(); 0224 0225 // sort formats so that the order matches the order of the itemDatas in the xml files. 0226 auto formatList = QVector<Format>::fromList(d->formats.values()); 0227 std::sort(formatList.begin(), formatList.end(), [](const KSyntaxHighlighting::Format &lhs, const KSyntaxHighlighting::Format &rhs) { 0228 return lhs.id() < rhs.id(); 0229 }); 0230 0231 return formatList; 0232 } 0233 0234 QVector<Definition> Definition::includedDefinitions() const 0235 { 0236 d->load(); 0237 0238 // init worklist and result used as guard with this definition 0239 QVector<const DefinitionData *> queue{d.get()}; 0240 QVector<Definition> definitions{*this}; 0241 while (!queue.empty()) { 0242 const auto *def = queue.back(); 0243 queue.pop_back(); 0244 for (const auto &defRef : std::as_const(def->immediateIncludedDefinitions)) { 0245 const auto definition = defRef.definition(); 0246 if (!definitions.contains(definition)) { 0247 definitions.push_back(definition); 0248 queue.push_back(definition.d.get()); 0249 } 0250 } 0251 } 0252 0253 // remove the 1st entry, since it is this Definition 0254 definitions.front() = std::move(definitions.back()); 0255 definitions.pop_back(); 0256 0257 return definitions; 0258 } 0259 0260 QString Definition::singleLineCommentMarker() const 0261 { 0262 d->load(); 0263 return d->singleLineCommentMarker; 0264 } 0265 0266 CommentPosition Definition::singleLineCommentPosition() const 0267 { 0268 d->load(); 0269 return d->singleLineCommentPosition; 0270 } 0271 0272 QPair<QString, QString> Definition::multiLineCommentMarker() const 0273 { 0274 d->load(); 0275 return {d->multiLineCommentStartMarker, d->multiLineCommentEndMarker}; 0276 } 0277 0278 QVector<QPair<QChar, QString>> Definition::characterEncodings() const 0279 { 0280 d->load(); 0281 return d->characterEncodings; 0282 } 0283 0284 Context *DefinitionData::initialContext() 0285 { 0286 Q_ASSERT(!contexts.empty()); 0287 return &contexts.front(); 0288 } 0289 0290 Context *DefinitionData::contextByName(QStringView wantedName) 0291 { 0292 for (auto &context : contexts) { 0293 if (context.name() == wantedName) { 0294 return &context; 0295 } 0296 } 0297 return nullptr; 0298 } 0299 0300 KeywordList *DefinitionData::keywordList(const QString &wantedName) 0301 { 0302 auto it = keywordLists.find(wantedName); 0303 return (it == keywordLists.end()) ? nullptr : &it.value(); 0304 } 0305 0306 Format DefinitionData::formatByName(const QString &wantedName) const 0307 { 0308 const auto it = formats.constFind(wantedName); 0309 if (it != formats.constEnd()) { 0310 return it.value(); 0311 } 0312 0313 return Format(); 0314 } 0315 0316 bool DefinitionData::isLoaded() const 0317 { 0318 return !contexts.empty(); 0319 } 0320 0321 namespace 0322 { 0323 std::atomic<uint64_t> definitionId{1}; 0324 } 0325 0326 bool DefinitionData::load(OnlyKeywords onlyKeywords) 0327 { 0328 if (fileName.isEmpty()) { 0329 return false; 0330 } 0331 0332 if (isLoaded()) { 0333 return true; 0334 } 0335 0336 if (bool(onlyKeywords) && keywordIsLoaded) { 0337 return true; 0338 } 0339 0340 QFile file(fileName); 0341 if (!file.open(QFile::ReadOnly)) { 0342 return false; 0343 } 0344 0345 QXmlStreamReader reader(&file); 0346 while (!reader.atEnd()) { 0347 const auto token = reader.readNext(); 0348 if (token != QXmlStreamReader::StartElement) { 0349 continue; 0350 } 0351 0352 if (reader.name() == QLatin1String("highlighting")) { 0353 loadHighlighting(reader, onlyKeywords); 0354 if (bool(onlyKeywords)) { 0355 return true; 0356 } 0357 } 0358 0359 else if (reader.name() == QLatin1String("general")) { 0360 loadGeneral(reader); 0361 } 0362 } 0363 0364 for (auto it = keywordLists.begin(); it != keywordLists.end(); ++it) { 0365 it->setCaseSensitivity(caseSensitive); 0366 } 0367 0368 resolveContexts(); 0369 0370 id = definitionId.fetch_add(1, std::memory_order_relaxed); 0371 0372 return true; 0373 } 0374 0375 void DefinitionData::clear() 0376 { 0377 // keep only name and repo, so we can re-lookup to make references persist over repo reloads 0378 id = 0; 0379 keywordLists.clear(); 0380 contexts.clear(); 0381 formats.clear(); 0382 contextDatas.clear(); 0383 immediateIncludedDefinitions.clear(); 0384 wordDelimiters = WordDelimiters(); 0385 wordWrapDelimiters = wordDelimiters; 0386 keywordIsLoaded = false; 0387 hasFoldingRegions = false; 0388 indentationBasedFolding = false; 0389 foldingIgnoreList.clear(); 0390 singleLineCommentMarker.clear(); 0391 singleLineCommentPosition = CommentPosition::StartOfLine; 0392 multiLineCommentStartMarker.clear(); 0393 multiLineCommentEndMarker.clear(); 0394 characterEncodings.clear(); 0395 0396 fileName.clear(); 0397 section.clear(); 0398 style.clear(); 0399 indenter.clear(); 0400 author.clear(); 0401 license.clear(); 0402 mimetypes.clear(); 0403 extensions.clear(); 0404 caseSensitive = Qt::CaseSensitive; 0405 version = 0.0f; 0406 priority = 0; 0407 hidden = false; 0408 } 0409 0410 bool DefinitionData::loadMetaData(const QString &definitionFileName) 0411 { 0412 fileName = definitionFileName; 0413 0414 QFile file(definitionFileName); 0415 if (!file.open(QFile::ReadOnly)) { 0416 return false; 0417 } 0418 0419 QXmlStreamReader reader(&file); 0420 while (!reader.atEnd()) { 0421 const auto token = reader.readNext(); 0422 if (token != QXmlStreamReader::StartElement) { 0423 continue; 0424 } 0425 if (reader.name() == QLatin1String("language")) { 0426 return loadLanguage(reader); 0427 } 0428 } 0429 0430 return false; 0431 } 0432 0433 bool DefinitionData::loadMetaData(const QString &file, const QCborMap &obj) 0434 { 0435 name = obj.value(QLatin1String("name")).toString(); 0436 section = obj.value(QLatin1String("section")).toString(); 0437 version = obj.value(QLatin1String("version")).toInteger(); 0438 priority = obj.value(QLatin1String("priority")).toInteger(); 0439 style = obj.value(QLatin1String("style")).toString(); 0440 author = obj.value(QLatin1String("author")).toString(); 0441 license = obj.value(QLatin1String("license")).toString(); 0442 indenter = obj.value(QLatin1String("indenter")).toString(); 0443 hidden = obj.value(QLatin1String("hidden")).toBool(); 0444 fileName = file; 0445 0446 const auto exts = obj.value(QLatin1String("extensions")).toString(); 0447 for (const auto &ext : exts.split(QLatin1Char(';'), Qt::SkipEmptyParts)) { 0448 extensions.push_back(ext); 0449 } 0450 const auto mts = obj.value(QLatin1String("mimetype")).toString(); 0451 for (const auto &mt : mts.split(QLatin1Char(';'), Qt::SkipEmptyParts)) { 0452 mimetypes.push_back(mt); 0453 } 0454 0455 return true; 0456 } 0457 0458 bool DefinitionData::loadLanguage(QXmlStreamReader &reader) 0459 { 0460 Q_ASSERT(reader.name() == QLatin1String("language")); 0461 Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement); 0462 0463 if (!checkKateVersion(reader.attributes().value(QLatin1String("kateversion")))) { 0464 return false; 0465 } 0466 0467 name = reader.attributes().value(QLatin1String("name")).toString(); 0468 section = reader.attributes().value(QLatin1String("section")).toString(); 0469 // toFloat instead of toInt for backward compatibility with old Kate files 0470 version = reader.attributes().value(QLatin1String("version")).toFloat(); 0471 priority = reader.attributes().value(QLatin1String("priority")).toInt(); 0472 hidden = Xml::attrToBool(reader.attributes().value(QLatin1String("hidden"))); 0473 style = reader.attributes().value(QLatin1String("style")).toString(); 0474 indenter = reader.attributes().value(QLatin1String("indenter")).toString(); 0475 author = reader.attributes().value(QLatin1String("author")).toString(); 0476 license = reader.attributes().value(QLatin1String("license")).toString(); 0477 const auto exts = reader.attributes().value(QLatin1String("extensions")); 0478 for (const auto &ext : exts.split(QLatin1Char(';'), Qt::SkipEmptyParts)) { 0479 extensions.push_back(ext.toString()); 0480 } 0481 const auto mts = reader.attributes().value(QLatin1String("mimetype")); 0482 for (const auto &mt : mts.split(QLatin1Char(';'), Qt::SkipEmptyParts)) { 0483 mimetypes.push_back(mt.toString()); 0484 } 0485 if (reader.attributes().hasAttribute(QLatin1String("casesensitive"))) { 0486 caseSensitive = Xml::attrToBool(reader.attributes().value(QLatin1String("casesensitive"))) ? Qt::CaseSensitive : Qt::CaseInsensitive; 0487 } 0488 return true; 0489 } 0490 0491 void DefinitionData::loadHighlighting(QXmlStreamReader &reader, OnlyKeywords onlyKeywords) 0492 { 0493 Q_ASSERT(reader.name() == QLatin1String("highlighting")); 0494 Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement); 0495 0496 // skip highlighting 0497 reader.readNext(); 0498 0499 while (!reader.atEnd()) { 0500 switch (reader.tokenType()) { 0501 case QXmlStreamReader::StartElement: 0502 if (reader.name() == QLatin1String("list")) { 0503 if (!keywordIsLoaded) { 0504 KeywordList keywords; 0505 keywords.load(reader); 0506 keywordLists.insert(keywords.name(), keywords); 0507 } else { 0508 reader.skipCurrentElement(); 0509 reader.readNext(); // Skip </list> 0510 } 0511 } else if (bool(onlyKeywords)) { 0512 resolveIncludeKeywords(); 0513 return; 0514 } else if (reader.name() == QLatin1String("contexts")) { 0515 resolveIncludeKeywords(); 0516 loadContexts(reader); 0517 reader.readNext(); 0518 } else if (reader.name() == QLatin1String("itemDatas")) { 0519 loadItemData(reader); 0520 } else { 0521 reader.readNext(); 0522 } 0523 break; 0524 case QXmlStreamReader::EndElement: 0525 return; 0526 default: 0527 reader.readNext(); 0528 break; 0529 } 0530 } 0531 } 0532 0533 void DefinitionData::resolveIncludeKeywords() 0534 { 0535 if (keywordIsLoaded) { 0536 return; 0537 } 0538 0539 keywordIsLoaded = true; 0540 0541 for (auto it = keywordLists.begin(); it != keywordLists.end(); ++it) { 0542 it->resolveIncludeKeywords(*this); 0543 } 0544 } 0545 0546 void DefinitionData::loadContexts(QXmlStreamReader &reader) 0547 { 0548 Q_ASSERT(reader.name() == QLatin1String("contexts")); 0549 Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement); 0550 0551 contextDatas.reserve(32); 0552 0553 while (!reader.atEnd()) { 0554 switch (reader.tokenType()) { 0555 case QXmlStreamReader::StartElement: 0556 if (reader.name() == QLatin1String("context")) { 0557 contextDatas.push_back(HighlightingContextData()); 0558 contextDatas.back().load(name, reader); 0559 } 0560 reader.readNext(); 0561 break; 0562 case QXmlStreamReader::EndElement: 0563 return; 0564 default: 0565 reader.readNext(); 0566 break; 0567 } 0568 } 0569 } 0570 0571 void DefinitionData::resolveContexts() 0572 { 0573 contexts.reserve(contextDatas.size()); 0574 0575 /** 0576 * Transform all HighlightingContextData to Context. 0577 * This is necessary so that Context::resolveContexts() can find the referenced contexts. 0578 */ 0579 for (const auto &contextData : std::as_const(contextDatas)) { 0580 contexts.emplace_back(*this, contextData); 0581 } 0582 0583 /** 0584 * Resolves contexts and rules. 0585 */ 0586 auto ctxIt = contexts.begin(); 0587 for (const auto &contextData : std::as_const(contextDatas)) { 0588 ctxIt->resolveContexts(*this, contextData); 0589 ++ctxIt; 0590 } 0591 0592 /** 0593 * To free the memory, constDatas is emptied because it is no longer used. 0594 */ 0595 contextDatas.clear(); 0596 contextDatas.shrink_to_fit(); 0597 0598 /** 0599 * Resolved includeRules. 0600 */ 0601 for (auto &context : contexts) { 0602 context.resolveIncludes(*this); 0603 } 0604 } 0605 0606 void DefinitionData::loadItemData(QXmlStreamReader &reader) 0607 { 0608 Q_ASSERT(reader.name() == QLatin1String("itemDatas")); 0609 Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement); 0610 0611 while (!reader.atEnd()) { 0612 switch (reader.tokenType()) { 0613 case QXmlStreamReader::StartElement: 0614 if (reader.name() == QLatin1String("itemData")) { 0615 Format f; 0616 auto formatData = FormatPrivate::detachAndGet(f); 0617 formatData->definitionName = name; 0618 formatData->load(reader); 0619 formatData->id = RepositoryPrivate::get(repo)->nextFormatId(); 0620 formats.insert(f.name(), f); 0621 reader.readNext(); 0622 } 0623 reader.readNext(); 0624 break; 0625 case QXmlStreamReader::EndElement: 0626 return; 0627 default: 0628 reader.readNext(); 0629 break; 0630 } 0631 } 0632 } 0633 0634 void DefinitionData::loadGeneral(QXmlStreamReader &reader) 0635 { 0636 Q_ASSERT(reader.name() == QLatin1String("general")); 0637 Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement); 0638 reader.readNext(); 0639 0640 // reference counter to count XML child elements, to not return too early 0641 int elementRefCounter = 1; 0642 0643 while (!reader.atEnd()) { 0644 switch (reader.tokenType()) { 0645 case QXmlStreamReader::StartElement: 0646 ++elementRefCounter; 0647 0648 if (reader.name() == QLatin1String("keywords")) { 0649 if (reader.attributes().hasAttribute(QLatin1String("casesensitive"))) { 0650 caseSensitive = Xml::attrToBool(reader.attributes().value(QLatin1String("casesensitive"))) ? Qt::CaseSensitive : Qt::CaseInsensitive; 0651 } 0652 0653 // adapt wordDelimiters 0654 wordDelimiters.append(reader.attributes().value(QLatin1String("additionalDeliminator"))); 0655 wordDelimiters.remove(reader.attributes().value(QLatin1String("weakDeliminator"))); 0656 0657 // adapt WordWrapDelimiters 0658 auto wordWrapDeliminatorAttr = reader.attributes().value(QLatin1String("wordWrapDeliminator")); 0659 if (wordWrapDeliminatorAttr.isEmpty()) { 0660 wordWrapDelimiters = wordDelimiters; 0661 } else { 0662 wordWrapDelimiters.append(wordWrapDeliminatorAttr); 0663 } 0664 } else if (reader.name() == QLatin1String("folding")) { 0665 if (reader.attributes().hasAttribute(QLatin1String("indentationsensitive"))) { 0666 indentationBasedFolding = Xml::attrToBool(reader.attributes().value(QLatin1String("indentationsensitive"))); 0667 } 0668 } else if (reader.name() == QLatin1String("emptyLines")) { 0669 loadFoldingIgnoreList(reader); 0670 } else if (reader.name() == QLatin1String("comments")) { 0671 loadComments(reader); 0672 } else if (reader.name() == QLatin1String("spellchecking")) { 0673 loadSpellchecking(reader); 0674 } else { 0675 reader.skipCurrentElement(); 0676 } 0677 reader.readNext(); 0678 break; 0679 case QXmlStreamReader::EndElement: 0680 --elementRefCounter; 0681 if (elementRefCounter == 0) { 0682 return; 0683 } 0684 reader.readNext(); 0685 break; 0686 default: 0687 reader.readNext(); 0688 break; 0689 } 0690 } 0691 } 0692 0693 void DefinitionData::loadComments(QXmlStreamReader &reader) 0694 { 0695 Q_ASSERT(reader.name() == QLatin1String("comments")); 0696 Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement); 0697 reader.readNext(); 0698 0699 // reference counter to count XML child elements, to not return too early 0700 int elementRefCounter = 1; 0701 0702 while (!reader.atEnd()) { 0703 switch (reader.tokenType()) { 0704 case QXmlStreamReader::StartElement: 0705 ++elementRefCounter; 0706 if (reader.name() == QLatin1String("comment")) { 0707 const bool isSingleLine = reader.attributes().value(QLatin1String("name")) == QLatin1String("singleLine"); 0708 if (isSingleLine) { 0709 singleLineCommentMarker = reader.attributes().value(QLatin1String("start")).toString(); 0710 const bool afterWhiteSpace = reader.attributes().value(QLatin1String("position")) == QLatin1String("afterwhitespace"); 0711 singleLineCommentPosition = afterWhiteSpace ? CommentPosition::AfterWhitespace : CommentPosition::StartOfLine; 0712 } else { 0713 multiLineCommentStartMarker = reader.attributes().value(QLatin1String("start")).toString(); 0714 multiLineCommentEndMarker = reader.attributes().value(QLatin1String("end")).toString(); 0715 } 0716 } 0717 reader.readNext(); 0718 break; 0719 case QXmlStreamReader::EndElement: 0720 --elementRefCounter; 0721 if (elementRefCounter == 0) { 0722 return; 0723 } 0724 reader.readNext(); 0725 break; 0726 default: 0727 reader.readNext(); 0728 break; 0729 } 0730 } 0731 } 0732 0733 void DefinitionData::loadFoldingIgnoreList(QXmlStreamReader &reader) 0734 { 0735 Q_ASSERT(reader.name() == QLatin1String("emptyLines")); 0736 Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement); 0737 reader.readNext(); 0738 0739 // reference counter to count XML child elements, to not return too early 0740 int elementRefCounter = 1; 0741 0742 while (!reader.atEnd()) { 0743 switch (reader.tokenType()) { 0744 case QXmlStreamReader::StartElement: 0745 ++elementRefCounter; 0746 if (reader.name() == QLatin1String("emptyLine")) { 0747 foldingIgnoreList << reader.attributes().value(QLatin1String("regexpr")).toString(); 0748 } 0749 reader.readNext(); 0750 break; 0751 case QXmlStreamReader::EndElement: 0752 --elementRefCounter; 0753 if (elementRefCounter == 0) { 0754 return; 0755 } 0756 reader.readNext(); 0757 break; 0758 default: 0759 reader.readNext(); 0760 break; 0761 } 0762 } 0763 } 0764 0765 void DefinitionData::loadSpellchecking(QXmlStreamReader &reader) 0766 { 0767 Q_ASSERT(reader.name() == QLatin1String("spellchecking")); 0768 Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement); 0769 reader.readNext(); 0770 0771 // reference counter to count XML child elements, to not return too early 0772 int elementRefCounter = 1; 0773 0774 while (!reader.atEnd()) { 0775 switch (reader.tokenType()) { 0776 case QXmlStreamReader::StartElement: 0777 ++elementRefCounter; 0778 if (reader.name() == QLatin1String("encoding")) { 0779 const auto charRef = reader.attributes().value(QLatin1String("char")); 0780 if (!charRef.isEmpty()) { 0781 const auto str = reader.attributes().value(QLatin1String("string")); 0782 characterEncodings.push_back({charRef[0], str.toString()}); 0783 } 0784 } 0785 reader.readNext(); 0786 break; 0787 case QXmlStreamReader::EndElement: 0788 --elementRefCounter; 0789 if (elementRefCounter == 0) { 0790 return; 0791 } 0792 reader.readNext(); 0793 break; 0794 default: 0795 reader.readNext(); 0796 break; 0797 } 0798 } 0799 } 0800 0801 bool DefinitionData::checkKateVersion(QStringView verStr) 0802 { 0803 const auto idx = verStr.indexOf(QLatin1Char('.')); 0804 if (idx <= 0) { 0805 qCWarning(Log) << "Skipping" << fileName << "due to having no valid kateversion attribute:" << verStr; 0806 return false; 0807 } 0808 const auto major = verStr.left(idx).toString().toInt(); 0809 const auto minor = verStr.mid(idx + 1).toString().toInt(); 0810 0811 if (major > SyntaxHighlighting_VERSION_MAJOR || (major == SyntaxHighlighting_VERSION_MAJOR && minor > SyntaxHighlighting_VERSION_MINOR)) { 0812 qCWarning(Log) << "Skipping" << fileName << "due to being too new, version:" << verStr; 0813 return false; 0814 } 0815 0816 return true; 0817 } 0818 0819 quint16 DefinitionData::foldingRegionId(const QString &foldName) 0820 { 0821 hasFoldingRegions = true; 0822 return RepositoryPrivate::get(repo)->foldingRegionId(name, foldName); 0823 } 0824 0825 void DefinitionData::addImmediateIncludedDefinition(const Definition &def) 0826 { 0827 if (get(def) != this) { 0828 DefinitionRef defRef(def); 0829 if (!immediateIncludedDefinitions.contains(defRef)) { 0830 immediateIncludedDefinitions.push_back(std::move(defRef)); 0831 } 0832 } 0833 } 0834 0835 DefinitionRef::DefinitionRef() = default; 0836 0837 DefinitionRef::DefinitionRef(const Definition &def) 0838 : d(def.d) 0839 { 0840 } 0841 0842 DefinitionRef::DefinitionRef(Definition &&def) 0843 : d(std::move(def.d)) 0844 { 0845 } 0846 0847 DefinitionRef &DefinitionRef::operator=(const Definition &def) 0848 { 0849 d = def.d; 0850 return *this; 0851 } 0852 0853 DefinitionRef &DefinitionRef::operator=(Definition &&def) 0854 { 0855 d = std::move(def.d); 0856 return *this; 0857 } 0858 0859 Definition DefinitionRef::definition() const 0860 { 0861 if (!d.expired()) { 0862 return Definition(d.lock()); 0863 } 0864 return Definition(); 0865 } 0866 0867 bool DefinitionRef::operator==(const DefinitionRef &other) const 0868 { 0869 return !d.owner_before(other.d) && !other.d.owner_before(d); 0870 } 0871 0872 bool DefinitionRef::operator==(const Definition &other) const 0873 { 0874 return !d.owner_before(other.d) && !other.d.owner_before(d); 0875 } 0876 0877 #include "moc_definition.cpp"