File indexing completed on 2024-04-28 15:31:18

0001 /*
0002     SPDX-FileCopyrightText: 2008-2009 Erlend Hamberg <ehamberg@gmail.com>
0003     SPDX-FileCopyrightText: 2009 Paul Gideon Dann <pdgiddie@gmail.com>
0004     SPDX-FileCopyrightText: 2011 Svyatoslav Kuzmich <svatoslav1@gmail.com>
0005     SPDX-FileCopyrightText: 2012-2013 Simon St James <kdedevel@etotheipiplusone.com>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include <vimode/inputmodemanager.h>
0011 
0012 #include <QApplication>
0013 #include <QString>
0014 
0015 #include <KConfig>
0016 #include <KConfigGroup>
0017 #include <KLocalizedString>
0018 
0019 #include "completionrecorder.h"
0020 #include "completionreplayer.h"
0021 #include "definitions.h"
0022 #include "globalstate.h"
0023 #include "jumps.h"
0024 #include "kateconfig.h"
0025 #include "katedocument.h"
0026 #include "kateglobal.h"
0027 #include "kateviewinternal.h"
0028 #include "kateviinputmode.h"
0029 #include "lastchangerecorder.h"
0030 #include "macrorecorder.h"
0031 #include "macros.h"
0032 #include "marks.h"
0033 #include "registers.h"
0034 #include "searcher.h"
0035 #include "variable.h"
0036 #include <vimode/emulatedcommandbar/emulatedcommandbar.h>
0037 #include <vimode/keymapper.h>
0038 #include <vimode/keyparser.h>
0039 #include <vimode/modes/insertvimode.h>
0040 #include <vimode/modes/normalvimode.h>
0041 #include <vimode/modes/replacevimode.h>
0042 #include <vimode/modes/visualvimode.h>
0043 
0044 using namespace KateVi;
0045 
0046 InputModeManager::InputModeManager(KateViInputMode *inputAdapter, KTextEditor::ViewPrivate *view, KateViewInternal *viewInternal)
0047     : m_inputAdapter(inputAdapter)
0048 {
0049     m_currentViMode = ViMode::NormalMode;
0050     m_previousViMode = ViMode::NormalMode;
0051 
0052     m_viNormalMode = new NormalViMode(this, view, viewInternal);
0053     m_viInsertMode = new InsertViMode(this, view, viewInternal);
0054     m_viVisualMode = new VisualViMode(this, view, viewInternal);
0055     m_viReplaceMode = new ReplaceViMode(this, view, viewInternal);
0056 
0057     m_view = view;
0058     m_viewInternal = viewInternal;
0059 
0060     m_insideHandlingKeyPressCount = 0;
0061 
0062     m_keyMapperStack.push(QSharedPointer<KeyMapper>(new KeyMapper(this, m_view->doc(), m_view)));
0063 
0064     m_temporaryNormalMode = false;
0065 
0066     m_jumps = new Jumps();
0067     m_marks = new Marks(this);
0068 
0069     m_searcher = new Searcher(this);
0070     m_completionRecorder = new CompletionRecorder(this);
0071     m_completionReplayer = new CompletionReplayer(this);
0072 
0073     m_macroRecorder = new MacroRecorder(this);
0074 
0075     m_lastChangeRecorder = new LastChangeRecorder(this);
0076 
0077     // We have to do this outside of NormalMode, as we don't want
0078     // VisualMode (which inherits from NormalMode) to respond
0079     // to changes in the document as well.
0080     m_viNormalMode->beginMonitoringDocumentChanges();
0081 }
0082 
0083 InputModeManager::~InputModeManager()
0084 {
0085     delete m_viNormalMode;
0086     delete m_viInsertMode;
0087     delete m_viVisualMode;
0088     delete m_viReplaceMode;
0089     delete m_jumps;
0090     delete m_marks;
0091     delete m_searcher;
0092     delete m_macroRecorder;
0093     delete m_completionRecorder;
0094     delete m_completionReplayer;
0095     delete m_lastChangeRecorder;
0096 }
0097 
0098 bool InputModeManager::handleKeypress(const QKeyEvent *e)
0099 {
0100     m_insideHandlingKeyPressCount++;
0101     bool res = false;
0102     bool keyIsPartOfMapping = false;
0103     const bool isSyntheticSearchCompletedKeyPress = m_inputAdapter->viModeEmulatedCommandBar()->isSendingSyntheticSearchCompletedKeypress();
0104 
0105     // With macros, we want to record the keypresses *before* they are mapped, but if they end up *not* being part
0106     // of a mapping, we don't want to record them when they are played back by m_keyMapper, hence
0107     // the "!isPlayingBackRejectedKeys()". And obviously, since we're recording keys before they are mapped, we don't
0108     // want to also record the executed mapping, as when we replayed the macro, we'd get duplication!
0109     if (m_macroRecorder->isRecording() && !m_macroRecorder->isReplaying() && !isSyntheticSearchCompletedKeyPress && !keyMapper()->isExecutingMapping()
0110         && !keyMapper()->isPlayingBackRejectedKeys() && !lastChangeRecorder()->isReplaying()) {
0111         m_macroRecorder->record(*e);
0112     }
0113 
0114     if (!m_lastChangeRecorder->isReplaying() && !isSyntheticSearchCompletedKeyPress) {
0115         if (e->key() == Qt::Key_AltGr) {
0116             return true; // do nothing
0117         }
0118 
0119         // Hand off to the key mapper, and decide if this key is part of a mapping.
0120         if (e->key() != Qt::Key_Control && e->key() != Qt::Key_Shift && e->key() != Qt::Key_Alt && e->key() != Qt::Key_Meta) {
0121             const QChar key = KeyParser::self()->KeyEventToQChar(*e);
0122             if (keyMapper()->handleKeypress(key)) {
0123                 keyIsPartOfMapping = true;
0124                 res = true;
0125             }
0126         }
0127     }
0128 
0129     if (!keyIsPartOfMapping) {
0130         if (!m_lastChangeRecorder->isReplaying() && !isSyntheticSearchCompletedKeyPress) {
0131             // record key press so that it can be repeated via "."
0132             m_lastChangeRecorder->record(*e);
0133         }
0134 
0135         if (m_inputAdapter->viModeEmulatedCommandBar()->isActive()) {
0136             res = m_inputAdapter->viModeEmulatedCommandBar()->handleKeyPress(e);
0137         } else {
0138             res = getCurrentViModeHandler()->handleKeypress(e);
0139         }
0140     }
0141 
0142     m_insideHandlingKeyPressCount--;
0143     Q_ASSERT(m_insideHandlingKeyPressCount >= 0);
0144 
0145     return res;
0146 }
0147 
0148 void InputModeManager::feedKeyPresses(const QString &keyPresses) const
0149 {
0150     int key;
0151     Qt::KeyboardModifiers mods;
0152     QString text;
0153 
0154     for (const QChar c : keyPresses) {
0155         QString decoded = KeyParser::self()->decodeKeySequence(QString(c));
0156         key = -1;
0157         mods = Qt::NoModifier;
0158         text.clear();
0159 
0160         if (decoded.length() > 1) { // special key
0161 
0162             // remove the angle brackets
0163             decoded.remove(0, 1);
0164             decoded.remove(decoded.indexOf(QLatin1Char('>')), 1);
0165 
0166             // check if one or more modifier keys where used
0167             if (decoded.indexOf(QLatin1String("s-")) != -1 || decoded.indexOf(QLatin1String("c-")) != -1 || decoded.indexOf(QLatin1String("m-")) != -1
0168                 || decoded.indexOf(QLatin1String("a-")) != -1) {
0169                 int s = decoded.indexOf(QLatin1String("s-"));
0170                 if (s != -1) {
0171                     mods |= Qt::ShiftModifier;
0172                     decoded.remove(s, 2);
0173                 }
0174 
0175                 int c = decoded.indexOf(QLatin1String("c-"));
0176                 if (c != -1) {
0177                     mods |= CONTROL_MODIFIER;
0178                     decoded.remove(c, 2);
0179                 }
0180 
0181                 int a = decoded.indexOf(QLatin1String("a-"));
0182                 if (a != -1) {
0183                     mods |= Qt::AltModifier;
0184                     decoded.remove(a, 2);
0185                 }
0186 
0187                 int m = decoded.indexOf(QLatin1String("m-"));
0188                 if (m != -1) {
0189                     mods |= META_MODIFIER;
0190                     decoded.remove(m, 2);
0191                 }
0192 
0193                 if (decoded.length() > 1) {
0194                     key = KeyParser::self()->vi2qt(decoded);
0195                 } else if (decoded.length() == 1) {
0196                     key = int(decoded.at(0).toUpper().toLatin1());
0197                     text = decoded.at(0);
0198                 }
0199             } else { // no modifiers
0200                 key = KeyParser::self()->vi2qt(decoded);
0201             }
0202         } else {
0203             key = decoded.at(0).unicode();
0204             text = decoded.at(0);
0205         }
0206 
0207         if (key == -1) {
0208             continue;
0209         }
0210 
0211         // We have to be clever about which widget we dispatch to, as we can trigger
0212         // shortcuts if we're not careful (even if Vim mode is configured to steal shortcuts).
0213         QKeyEvent k(QEvent::KeyPress, key, mods, text);
0214         QWidget *destWidget = nullptr;
0215         if (QApplication::activePopupWidget()) {
0216             // According to the docs, the activePopupWidget, if present, takes all events.
0217             destWidget = QApplication::activePopupWidget();
0218         } else if (QApplication::focusWidget()) {
0219             if (QApplication::focusWidget()->focusProxy()) {
0220                 destWidget = QApplication::focusWidget()->focusProxy();
0221             } else {
0222                 destWidget = QApplication::focusWidget();
0223             }
0224         } else {
0225             destWidget = m_view->focusProxy();
0226         }
0227         QApplication::sendEvent(destWidget, &k);
0228     }
0229 }
0230 
0231 bool InputModeManager::isHandlingKeypress() const
0232 {
0233     return m_insideHandlingKeyPressCount > 0;
0234 }
0235 
0236 void InputModeManager::storeLastChangeCommand()
0237 {
0238     m_lastChange = m_lastChangeRecorder->encodedChanges();
0239     m_lastChangeCompletionsLog = m_completionRecorder->currentChangeCompletionsLog();
0240 }
0241 
0242 void InputModeManager::repeatLastChange()
0243 {
0244     m_lastChangeRecorder->replay(m_lastChange, m_lastChangeCompletionsLog);
0245 }
0246 
0247 void InputModeManager::clearCurrentChangeLog()
0248 {
0249     m_lastChangeRecorder->clear();
0250     m_completionRecorder->clearCurrentChangeCompletionsLog();
0251 }
0252 
0253 void InputModeManager::doNotLogCurrentKeypress()
0254 {
0255     m_macroRecorder->dropLast();
0256     m_lastChangeRecorder->dropLast();
0257 }
0258 
0259 void InputModeManager::changeViMode(ViMode newMode)
0260 {
0261     m_previousViMode = m_currentViMode;
0262     m_currentViMode = newMode;
0263 }
0264 
0265 ViMode InputModeManager::getCurrentViMode() const
0266 {
0267     return m_currentViMode;
0268 }
0269 
0270 KTextEditor::View::ViewMode InputModeManager::getCurrentViewMode() const
0271 {
0272     switch (m_currentViMode) {
0273     case ViMode::InsertMode:
0274         return KTextEditor::View::ViModeInsert;
0275     case ViMode::VisualMode:
0276         return KTextEditor::View::ViModeVisual;
0277     case ViMode::VisualLineMode:
0278         return KTextEditor::View::ViModeVisualLine;
0279     case ViMode::VisualBlockMode:
0280         return KTextEditor::View::ViModeVisualBlock;
0281     case ViMode::ReplaceMode:
0282         return KTextEditor::View::ViModeReplace;
0283     case ViMode::NormalMode:
0284     default:
0285         return KTextEditor::View::ViModeNormal;
0286     }
0287 }
0288 
0289 ViMode InputModeManager::getPreviousViMode() const
0290 {
0291     return m_previousViMode;
0292 }
0293 
0294 bool InputModeManager::isAnyVisualMode() const
0295 {
0296     return ((m_currentViMode == ViMode::VisualMode) || (m_currentViMode == ViMode::VisualLineMode) || (m_currentViMode == ViMode::VisualBlockMode));
0297 }
0298 
0299 ::ModeBase *InputModeManager::getCurrentViModeHandler() const
0300 {
0301     switch (m_currentViMode) {
0302     case ViMode::NormalMode:
0303         return m_viNormalMode;
0304     case ViMode::InsertMode:
0305         return m_viInsertMode;
0306     case ViMode::VisualMode:
0307     case ViMode::VisualLineMode:
0308     case ViMode::VisualBlockMode:
0309         return m_viVisualMode;
0310     case ViMode::ReplaceMode:
0311         return m_viReplaceMode;
0312     }
0313     return nullptr;
0314 }
0315 
0316 void InputModeManager::viEnterNormalMode()
0317 {
0318     bool moveCursorLeft = (m_currentViMode == ViMode::InsertMode || m_currentViMode == ViMode::ReplaceMode) && m_viewInternal->cursorPosition().column() > 0;
0319 
0320     if (!m_lastChangeRecorder->isReplaying() && (m_currentViMode == ViMode::InsertMode || m_currentViMode == ViMode::ReplaceMode)) {
0321         // '^ is the insert mark and "^ is the insert register,
0322         // which holds the last inserted text
0323         KTextEditor::Range r(m_view->cursorPosition(), m_marks->getInsertStopped());
0324 
0325         if (r.isValid()) {
0326             QString insertedText = m_view->doc()->text(r);
0327             m_inputAdapter->globalState()->registers()->setInsertStopped(insertedText);
0328         }
0329 
0330         m_marks->setInsertStopped(KTextEditor::Cursor(m_view->cursorPosition()));
0331     }
0332 
0333     changeViMode(ViMode::NormalMode);
0334 
0335     if (moveCursorLeft) {
0336         m_viewInternal->cursorPrevChar();
0337     }
0338     m_inputAdapter->setCaretStyle(KateRenderer::Block);
0339     m_viewInternal->update();
0340 }
0341 
0342 void InputModeManager::viEnterInsertMode()
0343 {
0344     changeViMode(ViMode::InsertMode);
0345     m_marks->setInsertStopped(KTextEditor::Cursor(m_view->cursorPosition()));
0346     if (getTemporaryNormalMode()) {
0347         // Ensure the key log contains a request to re-enter Insert mode, else the keystrokes made
0348         // after returning from temporary normal mode will be treated as commands!
0349         m_lastChangeRecorder->record(QKeyEvent(QEvent::KeyPress, Qt::Key_I, Qt::NoModifier, QStringLiteral("i")));
0350     }
0351     m_inputAdapter->setCaretStyle(KateRenderer::Line);
0352     setTemporaryNormalMode(false);
0353     m_viewInternal->update();
0354 }
0355 
0356 void InputModeManager::viEnterVisualMode(ViMode mode)
0357 {
0358     changeViMode(mode);
0359 
0360     // If the selection is inclusive, the caret should be a block.
0361     // If the selection is exclusive, the caret should be a line.
0362     m_inputAdapter->setCaretStyle(KateRenderer::Block);
0363     m_viewInternal->update();
0364     getViVisualMode()->setVisualModeType(mode);
0365     getViVisualMode()->init();
0366 }
0367 
0368 void InputModeManager::viEnterReplaceMode()
0369 {
0370     changeViMode(ViMode::ReplaceMode);
0371     m_marks->setStartEditYanked(KTextEditor::Cursor(m_view->cursorPosition()));
0372     m_inputAdapter->setCaretStyle(KateRenderer::Underline);
0373     m_viewInternal->update();
0374 }
0375 
0376 NormalViMode *InputModeManager::getViNormalMode()
0377 {
0378     return m_viNormalMode;
0379 }
0380 
0381 InsertViMode *InputModeManager::getViInsertMode()
0382 {
0383     return m_viInsertMode;
0384 }
0385 
0386 VisualViMode *InputModeManager::getViVisualMode()
0387 {
0388     return m_viVisualMode;
0389 }
0390 
0391 ReplaceViMode *InputModeManager::getViReplaceMode()
0392 {
0393     return m_viReplaceMode;
0394 }
0395 
0396 const QString InputModeManager::getVerbatimKeys() const
0397 {
0398     QString cmd;
0399 
0400     switch (getCurrentViMode()) {
0401     case ViMode::NormalMode:
0402         cmd = m_viNormalMode->getVerbatimKeys();
0403         break;
0404     case ViMode::InsertMode:
0405     case ViMode::ReplaceMode:
0406         // ...
0407         break;
0408     case ViMode::VisualMode:
0409     case ViMode::VisualLineMode:
0410     case ViMode::VisualBlockMode:
0411         cmd = m_viVisualMode->getVerbatimKeys();
0412         break;
0413     }
0414 
0415     return cmd;
0416 }
0417 
0418 void InputModeManager::readSessionConfig(const KConfigGroup &config)
0419 {
0420     m_jumps->readSessionConfig(config);
0421     m_marks->readSessionConfig(config);
0422 }
0423 
0424 void InputModeManager::writeSessionConfig(KConfigGroup &config)
0425 {
0426     m_jumps->writeSessionConfig(config);
0427     m_marks->writeSessionConfig(config);
0428 }
0429 
0430 void InputModeManager::reset()
0431 {
0432     if (m_viVisualMode) {
0433         m_viVisualMode->reset();
0434     }
0435 }
0436 
0437 KeyMapper *InputModeManager::keyMapper()
0438 {
0439     return m_keyMapperStack.top().data();
0440 }
0441 
0442 void InputModeManager::updateCursor(const KTextEditor::Cursor c)
0443 {
0444     m_inputAdapter->updateCursor(c);
0445 }
0446 
0447 GlobalState *InputModeManager::globalState() const
0448 {
0449     return m_inputAdapter->globalState();
0450 }
0451 
0452 KTextEditor::ViewPrivate *InputModeManager::view() const
0453 {
0454     return m_view;
0455 }
0456 
0457 void InputModeManager::pushKeyMapper(QSharedPointer<KeyMapper> mapper)
0458 {
0459     m_keyMapperStack.push(mapper);
0460 }
0461 
0462 void InputModeManager::popKeyMapper()
0463 {
0464     m_keyMapperStack.pop();
0465 }