1/*
2 SPDX-FileCopyrightText: KDE Developers
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "katepartdebug.h"
8#include "kateviinputmode.h"
9#include <vimode/emulatedcommandbar/emulatedcommandbar.h>
10#include <vimode/inputmodemanager.h>
11#include <vimode/keyparser.h>
12#include <vimode/mappings.h>
13
14#include <KConfigGroup>
15
16using namespace KateVi;
17
18void Mappings::readConfig(const KConfigGroup &config)
19{
20 readMappings(config, QStringLiteral("Normal"), mappingMode: NormalModeMapping);
21 readMappings(config, QStringLiteral("Visual"), mappingMode: VisualModeMapping);
22 readMappings(config, QStringLiteral("Insert"), mappingMode: InsertModeMapping);
23 readMappings(config, QStringLiteral("Command"), mappingMode: CommandModeMapping);
24}
25
26void Mappings::writeConfig(KConfigGroup &config) const
27{
28 writeMappings(config, QStringLiteral("Normal"), mappingMode: NormalModeMapping);
29 writeMappings(config, QStringLiteral("Visual"), mappingMode: VisualModeMapping);
30 writeMappings(config, QStringLiteral("Insert"), mappingMode: InsertModeMapping);
31 writeMappings(config, QStringLiteral("Command"), mappingMode: CommandModeMapping);
32}
33
34void Mappings::writeMappings(KConfigGroup &config, const QString &mappingModeName, MappingMode mappingMode) const
35{
36 config.writeEntry(key: mappingModeName + QLatin1String(" Mode Mapping Keys"), value: getAll(mode: mappingMode, decode: true));
37 QStringList l;
38 QList<bool> recursives;
39 const auto all = getAll(mode: mappingMode);
40 l.reserve(asize: all.size());
41 recursives.reserve(asize: all.size());
42 for (const QString &s : all) {
43 l << KeyParser::self()->decodeKeySequence(keys: get(mode: mappingMode, from: s));
44 recursives << isRecursive(mode: mappingMode, from: s);
45 }
46 config.writeEntry(key: mappingModeName + QLatin1String(" Mode Mappings"), value: l);
47 config.writeEntry(key: mappingModeName + QLatin1String(" Mode Mappings Recursion"), value: recursives);
48
49 QChar leader = (m_leader.isNull()) ? QChar::fromLatin1(c: '\\') : m_leader;
50 config.writeEntry(QStringLiteral("Map Leader"), value: QString(leader));
51}
52
53void Mappings::readMappings(const KConfigGroup &config, const QString &mappingModeName, MappingMode mappingMode)
54{
55 const QStringList keys = config.readEntry(key: mappingModeName + QLatin1String(" Mode Mapping Keys"), aDefault: QStringList());
56 const QStringList mappings = config.readEntry(key: mappingModeName + QLatin1String(" Mode Mappings"), aDefault: QStringList());
57 const QList<bool> isRecursive = config.readEntry(key: mappingModeName + QLatin1String(" Mode Mappings Recursion"), aDefault: QList<bool>());
58
59 const QString &mapLeader = config.readEntry(QStringLiteral("Map Leader"), aDefault: QString());
60 m_leader = (mapLeader.isEmpty()) ? QChar::fromLatin1(c: '\\') : mapLeader[0];
61
62 // sanity check
63 if (keys.length() == mappings.length()) {
64 for (int i = 0; i < keys.length(); i++) {
65 // "Recursion" is a newly-introduced part of the config that some users won't have,
66 // so rather than abort (and lose our mappings) if there are not enough entries, simply
67 // treat any missing ones as Recursive (for backwards compatibility).
68 MappingRecursion recursion = Recursive;
69 if (isRecursive.size() > i && !isRecursive.at(i)) {
70 recursion = NonRecursive;
71 }
72 add(mode: mappingMode, from: keys.at(i), to: mappings.at(i), recursion);
73 }
74 } else {
75 qCDebug(LOG_KTE) << "Error when reading mappings from " << mappingModeName << " config: number of keys != number of values";
76 }
77}
78
79void Mappings::add(MappingMode mode, const QString &from, const QString &to, MappingRecursion recursion)
80{
81 const QString &encodedMapping = KeyParser::self()->encodeKeySequence(keys: from);
82
83 if (from.isEmpty()) {
84 return;
85 }
86
87 const QString encodedTo = KeyParser::self()->encodeKeySequence(keys: to);
88 Mapping mapping = {.encoded: encodedTo, .recursive: (recursion == Recursive), .temporary: false};
89
90 // Add this mapping as is.
91 m_mappings[mode][encodedMapping] = mapping;
92
93 // In normal mode replace the <leader> with its value.
94 if (mode == NormalModeMapping) {
95 QString other = from;
96 other.replace(before: QLatin1String("<leader>"), after: m_leader);
97 other = KeyParser::self()->encodeKeySequence(keys: other);
98 if (other != encodedMapping) {
99 mapping.temporary = true;
100 m_mappings[mode][other] = mapping;
101 }
102 }
103}
104
105void Mappings::remove(MappingMode mode, const QString &from)
106{
107 const QString &encodedMapping = KeyParser::self()->encodeKeySequence(keys: from);
108 m_mappings[mode].remove(key: encodedMapping);
109}
110
111void Mappings::clear(MappingMode mode)
112{
113 m_mappings[mode].clear();
114}
115
116QString Mappings::get(MappingMode mode, const QString &from, bool decode, bool includeTemporary) const
117{
118 if (!m_mappings[mode].contains(key: from)) {
119 return QString();
120 }
121
122 const Mapping &mapping = m_mappings[mode][from];
123 if (mapping.temporary && !includeTemporary) {
124 return QString();
125 }
126
127 const QString &ret = mapping.encoded;
128 if (decode) {
129 return KeyParser::self()->decodeKeySequence(keys: ret);
130 }
131
132 return ret;
133}
134
135QStringList Mappings::getAll(MappingMode mode, bool decode, bool includeTemporary) const
136{
137 QStringList mappings;
138 const QHash<QString, Mapping> mappingsForMode = m_mappings[mode];
139
140 for (auto i = mappingsForMode.begin(); i != mappingsForMode.end(); i++) {
141 if (!includeTemporary && i.value().temporary) {
142 continue;
143 }
144
145 if (decode) {
146 mappings << KeyParser::self()->decodeKeySequence(keys: i.key());
147 } else {
148 mappings << i.key();
149 }
150 }
151 return mappings;
152}
153
154bool Mappings::isRecursive(MappingMode mode, const QString &from) const
155{
156 if (!m_mappings[mode].contains(key: from)) {
157 return false;
158 }
159
160 return m_mappings[mode][from].recursive;
161}
162
163void Mappings::setLeader(const QChar &leader)
164{
165 m_leader = leader;
166}
167
168Mappings::MappingMode Mappings::mappingModeForCurrentViMode(KateViInputMode *viInputMode)
169{
170 if (viInputMode->viModeEmulatedCommandBar()->isActive()) {
171 return CommandModeMapping;
172 }
173 const ViMode mode = viInputMode->viInputModeManager()->getCurrentViMode();
174 switch (mode) {
175 case ViMode::NormalMode:
176 return NormalModeMapping;
177 case ViMode::VisualMode:
178 case ViMode::VisualLineMode:
179 case ViMode::VisualBlockMode:
180 return VisualModeMapping;
181 case ViMode::InsertMode:
182 case ViMode::ReplaceMode:
183 return InsertModeMapping;
184 default:
185 Q_ASSERT(false && "unrecognised ViMode!");
186 return NormalModeMapping; // Return arbitrary mode to satisfy compiler.
187 }
188}
189

source code of ktexteditor/src/vimode/mappings.cpp