FakeVim: Share some data among editors with same document

Task-number: QTCREATORBUG-12140
Change-Id: Ieabb331745395c7bb012c9c26ef18c14bcba7cc4
Reviewed-by: hjk <hjk121@nokiamail.com>
This commit is contained in:
hluk
2014-04-30 17:32:25 +02:00
committed by hjk
parent e9e6958217
commit c783fba706

View File

@@ -87,6 +87,7 @@
#include <QTextDocumentFragment> #include <QTextDocumentFragment>
#include <QTextEdit> #include <QTextEdit>
#include <QMimeData> #include <QMimeData>
#include <QSharedPointer>
#include <algorithm> #include <algorithm>
#include <climits> #include <climits>
@@ -101,7 +102,7 @@
//#define DEBUG_UNDO 1 //#define DEBUG_UNDO 1
#if DEBUG_UNDO #if DEBUG_UNDO
# define UNDO_DEBUG(s) qDebug() << << revision() << s # define UNDO_DEBUG(s) qDebug() << "REV" << revision() << s
#else #else
# define UNDO_DEBUG(s) # define UNDO_DEBUG(s)
#endif #endif
@@ -279,6 +280,11 @@ struct CursorPosition
int column; // Position on line. int column; // Position on line.
}; };
QDebug operator<<(QDebug ts, const CursorPosition &pos)
{
return ts << "(line: " << pos.line << ", column: " << pos.column << ")";
}
class Mark class Mark
{ {
public: public:
@@ -1831,12 +1837,11 @@ public:
QChar characterAtCursor() const QChar characterAtCursor() const
{ return document()->characterAt(position()); } { return document()->characterAt(position()); }
int m_editBlockLevel; // current level of edit blocks
void joinPreviousEditBlock(); void joinPreviousEditBlock();
void beginEditBlock(bool largeEditBlock = false); void beginEditBlock(bool largeEditBlock = false);
void beginLargeEditBlock() { beginEditBlock(true); } void beginLargeEditBlock() { beginEditBlock(true); }
void endEditBlock(); void endEditBlock();
void breakEditBlock() { m_breakEditBlock = true; } void breakEditBlock() { m_buffer->breakEditBlock = true; }
Q_SLOT void onContentsChanged(int position, int charsRemoved, int charsAdded); Q_SLOT void onContentsChanged(int position, int charsRemoved, int charsAdded);
Q_SLOT void onUndoCommandAdded(); Q_SLOT void onUndoCommandAdded();
@@ -1903,19 +1908,6 @@ public:
int m_register; int m_register;
BlockInsertMode m_visualBlockInsert; BlockInsertMode 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_fakeEnd;
bool m_anchorPastEnd; bool m_anchorPastEnd;
bool m_positionPastEnd; // '$' & 'l' in visual mode can move past eol bool m_positionPastEnd; // '$' & 'l' in visual mode can move past eol
@@ -1923,9 +1915,6 @@ public:
QString m_currentFileName; QString m_currentFileName;
int m_findStartPosition; int m_findStartPosition;
QString m_lastInsertion;
bool m_breakEditBlock;
int anchor() const { return m_cursor.anchor(); } int anchor() const { return m_cursor.anchor(); }
int position() const { return m_cursor.position(); } int position() const { return m_cursor.position(); }
@@ -1981,11 +1970,6 @@ public:
void undo(); void undo();
void redo(); void redo();
void pushUndoState(bool overwrite = true); void pushUndoState(bool overwrite = true);
// revision -> state
QStack<State> m_undo;
QStack<State> m_redo;
State m_undoState;
int m_lastRevision;
// extra data for '.' // extra data for '.'
void replay(const QString &text, int repeat = 1); void replay(const QString &text, int repeat = 1);
@@ -1996,8 +1980,6 @@ public:
// visual modes // visual modes
void toggleVisualMode(VisualMode visualMode); void toggleVisualMode(VisualMode visualMode);
void leaveVisualMode(); void leaveVisualMode();
VisualMode m_lastVisualMode;
bool m_lastVisualModeInverted;
// marks // marks
Mark mark(QChar code) const; Mark mark(QChar code) const;
@@ -2006,7 +1988,6 @@ public:
bool jumpToMark(QChar mark, bool backTickMode); bool jumpToMark(QChar mark, bool backTickMode);
// update marks on undo/redo // update marks on undo/redo
void updateMarks(const Marks &newMarks); void updateMarks(const Marks &newMarks);
Marks m_marks; // local marks
CursorPosition markLessPosition() const { return mark(QLatin1Char('<')).position(document()); } CursorPosition markLessPosition() const { return mark(QLatin1Char('<')).position(document()); }
CursorPosition markGreaterPosition() const { return mark(QLatin1Char('>')).position(document()); } CursorPosition markGreaterPosition() const { return mark(QLatin1Char('>')).position(document()); }
@@ -2035,9 +2016,6 @@ public:
void recordJump(int position = -1); void recordJump(int position = -1);
void jump(int distance); void jump(int distance);
QStack<CursorPosition> m_jumpListUndo;
QStack<CursorPosition> m_jumpListRedo;
CursorPosition m_lastChangePosition;
QList<QTextEdit::ExtraSelection> m_extraSelections; QList<QTextEdit::ExtraSelection> m_extraSelections;
QTextCursor m_searchCursor; QTextCursor m_searchCursor;
@@ -2079,6 +2057,54 @@ public:
void miniBufferTextEdited(const QString &text, int cursorPos, int anchorPos); void miniBufferTextEdited(const QString &text, int cursorPos, int anchorPos);
// Data shared among editors with same document.
struct BufferData
{
BufferData()
: lastRevision(0)
, editBlockLevel(0)
, breakEditBlock(false)
, lastVisualMode(NoVisualMode)
, lastVisualModeInverted(false)
{}
QStack<State> undo;
QStack<State> redo;
State undoState;
int lastRevision;
int editBlockLevel; // current level of edit blocks
bool breakEditBlock; // if true, joinPreviousEditBlock() starts new edit block
QStack<CursorPosition> jumpListUndo;
QStack<CursorPosition> jumpListRedo;
CursorPosition lastChangePosition;
VisualMode lastVisualMode;
bool lastVisualModeInverted;
Marks marks;
// 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;
} insertState;
QString lastInsertion;
};
typedef QSharedPointer<BufferData> BufferDataPtr;
void pullOrCreateBufferData();
BufferDataPtr m_buffer;
// Data shared among all editors. // Data shared among all editors.
static struct GlobalData static struct GlobalData
{ {
@@ -2191,7 +2217,7 @@ FakeVimHandler::Private::Private(FakeVimHandler *parent, QWidget *widget)
connect(EDITOR(document()), SIGNAL(contentsChange(int,int,int)), connect(EDITOR(document()), SIGNAL(contentsChange(int,int,int)),
SLOT(onContentsChanged(int,int,int))); SLOT(onContentsChanged(int,int,int)));
connect(EDITOR(document()), SIGNAL(undoCommandAdded()), SLOT(onUndoCommandAdded())); connect(EDITOR(document()), SIGNAL(undoCommandAdded()), SLOT(onUndoCommandAdded()));
m_lastRevision = revision(); m_buffer->lastRevision = revision();
} }
} }
@@ -2204,8 +2230,6 @@ void FakeVimHandler::Private::init()
m_positionPastEnd = false; m_positionPastEnd = false;
m_anchorPastEnd = false; m_anchorPastEnd = false;
m_register = '"'; m_register = '"';
m_lastVisualMode = NoVisualMode;
m_lastVisualModeInverted = false;
m_targetColumn = 0; m_targetColumn = 0;
m_visualTargetColumn = 0; m_visualTargetColumn = 0;
m_targetColumnWrapped = 0; m_targetColumnWrapped = 0;
@@ -2213,16 +2237,14 @@ void FakeVimHandler::Private::init()
m_oldInternalPosition = -1; m_oldInternalPosition = -1;
m_oldExternalAnchor = -1; m_oldExternalAnchor = -1;
m_oldExternalPosition = -1; m_oldExternalPosition = -1;
m_insertState = InsertState();
m_breakEditBlock = false;
m_searchStartPosition = 0; m_searchStartPosition = 0;
m_searchFromScreenLine = 0; m_searchFromScreenLine = 0;
m_editBlockLevel = 0;
m_firstVisibleLine = 0; m_firstVisibleLine = 0;
m_ctrlVAccumulator = 0; m_ctrlVAccumulator = 0;
m_ctrlVLength = 0; m_ctrlVLength = 0;
m_ctrlVBase = 0; m_ctrlVBase = 0;
pullOrCreateBufferData();
setupCharClass(); setupCharClass();
} }
@@ -2263,6 +2285,8 @@ void FakeVimHandler::Private::enterFakeVim()
{ {
QTC_ASSERT(!m_inFakeVim, qDebug() << "enterFakeVim() shouldn't be called recursively!"; return); QTC_ASSERT(!m_inFakeVim, qDebug() << "enterFakeVim() shouldn't be called recursively!"; return);
pullOrCreateBufferData();
pullCursor(); pullCursor();
if (m_cursor.isNull()) if (m_cursor.isNull())
m_cursor = QTextCursor(document()); m_cursor = QTextCursor(document());
@@ -2525,57 +2549,61 @@ void FakeVimHandler::Private::commitInsertState()
if (!isInsertStateValid()) if (!isInsertStateValid())
return; return;
QString &lastInsertion = m_buffer->lastInsertion;
BufferData::InsertState &insertState = m_buffer->insertState;
// Get raw inserted text. // Get raw inserted text.
m_lastInsertion = textAt(m_insertState.pos1, m_insertState.pos2); lastInsertion = textAt(insertState.pos1, insertState.pos2);
// Escape special characters and spaces inserted by user (not by auto-indentation). // Escape special characters and spaces inserted by user (not by auto-indentation).
for (int i = m_lastInsertion.size() - 1; i >= 0; --i) { for (int i = lastInsertion.size() - 1; i >= 0; --i) {
const int pos = m_insertState.pos1 + i; const int pos = insertState.pos1 + i;
const ushort c = document()->characterAt(pos).unicode(); const ushort c = document()->characterAt(pos).unicode();
if (c == '<') if (c == '<')
m_lastInsertion.replace(i, 1, _("<LT>")); lastInsertion.replace(i, 1, _("<LT>"));
else if ((c == ' ' || c == '\t') && m_insertState.spaces.contains(pos)) else if ((c == ' ' || c == '\t') && insertState.spaces.contains(pos))
m_lastInsertion.replace(i, 1, _(c == ' ' ? "<SPACE>" : "<TAB>")); lastInsertion.replace(i, 1, _(c == ' ' ? "<SPACE>" : "<TAB>"));
} }
// Remove unnecessary backspaces. // Remove unnecessary backspaces.
while (m_insertState.backspaces > 0 && !m_lastInsertion.isEmpty() && m_lastInsertion[0].isSpace()) while (insertState.backspaces > 0 && !lastInsertion.isEmpty() && lastInsertion[0].isSpace())
--m_insertState.backspaces; --insertState.backspaces;
// backspaces in front of inserted text // backspaces in front of inserted text
m_lastInsertion.prepend(QString(_("<BS>")).repeated(m_insertState.backspaces)); lastInsertion.prepend(QString(_("<BS>")).repeated(insertState.backspaces));
// deletes after inserted text // deletes after inserted text
m_lastInsertion.prepend(QString(_("<DELETE>")).repeated(m_insertState.deletes)); lastInsertion.prepend(QString(_("<DELETE>")).repeated(insertState.deletes));
// Remove indentation. // Remove indentation.
m_lastInsertion.replace(QRegExp(_("(^|\n)[\\t ]+")), _("\\1")); lastInsertion.replace(QRegExp(_("(^|\n)[\\t ]+")), _("\\1"));
} }
void FakeVimHandler::Private::invalidateInsertState() void FakeVimHandler::Private::invalidateInsertState()
{ {
m_oldInternalPosition = position(); m_oldInternalPosition = position();
m_insertState.pos1 = -1; BufferData::InsertState &insertState = m_buffer->insertState;
m_insertState.pos2 = m_oldInternalPosition; insertState.pos1 = -1;
m_insertState.backspaces = 0; insertState.pos2 = m_oldInternalPosition;
m_insertState.deletes = 0; insertState.backspaces = 0;
m_insertState.spaces.clear(); insertState.deletes = 0;
m_insertState.insertingSpaces = false; insertState.spaces.clear();
m_insertState.textBeforeCursor = textAt(document()->findBlock(m_oldInternalPosition).position(), insertState.insertingSpaces = false;
insertState.textBeforeCursor = textAt(document()->findBlock(m_oldInternalPosition).position(),
m_oldInternalPosition); m_oldInternalPosition);
m_insertState.newLineBefore = false; insertState.newLineBefore = false;
m_insertState.newLineAfter = false; insertState.newLineAfter = false;
} }
bool FakeVimHandler::Private::isInsertStateValid() const bool FakeVimHandler::Private::isInsertStateValid() const
{ {
return m_insertState.pos1 != -1; return m_buffer->insertState.pos1 != -1;
} }
void FakeVimHandler::Private::clearLastInsertion() void FakeVimHandler::Private::clearLastInsertion()
{ {
invalidateInsertState(); invalidateInsertState();
m_lastInsertion.clear(); m_buffer->lastInsertion.clear();
m_insertState.pos1 = m_insertState.pos2; m_buffer->insertState.pos1 = m_buffer->insertState.pos2;
} }
void FakeVimHandler::Private::ensureCursorVisible() void FakeVimHandler::Private::ensureCursorVisible()
@@ -2634,7 +2662,7 @@ void FakeVimHandler::Private::importSelection()
g.visualMode = VisualLineMode; g.visualMode = VisualLineMode;
else else
g.visualMode = VisualCharMode; g.visualMode = VisualCharMode;
m_lastVisualMode = g.visualMode; m_buffer->lastVisualMode = g.visualMode;
} else { } else {
g.visualMode = NoVisualMode; g.visualMode = NoVisualMode;
} }
@@ -2767,7 +2795,7 @@ void FakeVimHandler::Private::prependMapping(const Inputs &inputs)
g.commandBuffer.setHistoryAutoSave(false); g.commandBuffer.setHistoryAutoSave(false);
// start new edit block (undo/redo) only if necessary // start new edit block (undo/redo) only if necessary
bool editBlock = m_editBlockLevel == 0 && !(isInsertMode() && isInsertStateValid()); bool editBlock = m_buffer->editBlockLevel == 0 && !(isInsertMode() && isInsertStateValid());
if (editBlock) if (editBlock)
beginLargeEditBlock(); beginLargeEditBlock();
g.mapStates << MappingState(inputs.noremap(), inputs.silent(), editBlock); g.mapStates << MappingState(inputs.noremap(), inputs.silent(), editBlock);
@@ -2830,7 +2858,7 @@ void FakeVimHandler::Private::clearPendingInput()
g.mapDepth = 0; g.mapDepth = 0;
// Clear all started edit blocks. // Clear all started edit blocks.
while (m_editBlockLevel > 0) while (m_buffer->editBlockLevel > 0)
endEditBlock(); endEditBlock();
} }
@@ -2959,12 +2987,13 @@ bool FakeVimHandler::Private::isFirstNonBlankOnLine(int pos)
void FakeVimHandler::Private::pushUndoState(bool overwrite) void FakeVimHandler::Private::pushUndoState(bool overwrite)
{ {
if (m_editBlockLevel != 0 && m_undoState.isValid()) if (m_buffer->editBlockLevel != 0 && m_buffer->undoState.isValid())
return; // No need to save undo state for inner edit blocks. return; // No need to save undo state for inner edit blocks.
if (m_undoState.isValid() && !overwrite) if (m_buffer->undoState.isValid() && !overwrite)
return; return;
UNDO_DEBUG("PUSH UNDO");
int pos = position(); int pos = position();
if (!isInsertMode()) { if (!isInsertMode()) {
if (isVisualMode() || g.submode == DeleteSubMode if (isVisualMode() || g.submode == DeleteSubMode
@@ -2984,14 +3013,14 @@ void FakeVimHandler::Private::pushUndoState(bool overwrite)
} }
} }
m_redo.clear(); m_buffer->redo.clear();
m_lastChangePosition = CursorPosition(document(), pos); m_buffer->lastChangePosition = CursorPosition(document(), pos);
if (isVisualMode()) { if (isVisualMode()) {
setMark(QLatin1Char('<'), markLessPosition()); setMark(QLatin1Char('<'), markLessPosition());
setMark(QLatin1Char('>'), markGreaterPosition()); setMark(QLatin1Char('>'), markGreaterPosition());
} }
m_undoState = State(revision(), m_lastChangePosition, m_marks, m_lastVisualMode, m_buffer->undoState = State(revision(), m_buffer->lastChangePosition, m_buffer->marks,
m_lastVisualModeInverted); m_buffer->lastVisualMode, m_buffer->lastVisualModeInverted);
} }
void FakeVimHandler::Private::moveDown(int n) void FakeVimHandler::Private::moveDown(int n)
@@ -3374,14 +3403,14 @@ void FakeVimHandler::Private::resetCommandMode()
enterCommandMode(); enterCommandMode();
} else { } else {
clearCommandMode(); clearCommandMode();
const QString lastInsertion = m_lastInsertion; const QString lastInsertion = m_buffer->lastInsertion;
if (g.returnToMode == InsertMode) if (g.returnToMode == InsertMode)
enterInsertMode(); enterInsertMode();
else else
enterReplaceMode(); enterReplaceMode();
moveToTargetColumn(); moveToTargetColumn();
invalidateInsertState(); invalidateInsertState();
m_lastInsertion = lastInsertion; m_buffer->lastInsertion = lastInsertion;
} }
if (isNoVisualMode()) if (isNoVisualMode())
setAnchor(); setAnchor();
@@ -3403,7 +3432,7 @@ void FakeVimHandler::Private::updateSelection()
{ {
QList<QTextEdit::ExtraSelection> selections = m_extraSelections; QList<QTextEdit::ExtraSelection> selections = m_extraSelections;
if (hasConfig(ConfigShowMarks)) { if (hasConfig(ConfigShowMarks)) {
for (MarksIterator it(m_marks); it.hasNext(); ) { for (MarksIterator it(m_buffer->marks); it.hasNext(); ) {
it.next(); it.next();
QTextEdit::ExtraSelection sel; QTextEdit::ExtraSelection sel;
sel.cursor = m_cursor; sel.cursor = m_cursor;
@@ -4282,12 +4311,12 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input)
setAnchor(); setAnchor();
insertNewLine(); insertNewLine();
if (appendLine) { if (appendLine) {
m_insertState.newLineBefore = true; m_buffer->insertState.newLineBefore = true;
} else { } else {
moveUp(); moveUp();
m_oldInternalPosition = position(); m_oldInternalPosition = position();
m_insertState.pos1 = m_oldInternalPosition; m_buffer->insertState.pos1 = m_oldInternalPosition;
m_insertState.newLineAfter = true; m_buffer->insertState.newLineAfter = true;
} }
setTargetColumn(); setTargetColumn();
endEditBlock(); endEditBlock();
@@ -4366,13 +4395,13 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input)
handleStartOfLine(); handleStartOfLine();
scrollToLine(cursorLine() - sline); scrollToLine(cursorLine() - sline);
} else if (g.gflag && input.is('v')) { } else if (g.gflag && input.is('v')) {
if (m_lastVisualMode != NoVisualMode) { if (m_buffer->lastVisualMode != NoVisualMode) {
CursorPosition from = markLessPosition(); CursorPosition from = markLessPosition();
CursorPosition to = markGreaterPosition(); CursorPosition to = markGreaterPosition();
toggleVisualMode(m_lastVisualMode); toggleVisualMode(m_buffer->lastVisualMode);
setCursorPosition(m_lastVisualModeInverted ? to : from); setCursorPosition(m_buffer->lastVisualModeInverted ? to : from);
setAnchor(); setAnchor();
setCursorPosition(m_lastVisualModeInverted ? from : to); setCursorPosition(m_buffer->lastVisualModeInverted ? from : to);
setTargetColumn(); setTargetColumn();
} }
} else if (input.is('v')) { } else if (input.is('v')) {
@@ -4729,7 +4758,7 @@ bool FakeVimHandler::Private::handleMacroExecuteSubMode(const Input &input)
EventResult FakeVimHandler::Private::handleInsertOrReplaceMode(const Input &input) EventResult FakeVimHandler::Private::handleInsertOrReplaceMode(const Input &input)
{ {
if (position() < m_insertState.pos1 || position() > m_insertState.pos2) { if (position() < m_buffer->insertState.pos1 || position() > m_buffer->insertState.pos2) {
commitInsertState(); commitInsertState();
invalidateInsertState(); invalidateInsertState();
} }
@@ -4742,8 +4771,8 @@ EventResult FakeVimHandler::Private::handleInsertOrReplaceMode(const Input &inpu
if (!m_textedit && !m_plaintextedit) if (!m_textedit && !m_plaintextedit)
return EventHandled; return EventHandled;
if (!isInsertMode() || m_breakEditBlock if (!isInsertMode() || m_buffer->breakEditBlock
|| position() < m_insertState.pos1 || position() > m_insertState.pos2) { || position() < m_buffer->insertState.pos1 || position() > m_buffer->insertState.pos2) {
commitInsertState(); commitInsertState();
invalidateInsertState(); invalidateInsertState();
breakEditBlock(); breakEditBlock();
@@ -4764,7 +4793,7 @@ void FakeVimHandler::Private::handleReplaceMode(const Input &input)
commitInsertState(); commitInsertState();
moveLeft(qMin(1, leftDist())); moveLeft(qMin(1, leftDist()));
enterCommandMode(); enterCommandMode();
g.dotCommand.append(m_lastInsertion + _("<ESC>")); g.dotCommand.append(m_buffer->lastInsertion + _("<ESC>"));
} else if (input.isKey(Key_Left)) { } else if (input.isKey(Key_Left)) {
moveLeft(); moveLeft();
setTargetColumn(); setTargetColumn();
@@ -4795,18 +4824,18 @@ void FakeVimHandler::Private::handleReplaceMode(const Input &input)
void FakeVimHandler::Private::finishInsertMode() void FakeVimHandler::Private::finishInsertMode()
{ {
bool newLineAfter = m_insertState.newLineAfter; bool newLineAfter = m_buffer->insertState.newLineAfter;
bool newLineBefore = m_insertState.newLineBefore; bool newLineBefore = m_buffer->insertState.newLineBefore;
// Repeat insertion [count] times. // Repeat insertion [count] times.
// One instance was already physically inserted while typing. // One instance was already physically inserted while typing.
if (!m_breakEditBlock && isInsertStateValid()) { if (!m_buffer->breakEditBlock && isInsertStateValid()) {
commitInsertState(); commitInsertState();
QString text = m_lastInsertion; QString text = m_buffer->lastInsertion;
const QString dotCommand = g.dotCommand; const QString dotCommand = g.dotCommand;
const int repeat = count() - 1; const int repeat = count() - 1;
m_lastInsertion.clear(); m_buffer->lastInsertion.clear();
joinPreviousEditBlock(); joinPreviousEditBlock();
if (newLineAfter) { if (newLineAfter) {
@@ -4829,7 +4858,7 @@ void FakeVimHandler::Private::finishInsertMode()
CursorPosition pos(lastAnchor.line, insertColumn); CursorPosition pos(lastAnchor.line, insertColumn);
if (change) if (change)
pos.column = m_insertState.pos1 - document()->findBlock(m_insertState.pos1).position(); pos.column = m_buffer->insertState.pos1 - document()->findBlock(m_buffer->insertState.pos1).position();
// Cursor position after block insert is on the first selected line, // Cursor position after block insert is on the first selected line,
// last selected column for 's' command, otherwise first selected column. // last selected column for 's' command, otherwise first selected column.
@@ -4862,15 +4891,15 @@ void FakeVimHandler::Private::finishInsertMode()
endEditBlock(); endEditBlock();
breakEditBlock(); breakEditBlock();
m_lastInsertion = text; m_buffer->lastInsertion = text;
g.dotCommand = dotCommand; g.dotCommand = dotCommand;
} else { } else {
moveLeft(qMin(1, leftDist())); moveLeft(qMin(1, leftDist()));
} }
if (newLineBefore || newLineAfter) if (newLineBefore || newLineAfter)
m_lastInsertion.remove(0, m_lastInsertion.indexOf(QLatin1Char('\n')) + 1); m_buffer->lastInsertion.remove(0, m_buffer->lastInsertion.indexOf(QLatin1Char('\n')) + 1);
g.dotCommand.append(m_lastInsertion + _("<ESC>")); g.dotCommand.append(m_buffer->lastInsertion + _("<ESC>"));
enterCommandMode(); enterCommandMode();
setTargetColumn(); setTargetColumn();
@@ -4986,7 +5015,7 @@ void FakeVimHandler::Private::handleInsertMode(const Input &input)
} else if (input.isBackspace()) { } else if (input.isBackspace()) {
if (!handleInsertInEditor(input)) { if (!handleInsertInEditor(input)) {
joinPreviousEditBlock(); joinPreviousEditBlock();
if (!m_lastInsertion.isEmpty() if (!m_buffer->lastInsertion.isEmpty()
|| hasConfig(ConfigBackspace, "start") || hasConfig(ConfigBackspace, "start")
|| hasConfig(ConfigBackspace, "2")) { || hasConfig(ConfigBackspace, "2")) {
const int line = cursorLine() + 1; const int line = cursorLine() + 1;
@@ -5019,7 +5048,7 @@ void FakeVimHandler::Private::handleInsertMode(const Input &input)
} else if (input.isKey(Key_PageUp) || input.isControl('b')) { } else if (input.isKey(Key_PageUp) || input.isControl('b')) {
movePageUp(); movePageUp();
} else if (input.isKey(Key_Tab)) { } else if (input.isKey(Key_Tab)) {
m_insertState.insertingSpaces = true; m_buffer->insertState.insertingSpaces = true;
if (hasConfig(ConfigExpandTab)) { if (hasConfig(ConfigExpandTab)) {
const int ts = config(ConfigTabStop).toInt(); const int ts = config(ConfigTabStop).toInt();
const int col = logicalCursorColumn(); const int col = logicalCursorColumn();
@@ -5028,7 +5057,7 @@ void FakeVimHandler::Private::handleInsertMode(const Input &input)
} else { } else {
insertInInsertMode(input.raw()); insertInInsertMode(input.raw());
} }
m_insertState.insertingSpaces = false; m_buffer->insertState.insertingSpaces = false;
} else if (input.isControl('d')) { } else if (input.isControl('d')) {
// remove one level of indentation from the current line // remove one level of indentation from the current line
int shift = config(ConfigShiftWidth).toInt(); int shift = config(ConfigShiftWidth).toInt();
@@ -5060,14 +5089,14 @@ void FakeVimHandler::Private::handleInsertMode(const Input &input)
if (data && data->hasText()) if (data && data->hasText())
insertInInsertMode(data->text()); insertInInsertMode(data->text());
} else { } else {
m_insertState.insertingSpaces = input.isKey(Key_Space); m_buffer->insertState.insertingSpaces = input.isKey(Key_Space);
if (!handleInsertInEditor(input)) { if (!handleInsertInEditor(input)) {
const QString toInsert = input.text(); const QString toInsert = input.text();
if (toInsert.isEmpty()) if (toInsert.isEmpty())
return; return;
insertInInsertMode(toInsert); insertInInsertMode(toInsert);
} }
m_insertState.insertingSpaces = false; m_buffer->insertState.insertingSpaces = false;
} }
} }
@@ -5500,7 +5529,7 @@ bool FakeVimHandler::Private::handleExSubstituteCommand(const ExCommand &cmd)
} }
if (lastBlock.isValid()) { if (lastBlock.isValid()) {
m_undoState.position = CursorPosition(firstBlock.blockNumber(), 0); m_buffer->undoState.position = CursorPosition(firstBlock.blockNumber(), 0);
leaveVisualMode(); leaveVisualMode();
setPosition(lastBlock.position()); setPosition(lastBlock.position());
@@ -6391,9 +6420,9 @@ void FakeVimHandler::Private::indentText(const Range &range, QChar typedChar)
qSwap(beginBlock, endBlock); qSwap(beginBlock, endBlock);
// Don't remember current indentation in last text insertion. // Don't remember current indentation in last text insertion.
const QString lastInsertion = m_lastInsertion; const QString lastInsertion = m_buffer->lastInsertion;
emit q->indentRegion(beginBlock, endBlock, typedChar); emit q->indentRegion(beginBlock, endBlock, typedChar);
m_lastInsertion = lastInsertion; m_buffer->lastInsertion = lastInsertion;
} }
bool FakeVimHandler::Private::isElectricCharacter(QChar c) const bool FakeVimHandler::Private::isElectricCharacter(QChar c) const
@@ -6535,6 +6564,19 @@ void FakeVimHandler::Private::miniBufferTextEdited(const QString &text, int curs
} }
} }
void FakeVimHandler::Private::pullOrCreateBufferData()
{
const QVariant data = document()->property("FakeVimSharedData");
if (data.isValid()) {
// FakeVimHandler has been already created for this document (e.g. in other split).
m_buffer = data.value<BufferDataPtr>();
} else {
// FakeVimHandler has not been created for this document yet.
m_buffer = BufferDataPtr(new BufferData);
document()->setProperty("FakeVimSharedData", QVariant::fromValue(m_buffer));
}
}
// Helper to parse a-z,A-Z,48-57,_ // Helper to parse a-z,A-Z,48-57,_
static int someInt(const QString &str) static int someInt(const QString &str)
{ {
@@ -7316,7 +7358,7 @@ void FakeVimHandler::Private::joinLines(int count, bool preserveSpace)
void FakeVimHandler::Private::insertNewLine() void FakeVimHandler::Private::insertNewLine()
{ {
if ( m_editBlockLevel <= 1 && hasConfig(ConfigPassKeys) ) { if ( m_buffer->editBlockLevel <= 1 && hasConfig(ConfigPassKeys) ) {
QKeyEvent event(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, QLatin1String("\n")); QKeyEvent event(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, QLatin1String("\n"));
if (passEventToEditor(event)) if (passEventToEditor(event))
return; return;
@@ -7328,7 +7370,7 @@ void FakeVimHandler::Private::insertNewLine()
bool FakeVimHandler::Private::handleInsertInEditor(const Input &input) bool FakeVimHandler::Private::handleInsertInEditor(const Input &input)
{ {
if (m_editBlockLevel > 0 || !hasConfig(ConfigPassKeys)) if (m_buffer->editBlockLevel > 0 || !hasConfig(ConfigPassKeys))
return false; return false;
joinPreviousEditBlock(); joinPreviousEditBlock();
@@ -7530,7 +7572,7 @@ void FakeVimHandler::Private::toggleVisualMode(VisualMode visualMode)
m_positionPastEnd = false; m_positionPastEnd = false;
m_anchorPastEnd = false; m_anchorPastEnd = false;
g.visualMode = visualMode; g.visualMode = visualMode;
m_lastVisualMode = visualMode; m_buffer->lastVisualMode = visualMode;
updateMiniBuffer(); updateMiniBuffer();
} }
} }
@@ -7542,7 +7584,7 @@ void FakeVimHandler::Private::leaveVisualMode()
setMark(QLatin1Char('<'), markLessPosition()); setMark(QLatin1Char('<'), markLessPosition());
setMark(QLatin1Char('>'), markGreaterPosition()); setMark(QLatin1Char('>'), markGreaterPosition());
m_lastVisualModeInverted = anchor() > position(); m_buffer->lastVisualModeInverted = anchor() > position();
if (isVisualLineMode()) { if (isVisualLineMode()) {
g.rangemode = RangeLineMode; g.rangemode = RangeLineMode;
g.movetype = MoveLineWise; g.movetype = MoveLineWise;
@@ -7568,7 +7610,7 @@ QWidget *FakeVimHandler::Private::editor() const
void FakeVimHandler::Private::joinPreviousEditBlock() void FakeVimHandler::Private::joinPreviousEditBlock()
{ {
UNDO_DEBUG("JOIN"); UNDO_DEBUG("JOIN");
if (m_breakEditBlock) { if (m_buffer->breakEditBlock) {
beginEditBlock(); beginEditBlock();
QTextCursor tc(m_cursor); QTextCursor tc(m_cursor);
tc.setPosition(tc.position()); tc.setPosition(tc.position());
@@ -7576,78 +7618,79 @@ void FakeVimHandler::Private::joinPreviousEditBlock()
tc.insertText(_("X")); tc.insertText(_("X"));
tc.deletePreviousChar(); tc.deletePreviousChar();
tc.endEditBlock(); tc.endEditBlock();
m_breakEditBlock = false; m_buffer->breakEditBlock = false;
} else { } else {
if (m_editBlockLevel == 0 && !m_undo.empty()) if (m_buffer->editBlockLevel == 0 && !m_buffer->undo.empty())
m_undoState = m_undo.pop(); m_buffer->undoState = m_buffer->undo.pop();
beginEditBlock(); beginEditBlock();
} }
} }
void FakeVimHandler::Private::beginEditBlock(bool largeEditBlock) void FakeVimHandler::Private::beginEditBlock(bool largeEditBlock)
{ {
UNDO_DEBUG("BEGIN EDIT BLOCK"); UNDO_DEBUG("BEGIN EDIT BLOCK" << m_buffer->editBlockLevel + 1);
if (!largeEditBlock && !m_undoState.isValid()) if (!largeEditBlock && !m_buffer->undoState.isValid())
pushUndoState(false); pushUndoState(false);
if (m_editBlockLevel == 0) if (m_buffer->editBlockLevel == 0)
m_breakEditBlock = true; m_buffer->breakEditBlock = true;
++m_editBlockLevel; ++m_buffer->editBlockLevel;
} }
void FakeVimHandler::Private::endEditBlock() void FakeVimHandler::Private::endEditBlock()
{ {
UNDO_DEBUG("END EDIT BLOCK"); UNDO_DEBUG("END EDIT BLOCK" << m_buffer->editBlockLevel);
QTC_ASSERT(m_editBlockLevel > 0, QTC_ASSERT(m_buffer->editBlockLevel > 0,
qDebug() << "beginEditBlock() not called before endEditBlock()!"; return); qDebug() << "beginEditBlock() not called before endEditBlock()!"; return);
--m_editBlockLevel; --m_buffer->editBlockLevel;
if (m_editBlockLevel == 0 && m_undoState.isValid()) { if (m_buffer->editBlockLevel == 0 && m_buffer->undoState.isValid()) {
m_undo.push(m_undoState); m_buffer->undo.push(m_buffer->undoState);
m_undoState = State(); m_buffer->undoState = State();
} }
if (m_editBlockLevel == 0) if (m_buffer->editBlockLevel == 0)
m_breakEditBlock = false; m_buffer->breakEditBlock = false;
} }
void FakeVimHandler::Private::onContentsChanged(int position, int charsRemoved, int charsAdded) void FakeVimHandler::Private::onContentsChanged(int position, int charsRemoved, int charsAdded)
{ {
// Record inserted and deleted text in insert mode. // Record inserted and deleted text in insert mode.
if (isInsertMode() && (charsAdded > 0 || charsRemoved > 0)) { if (isInsertMode() && (charsAdded > 0 || charsRemoved > 0)) {
BufferData::InsertState &insertState = m_buffer->insertState;
if (!isInsertStateValid()) { if (!isInsertStateValid()) {
m_insertState.pos1 = m_oldInternalPosition; insertState.pos1 = m_oldInternalPosition;
g.dotCommand = _("i"); g.dotCommand = _("i");
resetCount(); resetCount();
} }
// Ignore changes outside inserted text (e.g. renaming other occurrences of a variable). // Ignore changes outside inserted text (e.g. renaming other occurrences of a variable).
if (position + charsRemoved >= m_insertState.pos1 && position <= m_insertState.pos2) { if (position + charsRemoved >= insertState.pos1 && position <= insertState.pos2) {
if (charsRemoved > 0) { if (charsRemoved > 0) {
if (position < m_insertState.pos1) { if (position < insertState.pos1) {
// backspaces // backspaces
const int bs = m_insertState.pos1 - position; const int bs = insertState.pos1 - position;
const QString inserted = textAt(position, m_oldInternalPosition); const QString inserted = textAt(position, m_oldInternalPosition);
const QString removed = m_insertState.textBeforeCursor.right(bs); const QString removed = insertState.textBeforeCursor.right(bs);
// Ignore backspaces if same text was just inserted. // Ignore backspaces if same text was just inserted.
if ( !inserted.endsWith(removed) ) { if ( !inserted.endsWith(removed) ) {
m_insertState.backspaces += bs; insertState.backspaces += bs;
m_insertState.pos1 = position; insertState.pos1 = position;
m_insertState.pos2 = qMax(position, m_insertState.pos2 - bs); insertState.pos2 = qMax(position, insertState.pos2 - bs);
} }
} else if (position + charsRemoved > m_insertState.pos2) { } else if (position + charsRemoved > insertState.pos2) {
// deletes // deletes
m_insertState.deletes += position + charsRemoved - m_insertState.pos2; insertState.deletes += position + charsRemoved - insertState.pos2;
} }
} else if (charsAdded > 0 && m_insertState.insertingSpaces) { } else if (charsAdded > 0 && insertState.insertingSpaces) {
for (int i = position; i < position + charsAdded; ++i) { for (int i = position; i < position + charsAdded; ++i) {
const QChar c = document()->characterAt(i); const QChar c = document()->characterAt(i);
if (c.unicode() == ' ' || c.unicode() == '\t') if (c.unicode() == ' ' || c.unicode() == '\t')
m_insertState.spaces.insert(i); insertState.spaces.insert(i);
} }
} }
m_insertState.pos2 = qMax(m_insertState.pos2 + charsAdded - charsRemoved, insertState.pos2 = qMax(insertState.pos2 + charsAdded - charsRemoved,
position + charsAdded); position + charsAdded);
m_oldInternalPosition = position + charsAdded; m_oldInternalPosition = position + charsAdded;
m_insertState.textBeforeCursor = textAt(document()->findBlock(m_oldInternalPosition).position(), insertState.textBeforeCursor = textAt(document()->findBlock(m_oldInternalPosition).position(),
m_oldInternalPosition); m_oldInternalPosition);
} }
} }
@@ -7659,20 +7702,22 @@ void FakeVimHandler::Private::onContentsChanged(int position, int charsRemoved,
void FakeVimHandler::Private::onUndoCommandAdded() void FakeVimHandler::Private::onUndoCommandAdded()
{ {
// Undo commands removed? // Undo commands removed?
if (m_lastRevision >= revision()) { UNDO_DEBUG("Undo added" << "previous: REV" << m_buffer->lastRevision);
const int removed = m_lastRevision - revision(); if (m_buffer->lastRevision >= revision()) {
for (int i = m_undo.size() - 1; i >= 0; --i) { UNDO_DEBUG("UNDO REMOVED!");
if ((m_undo[i].revision -= removed) < 0) { const int removed = m_buffer->lastRevision - revision();
m_undo.remove(0, i + 1); for (int i = m_buffer->undo.size() - 1; i >= 0; --i) {
if ((m_buffer->undo[i].revision -= removed) < 0) {
m_buffer->undo.remove(0, i + 1);
break; break;
} }
} }
} }
m_redo.clear(); m_buffer->redo.clear();
// External change while FakeVim disabled. // External change while FakeVim disabled.
if (m_editBlockLevel == 0 && !m_undo.isEmpty() && !isInsertMode()) if (m_buffer->editBlockLevel == 0 && !m_buffer->undo.isEmpty() && !isInsertMode())
m_undo.push(State()); m_buffer->undo.push(State());
} }
char FakeVimHandler::Private::currentModeCode() const char FakeVimHandler::Private::currentModeCode() const
@@ -7693,13 +7738,15 @@ char FakeVimHandler::Private::currentModeCode() const
void FakeVimHandler::Private::undoRedo(bool undo) void FakeVimHandler::Private::undoRedo(bool undo)
{ {
UNDO_DEBUG((undo ? "UNDO" : "REDO"));
// FIXME: That's only an approximaxtion. The real solution might // FIXME: That's only an approximaxtion. The real solution might
// be to store marks and old userData with QTextBlock setUserData // be to store marks and old userData with QTextBlock setUserData
// and retrieve them afterward. // and retrieve them afterward.
QStack<State> &stack = undo ? m_undo : m_redo; QStack<State> &stack = undo ? m_buffer->undo : m_buffer->redo;
QStack<State> &stack2 = undo ? m_redo : m_undo; QStack<State> &stack2 = undo ? m_buffer->redo : m_buffer->undo;
State state = m_undoState.isValid() ? m_undoState State state = m_buffer->undoState.isValid() ? m_buffer->undoState
: !stack.empty() ? stack.pop() : State(); : !stack.empty() ? stack.pop() : State();
CursorPosition lastPos(m_cursor); CursorPosition lastPos(m_cursor);
@@ -7707,11 +7754,12 @@ void FakeVimHandler::Private::undoRedo(bool undo)
const QString msg = undo ? FakeVimHandler::tr("Already at oldest change.") const QString msg = undo ? FakeVimHandler::tr("Already at oldest change.")
: FakeVimHandler::tr("Already at newest change."); : FakeVimHandler::tr("Already at newest change.");
showMessage(MessageInfo, msg); showMessage(MessageInfo, msg);
UNDO_DEBUG(msg);
return; return;
} }
clearMessage(); clearMessage();
++m_editBlockLevel; ++m_buffer->editBlockLevel;
// Do undo/redo [count] times to reach previous revision. // Do undo/redo [count] times to reach previous revision.
const int previousRevision = revision(); const int previousRevision = revision();
@@ -7725,18 +7773,18 @@ void FakeVimHandler::Private::undoRedo(bool undo)
} while (document()->isRedoAvailable() && state.revision > revision()); } while (document()->isRedoAvailable() && state.revision > revision());
} }
--m_editBlockLevel; --m_buffer->editBlockLevel;
if (state.isValid()) { if (state.isValid()) {
m_lastChangePosition = state.position; m_buffer->lastChangePosition = state.position;
Marks marks = m_marks; Marks marks = m_buffer->marks;
marks.swap(state.marks); marks.swap(state.marks);
updateMarks(marks); updateMarks(marks);
m_lastVisualMode = state.lastVisualMode; m_buffer->lastVisualMode = state.lastVisualMode;
m_lastVisualModeInverted = state.lastVisualModeInverted; m_buffer->lastVisualModeInverted = state.lastVisualModeInverted;
setMark(QLatin1Char('\''), lastPos); setMark(QLatin1Char('\''), lastPos);
setMark(QLatin1Char('`'), lastPos); setMark(QLatin1Char('`'), lastPos);
setCursorPosition(m_lastChangePosition); setCursorPosition(m_buffer->lastChangePosition);
setAnchor(); setAnchor();
state.revision = previousRevision; state.revision = previousRevision;
} else { } else {
@@ -7748,6 +7796,8 @@ void FakeVimHandler::Private::undoRedo(bool undo)
setTargetColumn(); setTargetColumn();
if (atEndOfLine()) if (atEndOfLine())
moveLeft(); moveLeft();
UNDO_DEBUG((undo ? "UNDONE" : "REDONE"));
} }
void FakeVimHandler::Private::undo() void FakeVimHandler::Private::undo()
@@ -7886,16 +7936,16 @@ void FakeVimHandler::Private::recordJump(int position)
: CursorPosition(m_cursor); : CursorPosition(m_cursor);
setMark(QLatin1Char('\''), pos); setMark(QLatin1Char('\''), pos);
setMark(QLatin1Char('`'), pos); setMark(QLatin1Char('`'), pos);
if (m_jumpListUndo.isEmpty() || m_jumpListUndo.top() != pos) if (m_buffer->jumpListUndo.isEmpty() || m_buffer->jumpListUndo.top() != pos)
m_jumpListUndo.push(pos); m_buffer->jumpListUndo.push(pos);
m_jumpListRedo.clear(); m_buffer->jumpListRedo.clear();
UNDO_DEBUG("jumps: " << m_jumpListUndo); UNDO_DEBUG("jumps: " << m_buffer->jumpListUndo);
} }
void FakeVimHandler::Private::jump(int distance) void FakeVimHandler::Private::jump(int distance)
{ {
QStack<CursorPosition> &from = (distance > 0) ? m_jumpListRedo : m_jumpListUndo; QStack<CursorPosition> &from = (distance > 0) ? m_buffer->jumpListRedo : m_buffer->jumpListUndo;
QStack<CursorPosition> &to = (distance > 0) ? m_jumpListUndo : m_jumpListRedo; QStack<CursorPosition> &to = (distance > 0) ? m_buffer->jumpListUndo : m_buffer->jumpListRedo;
int len = qMin(qAbs(distance), from.size()); int len = qMin(qAbs(distance), from.size());
CursorPosition m(m_cursor); CursorPosition m(m_cursor);
setMark(QLatin1Char('\''), m); setMark(QLatin1Char('\''), m);
@@ -8265,11 +8315,11 @@ Mark FakeVimHandler::Private::mark(QChar code) const
return CursorPosition(document(), qMax(anchor(), position())); return CursorPosition(document(), qMax(anchor(), position()));
} }
if (code == QLatin1Char('.')) if (code == QLatin1Char('.'))
return m_lastChangePosition; return m_buffer->lastChangePosition;
if (code.isUpper()) if (code.isUpper())
return g.marks.value(code); return g.marks.value(code);
return m_marks.value(code); return m_buffer->marks.value(code);
} }
void FakeVimHandler::Private::setMark(QChar code, CursorPosition position) void FakeVimHandler::Private::setMark(QChar code, CursorPosition position)
@@ -8277,7 +8327,7 @@ void FakeVimHandler::Private::setMark(QChar code, CursorPosition position)
if (code.isUpper()) if (code.isUpper())
g.marks[code] = Mark(position, m_currentFileName); g.marks[code] = Mark(position, m_currentFileName);
else else
m_marks[code] = Mark(position); m_buffer->marks[code] = Mark(position);
} }
bool FakeVimHandler::Private::jumpToMark(QChar mark, bool backTickMode) bool FakeVimHandler::Private::jumpToMark(QChar mark, bool backTickMode)
@@ -8292,8 +8342,8 @@ bool FakeVimHandler::Private::jumpToMark(QChar mark, bool backTickMode)
return false; return false;
} }
if ((mark == QLatin1Char('\'') || mark == QLatin1Char('`')) && !m_jumpListUndo.isEmpty()) if ((mark == QLatin1Char('\'') || mark == QLatin1Char('`')) && !m_buffer->jumpListUndo.isEmpty())
m_jumpListUndo.pop(); m_buffer->jumpListUndo.pop();
recordJump(); recordJump();
setCursorPosition(m.position(document())); setCursorPosition(m.position(document()));
if (!backTickMode) if (!backTickMode)
@@ -8309,7 +8359,7 @@ void FakeVimHandler::Private::updateMarks(const Marks &newMarks)
{ {
for (MarksIterator it(newMarks); it.hasNext(); ) { for (MarksIterator it(newMarks); it.hasNext(); ) {
it.next(); it.next();
m_marks[it.key()] = it.value(); m_buffer->marks[it.key()] = it.value();
} }
} }
@@ -8622,4 +8672,6 @@ bool FakeVimHandler::jumpToLocalMark(QChar mark, bool backTickMode)
} // namespace Internal } // namespace Internal
} // namespace FakeVim } // namespace FakeVim
Q_DECLARE_METATYPE(FakeVim::Internal::FakeVimHandler::Private::BufferDataPtr)
#include "fakevimhandler.moc" #include "fakevimhandler.moc"