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 }