File indexing completed on 2024-04-14 03:55:47
0001 /* 0002 SPDX-FileCopyrightText: 2003-2005 Anders Lund <anders@alweb.dk> 0003 SPDX-FileCopyrightText: 2001-2010 Christoph Cullmann <cullmann@kde.org> 0004 SPDX-FileCopyrightText: 2001 Charles Samuels <charles@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include <vimode/cmds.h> 0010 0011 #include "globalstate.h" 0012 #include "katecmd.h" 0013 #include "kateview.h" 0014 #include "kateviinputmode.h" 0015 #include "marks.h" 0016 #include <vimode/emulatedcommandbar/emulatedcommandbar.h> 0017 #include <vimode/inputmodemanager.h> 0018 #include <vimode/modes/normalvimode.h> 0019 #include <vimode/searcher.h> 0020 0021 #include <KLocalizedString> 0022 0023 #include <QRegularExpression> 0024 0025 using namespace KateVi; 0026 0027 // BEGIN ViCommands 0028 Commands *Commands::m_instance = nullptr; 0029 0030 bool Commands::exec(KTextEditor::View *view, const QString &_cmd, QString &msg, const KTextEditor::Range &range) 0031 { 0032 Q_UNUSED(range) 0033 // cast it hardcore, we know that it is really a kateview :) 0034 KTextEditor::ViewPrivate *v = static_cast<KTextEditor::ViewPrivate *>(view); 0035 0036 if (!v) { 0037 msg = i18n("Could not access view"); 0038 return false; 0039 } 0040 0041 // create a list of args 0042 QStringList args(_cmd.split(QRegularExpression(QStringLiteral("\\s+")), Qt::SkipEmptyParts)); 0043 QString cmd(args.takeFirst()); 0044 0045 // ALL commands that takes no arguments. 0046 if (mappingCommands().contains(cmd)) { 0047 if (cmd.endsWith(QLatin1String("unmap"))) { 0048 if (args.count() == 1) { 0049 m_viInputModeManager->globalState()->mappings()->remove(modeForMapCommand(cmd), args.at(0)); 0050 return true; 0051 } else { 0052 msg = i18n("Missing argument. Usage: %1 <from>", cmd); 0053 return false; 0054 } 0055 } else if (cmd == QLatin1String("nohlsearch") || cmd == QLatin1String("noh")) { 0056 m_viInputModeManager->searcher()->hideCurrentHighlight(); 0057 return true; 0058 } else if (cmd == QLatin1String("set-hlsearch") || cmd == QLatin1String("set-hls")) { 0059 m_viInputModeManager->searcher()->enableHighlightSearch(true); 0060 return true; 0061 } else if (cmd == QLatin1String("set-nohlsearch") || cmd == QLatin1String("set-nohls")) { 0062 m_viInputModeManager->searcher()->enableHighlightSearch(false); 0063 return true; 0064 } 0065 if (args.count() == 1) { 0066 msg = m_viInputModeManager->globalState()->mappings()->get(modeForMapCommand(cmd), args.at(0), true); 0067 if (msg.isEmpty()) { 0068 msg = i18n("No mapping found for \"%1\"", args.at(0)); 0069 return false; 0070 } else { 0071 msg = i18n("\"%1\" is mapped to \"%2\"", args.at(0), msg); 0072 } 0073 } else if (args.count() == 2) { 0074 Mappings::MappingRecursion mappingRecursion = (isMapCommandRecursive(cmd)) ? Mappings::Recursive : Mappings::NonRecursive; 0075 m_viInputModeManager->globalState()->mappings()->add(modeForMapCommand(cmd), args.at(0), args.at(1), mappingRecursion); 0076 } else { 0077 msg = i18n("Missing argument(s). Usage: %1 <from> [<to>]", cmd); 0078 return false; 0079 } 0080 0081 return true; 0082 } 0083 0084 NormalViMode *nm = m_viInputModeManager->getViNormalMode(); 0085 0086 if (cmd == QLatin1String("d") || cmd == QLatin1String("delete") || cmd == QLatin1String("j") || cmd == QLatin1String("c") || cmd == QLatin1String("change") 0087 || cmd == QLatin1String("<") || cmd == QLatin1String(">") || cmd == QLatin1String("y") || cmd == QLatin1String("yank")) { 0088 KTextEditor::Cursor start_cursor_position = v->cursorPosition(); 0089 0090 int count = 1; 0091 if (range.isValid()) { 0092 count = qAbs(range.end().line() - range.start().line()) + 1; 0093 v->setCursorPosition(KTextEditor::Cursor(qMin(range.start().line(), range.end().line()), 0)); 0094 } 0095 0096 static const QRegularExpression number(QStringLiteral("^(\\d+)$")); 0097 for (int i = 0; i < args.count(); i++) { 0098 auto match = number.match(args.at(i)); 0099 if (match.hasMatch()) { 0100 count += match.captured(0).toInt() - 1; 0101 } 0102 0103 QChar r = args.at(i).at(0); 0104 if (args.at(i).size() == 1 0105 && ((r >= QLatin1Char('a') && r <= QLatin1Char('z')) || r == QLatin1Char('_') || r == QLatin1Char('-') || r == QLatin1Char('+') 0106 || r == QLatin1Char('*'))) { 0107 nm->setRegister(r); 0108 } 0109 } 0110 0111 nm->setCount(count); 0112 0113 if (cmd == QLatin1String("d") || cmd == QLatin1String("delete")) { 0114 nm->commandDeleteLine(); 0115 } 0116 if (cmd == QLatin1String("j")) { 0117 nm->commandJoinLines(); 0118 } 0119 if (cmd == QLatin1String("c") || cmd == QLatin1String("change")) { 0120 nm->commandChangeLine(); 0121 } 0122 if (cmd == QLatin1String("<")) { 0123 nm->commandUnindentLine(); 0124 } 0125 if (cmd == QLatin1String(">")) { 0126 nm->commandIndentLine(); 0127 } 0128 if (cmd == QLatin1String("y") || cmd == QLatin1String("yank")) { 0129 nm->commandYankLine(); 0130 v->setCursorPosition(start_cursor_position); 0131 } 0132 0133 // TODO - should we resetParser, here? We'd have to make it public, if so. 0134 // Or maybe synthesise a KateViCommand to execute instead ... ? 0135 nm->setCount(0); 0136 0137 return true; 0138 } 0139 0140 if (cmd == QLatin1String("mark") || cmd == QLatin1String("ma") || cmd == QLatin1String("k")) { 0141 if (args.count() == 0) { 0142 if (cmd == QLatin1String("mark")) { 0143 // TODO: show up mark list; 0144 } else { 0145 msg = i18n("Wrong arguments"); 0146 return false; 0147 } 0148 } else if (args.count() == 1) { 0149 QChar r = args.at(0).at(0); 0150 int line; 0151 if ((r >= QLatin1Char('a') && r <= QLatin1Char('z')) || r == QLatin1Char('_') || r == QLatin1Char('+') || r == QLatin1Char('*')) { 0152 if (range.isValid()) { 0153 line = qMax(range.end().line(), range.start().line()); 0154 } else { 0155 line = v->cursorPosition().line(); 0156 } 0157 0158 m_viInputModeManager->marks()->setUserMark(r, KTextEditor::Cursor(line, 0)); 0159 } 0160 } else { 0161 msg = i18n("Wrong arguments"); 0162 return false; 0163 } 0164 return true; 0165 } 0166 0167 // should not happen :) 0168 msg = i18n("Unknown command '%1'", cmd); 0169 return false; 0170 } 0171 0172 bool Commands::supportsRange(const QString &range) 0173 { 0174 static QStringList l; 0175 0176 if (l.isEmpty()) { 0177 l << QStringLiteral("d") << QStringLiteral("delete") << QStringLiteral("j") << QStringLiteral("c") << QStringLiteral("change") << QStringLiteral("<") 0178 << QStringLiteral(">") << QStringLiteral("y") << QStringLiteral("yank") << QStringLiteral("ma") << QStringLiteral("mark") << QStringLiteral("k"); 0179 } 0180 0181 return l.contains(range.split(QLatin1Char(' ')).at(0)); 0182 } 0183 0184 KCompletion *Commands::completionObject(KTextEditor::View *view, const QString &cmd) 0185 { 0186 Q_UNUSED(view) 0187 0188 KTextEditor::ViewPrivate *v = static_cast<KTextEditor::ViewPrivate *>(view); 0189 0190 if (v && (cmd == QLatin1String("nn") || cmd == QLatin1String("nnoremap"))) { 0191 QStringList l = m_viInputModeManager->globalState()->mappings()->getAll(Mappings::NormalModeMapping); 0192 0193 KateCmdShellCompletion *co = new KateCmdShellCompletion(); 0194 co->setItems(l); 0195 co->setIgnoreCase(false); 0196 return co; 0197 } 0198 return nullptr; 0199 } 0200 0201 const QStringList &Commands::mappingCommands() 0202 { 0203 static QStringList mappingsCommands; 0204 if (mappingsCommands.isEmpty()) { 0205 mappingsCommands << QStringLiteral("nmap") << QStringLiteral("nm") << QStringLiteral("noremap") << QStringLiteral("nnoremap") << QStringLiteral("nn") 0206 << QStringLiteral("no") << QStringLiteral("vmap") << QStringLiteral("vm") << QStringLiteral("vnoremap") << QStringLiteral("vn") 0207 << QStringLiteral("imap") << QStringLiteral("im") << QStringLiteral("inoremap") << QStringLiteral("ino") << QStringLiteral("cmap") 0208 << QStringLiteral("cm") << QStringLiteral("cnoremap") << QStringLiteral("cno"); 0209 0210 mappingsCommands << QStringLiteral("nunmap") << QStringLiteral("vunmap") << QStringLiteral("iunmap") << QStringLiteral("cunmap"); 0211 0212 mappingsCommands << QStringLiteral("nohlsearch") << QStringLiteral("noh") << QStringLiteral("set-hlsearch") << QStringLiteral("set-hls") 0213 << QStringLiteral("set-nohlsearch") << QStringLiteral("set-nohls"); 0214 } 0215 return mappingsCommands; 0216 } 0217 0218 Mappings::MappingMode Commands::modeForMapCommand(const QString &mapCommand) 0219 { 0220 if (mapCommand.startsWith(u'v')) { 0221 if (mapCommand == u"vmap" || mapCommand == u"vm" || mapCommand == u"vn" || mapCommand == u"vnoremap" || mapCommand == u"vunmap") { 0222 return Mappings::VisualModeMapping; 0223 } 0224 } 0225 0226 if (mapCommand.startsWith(u'i')) { 0227 if (mapCommand == u"imap" || mapCommand == u"im" || mapCommand == u"ino" || mapCommand == u"inoremap" || mapCommand == u"iunmap") { 0228 return Mappings::InsertModeMapping; 0229 } 0230 } 0231 0232 if (mapCommand.startsWith(u'c')) { 0233 if (mapCommand == u"cmap" || mapCommand == u"cm" || mapCommand == u"cno" || mapCommand == u"cnoremap" || mapCommand == u"cunmap") { 0234 return Mappings::CommandModeMapping; 0235 } 0236 } 0237 0238 // if (mapCommand == u"nunmap") { 0239 // return Mappings::NormalModeMapping; 0240 // } 0241 return Mappings::NormalModeMapping; 0242 } 0243 0244 bool Commands::isMapCommandRecursive(const QString &mapCommand) 0245 { 0246 return mapCommand == u"nmap" || mapCommand == u"nm" // 0247 || mapCommand == u"vmap" || mapCommand == u"vm" // 0248 || mapCommand == u"imap" || mapCommand == u"im" // 0249 || mapCommand == u"cmap" || mapCommand == u"cm"; 0250 } 0251 0252 // END ViCommands 0253 0254 // BEGIN SedReplace 0255 SedReplace *SedReplace::m_instance = nullptr; 0256 0257 bool SedReplace::interactiveSedReplace(KTextEditor::ViewPrivate *, std::shared_ptr<InteractiveSedReplacer> interactiveSedReplace) 0258 { 0259 EmulatedCommandBar *emulatedCommandBar = m_viInputModeManager->inputAdapter()->viModeEmulatedCommandBar(); 0260 emulatedCommandBar->startInteractiveSearchAndReplace(interactiveSedReplace); 0261 return true; 0262 } 0263 // END SedReplace