aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs/3rdparty/syntax-highlighting/src/lib/context.cpp
blob: af269d14d030bde50693dfd82afd3569e741e785 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
    SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
    SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>

    SPDX-License-Identifier: MIT
*/

#include "context_p.h"
#include "definition_p.h"
#include "format.h"
#include "ksyntaxhighlighting_logging.h"
#include "repository.h"
#include "rule_p.h"
#include "xml_p.h"

#include <QString>
#include <QXmlStreamReader>

using namespace KSyntaxHighlighting;

Context::Context(const DefinitionData &def, const HighlightingContextData &data)
    : m_name(data.name)
    , m_attributeFormat(data.attribute.isEmpty() ? Format() : def.formatByName(data.attribute))
    , m_indentationBasedFolding(!data.noIndentationBasedFolding && def.indentationBasedFolding)
{
    if (!data.attribute.isEmpty() && !m_attributeFormat.isValid()) {
        qCWarning(Log) << "Context: Unknown format" << data.attribute << "in context" << m_name << "of definition" << def.name;
    }
}

bool Context::indentationBasedFoldingEnabled() const
{
    return m_indentationBasedFolding;
}

void Context::resolveContexts(DefinitionData &def, const HighlightingContextData &data)
{
    m_lineEndContext.resolve(def, data.lineEndContext);
    m_lineEmptyContext.resolve(def, data.lineEmptyContext);
    m_fallthroughContext.resolve(def, data.fallthroughContext);
    m_stopEmptyLineContextSwitchLoop = data.stopEmptyLineContextSwitchLoop;

    /**
     * line end context switches only when lineEmptyContext is #stay. This avoids
     * skipping empty lines after a line continuation character (see bug 405903)
     */
    if (m_lineEmptyContext.isStay()) {
        m_lineEmptyContext = m_lineEndContext;
    }

    m_rules.reserve(data.rules.size());
    for (const auto &ruleData : data.rules) {
        m_rules.push_back(Rule::create(def, ruleData, m_name));
        if (!m_rules.back()) {
            m_rules.pop_back();
        }
    }
}

void Context::resolveIncludes(DefinitionData &def)
{
    if (m_resolveState == Resolved) {
        return;
    }
    if (m_resolveState == Resolving) {
        qCWarning(Log) << "Cyclic dependency!";
        return;
    }

    Q_ASSERT(m_resolveState == Unresolved);
    m_resolveState = Resolving; // cycle guard

    for (auto it = m_rules.begin(); it != m_rules.end();) {
        const IncludeRules *includeRules = it->get()->castToIncludeRules();
        if (!includeRules) {
            m_hasDynamicRule = m_hasDynamicRule || it->get()->isDynamic();
            ++it;
            continue;
        }

        Context *context = nullptr;
        DefinitionData *defData = &def;

        const auto &contextName = includeRules->contextName();
        const int idx = contextName.indexOf(QLatin1String("##"));

        if (idx == -1) { // local include
            context = def.contextByName(contextName);
        } else {
            auto definitionName = contextName.mid(idx + 2);
            auto includedDef = def.repo->definitionForName(definitionName);
            if (!includedDef.isValid()) {
                qCWarning(Log) << "Unable to resolve external include rule for definition" << definitionName << "in" << def.name;
                ++it;
                continue;
            }
            defData = DefinitionData::get(includedDef);
            def.addImmediateIncludedDefinition(includedDef);
            defData->load();
            if (idx == 0) {
                context = defData->initialContext();
            } else {
                context = defData->contextByName(QStringView(contextName).left(idx));
            }
        }

        if (!context) {
            qCWarning(Log) << "Unable to resolve include rule for definition" << contextName << "in" << def.name;
            ++it;
            continue;
        }

        if (context == this) {
            qCWarning(Log) << "Unable to resolve self include rule for definition" << contextName << "in" << def.name;
            ++it;
            continue;
        }

        if (context->m_resolveState != Resolved) {
            context->resolveIncludes(*defData);
        }

        m_hasDynamicRule = m_hasDynamicRule || context->m_hasDynamicRule;

        /**
         * handle included attribute
         * transitive closure: we might include attributes included from somewhere else
         */
        if (includeRules->includeAttribute()) {
            m_attributeFormat = context->m_attributeFormat;
        }

        it = m_rules.erase(it);
        it = m_rules.insert(it, context->rules().begin(), context->rules().end());
        it += context->rules().size();
    }

    m_resolveState = Resolved;
}