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