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 }