File indexing completed on 2024-05-12 15:50:05

0001 /*
0002     SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
0003     SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
0004 
0005     SPDX-License-Identifier: MIT
0006 */
0007 
0008 #include "definition_p.h"
0009 #include "keywordlist_p.h"
0010 #include "ksyntaxhighlighting_logging.h"
0011 #include "repository.h"
0012 
0013 #include <QXmlStreamReader>
0014 
0015 #include <algorithm>
0016 
0017 using namespace KSyntaxHighlighting;
0018 
0019 namespace
0020 {
0021 struct KeywordComparator {
0022     Qt::CaseSensitivity caseSensitive;
0023 
0024     bool operator()(QStringView a, QStringView b) const
0025     {
0026         if (a.size() < b.size()) {
0027             return true;
0028         }
0029 
0030         if (a.size() > b.size()) {
0031             return false;
0032         }
0033 
0034         return a.compare(b, caseSensitive) < 0;
0035     }
0036 };
0037 
0038 }
0039 
0040 bool KeywordList::contains(QStringView str, Qt::CaseSensitivity caseSensitive) const
0041 {
0042     /**
0043      * get right vector to search in
0044      */
0045     const auto &vectorToSearch = (caseSensitive == Qt::CaseSensitive) ? m_keywordsSortedCaseSensitive : m_keywordsSortedCaseInsensitive;
0046 
0047     /**
0048      * search with right predicate
0049      */
0050     return std::binary_search(vectorToSearch.begin(), vectorToSearch.end(), QStringView(str), KeywordComparator{caseSensitive});
0051 }
0052 
0053 void KeywordList::load(QXmlStreamReader &reader)
0054 {
0055     Q_ASSERT(reader.name() == QLatin1String("list"));
0056     Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement);
0057 
0058     m_name = reader.attributes().value(QLatin1String("name")).toString();
0059 
0060     while (!reader.atEnd()) {
0061         switch (reader.tokenType()) {
0062         case QXmlStreamReader::StartElement:
0063             if (reader.name() == QLatin1String("item")) {
0064                 m_keywords.append(reader.readElementText().trimmed());
0065                 reader.readNextStartElement();
0066                 break;
0067             } else if (reader.name() == QLatin1String("include")) {
0068                 m_includes.append(reader.readElementText().trimmed());
0069                 reader.readNextStartElement();
0070                 break;
0071             }
0072             reader.readNext();
0073             break;
0074         case QXmlStreamReader::EndElement:
0075             reader.readNext();
0076             return;
0077         default:
0078             reader.readNext();
0079             break;
0080         }
0081     }
0082 }
0083 
0084 void KeywordList::setCaseSensitivity(Qt::CaseSensitivity caseSensitive)
0085 {
0086     /**
0087      * remember default case-sensitivity and init lookup for it
0088      */
0089     m_caseSensitive = caseSensitive;
0090     initLookupForCaseSensitivity(m_caseSensitive);
0091 }
0092 
0093 void KeywordList::initLookupForCaseSensitivity(Qt::CaseSensitivity caseSensitive)
0094 {
0095     /**
0096      * get right vector to sort, if non-empty, we are done
0097      */
0098     auto &vectorToSort = (caseSensitive == Qt::CaseSensitive) ? m_keywordsSortedCaseSensitive : m_keywordsSortedCaseInsensitive;
0099     if (!vectorToSort.empty()) {
0100         return;
0101     }
0102 
0103     /**
0104      * fill vector with refs to keywords
0105      */
0106     vectorToSort.reserve(m_keywords.size());
0107     for (const auto &keyword : std::as_const(m_keywords)) {
0108         vectorToSort.push_back(keyword);
0109     }
0110 
0111     /**
0112      * sort with right predicate
0113      */
0114     std::sort(vectorToSort.begin(), vectorToSort.end(), KeywordComparator{caseSensitive});
0115 }
0116 
0117 void KeywordList::resolveIncludeKeywords(DefinitionData &def)
0118 {
0119     while (!m_includes.isEmpty()) {
0120         const auto kw_include = std::move(m_includes.back());
0121         m_includes.pop_back();
0122 
0123         const auto idx = kw_include.indexOf(QLatin1String("##"));
0124         KeywordList *keywords = nullptr;
0125 
0126         if (idx >= 0) {
0127             auto defName = kw_include.mid(idx + 2);
0128             auto includeDef = def.repo->definitionForName(defName);
0129             if (includeDef.isValid()) {
0130                 auto listName = kw_include.left(idx);
0131                 auto defData = DefinitionData::get(includeDef);
0132                 defData->load(DefinitionData::OnlyKeywords(true));
0133                 keywords = defData->keywordList(listName);
0134             } else {
0135                 qCWarning(Log) << "Unable to resolve external include keyword for definition" << defName << "in" << def.name;
0136             }
0137         } else {
0138             keywords = def.keywordList(kw_include);
0139         }
0140 
0141         if (keywords) {
0142             if (this != keywords) {
0143                 keywords->resolveIncludeKeywords(def);
0144             }
0145             m_keywords += keywords->m_keywords;
0146         } else {
0147             qCWarning(Log) << "Unresolved include keyword" << kw_include << "in" << def.name;
0148         }
0149     }
0150 }