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 | |
16 | using namespace KateVi; |
17 | |
18 | void 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 | |
26 | void 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 | |
34 | void 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 | |
53 | void 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 | |
79 | void 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 | |
105 | void 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 | |
111 | void Mappings::clear(MappingMode mode) |
112 | { |
113 | m_mappings[mode].clear(); |
114 | } |
115 | |
116 | QString 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 | |
135 | QStringList 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 | |
154 | bool 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 | |
163 | void Mappings::setLeader(const QChar &leader) |
164 | { |
165 | m_leader = leader; |
166 | } |
167 | |
168 | Mappings::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 | |