fakevim: <C-O> to execute single command and return to insert mode

After Ctrl+O combination in insert or replace mode enter command mode
and after a single command (can consist of entering Ex command or can be
switch to other buffer) or escape key return to previous insert or
replace mode. One exception is that if entering insert mode is part of
the command after <C-O> pressing escape key will exit to command mode
(Vim behavior).

Change-Id: I37d684b691157001e2cab156687d6771afdec7b9
Reviewed-by: hjk <qthjk@ovi.com>
This commit is contained in:
Lukas Holecek
2012-11-10 10:36:05 +01:00
committed by hjk
parent 9ab2d3d0a7
commit f302f3b8c2
2 changed files with 94 additions and 37 deletions

View File

@@ -217,6 +217,7 @@ struct FakeVimPlugin::TestData
void FakeVimPlugin::setup(TestData *data) void FakeVimPlugin::setup(TestData *data)
{ {
setupTest(&data->title, &data->handler, &data->edit); setupTest(&data->title, &data->handler, &data->edit);
data->handler->handleInput("<ESC><ESC>gg");
} }
@@ -426,6 +427,24 @@ void FakeVimPlugin::test_vim_insert()
data.setText("abc" N "def"); data.setText("abc" N "def");
KEYS("3O 123<esc>", " 123" N " 123" N " 12" X "3" N "abc" N "def"); KEYS("3O 123<esc>", " 123" N " 123" N " 12" X "3" N "abc" N "def");
INTEGRITY(false); INTEGRITY(false);
// <C-O>
data.setText("abc" N "d" X "ef");
KEYS("i<c-o>xX", "abc" N "dX" X "f");
KEYS("i<c-o><end>", "abc" N "dXf" X);
data.setText("ab" X "c" N "def");
KEYS("i<c-o>rX", "ab" X "X" N "def");
data.setText("abc" N "def");
KEYS("A<c-o>x", "ab" X N "def");
data.setText("abc" N "de" X "f");
KEYS("i<c-o>0x", "abc" N "x" X "def");
data.setText("abc" N "de" X "f");
KEYS("i<c-o>ggx", "x" X "abc" N "def");
// <INSERT> to toggle between insert and replace mode
data.setText("abc" N "def");
KEYS("<insert>XYZ<insert>xyz<esc>", "XYZxy" X "z" N "def");
KEYS("<insert><insert>" "<c-o>0<c-o>j" "XY<insert>Z", "XYZxyz" N "XYZ" X "f");
} }
void FakeVimPlugin::test_vim_fFtT() void FakeVimPlugin::test_vim_fFtT()
@@ -1821,8 +1840,6 @@ void FakeVimPlugin::test_map()
KEYS("Y", X "abc" N "def"); KEYS("Y", X "abc" N "def");
data.doCommand("unmap X|unmap Y"); data.doCommand("unmap X|unmap Y");
NOT_IMPLEMENTED
// <C-o> // <C-o>
data.setText("abc def"); data.setText("abc def");
data.doCommand("imap X <c-o>:%s/def/xxx/<cr>"); data.doCommand("imap X <c-o>:%s/def/xxx/<cr>");

View File

@@ -1341,6 +1341,8 @@ public:
friend class FakeVimHandler; friend class FakeVimHandler;
void init(); void init();
void focus();
EventResult handleKey(const Input &input); EventResult handleKey(const Input &input);
EventResult handleDefaultKey(const Input &input); EventResult handleDefaultKey(const Input &input);
void handleMappedKeys(); void handleMappedKeys();
@@ -1532,7 +1534,7 @@ public:
void enterInsertMode(); void enterInsertMode();
void enterReplaceMode(); void enterReplaceMode();
void enterCommandMode(); void enterCommandMode(Mode returnToMode = CommandMode);
void enterExMode(const QString &contents = QString()); void enterExMode(const QString &contents = QString());
void showMessage(MessageLevel level, const QString &msg); void showMessage(MessageLevel level, const QString &msg);
void clearMessage() { showMessage(MessageInfo, QString()); } void clearMessage() { showMessage(MessageInfo, QString()); }
@@ -1761,7 +1763,7 @@ public:
{ {
GlobalData() GlobalData()
: mappings(), currentMap(&mappings), inputTimer(-1), currentMessageLevel(MessageInfo), : mappings(), currentMap(&mappings), inputTimer(-1), currentMessageLevel(MessageInfo),
lastSearchForward(false), findPending(false) lastSearchForward(false), findPending(false), returnToMode(CommandMode)
{ {
// default mapping state - shouldn't be removed // default mapping state - shouldn't be removed
mapStates << MappingState(); mapStates << MappingState();
@@ -1803,6 +1805,9 @@ public:
// Global marks. // Global marks.
Marks marks; Marks marks;
// Return to insert/replace mode after single command (<C-O>).
Mode returnToMode;
} g; } g;
}; };
@@ -1854,6 +1859,17 @@ void FakeVimHandler::Private::init()
setupCharClass(); setupCharClass();
} }
void FakeVimHandler::Private::focus()
{
stopIncrementalFind();
if (g.returnToMode != CommandMode && g.currentCommand.isEmpty() && m_mode != ExMode) {
// Return to insert mode.
resetCommandMode();
updateMiniBuffer();
updateCursorShape();
}
}
bool FakeVimHandler::Private::wantsOverride(QKeyEvent *ev) bool FakeVimHandler::Private::wantsOverride(QKeyEvent *ev)
{ {
const int key = ev->key(); const int key = ev->key();
@@ -1867,7 +1883,8 @@ bool FakeVimHandler::Private::wantsOverride(QKeyEvent *ev)
if (isNoVisualMode() if (isNoVisualMode()
&& m_mode == CommandMode && m_mode == CommandMode
&& m_submode == NoSubMode && m_submode == NoSubMode
&& g.currentCommand.isEmpty()) && g.currentCommand.isEmpty()
&& g.returnToMode == CommandMode)
return false; return false;
return true; return true;
} }
@@ -2009,7 +2026,7 @@ void FakeVimHandler::Private::installEventFilter()
void FakeVimHandler::Private::setupWidget() void FakeVimHandler::Private::setupWidget()
{ {
enterCommandMode(); resetCommandMode();
if (m_textedit) { if (m_textedit) {
m_textedit->setLineWrapMode(QTextEdit::NoWrap); m_textedit->setLineWrapMode(QTextEdit::NoWrap);
} else if (m_plaintextedit) { } else if (m_plaintextedit) {
@@ -2325,8 +2342,9 @@ void FakeVimHandler::Private::updateFind(bool isComplete)
g.currentMessage.clear(); g.currentMessage.clear();
const QString &needle = g.searchBuffer.contents();
SearchData sd; SearchData sd;
sd.needle = g.searchBuffer.contents(); sd.needle = needle;
sd.forward = g.lastSearchForward; sd.forward = g.lastSearchForward;
sd.highlightMatches = isComplete; sd.highlightMatches = isComplete;
if (isComplete) { if (isComplete) {
@@ -2573,7 +2591,8 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement)
if (m_movetype == MoveLineWise) if (m_movetype == MoveLineWise)
insertAutomaticIndentation(true); insertAutomaticIndentation(true);
endEditBlock(); endEditBlock();
enterInsertMode(); setTargetColumn();
g.returnToMode = InsertMode;
} else if (m_submode == DeleteSubMode) { } else if (m_submode == DeleteSubMode) {
setUndoPosition(); setUndoPosition();
removeText(currentRange()); removeText(currentRange());
@@ -2643,6 +2662,13 @@ void FakeVimHandler::Private::resetCommandMode()
if (isNoVisualMode()) if (isNoVisualMode())
setAnchor(); setAnchor();
g.currentCommand.clear(); g.currentCommand.clear();
if (g.returnToMode != CommandMode) {
if (g.returnToMode == InsertMode)
enterInsertMode();
else
enterReplaceMode();
moveToTargetColumn();
}
} }
void FakeVimHandler::Private::updateSelection() void FakeVimHandler::Private::updateSelection()
@@ -2718,7 +2744,12 @@ void FakeVimHandler::Private::updateMiniBuffer()
msg = "REPLACE"; msg = "REPLACE";
} else { } else {
QTC_CHECK(m_mode == CommandMode && m_subsubmode != SearchSubSubMode); QTC_CHECK(m_mode == CommandMode && m_subsubmode != SearchSubSubMode);
msg = "COMMAND"; if (g.returnToMode == CommandMode)
msg = "COMMAND";
else if (g.returnToMode == InsertMode)
msg = "(insert)";
else
msg = "(replace)";
} }
emit q->commandBufferChanged(msg, cursorPos, messageLevel, q); emit q->commandBufferChanged(msg, cursorPos, messageLevel, q);
@@ -3261,7 +3292,7 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input)
QString savedCommand = g.dotCommand; QString savedCommand = g.dotCommand;
g.dotCommand.clear(); g.dotCommand.clear();
replay(savedCommand); replay(savedCommand);
enterCommandMode(); resetCommandMode();
g.dotCommand = savedCommand; g.dotCommand = savedCommand;
} else if (input.is('<') || input.is('>') || input.is('=')) { } else if (input.is('<') || input.is('>') || input.is('=')) {
if (isNoVisualMode()) { if (isNoVisualMode()) {
@@ -3876,10 +3907,7 @@ EventResult FakeVimHandler::Private::handleReplaceMode(const Input &input)
if (input.isEscape()) { if (input.isEscape()) {
moveLeft(qMin(1, leftDist())); moveLeft(qMin(1, leftDist()));
setTargetColumn(); setTargetColumn();
m_submode = NoSubMode; enterCommandMode();
m_mode = CommandMode;
finishMovement();
updateMiniBuffer();
} else if (input.isKey(Key_Left)) { } else if (input.isKey(Key_Left)) {
breakEditBlock(); breakEditBlock();
moveLeft(1); moveLeft(1);
@@ -3895,6 +3923,10 @@ EventResult FakeVimHandler::Private::handleReplaceMode(const Input &input)
} else if (input.isKey(Key_Down)) { } else if (input.isKey(Key_Down)) {
breakEditBlock(); breakEditBlock();
moveDown(1); moveDown(1);
} else if (input.isKey(Key_Insert)) {
m_mode = InsertMode;
} else if (input.isControl('o')) {
enterCommandMode(ReplaceMode);
} else { } else {
joinPreviousEditBlock(); joinPreviousEditBlock();
if (!atEndOfLine()) { if (!atEndOfLine()) {
@@ -3910,6 +3942,8 @@ EventResult FakeVimHandler::Private::handleReplaceMode(const Input &input)
endEditBlock(); endEditBlock();
setTargetColumn(); setTargetColumn();
} }
updateMiniBuffer();
return EventHandled; return EventHandled;
} }
@@ -3960,12 +3994,13 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
g.dotCommand += m_lastInsertion; g.dotCommand += m_lastInsertion;
g.dotCommand += QChar(27); g.dotCommand += QChar(27);
enterCommandMode(); enterCommandMode();
m_submode = NoSubMode;
m_ctrlVActive = false; m_ctrlVActive = false;
m_opcount.clear(); m_opcount.clear();
m_mvcount.clear(); m_mvcount.clear();
} else if (m_ctrlVActive) { } else if (m_ctrlVActive) {
insertInInsertMode(input.raw()); insertInInsertMode(input.raw());
} else if (input.isControl('o')) {
enterCommandMode(InsertMode);
} else if (input.isControl('v')) { } else if (input.isControl('v')) {
m_ctrlVActive = true; m_ctrlVActive = true;
} else if (input.isControl('w')) { } else if (input.isControl('w')) {
@@ -3976,10 +4011,7 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
Range range(beginPos, endPos, RangeCharMode); Range range(beginPos, endPos, RangeCharMode);
removeText(range); removeText(range);
} else if (input.isKey(Key_Insert)) { } else if (input.isKey(Key_Insert)) {
if (m_mode == ReplaceMode) m_mode = ReplaceMode;
m_mode = InsertMode;
else
m_mode = ReplaceMode;
} else if (input.isKey(Key_Left)) { } else if (input.isKey(Key_Left)) {
moveLeft(count()); moveLeft(count());
setTargetColumn(); setTargetColumn();
@@ -4145,7 +4177,8 @@ EventResult FakeVimHandler::Private::handleExMode(const Input &input)
{ {
if (input.isEscape()) { if (input.isEscape()) {
g.commandBuffer.clear(); g.commandBuffer.clear();
enterCommandMode(); enterCommandMode(g.returnToMode);
resetCommandMode();
m_ctrlVActive = false; m_ctrlVActive = false;
} else if (m_ctrlVActive) { } else if (m_ctrlVActive) {
g.commandBuffer.insertChar(input.raw()); g.commandBuffer.insertChar(input.raw());
@@ -4154,10 +4187,12 @@ EventResult FakeVimHandler::Private::handleExMode(const Input &input)
m_ctrlVActive = true; m_ctrlVActive = true;
return EventHandled; return EventHandled;
} else if (input.isBackspace()) { } else if (input.isBackspace()) {
if (g.commandBuffer.isEmpty()) if (g.commandBuffer.isEmpty()) {
enterCommandMode(); enterCommandMode(g.returnToMode);
else resetCommandMode();
} else {
g.commandBuffer.deleteChar(); g.commandBuffer.deleteChar();
}
} else if (input.isKey(Key_Tab)) { } else if (input.isKey(Key_Tab)) {
// FIXME: Complete actual commands. // FIXME: Complete actual commands.
g.commandBuffer.historyUp(); g.commandBuffer.historyUp();
@@ -4190,10 +4225,11 @@ EventResult FakeVimHandler::Private::handleSearchSubSubMode(const Input &input)
g.searchBuffer.clear(); g.searchBuffer.clear();
setAnchorAndPosition(m_searchStartPosition, m_searchStartPosition); setAnchorAndPosition(m_searchStartPosition, m_searchStartPosition);
scrollToLine(m_searchFromScreenLine); scrollToLine(m_searchFromScreenLine);
enterCommandMode(); enterCommandMode(g.returnToMode);
resetCommandMode();
} else if (input.isBackspace()) { } else if (input.isBackspace()) {
if (g.searchBuffer.isEmpty()) { if (g.searchBuffer.isEmpty()) {
enterCommandMode(); resetCommandMode();
} else { } else {
g.searchBuffer.deleteChar(); g.searchBuffer.deleteChar();
} }
@@ -4217,7 +4253,8 @@ EventResult FakeVimHandler::Private::handleSearchSubSubMode(const Input &input)
showMessage(MessageCommand, g.searchBuffer.display()); showMessage(MessageCommand, g.searchBuffer.display());
else else
handled = EventCancelled; handled = EventCancelled;
enterCommandMode(); enterCommandMode(g.returnToMode);
resetCommandMode();
g.searchBuffer.clear(); g.searchBuffer.clear();
} else if (input.isKey(Key_Up) || input.isKey(Key_PageUp)) { } else if (input.isKey(Key_Up) || input.isKey(Key_PageUp)) {
g.searchBuffer.historyUp(); g.searchBuffer.historyUp();
@@ -5122,7 +5159,7 @@ void FakeVimHandler::Private::handleExCommand(const QString &line0)
//qDebug() << "CMD: " << cmd; //qDebug() << "CMD: " << cmd;
enterCommandMode(); enterCommandMode(g.returnToMode);
beginLargeEditBlock(); beginLargeEditBlock();
ExCommand cmd; ExCommand cmd;
@@ -5135,6 +5172,8 @@ void FakeVimHandler::Private::handleExCommand(const QString &line0)
lastCommand = line; lastCommand = line;
} }
endEditBlock(); endEditBlock();
resetCommandMode();
} }
bool FakeVimHandler::Private::handleExCommandHelper(ExCommand &cmd) bool FakeVimHandler::Private::handleExCommandHelper(ExCommand &cmd)
@@ -5420,17 +5459,14 @@ void FakeVimHandler::Private::moveToTargetColumn()
const QTextBlock &bl = block(); const QTextBlock &bl = block();
//Column column = cursorColumn(); //Column column = cursorColumn();
//int logical = logical //int logical = logical
const int maxcol = bl.length() - 2; const int pos = lastPositionInLine(bl.blockNumber() + 1, false);
if (m_targetColumn == -1) { if (m_targetColumn == -1) {
setPosition(bl.position() + qMax(0, maxcol)); setPosition(pos);
return; return;
} }
const int physical = logicalToPhysicalColumn(m_targetColumn, bl.text()); const int physical = bl.position() + logicalToPhysicalColumn(m_targetColumn, bl.text());
//qDebug() << "CORRECTING COLUMN FROM: " << logical << "TO" << m_targetColumn; //qDebug() << "CORRECTING COLUMN FROM: " << logical << "TO" << m_targetColumn;
if (physical >= maxcol) setPosition(qMin(pos, physical));
setPosition(bl.position() + qMax(0, maxcol));
else
setPosition(bl.position() + physical);
} }
/* if simple is given: /* if simple is given:
@@ -6463,6 +6499,7 @@ void FakeVimHandler::Private::enterReplaceMode()
m_subsubmode = NoSubSubMode; m_subsubmode = NoSubSubMode;
m_lastInsertion.clear(); m_lastInsertion.clear();
m_lastDeletion.clear(); m_lastDeletion.clear();
g.returnToMode = ReplaceMode;
} }
void FakeVimHandler::Private::enterInsertMode() void FakeVimHandler::Private::enterInsertMode()
@@ -6472,15 +6509,17 @@ void FakeVimHandler::Private::enterInsertMode()
m_subsubmode = NoSubSubMode; m_subsubmode = NoSubSubMode;
m_lastInsertion.clear(); m_lastInsertion.clear();
m_lastDeletion.clear(); m_lastDeletion.clear();
g.returnToMode = InsertMode;
} }
void FakeVimHandler::Private::enterCommandMode() void FakeVimHandler::Private::enterCommandMode(Mode returnToMode)
{ {
if (atEndOfLine()) if (atEndOfLine())
moveLeft(); moveLeft();
m_mode = CommandMode; m_mode = CommandMode;
m_submode = NoSubMode; m_submode = NoSubMode;
m_subsubmode = NoSubSubMode; m_subsubmode = NoSubSubMode;
g.returnToMode = returnToMode;
} }
void FakeVimHandler::Private::enterExMode(const QString &contents) void FakeVimHandler::Private::enterExMode(const QString &contents)
@@ -7012,7 +7051,8 @@ bool FakeVimHandler::eventFilter(QObject *ob, QEvent *ev)
return res == EventHandled || res == EventCancelled; return res == EventHandled || res == EventCancelled;
} }
if (active && ev->type() == QEvent::KeyPress) { if (active && ev->type() == QEvent::KeyPress &&
(ob == d->editor() || (d->m_mode == ExMode || d->m_subsubmode == SearchSubSubMode))) {
QKeyEvent *kev = static_cast<QKeyEvent *>(ev); QKeyEvent *kev = static_cast<QKeyEvent *>(ev);
KEY_DEBUG("KEYPRESS" << kev->key() << kev->text() << QChar(kev->key())); KEY_DEBUG("KEYPRESS" << kev->key() << kev->text() << QChar(kev->key()));
EventResult res = d->handleEvent(kev); EventResult res = d->handleEvent(kev);
@@ -7037,7 +7077,7 @@ bool FakeVimHandler::eventFilter(QObject *ob, QEvent *ev)
} }
if (active && ev->type() == QEvent::FocusIn && ob == d->editor()) { if (active && ev->type() == QEvent::FocusIn && ob == d->editor()) {
d->stopIncrementalFind(); d->focus();
} }
return QObject::eventFilter(ob, ev); return QObject::eventFilter(ob, ev);