forked from qt-creator/qt-creator
FakeVim: Monitor inserted text using signals from QTextDocument
Use QTextDocument::onContentsChanged() to monitor text inserted and removed in insert mode for dot command (or just repeat with 2i, 3o etc.) to re-insert same text. Works even if auto-completion is replacing '.' with '->' for pointers. Change-Id: Ie39edcdc9ec60bcf6c7d10f021248c3a0aee76b6 Reviewed-by: hjk <hjk121@nokiamail.com>
This commit is contained in:
@@ -3292,8 +3292,16 @@ void FakeVimPlugin::test_vim_qtcreator()
|
||||
"");
|
||||
|
||||
// Record long insert mode.
|
||||
KEYS("qb" "4s" "bool" "<down>" "Q_<insert>ASSERT" "<down><down>" "<insert><bs>2"
|
||||
"<c-o>2w<delete>1" "<c-o>:s/true/false<cr><esc>" "q",
|
||||
KEYS("qb"
|
||||
"4s" "bool" // 1
|
||||
"<down>"
|
||||
"Q_<insert>ASSERT" // 2
|
||||
"<down><down>"
|
||||
"<insert><bs>2" // 3
|
||||
"<c-o>2w"
|
||||
"<delete>1" // 4
|
||||
"<c-o>:s/true/false<cr><esc>" // 5
|
||||
"q",
|
||||
"bool f(int arg1, int arg2 = 0) {" N
|
||||
" Q_ASSERT(arg1 >= 0);" N
|
||||
" if (arg1 > 0) return true;" N
|
||||
@@ -3301,35 +3309,35 @@ void FakeVimPlugin::test_vim_qtcreator()
|
||||
"}" N
|
||||
"");
|
||||
|
||||
KEYS("u",
|
||||
KEYS("u", // 5
|
||||
"bool f(int arg1, int arg2 = 0) {" N
|
||||
" Q_ASSERT(arg1 >= 0);" N
|
||||
" if (arg1 > 0) return true;" N
|
||||
X " if (arg2 > 1) return true;" N
|
||||
"}" N
|
||||
"");
|
||||
KEYS("u",
|
||||
KEYS("u", // 4
|
||||
"bool f(int arg1, int arg2 = 0) {" N
|
||||
" Q_ASSERT(arg1 >= 0);" N
|
||||
" if (arg1 > 0) return true;" N
|
||||
" if (arg2 > " X "0) return true;" N
|
||||
"}" N
|
||||
"");
|
||||
KEYS("u",
|
||||
KEYS("u", // 3
|
||||
"bool f(int arg1, int arg2 = 0) {" N
|
||||
" Q_ASSERT(arg1 >= 0);" N
|
||||
" if (arg1 > 0) return true;" N
|
||||
" if (arg1" X " > 0) return true;" N
|
||||
"}" N
|
||||
"");
|
||||
KEYS("u",
|
||||
KEYS("u", // 2
|
||||
"bool f(int arg1, int arg2 = 0) {" N
|
||||
" " X "assert(arg1 >= 0);" N
|
||||
" if (arg1 > 0) return true;" N
|
||||
" if (arg1 > 0) return true;" N
|
||||
"}" N
|
||||
"");
|
||||
KEYS("u",
|
||||
KEYS("u", // 1
|
||||
X "void f(int arg1, int arg2 = 0) {" N
|
||||
" assert(arg1 >= 0);" N
|
||||
" if (arg1 > 0) return true;" N
|
||||
@@ -3374,4 +3382,28 @@ void FakeVimPlugin::test_vim_qtcreator()
|
||||
" if (arg2 > 1) return false;" N
|
||||
"}" N
|
||||
"");
|
||||
|
||||
// Macros
|
||||
data.setText(
|
||||
"void f(int arg1) {" N
|
||||
"}" N
|
||||
"");
|
||||
KEYS("2o" "#ifdef HAS_FEATURE<cr>doSomething();<cr>"
|
||||
"#else<cr>"
|
||||
"doSomethingElse<bs><bs><bs><bs>2();<cr>"
|
||||
"#endif"
|
||||
"<esc>",
|
||||
"void f(int arg1) {" N
|
||||
"#ifdef HAS_FEATURE" N
|
||||
" doSomething();" N
|
||||
"#else" N
|
||||
" doSomething2();" N
|
||||
"#endif" N
|
||||
"#ifdef HAS_FEATURE" N
|
||||
" doSomething();" N
|
||||
"#else" N
|
||||
" doSomething2();" N
|
||||
"#endi" X "f" N
|
||||
"}" N
|
||||
"");
|
||||
}
|
||||
|
@@ -1458,8 +1458,9 @@ public:
|
||||
void clearPendingInput();
|
||||
void waitForMapping();
|
||||
EventResult stopWaitForMapping(bool hasInput);
|
||||
EventResult handleInsertMode(const Input &);
|
||||
EventResult handleReplaceMode(const Input &);
|
||||
EventResult handleInsertOrReplaceMode(const Input &);
|
||||
void handleInsertMode(const Input &);
|
||||
void handleReplaceMode(const Input &);
|
||||
|
||||
EventResult handleCommandMode(const Input &);
|
||||
|
||||
@@ -1525,6 +1526,7 @@ public:
|
||||
int lastPositionInLine(int line, bool onlyVisibleLines = true) const; // 1 based line, 0 based pos
|
||||
int lineForPosition(int pos) const; // 1 based line, 0 based pos
|
||||
QString lineContents(int line) const; // 1 based line
|
||||
QString textAt(int from, int to) const;
|
||||
void setLineContents(int line, const QString &contents); // 1 based line
|
||||
int blockBoundary(const QString &left, const QString &right,
|
||||
bool end, int count) const; // end or start position of current code block
|
||||
@@ -1651,9 +1653,10 @@ public:
|
||||
|
||||
bool handleFfTt(QString key);
|
||||
|
||||
void enterInsertMode();
|
||||
void initVisualInsertMode(QChar command);
|
||||
void enterReplaceMode();
|
||||
void enterInsertMode();
|
||||
void enterInsertOrReplaceMode(Mode mode);
|
||||
void enterCommandMode(Mode returnToMode = CommandMode);
|
||||
void enterExMode(const QString &contents = QString());
|
||||
void showMessage(MessageLevel level, const QString &msg);
|
||||
@@ -1675,7 +1678,7 @@ public:
|
||||
void endEditBlock();
|
||||
void breakEditBlock() { m_breakEditBlock = true; }
|
||||
|
||||
Q_SLOT void onContentsChanged();
|
||||
Q_SLOT void onContentsChanged(int position, int charsRemoved, int charsAdded);
|
||||
Q_SLOT void onUndoCommandAdded();
|
||||
|
||||
bool isInsertMode() const { return g.mode == InsertMode || g.mode == ReplaceMode; }
|
||||
@@ -1700,7 +1703,10 @@ public:
|
||||
|
||||
Q_SLOT void importSelection();
|
||||
void exportSelection();
|
||||
void recordInsertion(const QString &insert = QString());
|
||||
void commitInsertState();
|
||||
void invalidateInsertState();
|
||||
bool isInsertStateValid() const;
|
||||
void clearLastInsertion();
|
||||
void ensureCursorVisible();
|
||||
void insertInInsertMode(const QString &text);
|
||||
|
||||
@@ -1721,10 +1727,22 @@ public:
|
||||
int m_oldInternalPosition; // copy from last event to check for external changes
|
||||
int m_oldInternalAnchor;
|
||||
int m_oldPosition; // FIXME: Merge with above.
|
||||
int m_oldDocumentLength;
|
||||
int m_register;
|
||||
bool m_visualBlockInsert;
|
||||
|
||||
// Insert state to get last inserted text.
|
||||
struct InsertState {
|
||||
int pos1;
|
||||
int pos2;
|
||||
int backspaces;
|
||||
int deletes;
|
||||
QSet<int> spaces;
|
||||
bool insertingSpaces;
|
||||
QString textBeforeCursor;
|
||||
bool newLineBefore;
|
||||
bool newLineAfter;
|
||||
} m_insertState;
|
||||
|
||||
bool m_fakeEnd;
|
||||
bool m_anchorPastEnd;
|
||||
bool m_positionPastEnd; // '$' & 'l' in visual mode can move past eol
|
||||
@@ -1781,10 +1799,8 @@ public:
|
||||
|
||||
void insertNewLine();
|
||||
|
||||
bool handleInsertInEditor(const Input &input, QString *insert);
|
||||
bool handleInsertInEditor(const Input &input);
|
||||
bool passEventToEditor(QEvent &event); // Pass event to editor widget without filtering. Returns true if event was processed.
|
||||
// Guess insert command for text modification which happened externally (e.g. code-completion).
|
||||
QString guessInsertCommand(int pos1, int pos2, int len1, int len2);
|
||||
|
||||
// undo handling
|
||||
int revision() const { return document()->revision(); }
|
||||
@@ -1799,7 +1815,7 @@ public:
|
||||
int m_lastUndoSteps;
|
||||
|
||||
// extra data for '.'
|
||||
void replay(const QString &text);
|
||||
void replay(const QString &text, int repeat = 1);
|
||||
void setDotCommand(const QString &cmd) { g.dotCommand = cmd; }
|
||||
void setDotCommand(const QString &cmd, int n) { g.dotCommand = cmd.arg(n); }
|
||||
QString visualDotCommand() const;
|
||||
@@ -1832,9 +1848,7 @@ public:
|
||||
QString tabExpand(int len) const;
|
||||
Column indentation(const QString &line) const;
|
||||
void insertAutomaticIndentation(bool goingDown, bool forceAutoIndent = false);
|
||||
bool removeAutomaticIndentation(); // true if something removed
|
||||
// number of autoindented characters
|
||||
int m_justAutoIndented;
|
||||
void handleStartOfLine();
|
||||
|
||||
// register handling
|
||||
@@ -1992,7 +2006,8 @@ FakeVimHandler::Private::Private(FakeVimHandler *parent, QWidget *widget)
|
||||
m_textedit = qobject_cast<QTextEdit *>(widget);
|
||||
m_plaintextedit = qobject_cast<QPlainTextEdit *>(widget);
|
||||
if (editor()) {
|
||||
connect(EDITOR(document()), SIGNAL(contentsChanged()), SLOT(onContentsChanged()));
|
||||
connect(EDITOR(document()), SIGNAL(contentsChange(int,int,int)),
|
||||
SLOT(onContentsChanged(int,int,int)));
|
||||
connect(EDITOR(document()), SIGNAL(undoCommandAdded()), SLOT(onUndoCommandAdded()));
|
||||
m_lastUndoSteps = document()->availableUndoSteps();
|
||||
}
|
||||
@@ -2012,14 +2027,13 @@ void FakeVimHandler::Private::init()
|
||||
m_lastVisualModeInverted = false;
|
||||
m_targetColumn = 0;
|
||||
m_visualTargetColumn = 0;
|
||||
m_justAutoIndented = 0;
|
||||
m_ctrlVActive = false;
|
||||
m_oldInternalAnchor = -1;
|
||||
m_oldInternalPosition = -1;
|
||||
m_oldExternalAnchor = -1;
|
||||
m_oldExternalPosition = -1;
|
||||
m_oldPosition = -1;
|
||||
m_oldDocumentLength = -1;
|
||||
m_insertState = InsertState();
|
||||
m_breakEditBlock = false;
|
||||
m_searchStartPosition = 0;
|
||||
m_searchFromScreenLine = 0;
|
||||
@@ -2032,8 +2046,10 @@ void FakeVimHandler::Private::init()
|
||||
void FakeVimHandler::Private::focus()
|
||||
{
|
||||
stopIncrementalFind();
|
||||
if (!isInsertMode()) {
|
||||
leaveVisualMode();
|
||||
resetCommandMode();
|
||||
}
|
||||
updateCursorShape();
|
||||
if (!g.inFakeVim || g.mode != CommandMode)
|
||||
updateMiniBuffer();
|
||||
@@ -2298,31 +2314,62 @@ void FakeVimHandler::Private::exportSelection()
|
||||
m_oldExternalAnchor = anchor();
|
||||
}
|
||||
|
||||
void FakeVimHandler::Private::recordInsertion(const QString &insert)
|
||||
void FakeVimHandler::Private::commitInsertState()
|
||||
{
|
||||
const int pos = position();
|
||||
if (!isInsertStateValid())
|
||||
return;
|
||||
|
||||
if (insert.isNull()) {
|
||||
const int dist = pos - m_oldPosition;
|
||||
// Get raw inserted text.
|
||||
m_lastInsertion = textAt(m_insertState.pos1, m_insertState.pos2);
|
||||
|
||||
if (dist > 0) {
|
||||
Range range(m_oldPosition, pos);
|
||||
QString text = selectText(range);
|
||||
// escape text like <ESC>
|
||||
text.replace(_("<"), _("<LT>"));
|
||||
m_lastInsertion.append(text);
|
||||
} else if (dist < 0) {
|
||||
m_lastInsertion.resize(m_lastInsertion.size() + dist);
|
||||
}
|
||||
} else {
|
||||
m_lastInsertion += insert;
|
||||
// Escape special characters and spaces inserted by user (not by auto-indentation).
|
||||
for (int i = m_lastInsertion.size() - 1; i >= 0; --i) {
|
||||
const int pos = m_insertState.pos1 + i;
|
||||
const ushort c = document()->characterAt(pos).unicode();
|
||||
if (c == '<')
|
||||
m_lastInsertion.replace(i, 1, _("<LT>"));
|
||||
else if ((c == ' ' || c == '\t') && m_insertState.spaces.contains(pos))
|
||||
m_lastInsertion.replace(i, 1, _(c == ' ' ? "<SPACE>" : "<TAB>"));
|
||||
}
|
||||
|
||||
if (m_oldPosition != pos) {
|
||||
m_oldPosition = pos;
|
||||
setTargetColumn();
|
||||
}
|
||||
m_oldDocumentLength = document()->characterCount();
|
||||
// Remove unnecessary backspaces.
|
||||
while (m_insertState.backspaces > 0 && !m_lastInsertion.isEmpty() && m_lastInsertion[0].isSpace())
|
||||
--m_insertState.backspaces;
|
||||
|
||||
// backspaces in front of inserted text
|
||||
m_lastInsertion.prepend(QString(_("<BS>")).repeated(m_insertState.backspaces));
|
||||
// deletes after inserted text
|
||||
m_lastInsertion.prepend(QString(_("<DELETE>")).repeated(m_insertState.deletes));
|
||||
|
||||
// Remove indentation.
|
||||
m_lastInsertion.replace(QRegExp(_("(^|\n)[\\t ]+")), _("\\1"));
|
||||
}
|
||||
|
||||
void FakeVimHandler::Private::invalidateInsertState()
|
||||
{
|
||||
m_oldPosition = position();
|
||||
m_insertState.pos1 = -1;
|
||||
m_insertState.pos2 = m_oldPosition;
|
||||
m_insertState.backspaces = 0;
|
||||
m_insertState.deletes = 0;
|
||||
m_insertState.spaces.clear();
|
||||
m_insertState.insertingSpaces = false;
|
||||
m_insertState.textBeforeCursor = textAt(document()->findBlock(m_oldPosition).position(),
|
||||
m_oldPosition);
|
||||
m_insertState.newLineBefore = false;
|
||||
m_insertState.newLineAfter = false;
|
||||
}
|
||||
|
||||
bool FakeVimHandler::Private::isInsertStateValid() const
|
||||
{
|
||||
return m_insertState.pos1 != -1;
|
||||
}
|
||||
|
||||
void FakeVimHandler::Private::clearLastInsertion()
|
||||
{
|
||||
invalidateInsertState();
|
||||
m_lastInsertion.clear();
|
||||
m_insertState.pos1 = m_insertState.pos2;
|
||||
}
|
||||
|
||||
void FakeVimHandler::Private::ensureCursorVisible()
|
||||
@@ -2469,10 +2516,8 @@ EventResult FakeVimHandler::Private::handleDefaultKey(const Input &input)
|
||||
return handleSearchSubSubMode(input);
|
||||
else if (g.mode == CommandMode)
|
||||
return handleCommandMode(input);
|
||||
else if (g.mode == InsertMode)
|
||||
return handleInsertMode(input);
|
||||
else if (g.mode == ReplaceMode)
|
||||
return handleReplaceMode(input);
|
||||
else if (g.mode == InsertMode || g.mode == ReplaceMode)
|
||||
return handleInsertOrReplaceMode(input);
|
||||
else if (g.mode == ExMode)
|
||||
return handleExMode(input);
|
||||
return EventUnhandled;
|
||||
@@ -2965,8 +3010,6 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement)
|
||||
insertAutomaticIndentation(true);
|
||||
endEditBlock();
|
||||
setTargetColumn();
|
||||
m_lastInsertion.clear();
|
||||
g.returnToMode = InsertMode;
|
||||
} else if (g.submode == DeleteSubMode) {
|
||||
pushUndoState(false);
|
||||
beginEditBlock();
|
||||
@@ -3037,7 +3080,13 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement)
|
||||
if (!dotCommand.isEmpty() && !dotCommandMovement.isEmpty())
|
||||
setDotCommand(dotCommand + dotCommandMovement);
|
||||
|
||||
// Change command continues in insert mode.
|
||||
if (g.submode == ChangeSubMode) {
|
||||
clearCommandMode();
|
||||
enterInsertMode();
|
||||
} else {
|
||||
resetCommandMode();
|
||||
}
|
||||
}
|
||||
|
||||
void FakeVimHandler::Private::resetCommandMode()
|
||||
@@ -3052,8 +3101,8 @@ void FakeVimHandler::Private::resetCommandMode()
|
||||
else
|
||||
enterReplaceMode();
|
||||
moveToTargetColumn();
|
||||
invalidateInsertState();
|
||||
m_lastInsertion = lastInsertion;
|
||||
m_oldPosition = position();
|
||||
}
|
||||
if (isNoVisualMode())
|
||||
setAnchor();
|
||||
@@ -3901,19 +3950,20 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input)
|
||||
}
|
||||
const int line = lineNumber(block());
|
||||
|
||||
enterInsertMode();
|
||||
beginEditBlock();
|
||||
enterInsertMode();
|
||||
setPosition(appendLine ? lastPositionInLine(line) : firstPositionInLine(line));
|
||||
clearLastInsertion();
|
||||
setAnchor();
|
||||
insertNewLine();
|
||||
if (appendLine) {
|
||||
setPosition(lastPositionInLine(line));
|
||||
setAnchor();
|
||||
insertNewLine();
|
||||
m_insertState.newLineBefore = true;
|
||||
} else {
|
||||
setPosition(firstPositionInLine(line));
|
||||
setAnchor();
|
||||
insertNewLine();
|
||||
moveUp();
|
||||
m_oldPosition = position();
|
||||
m_insertState.pos1 = m_oldPosition;
|
||||
m_insertState.newLineAfter = true;
|
||||
}
|
||||
recordInsertion(QString::fromLatin1("\n"));
|
||||
setTargetColumn();
|
||||
endEditBlock();
|
||||
|
||||
@@ -4365,44 +4415,55 @@ bool FakeVimHandler::Private::handleMacroExecuteSubMode(const Input &input)
|
||||
return result;
|
||||
}
|
||||
|
||||
EventResult FakeVimHandler::Private::handleReplaceMode(const Input &input)
|
||||
EventResult FakeVimHandler::Private::handleInsertOrReplaceMode(const Input &input)
|
||||
{
|
||||
bool clearLastInsertion = m_breakEditBlock;
|
||||
if (m_oldPosition != position()) {
|
||||
if (clearLastInsertion) {
|
||||
clearLastInsertion = false;
|
||||
m_lastInsertion = _("<INSERT>");
|
||||
}
|
||||
recordInsertion();
|
||||
if (position() < m_insertState.pos1 || position() > m_insertState.pos2) {
|
||||
commitInsertState();
|
||||
invalidateInsertState();
|
||||
}
|
||||
|
||||
if (g.mode == InsertMode)
|
||||
handleInsertMode(input);
|
||||
else
|
||||
handleReplaceMode(input);
|
||||
|
||||
if (!isInsertMode() || m_breakEditBlock
|
||||
|| position() < m_insertState.pos1 || position() > m_insertState.pos2) {
|
||||
commitInsertState();
|
||||
invalidateInsertState();
|
||||
breakEditBlock();
|
||||
} else if (m_oldPosition == position()) {
|
||||
setTargetColumn();
|
||||
}
|
||||
|
||||
updateMiniBuffer();
|
||||
|
||||
// We don't want fancy stuff in insert mode.
|
||||
return EventHandled;
|
||||
}
|
||||
|
||||
void FakeVimHandler::Private::handleReplaceMode(const Input &input)
|
||||
{
|
||||
if (input.isEscape()) {
|
||||
commitInsertState();
|
||||
moveLeft(qMin(1, leftDist()));
|
||||
enterCommandMode();
|
||||
g.dotCommand += m_lastInsertion;
|
||||
g.dotCommand += QChar(27);
|
||||
g.dotCommand.append(m_lastInsertion + _("<ESC>"));
|
||||
} else if (input.isKey(Key_Left)) {
|
||||
breakEditBlock();
|
||||
moveLeft(1);
|
||||
moveLeft();
|
||||
setTargetColumn();
|
||||
} else if (input.isKey(Key_Right)) {
|
||||
breakEditBlock();
|
||||
moveRight(1);
|
||||
moveRight();
|
||||
setTargetColumn();
|
||||
} else if (input.isKey(Key_Up)) {
|
||||
breakEditBlock();
|
||||
moveUp(1);
|
||||
moveUp();
|
||||
} else if (input.isKey(Key_Down)) {
|
||||
breakEditBlock();
|
||||
moveDown(1);
|
||||
moveDown();
|
||||
} else if (input.isKey(Key_Insert)) {
|
||||
g.mode = InsertMode;
|
||||
recordInsertion(_("<INSERT>"));
|
||||
} else if (input.isControl('o')) {
|
||||
enterCommandMode(ReplaceMode);
|
||||
} else {
|
||||
if (clearLastInsertion)
|
||||
m_lastInsertion = _("<INSERT>");
|
||||
joinPreviousEditBlock();
|
||||
if (!atEndOfLine()) {
|
||||
setAnchor();
|
||||
@@ -4413,38 +4474,34 @@ EventResult FakeVimHandler::Private::handleReplaceMode(const Input &input)
|
||||
setAnchor();
|
||||
insertText(text);
|
||||
endEditBlock();
|
||||
recordInsertion();
|
||||
}
|
||||
m_oldPosition = position();
|
||||
updateMiniBuffer();
|
||||
|
||||
return EventHandled;
|
||||
}
|
||||
|
||||
EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
|
||||
void FakeVimHandler::Private::handleInsertMode(const Input &input)
|
||||
{
|
||||
bool clearLastInsertion = m_breakEditBlock && !m_lastInsertion.isEmpty();
|
||||
int pos2 = position();
|
||||
int len2 = document()->characterCount();
|
||||
if (m_oldPosition != pos2 || m_oldDocumentLength != len2) {
|
||||
if (clearLastInsertion) {
|
||||
clearLastInsertion = false;
|
||||
m_lastInsertion.clear();
|
||||
g.dotCommand = _("i");
|
||||
}
|
||||
recordInsertion(guessInsertCommand(m_oldPosition, pos2, m_oldDocumentLength, len2));
|
||||
}
|
||||
|
||||
QString insert;
|
||||
if (input.isEscape()) {
|
||||
bool newLineAfter = m_insertState.newLineAfter;
|
||||
bool newLineBefore = m_insertState.newLineBefore;
|
||||
|
||||
// Repeat insertion [count] times.
|
||||
// One instance was already physically inserted while typing.
|
||||
if (!m_breakEditBlock && !m_lastInsertion.isEmpty()) {
|
||||
const QString text = m_lastInsertion;
|
||||
const int repeat = count();
|
||||
if (!m_breakEditBlock && isInsertStateValid()) {
|
||||
commitInsertState();
|
||||
|
||||
QString text = m_lastInsertion;
|
||||
const QString dotCommand = g.dotCommand;
|
||||
const int repeat = count() - 1;
|
||||
m_lastInsertion.clear();
|
||||
joinPreviousEditBlock();
|
||||
replay(text.repeated(repeat - 1));
|
||||
|
||||
if (newLineAfter) {
|
||||
text.chop(1);
|
||||
text.prepend(_("<END>\n"));
|
||||
} else if (newLineBefore) {
|
||||
text.prepend(_("<END>"));
|
||||
}
|
||||
|
||||
replay(text, repeat);
|
||||
|
||||
if (m_visualBlockInsert && !text.contains(QLatin1Char('\n'))) {
|
||||
const CursorPosition lastAnchor = mark(QLatin1Char('<')).position;
|
||||
@@ -4452,7 +4509,7 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
|
||||
CursorPosition startPos(lastAnchor.line,
|
||||
qMin(lastPosition.column, lastAnchor.column));
|
||||
CursorPosition pos = startPos;
|
||||
if (g.dotCommand.endsWith(QLatin1Char('A')))
|
||||
if (dotCommand.endsWith(QLatin1Char('A')))
|
||||
pos.column = qMax(lastPosition.column, lastAnchor.column) + 1;
|
||||
while (pos.line < lastPosition.line) {
|
||||
++pos.line;
|
||||
@@ -4462,7 +4519,7 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
|
||||
break;
|
||||
m_cursor = tc;
|
||||
if (tc.positionInBlock() == pos.column)
|
||||
replay(text.repeated(repeat));
|
||||
replay(text, repeat + 1);
|
||||
}
|
||||
|
||||
setCursorPosition(startPos);
|
||||
@@ -4474,14 +4531,14 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
|
||||
breakEditBlock();
|
||||
|
||||
m_lastInsertion = text;
|
||||
g.dotCommand = dotCommand;
|
||||
} else {
|
||||
moveLeft(qMin(1, leftDist()));
|
||||
}
|
||||
|
||||
// If command is 'o' or 'O' don't include the first line feed in dot command.
|
||||
if (g.dotCommand.endsWith(QLatin1Char('o'), Qt::CaseInsensitive))
|
||||
m_lastInsertion.remove(0, 1);
|
||||
g.dotCommand += m_lastInsertion + _("<ESC>");
|
||||
if (newLineBefore || newLineAfter)
|
||||
m_lastInsertion.remove(0, m_lastInsertion.indexOf(QLatin1Char('\n')) + 1);
|
||||
g.dotCommand.append(m_lastInsertion + _("<ESC>"));
|
||||
|
||||
enterCommandMode();
|
||||
setTargetColumn();
|
||||
@@ -4493,7 +4550,6 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
|
||||
enterCommandMode(InsertMode);
|
||||
} else if (input.isControl('v')) {
|
||||
m_ctrlVActive = true;
|
||||
insert = _("<C-V>");
|
||||
} else if (input.isControl('w')) {
|
||||
const int blockNumber = m_cursor.blockNumber();
|
||||
const int endPos = position();
|
||||
@@ -4503,10 +4559,8 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
|
||||
const int beginPos = position();
|
||||
Range range(beginPos, endPos, RangeCharMode);
|
||||
removeText(range);
|
||||
insert = _("<C-W>");
|
||||
} else if (input.isKey(Key_Insert)) {
|
||||
g.mode = ReplaceMode;
|
||||
insert = _("<INSERT>");
|
||||
} else if (input.isKey(Key_Left)) {
|
||||
moveLeft();
|
||||
setTargetColumn();
|
||||
@@ -4514,11 +4568,9 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
|
||||
moveToNextWordStart(1, false, false);
|
||||
setTargetColumn();
|
||||
} else if (input.isKey(Key_Down)) {
|
||||
//removeAutomaticIndentation();
|
||||
g.submode = NoSubMode;
|
||||
moveDown();
|
||||
} else if (input.isKey(Key_Up)) {
|
||||
//removeAutomaticIndentation();
|
||||
g.submode = NoSubMode;
|
||||
moveUp();
|
||||
} else if (input.isKey(Key_Right)) {
|
||||
@@ -4536,17 +4588,15 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
|
||||
setTargetColumn();
|
||||
m_targetColumn = -1;
|
||||
} else if (input.isReturn() || input.isControl('j') || input.isControl('m')) {
|
||||
if (!input.isReturn() || !handleInsertInEditor(input, &insert)) {
|
||||
if (!input.isReturn() || !handleInsertInEditor(input)) {
|
||||
joinPreviousEditBlock();
|
||||
g.submode = NoSubMode;
|
||||
insertNewLine();
|
||||
insert = _("\n");
|
||||
endEditBlock();
|
||||
}
|
||||
} else if (input.isBackspace()) {
|
||||
if (!handleInsertInEditor(input, &insert)) {
|
||||
if (!handleInsertInEditor(input)) {
|
||||
joinPreviousEditBlock();
|
||||
m_justAutoIndented = 0;
|
||||
if (!m_lastInsertion.isEmpty()
|
||||
|| hasConfig(ConfigBackspace, "start")
|
||||
|| hasConfig(ConfigBackspace, "2")) {
|
||||
@@ -4567,34 +4617,29 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
|
||||
m_cursor.deletePreviousChar();
|
||||
}
|
||||
}
|
||||
insert = _("<BS>");
|
||||
endEditBlock();
|
||||
}
|
||||
} else if (input.isKey(Key_Delete)) {
|
||||
if (!handleInsertInEditor(input, &insert)) {
|
||||
if (!handleInsertInEditor(input)) {
|
||||
joinPreviousEditBlock();
|
||||
m_cursor.deleteChar();
|
||||
insert = _("<DELETE>");
|
||||
endEditBlock();
|
||||
}
|
||||
} else if (input.isKey(Key_PageDown) || input.isControl('f')) {
|
||||
removeAutomaticIndentation();
|
||||
movePageDown();
|
||||
} else if (input.isKey(Key_PageUp) || input.isControl('b')) {
|
||||
removeAutomaticIndentation();
|
||||
movePageUp();
|
||||
} else if (input.isKey(Key_Tab)) {
|
||||
m_justAutoIndented = 0;
|
||||
m_insertState.insertingSpaces = true;
|
||||
if (hasConfig(ConfigExpandTab)) {
|
||||
const int ts = config(ConfigTabStop).toInt();
|
||||
const int col = logicalCursorColumn();
|
||||
QString str = QString(ts - col % ts, QLatin1Char(' '));
|
||||
m_lastInsertion.append(str);
|
||||
insertText(str);
|
||||
} else {
|
||||
insertInInsertMode(input.raw());
|
||||
}
|
||||
insert = _("\t");
|
||||
m_insertState.insertingSpaces = false;
|
||||
} else if (input.isControl('d')) {
|
||||
// remove one level of indentation from the current line
|
||||
int shift = config(ConfigShiftWidth).toInt();
|
||||
@@ -4613,60 +4658,33 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
|
||||
break;
|
||||
}
|
||||
removeText(Range(pos, pos+i));
|
||||
insert = _("<C-D>");
|
||||
//} else if (key >= control('a') && key <= control('z')) {
|
||||
// // ignore these
|
||||
} else if (input.isControl('p') || input.isControl('n')) {
|
||||
QTextCursor tc = m_cursor;
|
||||
moveToNextWordStart(1, false, false);
|
||||
QString str = selectText(Range(position(), tc.position()));
|
||||
m_cursor = tc;
|
||||
emit q->simpleCompletionRequested(str, input.isControl('n'));
|
||||
if (input.isControl('p'))
|
||||
insert = _("<C-P>");
|
||||
else
|
||||
insert = _("<C-N>");
|
||||
} else if (input.isShift(Qt::Key_Insert)) {
|
||||
// Insert text from clipboard.
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
const QMimeData *data = clipboard->mimeData();
|
||||
if (data && data->hasText())
|
||||
insertInInsertMode(data->text());
|
||||
insert = _("<S-INSERT>");
|
||||
} else {
|
||||
if (!handleInsertInEditor(input, &insert)) {
|
||||
insert = input.text();
|
||||
if (!insert.isEmpty()) {
|
||||
insertInInsertMode(insert);
|
||||
insert.replace(_("<"), _("<LT>"));
|
||||
} else {
|
||||
// We don't want fancy stuff in insert mode.
|
||||
return EventHandled;
|
||||
m_insertState.insertingSpaces = input.isKey(Key_Space);
|
||||
if (!handleInsertInEditor(input)) {
|
||||
const QString toInsert = input.text();
|
||||
if (toInsert.isEmpty())
|
||||
return;
|
||||
insertInInsertMode(toInsert);
|
||||
}
|
||||
m_insertState.insertingSpaces = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (insert.isNull()) {
|
||||
breakEditBlock();
|
||||
m_oldPosition = position();
|
||||
m_visualBlockInsert = false;
|
||||
resetCount();
|
||||
} else {
|
||||
if (clearLastInsertion) {
|
||||
m_lastInsertion.clear();
|
||||
g.dotCommand = _("i");
|
||||
}
|
||||
recordInsertion(insert);
|
||||
}
|
||||
updateMiniBuffer();
|
||||
|
||||
return EventHandled;
|
||||
}
|
||||
|
||||
void FakeVimHandler::Private::insertInInsertMode(const QString &text)
|
||||
{
|
||||
joinPreviousEditBlock();
|
||||
m_justAutoIndented = 0;
|
||||
insertText(text);
|
||||
if (hasConfig(ConfigSmartIndent) && isElectricCharacter(text.at(0))) {
|
||||
const QString leftText = block().text()
|
||||
@@ -5947,7 +5965,11 @@ void FakeVimHandler::Private::indentText(const Range &range, QChar typedChar)
|
||||
int endBlock = document()->findBlock(range.endPos).blockNumber();
|
||||
if (beginBlock > endBlock)
|
||||
qSwap(beginBlock, endBlock);
|
||||
|
||||
// Don't remember current indentation in last text insertion.
|
||||
const QString lastInsertion = m_lastInsertion;
|
||||
emit q->indentRegion(beginBlock, endBlock, typedChar);
|
||||
m_lastInsertion = lastInsertion;
|
||||
}
|
||||
|
||||
bool FakeVimHandler::Private::isElectricCharacter(QChar c) const
|
||||
@@ -6841,7 +6863,7 @@ void FakeVimHandler::Private::joinLines(int count, bool preserveSpace)
|
||||
|
||||
void FakeVimHandler::Private::insertNewLine()
|
||||
{
|
||||
if ( hasConfig(ConfigPassKeys) ) {
|
||||
if ( m_editBlockLevel <= 1 && hasConfig(ConfigPassKeys) ) {
|
||||
QKeyEvent event(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, QLatin1String("\n"));
|
||||
if (passEventToEditor(event))
|
||||
return;
|
||||
@@ -6851,27 +6873,19 @@ void FakeVimHandler::Private::insertNewLine()
|
||||
insertAutomaticIndentation(true);
|
||||
}
|
||||
|
||||
bool FakeVimHandler::Private::handleInsertInEditor(const Input &input, QString *insert)
|
||||
bool FakeVimHandler::Private::handleInsertInEditor(const Input &input)
|
||||
{
|
||||
if (m_editBlockLevel > 0 || !hasConfig(ConfigPassKeys))
|
||||
return false;
|
||||
|
||||
joinPreviousEditBlock();
|
||||
|
||||
const int pos1 = position();
|
||||
const int len1 = lastPositionInDocument();
|
||||
|
||||
QKeyEvent event(QEvent::KeyPress, input.key(),
|
||||
static_cast<Qt::KeyboardModifiers>(input.modifiers()), input.text());
|
||||
setAnchor();
|
||||
if (!passEventToEditor(event))
|
||||
return false;
|
||||
|
||||
const int pos2 = position();
|
||||
const int len2 = lastPositionInDocument();
|
||||
|
||||
*insert = guessInsertCommand(pos1, pos2, len1, len2);
|
||||
|
||||
endEditBlock();
|
||||
|
||||
return true;
|
||||
@@ -6898,75 +6912,19 @@ bool FakeVimHandler::Private::passEventToEditor(QEvent &event)
|
||||
return accepted;
|
||||
}
|
||||
|
||||
QString FakeVimHandler::Private::guessInsertCommand(int pos1, int pos2, int len1, int len2)
|
||||
{
|
||||
QString insert;
|
||||
|
||||
// Guess the inserted/deleted text.
|
||||
if (len1 > len2) {
|
||||
// Text deleted.
|
||||
if (pos1 == pos2) {
|
||||
// Text after cursor deleted.
|
||||
insert = QString(_("<C-O>%1x")).arg(len1 - len2);
|
||||
} else if (pos1 > pos2) {
|
||||
// Text in front of cursor deleted.
|
||||
const int backspaces = pos1 - pos2;
|
||||
insert = QString(_("<BS>")).repeated(backspaces);
|
||||
// Some text after cursor may have beed deleted too.
|
||||
const int deletes = len1 - len2 - backspaces;
|
||||
if (deletes > 0)
|
||||
insert.append(QString(_("<C-O>%1x")).arg(deletes));
|
||||
}
|
||||
} else if (len1 < len2) {
|
||||
// Text inserted.
|
||||
if (pos1 < pos2) {
|
||||
QTextCursor tc = m_cursor;
|
||||
tc.setPosition(pos1);
|
||||
tc.setPosition(pos2, KeepAnchor);
|
||||
insert = QString(tc.selectedText()).replace(_("<"), _("<LT>"));
|
||||
|
||||
const int textLen = pos2 - pos1;
|
||||
const int rest = len2 - len1 - textLen;
|
||||
if (rest > 0) {
|
||||
// Text inserted after new cursor position.
|
||||
// On dot command, cursor must insert the same text and move in front of it.
|
||||
tc.setPosition(pos2);
|
||||
tc.setPosition(pos2 + rest, KeepAnchor);
|
||||
insert.append(QString(tc.selectedText()).replace(_("<"), _("<LT>")));
|
||||
|
||||
const int up = document()->findBlock(pos2).blockNumber()
|
||||
- document()->findBlock(pos1).blockNumber();
|
||||
if (up > 0) {
|
||||
insert.append(QString(_("<UP>")).repeated(up));
|
||||
insert.append(_("<END>"));
|
||||
const int right = rightDist();
|
||||
if (right > 0)
|
||||
insert.append(QString(_("<LEFT>")).repeated(right));
|
||||
} else {
|
||||
insert.append(QString(_("<LEFT>")).repeated(rest));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Document length is unchanged so assume that no text inserted or deleted.
|
||||
// Check if cursor moved.
|
||||
const int right = pos2 - pos1;
|
||||
if (right > 0)
|
||||
insert = QString(_("<RIGHT>")).repeated(right);
|
||||
else if (right < 0)
|
||||
insert = QString(_("<LEFT>")).repeated(-right);
|
||||
else
|
||||
insert = _(""); // Empty non-null string.
|
||||
}
|
||||
|
||||
return insert;
|
||||
}
|
||||
|
||||
QString FakeVimHandler::Private::lineContents(int line) const
|
||||
{
|
||||
return document()->findBlockByLineNumber(line - 1).text();
|
||||
}
|
||||
|
||||
QString FakeVimHandler::Private::textAt(int from, int to) const
|
||||
{
|
||||
QTextCursor tc(document());
|
||||
tc.setPosition(from);
|
||||
tc.setPosition(to, KeepAnchor);
|
||||
return tc.selectedText().replace(ParagraphSeparator, QLatin1Char('\n'));
|
||||
}
|
||||
|
||||
void FakeVimHandler::Private::setLineContents(int line, const QString &contents)
|
||||
{
|
||||
QTextBlock block = document()->findBlockByLineNumber(line - 1);
|
||||
@@ -7155,7 +7113,7 @@ QWidget *FakeVimHandler::Private::editor() const
|
||||
void FakeVimHandler::Private::joinPreviousEditBlock()
|
||||
{
|
||||
UNDO_DEBUG("JOIN");
|
||||
if (m_breakEditBlock && m_editBlockLevel == 0) {
|
||||
if (m_breakEditBlock) {
|
||||
beginEditBlock();
|
||||
QTextCursor tc(m_cursor);
|
||||
tc.setPosition(tc.position());
|
||||
@@ -7163,6 +7121,7 @@ void FakeVimHandler::Private::joinPreviousEditBlock()
|
||||
tc.insertText(_("X"));
|
||||
tc.deletePreviousChar();
|
||||
tc.endEditBlock();
|
||||
m_breakEditBlock = false;
|
||||
} else {
|
||||
if (m_editBlockLevel == 0 && !m_undo.empty())
|
||||
m_undoState = m_undo.pop();
|
||||
@@ -7175,8 +7134,9 @@ void FakeVimHandler::Private::beginEditBlock(bool largeEditBlock)
|
||||
UNDO_DEBUG("BEGIN EDIT BLOCK");
|
||||
if (!largeEditBlock && !m_undoState.isValid())
|
||||
pushUndoState(false);
|
||||
if (m_editBlockLevel == 0)
|
||||
m_breakEditBlock = true;
|
||||
++m_editBlockLevel;
|
||||
m_breakEditBlock = false;
|
||||
}
|
||||
|
||||
void FakeVimHandler::Private::endEditBlock()
|
||||
@@ -7189,10 +7149,54 @@ void FakeVimHandler::Private::endEditBlock()
|
||||
m_undo.push(m_undoState);
|
||||
m_undoState = State();
|
||||
}
|
||||
if (m_editBlockLevel == 0)
|
||||
m_breakEditBlock = false;
|
||||
}
|
||||
|
||||
void FakeVimHandler::Private::onContentsChanged()
|
||||
void FakeVimHandler::Private::onContentsChanged(int position, int charsRemoved, int charsAdded)
|
||||
{
|
||||
// Record inserted and deleted text in insert mode.
|
||||
if (isInsertMode() && (charsAdded > 0 || charsRemoved > 0)) {
|
||||
if (!isInsertStateValid()) {
|
||||
m_insertState.pos1 = m_oldPosition;
|
||||
g.dotCommand = _("i");
|
||||
resetCount();
|
||||
}
|
||||
|
||||
// Ignore changes outside inserted text (e.g. renaming other occurrences of a variable).
|
||||
if (position + charsRemoved >= m_insertState.pos1 && position <= m_insertState.pos2) {
|
||||
if (charsRemoved > 0) {
|
||||
if (position < m_insertState.pos1) {
|
||||
// backspaces
|
||||
const int bs = m_insertState.pos1 - position;
|
||||
const QString inserted = textAt(position, position + charsAdded);
|
||||
const QString removed = m_insertState.textBeforeCursor.right(bs);
|
||||
// Ignore backspaces if same text was just inserted.
|
||||
if ( !inserted.startsWith(removed) ) {
|
||||
m_insertState.backspaces += bs;
|
||||
m_insertState.pos1 = position;
|
||||
m_insertState.pos2 = qMax(position, m_insertState.pos2 - bs);
|
||||
}
|
||||
} else if (position + charsRemoved > m_insertState.pos2) {
|
||||
// deletes
|
||||
m_insertState.deletes += position + charsRemoved - m_insertState.pos2;
|
||||
}
|
||||
} else if (charsAdded > 0 && m_insertState.insertingSpaces) {
|
||||
for (int i = position; i < position + charsAdded; ++i) {
|
||||
const QChar c = document()->characterAt(i);
|
||||
if (c.unicode() == ' ' || c.unicode() == '\t')
|
||||
m_insertState.spaces.insert(i);
|
||||
}
|
||||
}
|
||||
|
||||
m_insertState.pos2 = qMax(m_insertState.pos2 + charsAdded - charsRemoved,
|
||||
position + charsAdded);
|
||||
m_oldPosition = position + charsAdded;
|
||||
m_insertState.textBeforeCursor = textAt(document()->findBlock(m_oldPosition).position(),
|
||||
m_oldPosition);
|
||||
}
|
||||
}
|
||||
|
||||
if (!document()->isUndoAvailable())
|
||||
m_undo.clear();
|
||||
|
||||
@@ -7321,28 +7325,31 @@ void FakeVimHandler::Private::updateCursorShape()
|
||||
|
||||
void FakeVimHandler::Private::enterReplaceMode()
|
||||
{
|
||||
g.mode = ReplaceMode;
|
||||
g.submode = NoSubMode;
|
||||
g.subsubmode = NoSubSubMode;
|
||||
m_lastInsertion.clear();
|
||||
m_oldPosition = position();
|
||||
g.returnToMode = ReplaceMode;
|
||||
enterInsertOrReplaceMode(ReplaceMode);
|
||||
}
|
||||
|
||||
void FakeVimHandler::Private::enterInsertMode()
|
||||
{
|
||||
g.mode = InsertMode;
|
||||
g.submode = NoSubMode;
|
||||
g.subsubmode = NoSubSubMode;
|
||||
m_lastInsertion.clear();
|
||||
m_oldPosition = position();
|
||||
m_oldDocumentLength = document()->characterCount();
|
||||
if (g.returnToMode != InsertMode) {
|
||||
g.returnToMode = InsertMode;
|
||||
enterInsertOrReplaceMode(InsertMode);
|
||||
}
|
||||
|
||||
void FakeVimHandler::Private::enterInsertOrReplaceMode(Mode mode)
|
||||
{
|
||||
QTC_ASSERT(mode == InsertMode || mode == ReplaceMode, return);
|
||||
if (g.mode == mode)
|
||||
return;
|
||||
|
||||
if (mode == InsertMode && g.returnToMode != InsertMode) {
|
||||
// If entering insert mode from command mode, m_targetColumn shouldn't be -1 (end of line).
|
||||
if (m_targetColumn == -1)
|
||||
setTargetColumn();
|
||||
}
|
||||
|
||||
g.mode = mode;
|
||||
g.submode = NoSubMode;
|
||||
g.subsubmode = NoSubSubMode;
|
||||
g.returnToMode = mode;
|
||||
clearLastInsertion();
|
||||
}
|
||||
|
||||
void FakeVimHandler::Private::initVisualInsertMode(QChar command)
|
||||
@@ -7473,9 +7480,7 @@ void FakeVimHandler::Private::insertAutomaticIndentation(bool goingDown, bool fo
|
||||
if (hasConfig(ConfigSmartIndent)) {
|
||||
QTextBlock bl = block();
|
||||
Range range(bl.position(), bl.position());
|
||||
const int oldSize = bl.text().size();
|
||||
indentText(range, QLatin1Char('\n'));
|
||||
m_justAutoIndented = bl.text().size() - oldSize;
|
||||
} else {
|
||||
QTextBlock bl = goingDown ? block().previous() : block().next();
|
||||
QString text = bl.text();
|
||||
@@ -7486,37 +7491,28 @@ void FakeVimHandler::Private::insertAutomaticIndentation(bool goingDown, bool fo
|
||||
text.truncate(pos);
|
||||
// FIXME: handle 'smartindent' and 'cindent'
|
||||
insertText(text);
|
||||
m_justAutoIndented = text.size();
|
||||
}
|
||||
}
|
||||
|
||||
bool FakeVimHandler::Private::removeAutomaticIndentation()
|
||||
{
|
||||
if (!hasConfig(ConfigAutoIndent) || m_justAutoIndented == 0)
|
||||
return false;
|
||||
/*
|
||||
m_tc.movePosition(StartOfLine, KeepAnchor);
|
||||
m_tc.removeSelectedText();
|
||||
m_lastInsertion.chop(m_justAutoIndented);
|
||||
*/
|
||||
m_justAutoIndented = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FakeVimHandler::Private::handleStartOfLine()
|
||||
{
|
||||
if (hasConfig(ConfigStartOfLine))
|
||||
moveToFirstNonBlankOnLine();
|
||||
}
|
||||
|
||||
void FakeVimHandler::Private::replay(const QString &command)
|
||||
void FakeVimHandler::Private::replay(const QString &command, int repeat)
|
||||
{
|
||||
if (repeat <= 0)
|
||||
return;
|
||||
|
||||
//qDebug() << "REPLAY: " << quoteUnprintable(command);
|
||||
clearCommandMode();
|
||||
Inputs inputs(command);
|
||||
for (int i = 0; i < repeat; ++i) {
|
||||
foreach (const Input &in, inputs) {
|
||||
if (handleDefaultKey(in) != EventHandled)
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user