1/*
2 SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
3 SPDX-FileCopyrightText: 2018 Dominik Haumann <dhaumann@kde.org>
4 SPDX-FileCopyrightText: 2018 Christoph Cullmann <cullmann@kde.org>
5 SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
6
7 SPDX-License-Identifier: MIT
8*/
9
10#include "definition.h"
11#include "definition_p.h"
12#include "definitionref_p.h"
13
14#include "context_p.h"
15#include "format.h"
16#include "format_p.h"
17#include "highlightingdata_p.hpp"
18#include "ksyntaxhighlighting_logging.h"
19#include "ksyntaxhighlighting_version.h"
20#include "repository.h"
21#include "repository_p.h"
22#include "rule_p.h"
23#include "worddelimiters_p.h"
24#include "xml_p.h"
25
26#include <QCborMap>
27#include <QCoreApplication>
28#include <QFile>
29#include <QHash>
30#include <QStringList>
31#include <QXmlStreamReader>
32
33#include <algorithm>
34#include <atomic>
35
36using namespace KSyntaxHighlighting;
37
38DefinitionData::DefinitionData()
39 : wordDelimiters()
40 , wordWrapDelimiters(wordDelimiters)
41{
42}
43
44DefinitionData::~DefinitionData() = default;
45
46Definition::Definition()
47 : d(new DefinitionData)
48{
49 d->q = *this;
50}
51
52Definition::Definition(Definition &&other) noexcept = default;
53Definition::Definition(const Definition &) = default;
54Definition::~Definition() = default;
55Definition &Definition::operator=(Definition &&other) noexcept = default;
56Definition &Definition::operator=(const Definition &) = default;
57
58Definition::Definition(std::shared_ptr<DefinitionData> &&dd)
59 : d(std::move(dd))
60{
61}
62
63bool Definition::operator==(const Definition &other) const
64{
65 return d->fileName == other.d->fileName;
66}
67
68bool Definition::operator!=(const Definition &other) const
69{
70 return d->fileName != other.d->fileName;
71}
72
73bool Definition::isValid() const
74{
75 return d->repo && !d->fileName.isEmpty() && !d->name.isEmpty();
76}
77
78QString Definition::filePath() const
79{
80 return d->fileName;
81}
82
83QString Definition::name() const
84{
85 return d->name;
86}
87
88QString Definition::translatedName() const
89{
90 if (d->translatedName.isEmpty()) {
91 d->translatedName = QCoreApplication::instance()->translate("Language", d->nameUtf8.isEmpty() ? d->name.toUtf8().constData() : d->nameUtf8.constData());
92 }
93 return d->translatedName;
94}
95
96QString Definition::section() const
97{
98 return d->section;
99}
100
101QString Definition::translatedSection() const
102{
103 if (d->translatedSection.isEmpty()) {
104 d->translatedSection = QCoreApplication::instance()->translate("Language Section",
105 d->sectionUtf8.isEmpty() ? d->section.toUtf8().constData() : d->sectionUtf8.constData());
106 }
107 return d->translatedSection;
108}
109
110QList<QString> Definition::mimeTypes() const
111{
112 return d->mimetypes;
113}
114
115QList<QString> Definition::extensions() const
116{
117 return d->extensions;
118}
119
120int Definition::version() const
121{
122 return d->version;
123}
124
125int Definition::priority() const
126{
127 return d->priority;
128}
129
130bool Definition::isHidden() const
131{
132 return d->hidden;
133}
134
135QString Definition::style() const
136{
137 return d->style;
138}
139
140QString Definition::indenter() const
141{
142 return d->indenter;
143}
144
145QString Definition::author() const
146{
147 return d->author;
148}
149
150QString Definition::license() const
151{
152 return d->license;
153}
154
155bool Definition::isWordDelimiter(QChar c) const
156{
157 d->load();
158 return d->wordDelimiters.contains(c);
159}
160
161bool Definition::isWordWrapDelimiter(QChar c) const
162{
163 d->load();
164 return d->wordWrapDelimiters.contains(c);
165}
166
167bool Definition::foldingEnabled() const
168{
169 d->load();
170 if (d->hasFoldingRegions || indentationBasedFoldingEnabled()) {
171 return true;
172 }
173
174 // check included definitions
175 const auto defs = includedDefinitions();
176 for (const auto &def : defs) {
177 if (def.foldingEnabled()) {
178 d->hasFoldingRegions = true;
179 break;
180 }
181 }
182
183 return d->hasFoldingRegions;
184}
185
186bool Definition::indentationBasedFoldingEnabled() const
187{
188 d->load();
189 return d->indentationBasedFolding;
190}
191
192QStringList Definition::foldingIgnoreList() const
193{
194 d->load();
195 return d->foldingIgnoreList;
196}
197
198QStringList Definition::keywordLists() const
199{
200 d->load(onlyKeywords: DefinitionData::OnlyKeywords(true));
201 return d->keywordLists.keys();
202}
203
204QStringList Definition::keywordList(const QString &name) const
205{
206 d->load(onlyKeywords: DefinitionData::OnlyKeywords(true));
207 const auto list = d->keywordList(name);
208 return list ? list->keywords() : QStringList();
209}
210
211bool Definition::setKeywordList(const QString &name, const QStringList &content)
212{
213 d->load(onlyKeywords: DefinitionData::OnlyKeywords(true));
214 KeywordList *list = d->keywordList(name);
215 if (list) {
216 list->setKeywordList(content);
217 return true;
218 } else {
219 return false;
220 }
221}
222
223QList<Format> Definition::formats() const
224{
225 d->load();
226
227 // sort formats so that the order matches the order of the itemDatas in the xml files.
228 auto formatList = QList<Format>::fromList(d->formats.values());
229 std::sort(formatList.begin(), formatList.end(), [](const KSyntaxHighlighting::Format &lhs, const KSyntaxHighlighting::Format &rhs) {
230 return lhs.id() < rhs.id();
231 });
232
233 return formatList;
234}
235
236QList<Definition> Definition::includedDefinitions() const
237{
238 d->load();
239
240 // init worklist and result used as guard with this definition
241 QList<const DefinitionData *> queue{d.get()};
242 QList<Definition> definitions{*this};
243 while (!queue.empty()) {
244 const auto *def = queue.back();
245 queue.pop_back();
246 for (const auto &defRef : std::as_const(def->immediateIncludedDefinitions)) {
247 const auto definition = defRef.definition();
248 if (!definitions.contains(definition)) {
249 definitions.push_back(definition);
250 queue.push_back(definition.d.get());
251 }
252 }
253 }
254
255 // remove the 1st entry, since it is this Definition
256 definitions.front() = std::move(definitions.back());
257 definitions.pop_back();
258
259 return definitions;
260}
261
262QString Definition::singleLineCommentMarker() const
263{
264 d->load();
265 return d->singleLineCommentMarker;
266}
267
268CommentPosition Definition::singleLineCommentPosition() const
269{
270 d->load();
271 return d->singleLineCommentPosition;
272}
273
274QPair<QString, QString> Definition::multiLineCommentMarker() const
275{
276 d->load();
277 return {d->multiLineCommentStartMarker, d->multiLineCommentEndMarker};
278}
279
280QList<QPair<QChar, QString>> Definition::characterEncodings() const
281{
282 d->load();
283 return d->characterEncodings;
284}
285
286Context *DefinitionData::initialContext()
287{
288 Q_ASSERT(!contexts.empty());
289 return &contexts.front();
290}
291
292Context *DefinitionData::contextByName(QStringView wantedName)
293{
294 for (auto &context : contexts) {
295 if (context.name() == wantedName) {
296 return &context;
297 }
298 }
299 return nullptr;
300}
301
302KeywordList *DefinitionData::keywordList(const QString &wantedName)
303{
304 auto it = keywordLists.find(wantedName);
305 return (it == keywordLists.end()) ? nullptr : &it.value();
306}
307
308Format DefinitionData::formatByName(const QString &wantedName) const
309{
310 const auto it = formats.constFind(wantedName);
311 if (it != formats.constEnd()) {
312 return it.value();
313 }
314
315 return Format();
316}
317
318bool DefinitionData::isLoaded() const
319{
320 return !contexts.empty();
321}
322
323namespace
324{
325std::atomic<uint64_t> definitionId{1};
326}
327
328bool DefinitionData::load(OnlyKeywords onlyKeywords)
329{
330 if (fileName.isEmpty()) {
331 return false;
332 }
333
334 if (isLoaded()) {
335 return true;
336 }
337
338 if (bool(onlyKeywords) && keywordIsLoaded) {
339 return true;
340 }
341
342 QFile file(fileName);
343 if (!file.open(QFile::ReadOnly)) {
344 return false;
345 }
346
347 QXmlStreamReader reader(&file);
348 while (!reader.atEnd()) {
349 const auto token = reader.readNext();
350 if (token != QXmlStreamReader::StartElement) {
351 continue;
352 }
353
354 if (reader.name() == QLatin1String("highlighting")) {
355 loadHighlighting(reader&: reader, onlyKeywords);
356 if (bool(onlyKeywords)) {
357 return true;
358 }
359 }
360
361 else if (reader.name() == QLatin1String("general")) {
362 loadGeneral(reader&: reader);
363 }
364 }
365
366 for (auto it = keywordLists.begin(); it != keywordLists.end(); ++it) {
367 it->setCaseSensitivity(caseSensitive);
368 }
369
370 resolveContexts();
371
372 id = definitionId.fetch_add(i: 1, m: std::memory_order_relaxed);
373
374 return true;
375}
376
377void DefinitionData::clear()
378{
379 // keep only name and repo, so we can re-lookup to make references persist over repo reloads
380 id = 0;
381 keywordLists.clear();
382 contexts.clear();
383 formats.clear();
384 contextDatas.clear();
385 immediateIncludedDefinitions.clear();
386 wordDelimiters = WordDelimiters();
387 wordWrapDelimiters = wordDelimiters;
388 keywordIsLoaded = false;
389 hasFoldingRegions = false;
390 indentationBasedFolding = false;
391 foldingIgnoreList.clear();
392 singleLineCommentMarker.clear();
393 singleLineCommentPosition = CommentPosition::StartOfLine;
394 multiLineCommentStartMarker.clear();
395 multiLineCommentEndMarker.clear();
396 characterEncodings.clear();
397
398 fileName.clear();
399 nameUtf8.clear();
400 translatedName.clear();
401 section.clear();
402 sectionUtf8.clear();
403 translatedSection.clear();
404 style.clear();
405 indenter.clear();
406 author.clear();
407 license.clear();
408 mimetypes.clear();
409 extensions.clear();
410 caseSensitive = Qt::CaseSensitive;
411 version = 0.0f;
412 priority = 0;
413 hidden = false;
414
415 // purge our cache that is used to unify states
416 unify.clear();
417}
418
419bool DefinitionData::loadMetaData(const QString &definitionFileName)
420{
421 fileName = definitionFileName;
422
423 QFile file(definitionFileName);
424 if (!file.open(QFile::ReadOnly)) {
425 return false;
426 }
427
428 QXmlStreamReader reader(&file);
429 while (!reader.atEnd()) {
430 const auto token = reader.readNext();
431 if (token != QXmlStreamReader::StartElement) {
432 continue;
433 }
434 if (reader.name() == QLatin1String("language")) {
435 return loadLanguage(reader&: reader);
436 }
437 }
438
439 return false;
440}
441
442bool DefinitionData::loadMetaData(const QString &file, const QCborMap &obj)
443{
444 name = obj.value(QLatin1String("name")).toString();
445 nameUtf8 = obj.value(QLatin1String("name")).toByteArray();
446 section = obj.value(QLatin1String("section")).toString();
447 sectionUtf8 = obj.value(QLatin1String("section")).toByteArray();
448 version = obj.value(QLatin1String("version")).toInteger();
449 priority = obj.value(QLatin1String("priority")).toInteger();
450 style = obj.value(QLatin1String("style")).toString();
451 author = obj.value(QLatin1String("author")).toString();
452 license = obj.value(QLatin1String("license")).toString();
453 indenter = obj.value(QLatin1String("indenter")).toString();
454 hidden = obj.value(QLatin1String("hidden")).toBool();
455 fileName = file;
456
457 const auto exts = obj.value(QLatin1String("extensions")).toString();
458 extensions = exts.split(QLatin1Char(';'), Qt::SkipEmptyParts);
459
460 const auto mts = obj.value(QLatin1String("mimetype")).toString();
461 mimetypes = mts.split(QLatin1Char(';'), Qt::SkipEmptyParts);
462
463 return true;
464}
465
466bool DefinitionData::loadLanguage(QXmlStreamReader &reader)
467{
468 Q_ASSERT(reader.name() == QLatin1String("language"));
469 Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement);
470
471 if (!checkKateVersion(reader.attributes().value(QLatin1String("kateversion")))) {
472 return false;
473 }
474
475 name = reader.attributes().value(QLatin1String("name")).toString();
476 section = reader.attributes().value(QLatin1String("section")).toString();
477 // toFloat instead of toInt for backward compatibility with old Kate files
478 version = reader.attributes().value(QLatin1String("version")).toFloat();
479 priority = reader.attributes().value(QLatin1String("priority")).toInt();
480 hidden = Xml::attrToBool(reader.attributes().value(QLatin1String("hidden")));
481 style = reader.attributes().value(QLatin1String("style")).toString();
482 indenter = reader.attributes().value(QLatin1String("indenter")).toString();
483 author = reader.attributes().value(QLatin1String("author")).toString();
484 license = reader.attributes().value(QLatin1String("license")).toString();
485 const auto exts = reader.attributes().value(QLatin1String("extensions"));
486 for (const auto &ext : exts.split(QLatin1Char(';'), Qt::SkipEmptyParts)) {
487 extensions.push_back(ext.toString());
488 }
489 const auto mts = reader.attributes().value(QLatin1String("mimetype"));
490 for (const auto &mt : mts.split(QLatin1Char(';'), Qt::SkipEmptyParts)) {
491 mimetypes.push_back(mt.toString());
492 }
493 if (reader.attributes().hasAttribute(QLatin1String("casesensitive"))) {
494 caseSensitive = Xml::attrToBool(reader.attributes().value(QLatin1String("casesensitive"))) ? Qt::CaseSensitive : Qt::CaseInsensitive;
495 }
496 return true;
497}
498
499void DefinitionData::loadHighlighting(QXmlStreamReader &reader, OnlyKeywords onlyKeywords)
500{
501 Q_ASSERT(reader.name() == QLatin1String("highlighting"));
502 Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement);
503
504 // skip highlighting
505 reader.readNext();
506
507 while (!reader.atEnd()) {
508 switch (reader.tokenType()) {
509 case QXmlStreamReader::StartElement:
510 if (reader.name() == QLatin1String("list")) {
511 if (!keywordIsLoaded) {
512 KeywordList keywords;
513 keywords.load(reader);
514 keywordLists.insert(keywords.name(), keywords);
515 } else {
516 reader.skipCurrentElement();
517 reader.readNext(); // Skip </list>
518 }
519 } else if (bool(onlyKeywords)) {
520 resolveIncludeKeywords();
521 return;
522 } else if (reader.name() == QLatin1String("contexts")) {
523 resolveIncludeKeywords();
524 loadContexts(reader);
525 reader.readNext();
526 } else if (reader.name() == QLatin1String("itemDatas")) {
527 loadItemData(reader);
528 } else {
529 reader.readNext();
530 }
531 break;
532 case QXmlStreamReader::EndElement:
533 return;
534 default:
535 reader.readNext();
536 break;
537 }
538 }
539}
540
541void DefinitionData::resolveIncludeKeywords()
542{
543 if (keywordIsLoaded) {
544 return;
545 }
546
547 keywordIsLoaded = true;
548
549 for (auto it = keywordLists.begin(); it != keywordLists.end(); ++it) {
550 it->resolveIncludeKeywords(*this);
551 }
552}
553
554void DefinitionData::loadContexts(QXmlStreamReader &reader)
555{
556 Q_ASSERT(reader.name() == QLatin1String("contexts"));
557 Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement);
558
559 contextDatas.reserve(32);
560
561 while (!reader.atEnd()) {
562 switch (reader.tokenType()) {
563 case QXmlStreamReader::StartElement:
564 if (reader.name() == QLatin1String("context")) {
565 contextDatas.push_back(HighlightingContextData());
566 contextDatas.back().load(name, reader);
567 }
568 reader.readNext();
569 break;
570 case QXmlStreamReader::EndElement:
571 return;
572 default:
573 reader.readNext();
574 break;
575 }
576 }
577}
578
579void DefinitionData::resolveContexts()
580{
581 contexts.reserve(contextDatas.size());
582
583 /**
584 * Transform all HighlightingContextData to Context.
585 * This is necessary so that Context::resolveContexts() can find the referenced contexts.
586 */
587 for (const auto &contextData : std::as_const(contextDatas)) {
588 contexts.emplace_back(*this, contextData);
589 }
590
591 /**
592 * Resolves contexts and rules.
593 */
594 auto ctxIt = contexts.begin();
595 for (const auto &contextData : std::as_const(contextDatas)) {
596 ctxIt->resolveContexts(*this, contextData);
597 ++ctxIt;
598 }
599
600 /**
601 * To free the memory, constDatas is emptied because it is no longer used.
602 */
603 contextDatas.clear();
604 contextDatas.shrink_to_fit();
605
606 /**
607 * Resolved includeRules.
608 */
609 for (auto &context : contexts) {
610 context.resolveIncludes(def&: *this);
611 }
612}
613
614void DefinitionData::loadItemData(QXmlStreamReader &reader)
615{
616 Q_ASSERT(reader.name() == QLatin1String("itemDatas"));
617 Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement);
618
619 while (!reader.atEnd()) {
620 switch (reader.tokenType()) {
621 case QXmlStreamReader::StartElement:
622 if (reader.name() == QLatin1String("itemData")) {
623 Format f;
624 auto formatData = FormatPrivate::detachAndGet(format&: f);
625 formatData->definitionName = name;
626 formatData->load(reader);
627 formatData->id = RepositoryPrivate::get(repo)->nextFormatId();
628 formats.insert(f.name(), f);
629 reader.readNext();
630 }
631 reader.readNext();
632 break;
633 case QXmlStreamReader::EndElement:
634 return;
635 default:
636 reader.readNext();
637 break;
638 }
639 }
640}
641
642void DefinitionData::loadGeneral(QXmlStreamReader &reader)
643{
644 Q_ASSERT(reader.name() == QLatin1String("general"));
645 Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement);
646 reader.readNext();
647
648 // reference counter to count XML child elements, to not return too early
649 int elementRefCounter = 1;
650
651 while (!reader.atEnd()) {
652 switch (reader.tokenType()) {
653 case QXmlStreamReader::StartElement:
654 ++elementRefCounter;
655
656 if (reader.name() == QLatin1String("keywords")) {
657 if (reader.attributes().hasAttribute(QLatin1String("casesensitive"))) {
658 caseSensitive = Xml::attrToBool(reader.attributes().value(QLatin1String("casesensitive"))) ? Qt::CaseSensitive : Qt::CaseInsensitive;
659 }
660
661 // adapt wordDelimiters
662 wordDelimiters.append(reader.attributes().value(QLatin1String("additionalDeliminator")));
663 wordDelimiters.remove(reader.attributes().value(QLatin1String("weakDeliminator")));
664
665 // adapt WordWrapDelimiters
666 auto wordWrapDeliminatorAttr = reader.attributes().value(QLatin1String("wordWrapDeliminator"));
667 if (wordWrapDeliminatorAttr.isEmpty()) {
668 wordWrapDelimiters = wordDelimiters;
669 } else {
670 wordWrapDelimiters.append(wordWrapDeliminatorAttr);
671 }
672 } else if (reader.name() == QLatin1String("folding")) {
673 if (reader.attributes().hasAttribute(QLatin1String("indentationsensitive"))) {
674 indentationBasedFolding = Xml::attrToBool(reader.attributes().value(QLatin1String("indentationsensitive")));
675 }
676 } else if (reader.name() == QLatin1String("emptyLines")) {
677 loadFoldingIgnoreList(reader);
678 } else if (reader.name() == QLatin1String("comments")) {
679 loadComments(reader);
680 } else if (reader.name() == QLatin1String("spellchecking")) {
681 loadSpellchecking(reader);
682 } else {
683 reader.skipCurrentElement();
684 }
685 reader.readNext();
686 break;
687 case QXmlStreamReader::EndElement:
688 --elementRefCounter;
689 if (elementRefCounter == 0) {
690 return;
691 }
692 reader.readNext();
693 break;
694 default:
695 reader.readNext();
696 break;
697 }
698 }
699}
700
701void DefinitionData::loadComments(QXmlStreamReader &reader)
702{
703 Q_ASSERT(reader.name() == QLatin1String("comments"));
704 Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement);
705 reader.readNext();
706
707 // reference counter to count XML child elements, to not return too early
708 int elementRefCounter = 1;
709
710 while (!reader.atEnd()) {
711 switch (reader.tokenType()) {
712 case QXmlStreamReader::StartElement:
713 ++elementRefCounter;
714 if (reader.name() == QLatin1String("comment")) {
715 const bool isSingleLine = reader.attributes().value(QLatin1String("name")) == QLatin1String("singleLine");
716 if (isSingleLine) {
717 singleLineCommentMarker = reader.attributes().value(QLatin1String("start")).toString();
718 const bool afterWhiteSpace = reader.attributes().value(QLatin1String("position")) == QLatin1String("afterwhitespace");
719 singleLineCommentPosition = afterWhiteSpace ? CommentPosition::AfterWhitespace : CommentPosition::StartOfLine;
720 } else {
721 multiLineCommentStartMarker = reader.attributes().value(QLatin1String("start")).toString();
722 multiLineCommentEndMarker = reader.attributes().value(QLatin1String("end")).toString();
723 }
724 }
725 reader.readNext();
726 break;
727 case QXmlStreamReader::EndElement:
728 --elementRefCounter;
729 if (elementRefCounter == 0) {
730 return;
731 }
732 reader.readNext();
733 break;
734 default:
735 reader.readNext();
736 break;
737 }
738 }
739}
740
741void DefinitionData::loadFoldingIgnoreList(QXmlStreamReader &reader)
742{
743 Q_ASSERT(reader.name() == QLatin1String("emptyLines"));
744 Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement);
745 reader.readNext();
746
747 // reference counter to count XML child elements, to not return too early
748 int elementRefCounter = 1;
749
750 while (!reader.atEnd()) {
751 switch (reader.tokenType()) {
752 case QXmlStreamReader::StartElement:
753 ++elementRefCounter;
754 if (reader.name() == QLatin1String("emptyLine")) {
755 foldingIgnoreList << reader.attributes().value(QLatin1String("regexpr")).toString();
756 }
757 reader.readNext();
758 break;
759 case QXmlStreamReader::EndElement:
760 --elementRefCounter;
761 if (elementRefCounter == 0) {
762 return;
763 }
764 reader.readNext();
765 break;
766 default:
767 reader.readNext();
768 break;
769 }
770 }
771}
772
773void DefinitionData::loadSpellchecking(QXmlStreamReader &reader)
774{
775 Q_ASSERT(reader.name() == QLatin1String("spellchecking"));
776 Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement);
777 reader.readNext();
778
779 // reference counter to count XML child elements, to not return too early
780 int elementRefCounter = 1;
781
782 while (!reader.atEnd()) {
783 switch (reader.tokenType()) {
784 case QXmlStreamReader::StartElement:
785 ++elementRefCounter;
786 if (reader.name() == QLatin1String("encoding")) {
787 const auto charRef = reader.attributes().value(QLatin1String("char"));
788 if (!charRef.isEmpty()) {
789 const auto str = reader.attributes().value(QLatin1String("string"));
790 characterEncodings.push_back({charRef[0], str.toString()});
791 }
792 }
793 reader.readNext();
794 break;
795 case QXmlStreamReader::EndElement:
796 --elementRefCounter;
797 if (elementRefCounter == 0) {
798 return;
799 }
800 reader.readNext();
801 break;
802 default:
803 reader.readNext();
804 break;
805 }
806 }
807}
808
809bool DefinitionData::checkKateVersion(QStringView verStr)
810{
811 const auto idx = verStr.indexOf(QLatin1Char('.'));
812 if (idx <= 0) {
813 qCWarning(Log) << "Skipping" << fileName << "due to having no valid kateversion attribute:" << verStr;
814 return false;
815 }
816 const auto major = verStr.left(idx).toString().toInt();
817 const auto minor = verStr.mid(idx + 1).toString().toInt();
818
819 if (major > KSYNTAXHIGHLIGHTING_VERSION_MAJOR || (major == KSYNTAXHIGHLIGHTING_VERSION_MAJOR && minor > KSYNTAXHIGHLIGHTING_VERSION_MINOR)) {
820 qCWarning(Log) << "Skipping" << fileName << "due to being too new, version:" << verStr;
821 return false;
822 }
823
824 return true;
825}
826
827quint16 DefinitionData::foldingRegionId(const QString &foldName)
828{
829 hasFoldingRegions = true;
830 return RepositoryPrivate::get(repo)->foldingRegionId(name, foldName);
831}
832
833void DefinitionData::addImmediateIncludedDefinition(const Definition &def)
834{
835 if (get(def) != this) {
836 DefinitionRef defRef(def);
837 if (!immediateIncludedDefinitions.contains(defRef)) {
838 immediateIncludedDefinitions.push_back(std::move(defRef));
839 }
840 }
841}
842
843DefinitionRef::DefinitionRef() = default;
844
845DefinitionRef::DefinitionRef(const Definition &def)
846 : d(def.d)
847{
848}
849
850DefinitionRef::DefinitionRef(Definition &&def)
851 : d(std::move(def.d))
852{
853}
854
855DefinitionRef &DefinitionRef::operator=(const Definition &def)
856{
857 d = def.d;
858 return *this;
859}
860
861DefinitionRef &DefinitionRef::operator=(Definition &&def)
862{
863 d = std::move(def.d);
864 return *this;
865}
866
867Definition DefinitionRef::definition() const
868{
869 if (!d.expired()) {
870 return Definition(d.lock());
871 }
872 return Definition();
873}
874
875bool DefinitionRef::operator==(const DefinitionRef &other) const
876{
877 return !d.owner_before(rhs: other.d) && !other.d.owner_before(rhs: d);
878}
879
880bool DefinitionRef::operator==(const Definition &other) const
881{
882 return !d.owner_before(rhs: other.d) && !other.d.owner_before(rhs: d);
883}
884
885#include "moc_definition.cpp"
886

source code of syntax-highlighting/src/lib/definition.cpp