1 | /* |
2 | SPDX-FileCopyrightText: 2003-2005 Anders Lund <anders@alweb.dk> |
3 | SPDX-FileCopyrightText: 2001-2010 Christoph Cullmann <cullmann@kde.org> |
4 | SPDX-FileCopyrightText: 2001 Charles Samuels <charles@kde.org> |
5 | |
6 | SPDX-License-Identifier: LGPL-2.0-or-later |
7 | */ |
8 | |
9 | #include <vimode/cmds.h> |
10 | |
11 | #include "globalstate.h" |
12 | #include "katecmd.h" |
13 | #include "kateview.h" |
14 | #include "kateviinputmode.h" |
15 | #include "marks.h" |
16 | #include <vimode/emulatedcommandbar/emulatedcommandbar.h> |
17 | #include <vimode/inputmodemanager.h> |
18 | #include <vimode/modes/normalvimode.h> |
19 | #include <vimode/searcher.h> |
20 | |
21 | #include <KLocalizedString> |
22 | |
23 | #include <QRegularExpression> |
24 | |
25 | using namespace KateVi; |
26 | |
27 | // BEGIN ViCommands |
28 | Commands *Commands::m_instance = nullptr; |
29 | |
30 | bool Commands::exec(KTextEditor::View *view, const QString &_cmd, QString &msg, const KTextEditor::Range &range) |
31 | { |
32 | Q_UNUSED(range) |
33 | // cast it hardcore, we know that it is really a kateview :) |
34 | KTextEditor::ViewPrivate *v = static_cast<KTextEditor::ViewPrivate *>(view); |
35 | |
36 | if (!v) { |
37 | msg = i18n("Could not access view" ); |
38 | return false; |
39 | } |
40 | |
41 | // create a list of args |
42 | QStringList args(_cmd.split(sep: QRegularExpression(QStringLiteral("\\s+" )), behavior: Qt::SkipEmptyParts)); |
43 | QString cmd(args.takeFirst()); |
44 | |
45 | // ALL commands that takes no arguments. |
46 | if (mappingCommands().contains(str: cmd)) { |
47 | if (cmd.endsWith(s: QLatin1String("unmap" ))) { |
48 | if (args.count() == 1) { |
49 | m_viInputModeManager->globalState()->mappings()->remove(mode: modeForMapCommand(mapCommand: cmd), from: args.at(i: 0)); |
50 | return true; |
51 | } else { |
52 | msg = i18n("Missing argument. Usage: %1 <from>" , cmd); |
53 | return false; |
54 | } |
55 | } else if (cmd == QLatin1String("nohlsearch" ) || cmd == QLatin1String("noh" )) { |
56 | m_viInputModeManager->searcher()->hideCurrentHighlight(); |
57 | return true; |
58 | } else if (cmd == QLatin1String("set-hlsearch" ) || cmd == QLatin1String("set-hls" )) { |
59 | m_viInputModeManager->searcher()->enableHighlightSearch(enable: true); |
60 | return true; |
61 | } else if (cmd == QLatin1String("set-nohlsearch" ) || cmd == QLatin1String("set-nohls" )) { |
62 | m_viInputModeManager->searcher()->enableHighlightSearch(enable: false); |
63 | return true; |
64 | } |
65 | if (args.count() == 1) { |
66 | msg = m_viInputModeManager->globalState()->mappings()->get(mode: modeForMapCommand(mapCommand: cmd), from: args.at(i: 0), decode: true); |
67 | if (msg.isEmpty()) { |
68 | msg = i18n("No mapping found for \"%1\"" , args.at(0)); |
69 | return false; |
70 | } else { |
71 | msg = i18n("\"%1\" is mapped to \"%2\"" , args.at(0), msg); |
72 | } |
73 | } else if (args.count() == 2) { |
74 | Mappings::MappingRecursion mappingRecursion = (isMapCommandRecursive(mapCommand: cmd)) ? Mappings::Recursive : Mappings::NonRecursive; |
75 | m_viInputModeManager->globalState()->mappings()->add(mode: modeForMapCommand(mapCommand: cmd), from: args.at(i: 0), to: args.at(i: 1), recursion: mappingRecursion); |
76 | } else { |
77 | msg = i18n("Missing argument(s). Usage: %1 <from> [<to>]" , cmd); |
78 | return false; |
79 | } |
80 | |
81 | return true; |
82 | } |
83 | |
84 | NormalViMode *nm = m_viInputModeManager->getViNormalMode(); |
85 | |
86 | if (cmd == QLatin1String("d" ) || cmd == QLatin1String("delete" ) || cmd == QLatin1String("j" ) || cmd == QLatin1String("c" ) || cmd == QLatin1String("change" ) |
87 | || cmd == QLatin1String("<" ) || cmd == QLatin1String(">" ) || cmd == QLatin1String("y" ) || cmd == QLatin1String("yank" )) { |
88 | KTextEditor::Cursor start_cursor_position = v->cursorPosition(); |
89 | |
90 | int count = 1; |
91 | if (range.isValid()) { |
92 | count = qAbs(t: range.end().line() - range.start().line()) + 1; |
93 | v->setCursorPosition(KTextEditor::Cursor(qMin(a: range.start().line(), b: range.end().line()), 0)); |
94 | } |
95 | |
96 | static const QRegularExpression number(QStringLiteral("^(\\d+)$" )); |
97 | for (int i = 0; i < args.count(); i++) { |
98 | auto match = number.match(subject: args.at(i)); |
99 | if (match.hasMatch()) { |
100 | count += match.captured(nth: 0).toInt() - 1; |
101 | } |
102 | |
103 | QChar r = args.at(i).at(i: 0); |
104 | if (args.at(i).size() == 1 |
105 | && ((r >= QLatin1Char('a') && r <= QLatin1Char('z')) || r == QLatin1Char('_') || r == QLatin1Char('-') || r == QLatin1Char('+') |
106 | || r == QLatin1Char('*'))) { |
107 | nm->setRegister(r); |
108 | } |
109 | } |
110 | |
111 | nm->setCount(count); |
112 | |
113 | if (cmd == QLatin1String("d" ) || cmd == QLatin1String("delete" )) { |
114 | nm->commandDeleteLine(); |
115 | } |
116 | if (cmd == QLatin1String("j" )) { |
117 | nm->commandJoinLines(); |
118 | } |
119 | if (cmd == QLatin1String("c" ) || cmd == QLatin1String("change" )) { |
120 | nm->commandChangeLine(); |
121 | } |
122 | if (cmd == QLatin1String("<" )) { |
123 | nm->commandUnindentLine(); |
124 | } |
125 | if (cmd == QLatin1String(">" )) { |
126 | nm->commandIndentLine(); |
127 | } |
128 | if (cmd == QLatin1String("y" ) || cmd == QLatin1String("yank" )) { |
129 | nm->commandYankLine(); |
130 | v->setCursorPosition(start_cursor_position); |
131 | } |
132 | |
133 | // TODO - should we resetParser, here? We'd have to make it public, if so. |
134 | // Or maybe synthesise a KateViCommand to execute instead ... ? |
135 | nm->setCount(0); |
136 | |
137 | return true; |
138 | } |
139 | |
140 | if (cmd == QLatin1String("mark" ) || cmd == QLatin1String("ma" ) || cmd == QLatin1String("k" )) { |
141 | if (args.count() == 0) { |
142 | if (cmd == QLatin1String("mark" )) { |
143 | // TODO: show up mark list; |
144 | } else { |
145 | msg = i18n("Wrong arguments" ); |
146 | return false; |
147 | } |
148 | } else if (args.count() == 1) { |
149 | QChar r = args.at(i: 0).at(i: 0); |
150 | int line; |
151 | if ((r >= QLatin1Char('a') && r <= QLatin1Char('z')) || r == QLatin1Char('_') || r == QLatin1Char('+') || r == QLatin1Char('*')) { |
152 | if (range.isValid()) { |
153 | line = qMax(a: range.end().line(), b: range.start().line()); |
154 | } else { |
155 | line = v->cursorPosition().line(); |
156 | } |
157 | |
158 | m_viInputModeManager->marks()->setUserMark(mark: r, pos: KTextEditor::Cursor(line, 0)); |
159 | } |
160 | } else { |
161 | msg = i18n("Wrong arguments" ); |
162 | return false; |
163 | } |
164 | return true; |
165 | } |
166 | |
167 | // should not happen :) |
168 | msg = i18n("Unknown command '%1'" , cmd); |
169 | return false; |
170 | } |
171 | |
172 | bool Commands::supportsRange(const QString &range) |
173 | { |
174 | static QStringList l; |
175 | |
176 | if (l.isEmpty()) { |
177 | l << QStringLiteral("d" ) << QStringLiteral("delete" ) << QStringLiteral("j" ) << QStringLiteral("c" ) << QStringLiteral("change" ) << QStringLiteral("<" ) |
178 | << QStringLiteral(">" ) << QStringLiteral("y" ) << QStringLiteral("yank" ) << QStringLiteral("ma" ) << QStringLiteral("mark" ) << QStringLiteral("k" ); |
179 | } |
180 | |
181 | return l.contains(str: range.split(sep: QLatin1Char(' ')).at(i: 0)); |
182 | } |
183 | |
184 | KCompletion *Commands::completionObject(KTextEditor::View *view, const QString &cmd) |
185 | { |
186 | Q_UNUSED(view) |
187 | |
188 | KTextEditor::ViewPrivate *v = static_cast<KTextEditor::ViewPrivate *>(view); |
189 | |
190 | if (v && (cmd == QLatin1String("nn" ) || cmd == QLatin1String("nnoremap" ))) { |
191 | QStringList l = m_viInputModeManager->globalState()->mappings()->getAll(mode: Mappings::NormalModeMapping); |
192 | |
193 | KateCmdShellCompletion *co = new KateCmdShellCompletion(); |
194 | co->setItems(l); |
195 | co->setIgnoreCase(false); |
196 | return co; |
197 | } |
198 | return nullptr; |
199 | } |
200 | |
201 | const QStringList &Commands::mappingCommands() |
202 | { |
203 | static QStringList mappingsCommands; |
204 | if (mappingsCommands.isEmpty()) { |
205 | mappingsCommands << QStringLiteral("nmap" ) << QStringLiteral("nm" ) << QStringLiteral("noremap" ) << QStringLiteral("nnoremap" ) << QStringLiteral("nn" ) |
206 | << QStringLiteral("no" ) << QStringLiteral("vmap" ) << QStringLiteral("vm" ) << QStringLiteral("vnoremap" ) << QStringLiteral("vn" ) |
207 | << QStringLiteral("imap" ) << QStringLiteral("im" ) << QStringLiteral("inoremap" ) << QStringLiteral("ino" ) << QStringLiteral("cmap" ) |
208 | << QStringLiteral("cm" ) << QStringLiteral("cnoremap" ) << QStringLiteral("cno" ); |
209 | |
210 | mappingsCommands << QStringLiteral("nunmap" ) << QStringLiteral("vunmap" ) << QStringLiteral("iunmap" ) << QStringLiteral("cunmap" ); |
211 | |
212 | mappingsCommands << QStringLiteral("nohlsearch" ) << QStringLiteral("noh" ) << QStringLiteral("set-hlsearch" ) << QStringLiteral("set-hls" ) |
213 | << QStringLiteral("set-nohlsearch" ) << QStringLiteral("set-nohls" ); |
214 | } |
215 | return mappingsCommands; |
216 | } |
217 | |
218 | Mappings::MappingMode Commands::modeForMapCommand(const QString &mapCommand) |
219 | { |
220 | if (mapCommand.startsWith(c: u'v')) { |
221 | if (mapCommand == u"vmap" || mapCommand == u"vm" || mapCommand == u"vn" || mapCommand == u"vnoremap" || mapCommand == u"vunmap" ) { |
222 | return Mappings::VisualModeMapping; |
223 | } |
224 | } |
225 | |
226 | if (mapCommand.startsWith(c: u'i')) { |
227 | if (mapCommand == u"imap" || mapCommand == u"im" || mapCommand == u"ino" || mapCommand == u"inoremap" || mapCommand == u"iunmap" ) { |
228 | return Mappings::InsertModeMapping; |
229 | } |
230 | } |
231 | |
232 | if (mapCommand.startsWith(c: u'c')) { |
233 | if (mapCommand == u"cmap" || mapCommand == u"cm" || mapCommand == u"cno" || mapCommand == u"cnoremap" || mapCommand == u"cunmap" ) { |
234 | return Mappings::CommandModeMapping; |
235 | } |
236 | } |
237 | |
238 | // if (mapCommand == u"nunmap") { |
239 | // return Mappings::NormalModeMapping; |
240 | // } |
241 | return Mappings::NormalModeMapping; |
242 | } |
243 | |
244 | bool Commands::isMapCommandRecursive(const QString &mapCommand) |
245 | { |
246 | return mapCommand == u"nmap" || mapCommand == u"nm" // |
247 | || mapCommand == u"vmap" || mapCommand == u"vm" // |
248 | || mapCommand == u"imap" || mapCommand == u"im" // |
249 | || mapCommand == u"cmap" || mapCommand == u"cm" ; |
250 | } |
251 | |
252 | // END ViCommands |
253 | |
254 | // BEGIN SedReplace |
255 | SedReplace *SedReplace::m_instance = nullptr; |
256 | |
257 | bool SedReplace::interactiveSedReplace(KTextEditor::ViewPrivate *, std::shared_ptr<InteractiveSedReplacer> interactiveSedReplace) |
258 | { |
259 | EmulatedCommandBar *emulatedCommandBar = m_viInputModeManager->inputAdapter()->viModeEmulatedCommandBar(); |
260 | emulatedCommandBar->startInteractiveSearchAndReplace(interactiveSedReplace); |
261 | return true; |
262 | } |
263 | // END SedReplace |
264 | |