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