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

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