diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp index cc23eb100fa0b3bcaa46d8ca23a59dfb9aa82475..0d0164bef9881232cd06dbe31e704c3eac37fa18 100644 --- a/src/plugins/fakevim/fakevimhandler.cpp +++ b/src/plugins/fakevim/fakevimhandler.cpp @@ -112,6 +112,7 @@ const int ParagraphSeparator = 0x00002029; using namespace Qt; + enum Mode { InsertMode, @@ -202,6 +203,8 @@ enum EventResult EventPassedToCore }; +class UndoBreaker; + class FakeVimHandler::Private { public: @@ -215,8 +218,8 @@ public: void setupWidget(); void restoreWidget(); -private: friend class FakeVimHandler; + friend class UndoBreaker; static int shift(int key) { return key + 32; } static int control(int key) { return key + 256; } @@ -333,28 +336,16 @@ public: bool m_lastSearchForward; QString m_lastInsertion; - // undo handling - void recordOperation(const EditOperation &op); - void recordInsert(int position, const QString &data); - void recordRemove(int position, const QString &data); - void recordRemove(int position, int length); - - void recordRemoveNextChar(); - void recordInsertText(const QString &data); - QString recordRemoveSelectedText(); - void recordPosition(); - void recordBeginGroup(); - void recordEndGroup(); + QString removeSelectedText(); int anchor() const { return m_anchor; } int position() const { return m_tc.position(); } QString selectedText() const; + // undo handling void undo(); void redo(); - QStack<EditOperation> m_undoStack; - QStack<EditOperation> m_redoStack; - QStack<int> m_undoGroupStack; - QMap<int, int> m_undoCursorPosition; + QMap<int, int> m_undoCursorPosition; // revision -> position + bool m_needMoreUndo; // extra data for '.' QString m_dotCommand; @@ -396,6 +387,7 @@ public: int m_cursorWidth; void recordJump(); + void recordNewUndo(); QList<int> m_jumpListUndo; QList<int> m_jumpListRedo; @@ -515,7 +507,6 @@ EventResult FakeVimHandler::Private::handleEvent(QKeyEvent *ev) moveLeft(); EDITOR(setTextCursor(m_tc)); - //EDITOR(ensureCursorVisible()); return result; } @@ -621,7 +612,7 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand) m_anchor++; if (!dotCommand.isEmpty()) m_dotCommand = "c" + dotCommand; - QString text = recordRemoveSelectedText(); + QString text = removeSelectedText(); //qDebug() << "CHANGING TO INSERT MODE" << text; m_registers[m_register] = text; m_mode = InsertMode; @@ -633,8 +624,7 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand) m_anchor++; if (!dotCommand.isEmpty()) m_dotCommand = "d" + dotCommand; - m_registers[m_register] = recordRemoveSelectedText(); - recordEndGroup(); + m_registers[m_register] = removeSelectedText(); m_submode = NoSubMode; if (atEndOfLine()) moveLeft(); @@ -648,10 +638,12 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand) indentRegion(); m_submode = NoSubMode; } else if (m_submode == ShiftRightSubMode) { + recordJump(); shiftRegionRight(1); m_submode = NoSubMode; updateMiniBuffer(); } else if (m_submode == ShiftLeftSubMode) { + recordJump(); shiftRegionLeft(1); m_submode = NoSubMode; updateMiniBuffer(); @@ -881,12 +873,10 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, } else if (m_submode == ReplaceSubMode) { if (count() < rightDist() && text.size() == 1 && (text.at(0).isPrint() || text.at(0).isSpace())) { - recordBeginGroup(); setAnchor(); moveRight(count()); - recordRemoveSelectedText(); - recordInsertText(QString(count(), text.at(0))); - recordEndGroup(); + removeSelectedText(); + m_tc.insertText(QString(count(), text.at(0))); m_moveType = MoveExclusive; m_submode = NoSubMode; m_dotCommand = QString("%1r%2").arg(count()).arg(text); @@ -1027,7 +1017,6 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, finishMovement(); } else if (key == 'a') { m_mode = InsertMode; - recordBeginGroup(); m_lastInsertion.clear(); if (!atEndOfLine()) moveRight(); @@ -1035,7 +1024,6 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, } else if (key == 'A') { m_mode = InsertMode; moveToEndOfLine(); - recordBeginGroup(); m_lastInsertion.clear(); } else if (key == 'b') { m_moveType = MoveExclusive; @@ -1047,30 +1035,25 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, finishMovement(); } else if (key == 'c' && m_visualMode == NoVisualMode) { setAnchor(); - recordBeginGroup(); m_submode = ChangeSubMode; } else if (key == 'c' && m_visualMode == VisualCharMode) { - recordBeginGroup(); leaveVisualMode(); m_submode = ChangeSubMode; finishMovement(); } else if (key == 'C') { setAnchor(); - recordBeginGroup(); moveToEndOfLine(); - m_registers[m_register] = recordRemoveSelectedText(); + m_registers[m_register] = removeSelectedText(); m_mode = InsertMode; finishMovement(); } else if (key == 'd' && m_visualMode == NoVisualMode) { if (atEndOfLine()) moveLeft(); setAnchor(); - recordBeginGroup(); m_opcount = m_mvcount; m_mvcount.clear(); m_submode = DeleteSubMode; } else if ((key == 'd' || key == 'x') && m_visualMode == VisualCharMode) { - recordBeginGroup(); leaveVisualMode(); m_submode = DeleteSubMode; finishMovement(); @@ -1079,10 +1062,9 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, int beginLine = lineForPosition(m_marks['<']); int endLine = lineForPosition(m_marks['>']); selectRange(beginLine, endLine); - m_registers[m_register] = recordRemoveSelectedText(); + m_registers[m_register] = removeSelectedText(); } else if (key == 'D') { setAnchor(); - recordBeginGroup(); m_submode = DeleteSubMode; moveDown(qMax(count() - 1, 0)); m_moveType = MoveExclusive; @@ -1146,14 +1128,12 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, moveToFirstNonBlankOnLine(); finishMovement(); } else if (key == 'i') { - recordBeginGroup(); m_dotCommand = "i"; //QString("%1i").arg(count()); enterInsertMode(); updateMiniBuffer(); if (atEndOfLine()) moveLeft(); } else if (key == 'I') { - recordBeginGroup(); m_dotCommand = "I"; //QString("%1I").arg(count()); enterInsertMode(); if (m_gflag) @@ -1182,20 +1162,20 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, finishMovement("j"); m_desiredColumn = savedColumn; } else if (key == 'J') { - recordBeginGroup(); if (m_submode == NoSubMode) { for (int i = qMax(count(), 2) - 1; --i >= 0; ) { moveToEndOfLine(); - recordRemoveNextChar(); + setAnchor(); + moveRight(); while (characterAtCursor() == ' ') - recordRemoveNextChar(); + moveRight(); + removeSelectedText(); if (!m_gflag) - recordInsertText(" "); + m_tc.insertText(" "); } if (!m_gflag) moveLeft(); } - recordEndGroup(); } else if (key == 'k' || key == Key_Up) { int savedColumn = m_desiredColumn; if (m_submode == NoSubMode || m_submode == ZSubMode @@ -1235,8 +1215,6 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, search(lastSearchString(), !m_lastSearchForward); recordJump(); } else if (key == 'o' || key == 'O') { - recordBeginGroup(); - recordPosition(); m_dotCommand = QString("%1o").arg(count()); enterInsertMode(); moveToFirstNonBlankOnLine(); @@ -1244,31 +1222,29 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, if (key == 'O') moveUp(); moveToEndOfLine(); - recordInsertText("\n"); + m_tc.insertText("\n"); moveToStartOfLine(); if (0 && hasConfig(ConfigAutoIndent)) - recordInsertText(QString(indentDist(), ' ')); + m_tc.insertText(QString(indentDist(), ' ')); else - recordInsertText(QString(numSpaces, ' ')); + m_tc.insertText(QString(numSpaces, ' ')); } else if (key == control('o')) { if (!m_jumpListUndo.isEmpty()) { m_jumpListRedo.append(position()); setPosition(m_jumpListUndo.takeLast()); } } else if (key == 'p' || key == 'P') { - recordBeginGroup(); QString text = m_registers[m_register]; int n = lineCount(text); //qDebug() << "REGISTERS: " << m_registers << "MOVE: " << m_moveType; //qDebug() << "LINES: " << n << text << m_register; if (n > 0) { - recordPosition(); moveToStartOfLine(); m_desiredColumn = 0; for (int i = count(); --i >= 0; ) { if (key == 'p') moveDown(); - recordInsertText(text); + m_tc.insertText(text); moveUp(n); } } else { @@ -1276,11 +1252,10 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, for (int i = count(); --i >= 0; ) { if (key == 'p') moveRight(); - recordInsertText(text); + m_tc.insertText(text); moveLeft(); } } - recordEndGroup(); m_dotCommand = QString("%1p").arg(count()); finishMovement(); } else if (key == 'r') { @@ -1289,7 +1264,6 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, } else if (key == 'R') { // FIXME: right now we repeat the insertion count() times, // but not the deletion - recordBeginGroup(); m_lastInsertion.clear(); m_mode = InsertMode; m_submode = ReplaceSubMode; @@ -1297,10 +1271,9 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, } else if (key == control('r')) { redo(); } else if (key == 's') { - recordBeginGroup(); setAnchor(); moveRight(qMin(count(), rightDist())); - m_registers[m_register] = recordRemoveSelectedText(); + m_registers[m_register] = removeSelectedText(); m_dotCommand = "s"; //QString("%1s").arg(count()); m_opcount.clear(); m_mvcount.clear(); @@ -1352,7 +1325,6 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, m_moveType = MoveExclusive; if (atEndOfLine()) moveLeft(); - recordBeginGroup(); setAnchor(); m_submode = DeleteSubMode; moveRight(qMin(count(), rightDist())); @@ -1362,14 +1334,13 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, if (leftDist() > 0) { setAnchor(); moveLeft(qMin(count(), leftDist())); - m_registers[m_register] = recordRemoveSelectedText(); + m_registers[m_register] = removeSelectedText(); } finishMovement(); } else if (key == 'y' && m_visualMode == NoVisualMode) { m_savedYankPosition = m_tc.position(); if (atEndOfLine()) moveLeft(); - recordBeginGroup(); setAnchor(); m_submode = YankSubMode; } else if (key == 'y' && m_visualMode == VisualLineMode) { @@ -1388,19 +1359,16 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, m_moveType = MoveLineWise; finishMovement(); } else if (key == 'z') { - recordBeginGroup(); m_submode = ZSubMode; } else if (key == '~' && !atEndOfLine()) { - recordBeginGroup(); setAnchor(); moveRight(qMin(count(), rightDist())); - QString str = recordRemoveSelectedText(); + QString str = removeSelectedText(); for (int i = str.size(); --i >= 0; ) { QChar c = str.at(i); str[i] = c.isUpper() ? c.toLower() : c.toUpper(); } - recordInsertText(str); - recordEndGroup(); + m_tc.insertText(str); } else if (key == Key_PageDown || key == control('f')) { moveDown(count() * (linesOnScreen() - 2) - cursorLineOnScreen()); scrollToLineInDocument(cursorLineInDocument()); @@ -1414,7 +1382,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(int key, int unmodified, } else if (key == Key_Delete) { setAnchor(); moveRight(qMin(1, rightDist())); - recordRemoveSelectedText(); + removeSelectedText(); } else if (key == Key_Escape) { if (m_visualMode != NoVisualMode) { leaveVisualMode(); @@ -1443,12 +1411,10 @@ EventResult FakeVimHandler::Private::handleInsertMode(int key, int, m_tc.insertText(m_lastInsertion); data += m_lastInsertion; } - recordInsert(m_tc.position() - m_lastInsertion.size(), data); - recordEndGroup(); - //qDebug() << "UNDO: " << m_undoStack; moveLeft(qMin(1, leftDist())); m_dotCommand += m_lastInsertion; m_dotCommand += QChar(27); + recordNewUndo(); enterCommandMode(); } else if (key == Key_Left) { moveLeft(count()); @@ -1698,7 +1664,7 @@ void FakeVimHandler::Private::handleExCommand(const QString &cmd0) } else if (reDelete.indexIn(cmd) != -1) { // :d selectRange(beginLine, endLine); QString reg = reDelete.cap(2); - QString text = recordRemoveSelectedText(); + QString text = removeSelectedText(); if (!reg.isEmpty()) m_registers[reg.at(0).unicode()] = text; } else if (reWrite.indexIn(cmd) != -1) { // :w @@ -1761,8 +1727,7 @@ void FakeVimHandler::Private::handleExCommand(const QString &cmd0) } else if (cmd.startsWith("!")) { selectRange(beginLine, endLine); QString command = cmd.mid(1).trimmed(); - recordBeginGroup(); - QString text = recordRemoveSelectedText(); + QString text = removeSelectedText(); QProcess proc; proc.start(cmd.mid(1)); proc.waitForStarted(); @@ -1770,18 +1735,9 @@ void FakeVimHandler::Private::handleExCommand(const QString &cmd0) proc.closeWriteChannel(); proc.waitForFinished(); QString result = QString::fromUtf8(proc.readAllStandardOutput()); - recordInsertText(result); - recordEndGroup(); + m_tc.insertText(result); leaveVisualMode(); - setPosition(firstPositionInLine(beginLine)); - EditOperation op; - // FIXME: broken for "upward selection" - op.position = m_tc.position(); - op.from = text; - op.to = result; - recordOperation(op); - enterCommandMode(); //qDebug() << "FILTER: " << command; showBlackMessage(tr("%1 lines filtered").arg(text.count('\n'))); @@ -1985,18 +1941,15 @@ void FakeVimHandler::Private::shiftRegionRight(int repeat) QString indent(len, ' '); int firstPos = firstPositionInLine(beginLine); - recordBeginGroup(); //setPosition(firstPos); - //recordPosition(); for (int line = beginLine; line <= endLine; ++line) { setPosition(firstPositionInLine(line)); - recordInsertText(indent); + m_tc.insertText(indent); } setPosition(firstPos); moveToFirstNonBlankOnLine(); - recordEndGroup(); m_dotCommand = QString("%1>>").arg(endLine - beginLine + 1); } @@ -2010,9 +1963,7 @@ void FakeVimHandler::Private::shiftRegionLeft(int repeat) int tab = config(ConfigTabStop).toInt(); int firstPos = firstPositionInLine(beginLine); - recordBeginGroup(); //setPosition(firstPos); - //recordPosition(); for (int line = beginLine; line <= endLine; ++line) { int pos = firstPositionInLine(line); @@ -2030,13 +1981,12 @@ void FakeVimHandler::Private::shiftRegionLeft(int repeat) break; } setPosition(pos + i); - text = recordRemoveSelectedText(); + text = removeSelectedText(); setPosition(pos); } setPosition(firstPos); moveToFirstNonBlankOnLine(); - recordEndGroup(); m_dotCommand = QString("%1<<").arg(endLine - beginLine + 1); } @@ -2260,174 +2210,51 @@ QWidget *FakeVimHandler::Private::editor() const void FakeVimHandler::Private::undo() { + int current = EDITOR(document())->revision(); + m_tc.endEditBlock(); + m_needMoreUndo = false; EDITOR(undo()); + if (m_needMoreUndo) + EDITOR(undo()); + m_tc.beginEditBlock(); int rev = EDITOR(document())->revision(); - if (m_undoCursorPosition.contains(rev)) - m_tc.setPosition(m_undoCursorPosition[rev]); -#if 0 - if (m_undoStack.isEmpty()) { + if (current == rev) showBlackMessage(tr("Already at oldest change")); - } else { - EditOperation op = m_undoStack.pop(); - //qDebug() << "UNDO " << op; - if (op.itemCount > 0) { - for (int i = op.itemCount; --i >= 0; ) - undo(); - } else { - m_tc.setPosition(op.position, MoveAnchor); - if (!op.to.isEmpty()) { - m_tc.setPosition(op.position + op.to.size(), KeepAnchor); - m_tc.removeSelectedText(); - } - if (!op.from.isEmpty()) - m_tc.insertText(op.from); - m_tc.setPosition(op.position, MoveAnchor); - } - m_redoStack.push(op); + else showBlackMessage(QString()); - } -#endif + if (m_undoCursorPosition.contains(rev)) + m_tc.setPosition(m_undoCursorPosition[rev]); } void FakeVimHandler::Private::redo() { int current = EDITOR(document())->revision(); + m_tc.endEditBlock(); + m_needMoreUndo = false; EDITOR(redo()); + if (m_needMoreUndo) + EDITOR(redo()); + m_tc.beginEditBlock(); int rev = EDITOR(document())->revision(); - if (rev == current) { - showBlackMessage(tr("Already at newest change")); - } else { - showBlackMessage(QString()); - if (m_undoCursorPosition.contains(rev)) - m_tc.setPosition(m_undoCursorPosition[rev]); - } -#if 0 - if (m_redoStack.isEmpty()) { + if (rev == current) showBlackMessage(tr("Already at newest change")); - } else { - EditOperation op = m_redoStack.pop(); - //qDebug() << "REDO " << op; - if (op.itemCount > 0) { - for (int i = op.itemCount; --i >= 0; ) - redo(); - } else { - m_tc.setPosition(op.position, MoveAnchor); - if (!op.from.isEmpty()) { - m_tc.setPosition(op.position + op.from.size(), KeepAnchor); - m_tc.removeSelectedText(); - } - if (!op.to.isEmpty()) - m_tc.insertText(op.to); - m_tc.setPosition(op.position, MoveAnchor); - } - m_undoStack.push(op); + else showBlackMessage(QString()); - } -#endif -} - -void FakeVimHandler::Private::recordBeginGroup() -{ - //qDebug() << "PUSH"; - m_undoGroupStack.push(m_undoStack.size()); - EditOperation op; - op.position = m_tc.position(); - recordOperation(op); -} - -void FakeVimHandler::Private::recordEndGroup() -{ - if (m_undoGroupStack.isEmpty()) { - qWarning("fakevim: undo groups not balanced.\n"); - return; - } - EditOperation op; - op.itemCount = m_undoStack.size() - m_undoGroupStack.pop(); - //qDebug() << "POP " << op.itemCount << m_undoStack; - recordOperation(op); + if (m_undoCursorPosition.contains(rev)) + m_tc.setPosition(m_undoCursorPosition[rev]); } -QString FakeVimHandler::Private::recordRemoveSelectedText() +QString FakeVimHandler::Private::removeSelectedText() { - EditOperation op; //qDebug() << "POS: " << position() << " ANCHOR: " << anchor() << m_tc.anchor(); int pos = m_tc.position(); if (pos == anchor()) return QString(); m_tc.setPosition(anchor(), MoveAnchor); m_tc.setPosition(pos, KeepAnchor); - op.position = qMin(pos, anchor()); - op.from = m_tc.selection().toPlainText(); - //qDebug() << "OP: " << op; - recordOperation(op); + QString from = m_tc.selection().toPlainText(); m_tc.removeSelectedText(); - return op.from; -} - -void FakeVimHandler::Private::recordRemoveNextChar() -{ - setAnchor(); - moveRight(); - recordRemoveSelectedText(); -} - -void FakeVimHandler::Private::recordInsertText(const QString &data) -{ - EditOperation op; - op.position = m_tc.position(); - op.to = data; - recordOperation(op); - m_tc.insertText(data); -} - -void FakeVimHandler::Private::recordPosition() -{ - EditOperation op; - op.position = m_tc.position(); - m_undoStack.push(op); - m_redoStack.clear(); - UNDO_DEBUG("MOVE: " << op); - UNDO_DEBUG("\nUNDO STACK: " << m_undoStack << "\n"); - UNDO_DEBUG("\nREDO STACK: " << m_redoStack << "\n"); -} - -void FakeVimHandler::Private::recordOperation(const EditOperation &op) -{ - UNDO_DEBUG("RECORD OP: " << op); - // No need to record operations that actually do not change anything. - if (op.from.isEmpty() && op.to.isEmpty() && op.itemCount == 0) - return; - // No need to create groups with only one member. - if (op.itemCount == 1) - return; - m_undoStack.push(op); - m_redoStack.clear(); - UNDO_DEBUG("\nUNDO STACK: " << m_undoStack << "\n"); - UNDO_DEBUG("\nREDO STACK: " << m_redoStack << "\n"); -} - -void FakeVimHandler::Private::recordInsert(int position, const QString &data) -{ - EditOperation op; - op.position = position; - op.to = data; - recordOperation(op); -} - -void FakeVimHandler::Private::recordRemove(int position, int length) -{ - QTextCursor tc = m_tc; - tc.setPosition(position, MoveAnchor); - tc.setPosition(position + length, KeepAnchor); - recordRemove(position, tc.selection().toPlainText()); -} - -void FakeVimHandler::Private::recordRemove(int position, const QString &data) -{ - EditOperation op; - op.position = position; - op.from = data; - recordOperation(op); + return from; } void FakeVimHandler::Private::enterInsertMode() @@ -2459,6 +2286,21 @@ void FakeVimHandler::Private::recordJump() UNDO_DEBUG("jumps: " << m_jumpListUndo); } +struct UndoBreaker : public QAbstractUndoItem +{ + UndoBreaker(FakeVimHandler::Private *doc) : m_doc(doc) {} + void undo() { m_doc->m_needMoreUndo = true; } + void redo() { m_doc->m_needMoreUndo = true; } + FakeVimHandler::Private *m_doc; +}; + +void FakeVimHandler::Private::recordNewUndo() +{ + m_tc.endEditBlock(); + m_tc.document()->appendUndoItem(new UndoBreaker(this)); + m_tc.beginEditBlock(); +} + /////////////////////////////////////////////////////////////////////// // diff --git a/src/plugins/fakevim/fakevimhandler.h b/src/plugins/fakevim/fakevimhandler.h index 045d1502b5ddf1d8eeda38859e3908dba48877a9..74ec43c97712e9bb41b4ac78fc4b6c6ac0269b63 100644 --- a/src/plugins/fakevim/fakevimhandler.h +++ b/src/plugins/fakevim/fakevimhandler.h @@ -73,10 +73,11 @@ signals: void indentRegion(int *amount, int beginLine, int endLine, QChar typedChar); void completionRequested(); +public: + class Private; + private: bool eventFilter(QObject *ob, QEvent *ev); - - class Private; friend class Private; Private *d; }; diff --git a/tests/manual/fakevim/main.cpp b/tests/manual/fakevim/main.cpp index 1d31d998403c7eae8cf93cf14b342cf097c93a33..ec007922ff85a19cf55e9e7cd303eda21d672a51 100644 --- a/tests/manual/fakevim/main.cpp +++ b/tests/manual/fakevim/main.cpp @@ -119,6 +119,9 @@ int main(int argc, char *argv[]) &proxy, SLOT(changeStatusData(QString))); theFakeVimSetting(ConfigUseFakeVim)->setValue(true); + theFakeVimSetting(ConfigShiftWidth)->setValue(8); + theFakeVimSetting(ConfigTabStop)->setValue(8); + handler.installEventFilter(); handler.setupWidget(); if (args.size() >= 1)