File indexing completed on 2024-05-12 04:02:19
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(), 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.assign(m_keywords.constBegin(), m_keywords.constEnd()); 0107 0108 /** 0109 * sort with right predicate 0110 */ 0111 std::sort(vectorToSort.begin(), vectorToSort.end(), KeywordComparator{caseSensitive}); 0112 } 0113 0114 void KeywordList::resolveIncludeKeywords(DefinitionData &def) 0115 { 0116 while (!m_includes.isEmpty()) { 0117 const auto kw_include = std::move(m_includes.back()); 0118 m_includes.pop_back(); 0119 0120 const auto idx = kw_include.indexOf(QLatin1String("##")); 0121 KeywordList *keywords = nullptr; 0122 0123 if (idx >= 0) { 0124 auto defName = kw_include.mid(idx + 2); 0125 auto includeDef = def.repo->definitionForName(defName); 0126 if (includeDef.isValid()) { 0127 auto listName = kw_include.left(idx); 0128 auto defData = DefinitionData::get(includeDef); 0129 defData->load(DefinitionData::OnlyKeywords(true)); 0130 keywords = defData->keywordList(listName); 0131 } else { 0132 qCWarning(Log) << "Unable to resolve external include keyword for definition" << defName << "in" << def.name; 0133 } 0134 } else { 0135 keywords = def.keywordList(kw_include); 0136 } 0137 0138 if (keywords) { 0139 if (this != keywords) { 0140 keywords->resolveIncludeKeywords(def); 0141 } 0142 m_keywords += keywords->m_keywords; 0143 } else { 0144 qCWarning(Log) << "Unresolved include keyword" << kw_include << "in" << def.name; 0145 } 0146 } 0147 }