1/*
2 SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
3 SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
4
5 SPDX-License-Identifier: MIT
6*/
7
8#include "context_p.h"
9#include "definition_p.h"
10#include "format.h"
11#include "ksyntaxhighlighting_logging.h"
12#include "repository.h"
13#include "rule_p.h"
14#include "xml_p.h"
15
16#include <QString>
17#include <QXmlStreamReader>
18
19using namespace KSyntaxHighlighting;
20
21Context::Context(const DefinitionData &def, const HighlightingContextData &data)
22 : m_name(data.name)
23 , m_attributeFormat(data.attribute.isEmpty() ? Format() : def.formatByName(name: data.attribute))
24 , m_indentationBasedFolding(!data.noIndentationBasedFolding && def.indentationBasedFolding)
25{
26 if (!data.attribute.isEmpty() && !m_attributeFormat.isValid()) {
27 qCWarning(Log) << "Context: Unknown format" << data.attribute << "in context" << m_name << "of definition" << def.name;
28 }
29}
30
31bool Context::indentationBasedFoldingEnabled() const
32{
33 return m_indentationBasedFolding;
34}
35
36void Context::resolveContexts(DefinitionData &def, const HighlightingContextData &data)
37{
38 m_lineEndContext.resolve(def, contextInstr: data.lineEndContext);
39 m_lineEmptyContext.resolve(def, contextInstr: data.lineEmptyContext);
40 m_fallthroughContext.resolve(def, contextInstr: data.fallthroughContext);
41 m_stopEmptyLineContextSwitchLoop = data.stopEmptyLineContextSwitchLoop;
42
43 /**
44 * line end context switches only when lineEmptyContext is #stay. This avoids
45 * skipping empty lines after a line continuation character (see bug 405903)
46 */
47 if (m_lineEmptyContext.isStay()) {
48 m_lineEmptyContext = m_lineEndContext;
49 }
50
51 m_rules.reserve(n: data.rules.size());
52 for (const auto &ruleData : data.rules) {
53 m_rules.push_back(x: Rule::create(def, ruleData, lookupContextName: m_name));
54 if (!m_rules.back()) {
55 m_rules.pop_back();
56 }
57 }
58}
59
60void Context::resolveIncludes(DefinitionData &def)
61{
62 if (m_resolveState == Resolved) {
63 return;
64 }
65 if (m_resolveState == Resolving) {
66 qCWarning(Log) << "Cyclic dependency!";
67 return;
68 }
69
70 Q_ASSERT(m_resolveState == Unresolved);
71 m_resolveState = Resolving; // cycle guard
72
73 for (auto it = m_rules.begin(); it != m_rules.end();) {
74 const IncludeRules *includeRules = it->get()->castToIncludeRules();
75 if (!includeRules) {
76 m_hasDynamicRule = m_hasDynamicRule || it->get()->isDynamic();
77 ++it;
78 continue;
79 }
80
81 Context *context = nullptr;
82 DefinitionData *defData = &def;
83
84 const auto &contextName = includeRules->contextName();
85 const int idx = contextName.indexOf(s: QLatin1String("##"));
86
87 if (idx == -1) { // local include
88 context = def.contextByName(name: contextName);
89 } else {
90 auto definitionName = contextName.mid(position: idx + 2);
91 auto includedDef = def.repo->definitionForName(defName: definitionName);
92 if (!includedDef.isValid()) {
93 qCWarning(Log) << "Unable to resolve external include rule for definition" << definitionName << "in" << def.name;
94 ++it;
95 continue;
96 }
97 defData = DefinitionData::get(def: includedDef);
98 def.addImmediateIncludedDefinition(def: includedDef);
99 defData->load();
100 if (idx == 0) {
101 context = defData->initialContext();
102 } else {
103 context = defData->contextByName(name: QStringView(contextName).left(n: idx));
104 }
105 }
106
107 if (!context) {
108 qCWarning(Log) << "Unable to resolve include rule for definition" << contextName << "in" << def.name;
109 ++it;
110 continue;
111 }
112
113 if (context == this) {
114 qCWarning(Log) << "Unable to resolve self include rule for definition" << contextName << "in" << def.name;
115 ++it;
116 continue;
117 }
118
119 if (context->m_resolveState != Resolved) {
120 context->resolveIncludes(def&: *defData);
121 }
122
123 m_hasDynamicRule = m_hasDynamicRule || context->m_hasDynamicRule;
124
125 /**
126 * handle included attribute
127 * transitive closure: we might include attributes included from somewhere else
128 */
129 if (includeRules->includeAttribute()) {
130 m_attributeFormat = context->m_attributeFormat;
131 }
132
133 it = m_rules.erase(position: it);
134 it = m_rules.insert(position: it, first: context->rules().begin(), last: context->rules().end());
135 it += context->rules().size();
136 }
137
138 m_resolveState = Resolved;
139}
140

source code of syntax-highlighting/src/lib/context.cpp