File indexing completed on 2024-05-12 04:02:17
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 "context_p.h" 0009 #include "definition_p.h" 0010 #include "format.h" 0011 #include "ksyntaxhighlighting_logging.h" 0012 #include "repository.h" 0013 #include "rule_p.h" 0014 #include "xml_p.h" 0015 0016 #include <QString> 0017 #include <QXmlStreamReader> 0018 0019 using namespace KSyntaxHighlighting; 0020 0021 Context::Context(const DefinitionData &def, const HighlightingContextData &data) 0022 : m_name(data.name) 0023 , m_attributeFormat(data.attribute.isEmpty() ? Format() : def.formatByName(data.attribute)) 0024 , m_indentationBasedFolding(!data.noIndentationBasedFolding && def.indentationBasedFolding) 0025 { 0026 if (!data.attribute.isEmpty() && !m_attributeFormat.isValid()) { 0027 qCWarning(Log) << "Context: Unknown format" << data.attribute << "in context" << m_name << "of definition" << def.name; 0028 } 0029 } 0030 0031 bool Context::indentationBasedFoldingEnabled() const 0032 { 0033 return m_indentationBasedFolding; 0034 } 0035 0036 void Context::resolveContexts(DefinitionData &def, const HighlightingContextData &data) 0037 { 0038 m_lineEndContext.resolve(def, data.lineEndContext); 0039 m_lineEmptyContext.resolve(def, data.lineEmptyContext); 0040 m_fallthroughContext.resolve(def, data.fallthroughContext); 0041 m_stopEmptyLineContextSwitchLoop = data.stopEmptyLineContextSwitchLoop; 0042 0043 /** 0044 * line end context switches only when lineEmptyContext is #stay. This avoids 0045 * skipping empty lines after a line continuation character (see bug 405903) 0046 */ 0047 if (m_lineEmptyContext.isStay()) { 0048 m_lineEmptyContext = m_lineEndContext; 0049 } 0050 0051 m_rules.reserve(data.rules.size()); 0052 for (const auto &ruleData : data.rules) { 0053 m_rules.push_back(Rule::create(def, ruleData, m_name)); 0054 if (!m_rules.back()) { 0055 m_rules.pop_back(); 0056 } 0057 } 0058 } 0059 0060 void Context::resolveIncludes(DefinitionData &def) 0061 { 0062 if (m_resolveState == Resolved) { 0063 return; 0064 } 0065 if (m_resolveState == Resolving) { 0066 qCWarning(Log) << "Cyclic dependency!"; 0067 return; 0068 } 0069 0070 Q_ASSERT(m_resolveState == Unresolved); 0071 m_resolveState = Resolving; // cycle guard 0072 0073 for (auto it = m_rules.begin(); it != m_rules.end();) { 0074 const IncludeRules *includeRules = it->get()->castToIncludeRules(); 0075 if (!includeRules) { 0076 m_hasDynamicRule = m_hasDynamicRule || it->get()->isDynamic(); 0077 ++it; 0078 continue; 0079 } 0080 0081 Context *context = nullptr; 0082 DefinitionData *defData = &def; 0083 0084 const auto &contextName = includeRules->contextName(); 0085 const int idx = contextName.indexOf(QLatin1String("##")); 0086 0087 if (idx == -1) { // local include 0088 context = def.contextByName(contextName); 0089 } else { 0090 auto definitionName = contextName.mid(idx + 2); 0091 auto includedDef = def.repo->definitionForName(definitionName); 0092 if (!includedDef.isValid()) { 0093 qCWarning(Log) << "Unable to resolve external include rule for definition" << definitionName << "in" << def.name; 0094 ++it; 0095 continue; 0096 } 0097 defData = DefinitionData::get(includedDef); 0098 def.addImmediateIncludedDefinition(includedDef); 0099 defData->load(); 0100 if (idx == 0) { 0101 context = defData->initialContext(); 0102 } else { 0103 context = defData->contextByName(QStringView(contextName).left(idx)); 0104 } 0105 } 0106 0107 if (!context) { 0108 qCWarning(Log) << "Unable to resolve include rule for definition" << contextName << "in" << def.name; 0109 ++it; 0110 continue; 0111 } 0112 0113 if (context == this) { 0114 qCWarning(Log) << "Unable to resolve self include rule for definition" << contextName << "in" << def.name; 0115 ++it; 0116 continue; 0117 } 0118 0119 if (context->m_resolveState != Resolved) { 0120 context->resolveIncludes(*defData); 0121 } 0122 0123 m_hasDynamicRule = m_hasDynamicRule || context->m_hasDynamicRule; 0124 0125 /** 0126 * handle included attribute 0127 * transitive closure: we might include attributes included from somewhere else 0128 */ 0129 if (includeRules->includeAttribute()) { 0130 m_attributeFormat = context->m_attributeFormat; 0131 } 0132 0133 it = m_rules.erase(it); 0134 it = m_rules.insert(it, context->rules().begin(), context->rules().end()); 0135 it += context->rules().size(); 0136 } 0137 0138 m_resolveState = Resolved; 0139 }