File indexing completed on 2024-04-14 03:55:48

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