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"