1 /*
2     SPDX-FileCopyrightText: 2008-2009 Erlend Hamberg <ehamberg@gmail.com>
3     SPDX-FileCopyrightText: 2009 Paul Gideon Dann <pdgiddie@gmail.com>
4     SPDX-FileCopyrightText: 2011 Svyatoslav Kuzmich <svatoslav1@gmail.com>
5     SPDX-FileCopyrightText: 2012-2013 Simon St James <kdedevel@etotheipiplusone.com>
6 
7     SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9 
10 #include <vimode/inputmodemanager.h>
11 
12 #include <QApplication>
13 #include <QString>
14 
15 #include <KConfig>
16 #include <KConfigGroup>
17 #include <KLocalizedString>
18 
19 #include "completionrecorder.h"
20 #include "completionreplayer.h"
21 #include "globalstate.h"
22 #include "jumps.h"
23 #include "kateconfig.h"
24 #include "katedocument.h"
25 #include "kateglobal.h"
26 #include "kateviewinternal.h"
27 #include "kateviinputmode.h"
28 #include "lastchangerecorder.h"
29 #include "macrorecorder.h"
30 #include "macros.h"
31 #include "marks.h"
32 #include "registers.h"
33 #include "searcher.h"
34 #include "variable.h"
35 #include <vimode/emulatedcommandbar/emulatedcommandbar.h>
36 #include <vimode/keymapper.h>
37 #include <vimode/keyparser.h>
38 #include <vimode/modes/insertvimode.h>
39 #include <vimode/modes/normalvimode.h>
40 #include <vimode/modes/replacevimode.h>
41 #include <vimode/modes/visualvimode.h>
42 
43 using namespace KateVi;
44 
InputModeManager(KateViInputMode * inputAdapter,KTextEditor::ViewPrivate * view,KateViewInternal * viewInternal)45 InputModeManager::InputModeManager(KateViInputMode *inputAdapter, KTextEditor::ViewPrivate *view, KateViewInternal *viewInternal)
46     : m_inputAdapter(inputAdapter)
47 {
48     m_currentViMode = ViMode::NormalMode;
49     m_previousViMode = ViMode::NormalMode;
50 
51     m_viNormalMode = new NormalViMode(this, view, viewInternal);
52     m_viInsertMode = new InsertViMode(this, view, viewInternal);
53     m_viVisualMode = new VisualViMode(this, view, viewInternal);
54     m_viReplaceMode = new ReplaceViMode(this, view, viewInternal);
55 
56     m_view = view;
57     m_viewInternal = viewInternal;
58 
59     m_insideHandlingKeyPressCount = 0;
60 
61     m_keyMapperStack.push(QSharedPointer<KeyMapper>(new KeyMapper(this, m_view->doc(), m_view)));
62 
63     m_temporaryNormalMode = false;
64 
65     m_jumps = new Jumps();
66     m_marks = new Marks(this);
67 
68     m_searcher = new Searcher(this);
69     m_completionRecorder = new CompletionRecorder(this);
70     m_completionReplayer = new CompletionReplayer(this);
71 
72     m_macroRecorder = new MacroRecorder(this);
73 
74     m_lastChangeRecorder = new LastChangeRecorder(this);
75 
76     // We have to do this outside of NormalMode, as we don't want
77     // VisualMode (which inherits from NormalMode) to respond
78     // to changes in the document as well.
79     m_viNormalMode->beginMonitoringDocumentChanges();
80 }
81 
~InputModeManager()82 InputModeManager::~InputModeManager()
83 {
84     delete m_viNormalMode;
85     delete m_viInsertMode;
86     delete m_viVisualMode;
87     delete m_viReplaceMode;
88     delete m_jumps;
89     delete m_marks;
90     delete m_searcher;
91     delete m_macroRecorder;
92     delete m_completionRecorder;
93     delete m_completionReplayer;
94     delete m_lastChangeRecorder;
95 }
96 
handleKeypress(const QKeyEvent * e)97 bool InputModeManager::handleKeypress(const QKeyEvent *e)
98 {
99     m_insideHandlingKeyPressCount++;
100     bool res = false;
101     bool keyIsPartOfMapping = false;
102     const bool isSyntheticSearchCompletedKeyPress = m_inputAdapter->viModeEmulatedCommandBar()->isSendingSyntheticSearchCompletedKeypress();
103 
104     // With macros, we want to record the keypresses *before* they are mapped, but if they end up *not* being part
105     // of a mapping, we don't want to record them when they are played back by m_keyMapper, hence
106     // the "!isPlayingBackRejectedKeys()". And obviously, since we're recording keys before they are mapped, we don't
107     // want to also record the executed mapping, as when we replayed the macro, we'd get duplication!
108     if (m_macroRecorder->isRecording() && !m_macroRecorder->isReplaying() && !isSyntheticSearchCompletedKeyPress && !keyMapper()->isExecutingMapping()
109         && !keyMapper()->isPlayingBackRejectedKeys() && !lastChangeRecorder()->isReplaying()) {
110         m_macroRecorder->record(*e);
111     }
112 
113     if (!m_lastChangeRecorder->isReplaying() && !isSyntheticSearchCompletedKeyPress) {
114         if (e->key() == Qt::Key_AltGr) {
115             return true; // do nothing
116         }
117 
118         // Hand off to the key mapper, and decide if this key is part of a mapping.
119         if (e->key() != Qt::Key_Control && e->key() != Qt::Key_Shift && e->key() != Qt::Key_Alt && e->key() != Qt::Key_Meta) {
120             const QChar key = KeyParser::self()->KeyEventToQChar(*e);
121             if (keyMapper()->handleKeypress(key)) {
122                 keyIsPartOfMapping = true;
123                 res = true;
124             }
125         }
126     }
127 
128     if (!keyIsPartOfMapping) {
129         if (!m_lastChangeRecorder->isReplaying() && !isSyntheticSearchCompletedKeyPress) {
130             // record key press so that it can be repeated via "."
131             m_lastChangeRecorder->record(*e);
132         }
133 
134         if (m_inputAdapter->viModeEmulatedCommandBar()->isActive()) {
135             res = m_inputAdapter->viModeEmulatedCommandBar()->handleKeyPress(e);
136         } else {
137             res = getCurrentViModeHandler()->handleKeypress(e);
138         }
139     }
140 
141     m_insideHandlingKeyPressCount--;
142     Q_ASSERT(m_insideHandlingKeyPressCount >= 0);
143 
144     return res;
145 }
146 
feedKeyPresses(const QString & keyPresses) const147 void InputModeManager::feedKeyPresses(const QString &keyPresses) const
148 {
149     int key;
150     Qt::KeyboardModifiers mods;
151     QString text;
152 
153     for (const QChar c : keyPresses) {
154         QString decoded = KeyParser::self()->decodeKeySequence(QString(c));
155         key = -1;
156         mods = Qt::NoModifier;
157         text.clear();
158 
159         if (decoded.length() > 1) { // special key
160 
161             // remove the angle brackets
162             decoded.remove(0, 1);
163             decoded.remove(decoded.indexOf(QLatin1Char('>')), 1);
164 
165             // check if one or more modifier keys where used
166             if (decoded.indexOf(QLatin1String("s-")) != -1 || decoded.indexOf(QLatin1String("c-")) != -1 || decoded.indexOf(QLatin1String("m-")) != -1
167                 || decoded.indexOf(QLatin1String("a-")) != -1) {
168                 int s = decoded.indexOf(QLatin1String("s-"));
169                 if (s != -1) {
170                     mods |= Qt::ShiftModifier;
171                     decoded.remove(s, 2);
172                 }
173 
174                 int c = decoded.indexOf(QLatin1String("c-"));
175                 if (c != -1) {
176                     mods |= Qt::ControlModifier;
177                     decoded.remove(c, 2);
178                 }
179 
180                 int a = decoded.indexOf(QLatin1String("a-"));
181                 if (a != -1) {
182                     mods |= Qt::AltModifier;
183                     decoded.remove(a, 2);
184                 }
185 
186                 int m = decoded.indexOf(QLatin1String("m-"));
187                 if (m != -1) {
188                     mods |= Qt::MetaModifier;
189                     decoded.remove(m, 2);
190                 }
191 
192                 if (decoded.length() > 1) {
193                     key = KeyParser::self()->vi2qt(decoded);
194                 } else if (decoded.length() == 1) {
195                     key = int(decoded.at(0).toUpper().toLatin1());
196                     text = decoded.at(0);
197                 }
198             } else { // no modifiers
199                 key = KeyParser::self()->vi2qt(decoded);
200             }
201         } else {
202             key = decoded.at(0).unicode();
203             text = decoded.at(0);
204         }
205 
206         if (key == -1) {
207             continue;
208         }
209 
210         // We have to be clever about which widget we dispatch to, as we can trigger
211         // shortcuts if we're not careful (even if Vim mode is configured to steal shortcuts).
212         QKeyEvent k(QEvent::KeyPress, key, mods, text);
213         QWidget *destWidget = nullptr;
214         if (QApplication::activePopupWidget()) {
215             // According to the docs, the activePopupWidget, if present, takes all events.
216             destWidget = QApplication::activePopupWidget();
217         } else if (QApplication::focusWidget()) {
218             if (QApplication::focusWidget()->focusProxy()) {
219                 destWidget = QApplication::focusWidget()->focusProxy();
220             } else {
221                 destWidget = QApplication::focusWidget();
222             }
223         } else {
224             destWidget = m_view->focusProxy();
225         }
226         QApplication::sendEvent(destWidget, &k);
227     }
228 }
229 
isHandlingKeypress() const230 bool InputModeManager::isHandlingKeypress() const
231 {
232     return m_insideHandlingKeyPressCount > 0;
233 }
234 
storeLastChangeCommand()235 void InputModeManager::storeLastChangeCommand()
236 {
237     m_lastChange = m_lastChangeRecorder->encodedChanges();
238     m_lastChangeCompletionsLog = m_completionRecorder->currentChangeCompletionsLog();
239 }
240 
repeatLastChange()241 void InputModeManager::repeatLastChange()
242 {
243     m_lastChangeRecorder->replay(m_lastChange, m_lastChangeCompletionsLog);
244 }
245 
clearCurrentChangeLog()246 void InputModeManager::clearCurrentChangeLog()
247 {
248     m_lastChangeRecorder->clear();
249     m_completionRecorder->clearCurrentChangeCompletionsLog();
250 }
251 
doNotLogCurrentKeypress()252 void InputModeManager::doNotLogCurrentKeypress()
253 {
254     m_macroRecorder->dropLast();
255     m_lastChangeRecorder->dropLast();
256 }
257 
changeViMode(ViMode newMode)258 void InputModeManager::changeViMode(ViMode newMode)
259 {
260     m_previousViMode = m_currentViMode;
261     m_currentViMode = newMode;
262 }
263 
getCurrentViMode() const264 ViMode InputModeManager::getCurrentViMode() const
265 {
266     return m_currentViMode;
267 }
268 
getCurrentViewMode() const269 KTextEditor::View::ViewMode InputModeManager::getCurrentViewMode() const
270 {
271     switch (m_currentViMode) {
272     case ViMode::InsertMode:
273         return KTextEditor::View::ViModeInsert;
274     case ViMode::VisualMode:
275         return KTextEditor::View::ViModeVisual;
276     case ViMode::VisualLineMode:
277         return KTextEditor::View::ViModeVisualLine;
278     case ViMode::VisualBlockMode:
279         return KTextEditor::View::ViModeVisualBlock;
280     case ViMode::ReplaceMode:
281         return KTextEditor::View::ViModeReplace;
282     case ViMode::NormalMode:
283     default:
284         return KTextEditor::View::ViModeNormal;
285     }
286 }
287 
getPreviousViMode() const288 ViMode InputModeManager::getPreviousViMode() const
289 {
290     return m_previousViMode;
291 }
292 
isAnyVisualMode() const293 bool InputModeManager::isAnyVisualMode() const
294 {
295     return ((m_currentViMode == ViMode::VisualMode) || (m_currentViMode == ViMode::VisualLineMode) || (m_currentViMode == ViMode::VisualBlockMode));
296 }
297 
getCurrentViModeHandler() const298 ::ModeBase *InputModeManager::getCurrentViModeHandler() const
299 {
300     switch (m_currentViMode) {
301     case ViMode::NormalMode:
302         return m_viNormalMode;
303     case ViMode::InsertMode:
304         return m_viInsertMode;
305     case ViMode::VisualMode:
306     case ViMode::VisualLineMode:
307     case ViMode::VisualBlockMode:
308         return m_viVisualMode;
309     case ViMode::ReplaceMode:
310         return m_viReplaceMode;
311     }
312     return nullptr;
313 }
314 
viEnterNormalMode()315 void InputModeManager::viEnterNormalMode()
316 {
317     bool moveCursorLeft = (m_currentViMode == ViMode::InsertMode || m_currentViMode == ViMode::ReplaceMode) && m_viewInternal->cursorPosition().column() > 0;
318 
319     if (!m_lastChangeRecorder->isReplaying() && (m_currentViMode == ViMode::InsertMode || m_currentViMode == ViMode::ReplaceMode)) {
320         // '^ is the insert mark and "^ is the insert register,
321         // which holds the last inserted text
322         KTextEditor::Range r(m_view->cursorPosition(), m_marks->getInsertStopped());
323 
324         if (r.isValid()) {
325             QString insertedText = m_view->doc()->text(r);
326             m_inputAdapter->globalState()->registers()->setInsertStopped(insertedText);
327         }
328 
329         m_marks->setInsertStopped(KTextEditor::Cursor(m_view->cursorPosition()));
330     }
331 
332     changeViMode(ViMode::NormalMode);
333 
334     if (moveCursorLeft) {
335         m_viewInternal->cursorPrevChar();
336     }
337     m_inputAdapter->setCaretStyle(KateRenderer::Block);
338     m_viewInternal->update();
339 }
340 
viEnterInsertMode()341 void InputModeManager::viEnterInsertMode()
342 {
343     changeViMode(ViMode::InsertMode);
344     m_marks->setInsertStopped(KTextEditor::Cursor(m_view->cursorPosition()));
345     if (getTemporaryNormalMode()) {
346         // Ensure the key log contains a request to re-enter Insert mode, else the keystrokes made
347         // after returning from temporary normal mode will be treated as commands!
348         m_lastChangeRecorder->record(QKeyEvent(QEvent::KeyPress, Qt::Key_I, Qt::NoModifier, QStringLiteral("i")));
349     }
350     m_inputAdapter->setCaretStyle(KateRenderer::Line);
351     setTemporaryNormalMode(false);
352     m_viewInternal->update();
353 }
354 
viEnterVisualMode(ViMode mode)355 void InputModeManager::viEnterVisualMode(ViMode mode)
356 {
357     changeViMode(mode);
358 
359     // If the selection is inclusive, the caret should be a block.
360     // If the selection is exclusive, the caret should be a line.
361     m_inputAdapter->setCaretStyle(KateRenderer::Block);
362     m_viewInternal->update();
363     getViVisualMode()->setVisualModeType(mode);
364     getViVisualMode()->init();
365 }
366 
viEnterReplaceMode()367 void InputModeManager::viEnterReplaceMode()
368 {
369     changeViMode(ViMode::ReplaceMode);
370     m_marks->setStartEditYanked(KTextEditor::Cursor(m_view->cursorPosition()));
371     m_inputAdapter->setCaretStyle(KateRenderer::Underline);
372     m_viewInternal->update();
373 }
374 
getViNormalMode()375 NormalViMode *InputModeManager::getViNormalMode()
376 {
377     return m_viNormalMode;
378 }
379 
getViInsertMode()380 InsertViMode *InputModeManager::getViInsertMode()
381 {
382     return m_viInsertMode;
383 }
384 
getViVisualMode()385 VisualViMode *InputModeManager::getViVisualMode()
386 {
387     return m_viVisualMode;
388 }
389 
getViReplaceMode()390 ReplaceViMode *InputModeManager::getViReplaceMode()
391 {
392     return m_viReplaceMode;
393 }
394 
getVerbatimKeys() const395 const QString InputModeManager::getVerbatimKeys() const
396 {
397     QString cmd;
398 
399     switch (getCurrentViMode()) {
400     case ViMode::NormalMode:
401         cmd = m_viNormalMode->getVerbatimKeys();
402         break;
403     case ViMode::InsertMode:
404     case ViMode::ReplaceMode:
405         // ...
406         break;
407     case ViMode::VisualMode:
408     case ViMode::VisualLineMode:
409     case ViMode::VisualBlockMode:
410         cmd = m_viVisualMode->getVerbatimKeys();
411         break;
412     }
413 
414     return cmd;
415 }
416 
readSessionConfig(const KConfigGroup & config)417 void InputModeManager::readSessionConfig(const KConfigGroup &config)
418 {
419     m_jumps->readSessionConfig(config);
420     m_marks->readSessionConfig(config);
421 }
422 
writeSessionConfig(KConfigGroup & config)423 void InputModeManager::writeSessionConfig(KConfigGroup &config)
424 {
425     m_jumps->writeSessionConfig(config);
426     m_marks->writeSessionConfig(config);
427 }
428 
reset()429 void InputModeManager::reset()
430 {
431     if (m_viVisualMode) {
432         m_viVisualMode->reset();
433     }
434 }
435 
keyMapper()436 KeyMapper *InputModeManager::keyMapper()
437 {
438     return m_keyMapperStack.top().data();
439 }
440 
updateCursor(const KTextEditor::Cursor c)441 void InputModeManager::updateCursor(const KTextEditor::Cursor c)
442 {
443     m_inputAdapter->updateCursor(c);
444 }
445 
globalState() const446 GlobalState *InputModeManager::globalState() const
447 {
448     return m_inputAdapter->globalState();
449 }
450 
view() const451 KTextEditor::ViewPrivate *InputModeManager::view() const
452 {
453     return m_view;
454 }
455 
pushKeyMapper(QSharedPointer<KeyMapper> mapper)456 void InputModeManager::pushKeyMapper(QSharedPointer<KeyMapper> mapper)
457 {
458     m_keyMapperStack.push(mapper);
459 }
460 
popKeyMapper()461 void InputModeManager::popKeyMapper()
462 {
463     m_keyMapperStack.pop();
464 }
465