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 }