File indexing completed on 2024-04-28 03:58:09

0001 /*
0002     SPDX-FileCopyrightText: 2008 Erlend Hamberg <ehamberg@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 #include "katedocument.h"
0007 #include "kateviinputmode.h"
0008 
0009 #include "kateview.h"
0010 #include <utils/kateconfig.h>
0011 #include <view/kateviewinternal.h>
0012 #include <vimode/emulatedcommandbar/emulatedcommandbar.h>
0013 #include <vimode/inputmodemanager.h>
0014 #include <vimode/marks.h>
0015 #include <vimode/modes/replacevimode.h>
0016 
0017 using namespace KateVi;
0018 
0019 ReplaceViMode::ReplaceViMode(InputModeManager *viInputModeManager, KTextEditor::ViewPrivate *view, KateViewInternal *viewInternal)
0020     : ModeBase()
0021 {
0022     m_view = view;
0023     m_viewInternal = viewInternal;
0024     m_viInputModeManager = viInputModeManager;
0025     m_count = 1;
0026 }
0027 
0028 bool ReplaceViMode::commandInsertFromLine(int offset)
0029 {
0030     KTextEditor::Cursor c(m_view->cursorPosition());
0031 
0032     if (c.line() + offset >= doc()->lines() || c.line() + offset < 0) {
0033         return false;
0034     }
0035 
0036     // Fetch the new character from the specified line.
0037     KTextEditor::Cursor target(c.line() + offset, c.column());
0038     QChar ch = doc()->characterAt(target);
0039     if (ch == QChar::Null) {
0040         return false;
0041     }
0042 
0043     // The cursor is at the end of the line: just append the char.
0044     if (c.column() == doc()->lineLength(c.line())) {
0045         return doc()->insertText(c, ch);
0046     }
0047 
0048     // We can replace the current one with the fetched character.
0049     KTextEditor::Cursor next(c.line(), c.column() + 1);
0050     QChar removed = doc()->line(c.line()).at(c.column());
0051     if (doc()->replaceText(KTextEditor::Range(c, next), ch)) {
0052         overwrittenChar(removed);
0053         return true;
0054     }
0055     return false;
0056 }
0057 
0058 bool ReplaceViMode::commandMoveOneWordLeft()
0059 {
0060     KTextEditor::Cursor c(m_view->cursorPosition());
0061     c = findPrevWordStart(c.line(), c.column());
0062 
0063     if (!c.isValid()) {
0064         c = KTextEditor::Cursor(0, 0);
0065     }
0066 
0067     updateCursor(c);
0068     return true;
0069 }
0070 
0071 bool ReplaceViMode::commandMoveOneWordRight()
0072 {
0073     KTextEditor::Cursor c(m_view->cursorPosition());
0074     c = findNextWordStart(c.line(), c.column());
0075 
0076     if (!c.isValid()) {
0077         c = doc()->documentEnd();
0078     }
0079 
0080     updateCursor(c);
0081     return true;
0082 }
0083 
0084 bool ReplaceViMode::handleKeypress(const QKeyEvent *e)
0085 {
0086     // backspace should work even if the shift key is down
0087     if (e->modifiers() != CONTROL_MODIFIER && e->key() == Qt::Key_Backspace) {
0088         backspace();
0089         return true;
0090     }
0091 
0092     // on macOS the KeypadModifier is set for the arrow keys too
0093     if (e->modifiers() == Qt::NoModifier || e->modifiers() == Qt::KeypadModifier) {
0094         switch (e->key()) {
0095         case Qt::Key_Escape:
0096             m_overwritten.clear();
0097             leaveReplaceMode();
0098             return true;
0099         case Qt::Key_Left:
0100             m_overwritten.clear();
0101             m_view->cursorLeft();
0102             return true;
0103         case Qt::Key_Right:
0104             m_overwritten.clear();
0105             m_view->cursorRight();
0106             return true;
0107         case Qt::Key_Up:
0108             m_overwritten.clear();
0109             m_view->up();
0110             return true;
0111         case Qt::Key_Down:
0112             m_overwritten.clear();
0113             m_view->down();
0114             return true;
0115         case Qt::Key_Home:
0116             m_overwritten.clear();
0117             m_view->home();
0118             return true;
0119         case Qt::Key_End:
0120             m_overwritten.clear();
0121             m_view->end();
0122             return true;
0123         case Qt::Key_PageUp:
0124             m_overwritten.clear();
0125             m_view->pageUp();
0126             return true;
0127         case Qt::Key_PageDown:
0128             m_overwritten.clear();
0129             m_view->pageDown();
0130             return true;
0131         case Qt::Key_Delete:
0132             m_view->keyDelete();
0133             return true;
0134         case Qt::Key_Insert:
0135             startInsertMode();
0136             return true;
0137         case Qt::Key_Enter:
0138         case Qt::Key_Return:
0139             if (m_viInputModeManager->inputAdapter()->viModeEmulatedCommandBar()->isSendingSyntheticSearchCompletedKeypress()) {
0140                 // BUG #451076, Do not record/send return for a newline when doing a search via Ctrl+F/Edit->Find menu
0141                 m_viInputModeManager->doNotLogCurrentKeypress();
0142                 return true;
0143             }
0144             Q_FALLTHROUGH();
0145         default:
0146             return false;
0147         }
0148     } else if (e->modifiers() == CONTROL_MODIFIER) {
0149         switch (e->key()) {
0150         case Qt::Key_BracketLeft:
0151         case Qt::Key_C:
0152             startNormalMode();
0153             return true;
0154         case Qt::Key_E:
0155             commandInsertFromLine(1);
0156             return true;
0157         case Qt::Key_Y:
0158             commandInsertFromLine(-1);
0159             return true;
0160         case Qt::Key_W:
0161             commandBackWord();
0162             return true;
0163         case Qt::Key_U:
0164             commandBackLine();
0165             return true;
0166         case Qt::Key_Left:
0167             m_overwritten.clear();
0168             commandMoveOneWordLeft();
0169             return true;
0170         case Qt::Key_Right:
0171             m_overwritten.clear();
0172             commandMoveOneWordRight();
0173             return true;
0174         default:
0175             return false;
0176         }
0177     }
0178 
0179     return false;
0180 }
0181 
0182 void ReplaceViMode::backspace()
0183 {
0184     KTextEditor::Cursor c1(m_view->cursorPosition());
0185     KTextEditor::Cursor c2(c1.line(), c1.column() - 1);
0186 
0187     if (c1.column() > 0) {
0188         if (!m_overwritten.isEmpty()) {
0189             doc()->removeText(KTextEditor::Range(c1.line(), c1.column() - 1, c1.line(), c1.column()));
0190             doc()->insertText(c2, m_overwritten.right(1));
0191             m_overwritten.remove(m_overwritten.length() - 1, 1);
0192         }
0193         updateCursor(c2);
0194     }
0195 }
0196 
0197 void ReplaceViMode::commandBackWord()
0198 {
0199     KTextEditor::Cursor current(m_view->cursorPosition());
0200     KTextEditor::Cursor to(findPrevWordStart(current.line(), current.column()));
0201 
0202     if (!to.isValid()) {
0203         return;
0204     }
0205 
0206     while (current.isValid() && current != to) {
0207         backspace();
0208         current = m_view->cursorPosition();
0209     }
0210 }
0211 
0212 void ReplaceViMode::commandBackLine()
0213 {
0214     const int column = m_view->cursorPosition().column();
0215 
0216     for (int i = column; i >= 0 && !m_overwritten.isEmpty(); i--) {
0217         backspace();
0218     }
0219 }
0220 
0221 void ReplaceViMode::leaveReplaceMode()
0222 {
0223     // Redo replacement operation <count> times
0224     m_view->abortCompletion();
0225 
0226     if (m_count > 1) {
0227         // Look at added text so that we can repeat the addition
0228         const QString added = doc()->text(KTextEditor::Range(m_viInputModeManager->marks()->getStartEditYanked(), m_view->cursorPosition()));
0229         for (unsigned int i = 0; i < m_count - 1; i++) {
0230             KTextEditor::Cursor c(m_view->cursorPosition());
0231             KTextEditor::Cursor c2(c.line(), c.column() + added.length());
0232             doc()->replaceText(KTextEditor::Range(c, c2), added);
0233         }
0234     }
0235 
0236     startNormalMode();
0237 }