1 /*
2     SPDX-FileCopyrightText: KDE Developers
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "macros.h"
8 #include <vimode/keyparser.h>
9 
10 #include <KConfigGroup>
11 
12 using namespace KateVi;
13 
14 Macros::Macros() = default;
15 
16 Macros::~Macros() = default;
17 
18 void Macros::writeConfig(KConfigGroup &config) const
19 {
20     const auto macroKeys = m_macros.keys();
21     QStringList macroRegisters;
22     for (const QChar macroRegister : macroKeys) {
23         macroRegisters.append(macroRegister);
24     }
25     QStringList macroContents;
26     for (const QChar macroRegister : macroKeys) {
27         macroContents.append(KeyParser::self()->decodeKeySequence(m_macros[macroRegister]));
28     }
29     QStringList macroCompletions;
30     for (const QChar macroRegister : macroKeys) {
31         macroCompletions.append(QString::number(m_completions[macroRegister].length()));
32         for (const Completion &completionForMacro : m_completions[macroRegister]) {
33             macroCompletions.append(encodeMacroCompletionForConfig(completionForMacro));
34         }
35     }
36     config.writeEntry("Macro Registers", macroRegisters);
37     config.writeEntry("Macro Contents", macroContents);
38     config.writeEntry("Macro Completions", macroCompletions);
39 }
40 
41 void Macros::readConfig(const KConfigGroup &config)
42 {
43     const QStringList macroRegisters = config.readEntry("Macro Registers", QStringList());
44     const QStringList macroContents = config.readEntry("Macro Contents", QStringList());
45     const QStringList macroCompletions = config.readEntry("Macro Completions", QStringList());
46     int macroCompletionsIndex = 0;
47     if (macroRegisters.length() == macroContents.length()) {
48         for (int macroIndex = 0; macroIndex < macroRegisters.length(); macroIndex++) {
49             const QChar macroRegister = macroRegisters[macroIndex].at(0);
50             m_macros[macroRegister] = KeyParser::self()->encodeKeySequence(macroContents[macroIndex]);
51             macroCompletionsIndex = readMacroCompletions(macroRegister, macroCompletions, macroCompletionsIndex);
52         }
53     }
54 }
55 
56 void Macros::clear()
57 {
58     m_macros.clear();
59 }
60 
61 void Macros::remove(const QChar &reg)
62 {
63     m_macros.remove(reg);
64 }
65 
66 void Macros::store(const QChar &reg, const QList<QKeyEvent> &macroKeyEventLog, const CompletionList &completions)
67 {
68     m_macros[reg].clear();
69     QList<QKeyEvent> withoutClosingQ = macroKeyEventLog;
70     Q_ASSERT(!macroKeyEventLog.isEmpty() && macroKeyEventLog.last().key() == Qt::Key_Q);
71     withoutClosingQ.pop_back();
72     for (const QKeyEvent &keyEvent : std::as_const(withoutClosingQ)) {
73         const QChar key = KeyParser::self()->KeyEventToQChar(keyEvent);
74         m_macros[reg].append(key);
75     }
76     m_completions[reg] = completions;
77 }
78 
79 QString Macros::get(const QChar &reg) const
80 {
81     return m_macros.contains(reg) ? m_macros[reg] : QString();
82 }
83 
84 CompletionList Macros::getCompletions(const QChar &reg) const
85 {
86     return m_completions.contains(reg) ? m_completions[reg] : CompletionList();
87 }
88 
89 int Macros::readMacroCompletions(const QChar &reg, const QStringList &encodedMacroCompletions, int macroCompletionsIndex)
90 {
91     if (macroCompletionsIndex < encodedMacroCompletions.length()) {
92         bool parsedNumCompletionsSuccessfully = false;
93         const QString numCompletionsAsString = encodedMacroCompletions[macroCompletionsIndex++];
94         const int numCompletions = numCompletionsAsString.toInt(&parsedNumCompletionsSuccessfully);
95         int count = 0;
96         m_completions[reg].clear();
97         while (count < numCompletions && macroCompletionsIndex < encodedMacroCompletions.length()) {
98             const QString encodedMacroCompletion = encodedMacroCompletions[macroCompletionsIndex++];
99             count++;
100             m_completions[reg].append(decodeMacroCompletionFromConfig(encodedMacroCompletion));
101         }
102     }
103     return macroCompletionsIndex;
104 }
105 
106 QString Macros::encodeMacroCompletionForConfig(const Completion &completionForMacro) const
107 {
108     const bool endedWithSemiColon = completionForMacro.completedText().endsWith(QLatin1Char(';'));
109     QString encodedMacroCompletion = completionForMacro.completedText().remove(QStringLiteral("()")).remove(QLatin1Char(';'));
110     if (completionForMacro.completionType() == Completion::FunctionWithArgs) {
111         encodedMacroCompletion += QLatin1String("(...)");
112     } else if (completionForMacro.completionType() == Completion::FunctionWithoutArgs) {
113         encodedMacroCompletion += QLatin1String("()");
114     }
115     if (endedWithSemiColon) {
116         encodedMacroCompletion += QLatin1Char(';');
117     }
118     if (completionForMacro.removeTail()) {
119         encodedMacroCompletion += QLatin1Char('|');
120     }
121     return encodedMacroCompletion;
122 }
123 
124 Completion Macros::decodeMacroCompletionFromConfig(const QString &encodedMacroCompletion)
125 {
126     const bool removeTail = encodedMacroCompletion.endsWith(QLatin1Char('|'));
127     Completion::CompletionType completionType = Completion::PlainText;
128     if (encodedMacroCompletion.contains(QLatin1String("(...)"))) {
129         completionType = Completion::FunctionWithArgs;
130     } else if (encodedMacroCompletion.contains(QLatin1String("()"))) {
131         completionType = Completion::FunctionWithoutArgs;
132     }
133     QString completionText = encodedMacroCompletion;
134     completionText.replace(QLatin1String("(...)"), QLatin1String("()")).remove(QLatin1Char('|'));
135 
136     return Completion(completionText, removeTail, completionType);
137 }
138