forked from qt-creator/qt-creator
fakevim: Fix last position and selection (marks)
Change-Id: If75164be930dac3c4f3b9c26179ee54aa643ab4e Reviewed-by: hjk <qthjk@ovi.com>
This commit is contained in:
@@ -917,6 +917,36 @@ void FakeVimPlugin::test_vim_marks()
|
|||||||
KEYS("`'", " " "abc" N " " X "def" N " " "ghi");
|
KEYS("`'", " " "abc" N " " X "def" N " " "ghi");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FakeVimPlugin::test_vim_jumps()
|
||||||
|
{
|
||||||
|
TestData data;
|
||||||
|
setup(&data);
|
||||||
|
|
||||||
|
// last position
|
||||||
|
data.setText(" abc" N " def" N " ghi");
|
||||||
|
KEYS("G", " abc" N " def" N " " X "ghi");
|
||||||
|
KEYS("`'", X " abc" N " def" N " ghi");
|
||||||
|
KEYS("`'", " abc" N " def" N " " X "ghi");
|
||||||
|
KEYS("''", " " X "abc" N " def" N " ghi");
|
||||||
|
KEYS("<C-O>", " abc" N " def" N " " X "ghi");
|
||||||
|
KEYS("<C-I>", " " X "abc" N " def" N " ghi");
|
||||||
|
|
||||||
|
KEYS("lgUlhj", " aBc" N " " X "def" N " ghi");
|
||||||
|
KEYS("`.", " a" X "Bc" N " def" N " ghi");
|
||||||
|
KEYS("`'", " aBc" N " " X "def" N " ghi");
|
||||||
|
KEYS("'.", " " X "aBc" N " def" N " ghi");
|
||||||
|
KEYS("G", " aBc" N " def" N " " X "ghi");
|
||||||
|
KEYS("u", " a" X "bc" N " def" N " ghi");
|
||||||
|
KEYS("`'", " abc" N " def" N " " X "ghi");
|
||||||
|
KEYS("<c-r>", " a" X "Bc" N " def" N " ghi");
|
||||||
|
KEYS("jd$", " aBc" N " " X "d" N " ghi");
|
||||||
|
KEYS("''", " aBc" N " d" N " " X "ghi");
|
||||||
|
KEYS("`'", " aBc" N " " X "d" N " ghi");
|
||||||
|
KEYS("u", " aBc" N " d" X "ef" N " ghi");
|
||||||
|
KEYS("''", " aBc" N " " X "def" N " ghi");
|
||||||
|
KEYS("`'", " aBc" N " d" X "ef" N " ghi");
|
||||||
|
}
|
||||||
|
|
||||||
void FakeVimPlugin::test_vim_copy_paste()
|
void FakeVimPlugin::test_vim_copy_paste()
|
||||||
{
|
{
|
||||||
TestData data;
|
TestData data;
|
||||||
|
|||||||
@@ -235,15 +235,35 @@ enum EventResult
|
|||||||
EventPassedToCore
|
EventPassedToCore
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef QHash<int, QTextCursor> Marks;
|
struct CursorPosition
|
||||||
|
{
|
||||||
|
CursorPosition() : line(-1), column(-1) {}
|
||||||
|
CursorPosition(int block, int column) : line(block), column(column) {}
|
||||||
|
explicit CursorPosition(const QTextCursor &tc)
|
||||||
|
: line(tc.block().blockNumber()), column(tc.positionInBlock()) {}
|
||||||
|
CursorPosition(const QTextDocument *document, int position)
|
||||||
|
{
|
||||||
|
QTextBlock block = document->findBlock(position);
|
||||||
|
line = block.blockNumber();
|
||||||
|
column = position - block.position();
|
||||||
|
}
|
||||||
|
bool isValid() const { return line >= 0 && column >= 0; }
|
||||||
|
bool operator==(const CursorPosition &other) const
|
||||||
|
{ return line == other.line && column == other.column; }
|
||||||
|
bool operator!=(const CursorPosition &other) const { return !operator==(other); }
|
||||||
|
|
||||||
|
int line; // Line in document (from 0, folded lines included).
|
||||||
|
int column; // Position on line.
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef QHash<int, CursorPosition> Marks;
|
||||||
struct State
|
struct State
|
||||||
{
|
{
|
||||||
State() : revision(-1), position(-1), line(-1), marks() {}
|
State() : revision(-1), position(), marks() {}
|
||||||
State(int revision, int position, int line, const Marks &marks)
|
State(int revision, const CursorPosition &position, const Marks &marks)
|
||||||
: revision(revision), position(position), line(line), marks(marks) {}
|
: revision(revision), position(position), marks(marks) {}
|
||||||
int revision;
|
int revision;
|
||||||
int position;
|
CursorPosition position;
|
||||||
int line;
|
|
||||||
Marks marks;
|
Marks marks;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -259,15 +279,6 @@ QDebug operator<<(QDebug ts, const Column &col)
|
|||||||
return ts << "(p: " << col.physical << ", l: " << col.logical << ")";
|
return ts << "(p: " << col.physical << ", l: " << col.logical << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CursorPosition
|
|
||||||
{
|
|
||||||
// for jump history
|
|
||||||
CursorPosition() : position(-1), scrollLine(-1) {}
|
|
||||||
CursorPosition(int pos, int line) : position(pos), scrollLine(line) {}
|
|
||||||
int position; // Position in document
|
|
||||||
int scrollLine; // First visible line
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Register
|
struct Register
|
||||||
{
|
{
|
||||||
Register() : rangemode(RangeCharMode) {}
|
Register() : rangemode(RangeCharMode) {}
|
||||||
@@ -1273,6 +1284,7 @@ public:
|
|||||||
// The following use all zero-based counting.
|
// The following use all zero-based counting.
|
||||||
int cursorLineOnScreen() const;
|
int cursorLineOnScreen() const;
|
||||||
int cursorLine() const;
|
int cursorLine() const;
|
||||||
|
int cursorBlockNumber() const;
|
||||||
int physicalCursorColumn() const; // as stored in the data
|
int physicalCursorColumn() const; // as stored in the data
|
||||||
int logicalCursorColumn() const; // as visible on screen
|
int logicalCursorColumn() const; // as visible on screen
|
||||||
int physicalToLogicalColumn(int physical, const QString &text) const;
|
int physicalToLogicalColumn(int physical, const QString &text) const;
|
||||||
@@ -1282,11 +1294,11 @@ public:
|
|||||||
void scrollToLine(int line);
|
void scrollToLine(int line);
|
||||||
void scrollUp(int count);
|
void scrollUp(int count);
|
||||||
void scrollDown(int count) { scrollUp(-count); }
|
void scrollDown(int count) { scrollUp(-count); }
|
||||||
|
void alignViewportToCursor(Qt::AlignmentFlag align, int line = -1,
|
||||||
|
bool moveToNonBlank = false);
|
||||||
|
|
||||||
CursorPosition cursorPosition() const
|
void setCursorPosition(const CursorPosition &p);
|
||||||
{ return CursorPosition(position(), firstVisibleLine()); }
|
void setCursorPosition(QTextCursor *tc, const CursorPosition &p);
|
||||||
void setCursorPosition(const CursorPosition &p)
|
|
||||||
{ setPosition(p.position); scrollToLine(p.scrollLine); }
|
|
||||||
|
|
||||||
// Helper functions for indenting/
|
// Helper functions for indenting/
|
||||||
bool isElectricCharacter(QChar c) const;
|
bool isElectricCharacter(QChar c) const;
|
||||||
@@ -1522,9 +1534,9 @@ public:
|
|||||||
VisualMode m_oldVisualMode;
|
VisualMode m_oldVisualMode;
|
||||||
|
|
||||||
// marks as lines
|
// marks as lines
|
||||||
int mark(int code) const;
|
CursorPosition mark(int code) const;
|
||||||
void setMark(int code, int position);
|
void setMark(int code, CursorPosition position);
|
||||||
typedef QHashIterator<int, QTextCursor> MarksIterator;
|
typedef QHashIterator<int, CursorPosition> MarksIterator;
|
||||||
Marks m_marks;
|
Marks m_marks;
|
||||||
|
|
||||||
// vi style configuration
|
// vi style configuration
|
||||||
@@ -1555,7 +1567,7 @@ public:
|
|||||||
void jump(int distance);
|
void jump(int distance);
|
||||||
QStack<CursorPosition> m_jumpListUndo;
|
QStack<CursorPosition> m_jumpListUndo;
|
||||||
QStack<CursorPosition> m_jumpListRedo;
|
QStack<CursorPosition> m_jumpListRedo;
|
||||||
int m_lastChangePosition;
|
CursorPosition m_lastChangePosition;
|
||||||
|
|
||||||
QList<QTextEdit::ExtraSelection> m_extraSelections;
|
QList<QTextEdit::ExtraSelection> m_extraSelections;
|
||||||
QTextCursor m_searchCursor;
|
QTextCursor m_searchCursor;
|
||||||
@@ -1565,7 +1577,6 @@ public:
|
|||||||
QString m_lastSubstituteFlags;
|
QString m_lastSubstituteFlags;
|
||||||
QRegExp m_lastSubstitutePattern;
|
QRegExp m_lastSubstitutePattern;
|
||||||
QString m_lastSubstituteReplacement;
|
QString m_lastSubstituteReplacement;
|
||||||
QTextCursor m_lastSelectionCursor;
|
|
||||||
VisualMode m_lastSelectionMode;
|
VisualMode m_lastSelectionMode;
|
||||||
|
|
||||||
bool handleExCommandHelper(ExCommand &cmd); // Returns success.
|
bool handleExCommandHelper(ExCommand &cmd); // Returns success.
|
||||||
@@ -1677,7 +1688,6 @@ void FakeVimHandler::Private::init()
|
|||||||
m_oldExternalAnchor = -1;
|
m_oldExternalAnchor = -1;
|
||||||
m_oldExternalPosition = -1;
|
m_oldExternalPosition = -1;
|
||||||
m_oldPosition = -1;
|
m_oldPosition = -1;
|
||||||
m_lastChangePosition = -1;
|
|
||||||
m_breakEditBlock = false;
|
m_breakEditBlock = false;
|
||||||
m_searchStartPosition = 0;
|
m_searchStartPosition = 0;
|
||||||
m_searchFromScreenLine = 0;
|
m_searchFromScreenLine = 0;
|
||||||
@@ -2224,8 +2234,8 @@ void FakeVimHandler::Private::setUndoPosition(bool overwrite)
|
|||||||
m_redo.clear();
|
m_redo.clear();
|
||||||
while (!m_undo.empty() && m_undo.top().revision >= rev)
|
while (!m_undo.empty() && m_undo.top().revision >= rev)
|
||||||
m_undo.pop();
|
m_undo.pop();
|
||||||
m_undo.push(State(rev, pos, lineForPosition(pos), m_marks));
|
m_lastChangePosition = CursorPosition(document(), pos);
|
||||||
m_lastChangePosition = pos;
|
m_undo.push(State(rev, m_lastChangePosition, m_marks));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FakeVimHandler::Private::moveDown(int n)
|
void FakeVimHandler::Private::moveDown(int n)
|
||||||
@@ -2402,7 +2412,7 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement)
|
|||||||
const int la = lineForPosition(anchor());
|
const int la = lineForPosition(anchor());
|
||||||
const int lp = lineForPosition(position());
|
const int lp = lineForPosition(position());
|
||||||
if (m_register != '"') {
|
if (m_register != '"') {
|
||||||
setPosition(mark(m_register));
|
setCursorPosition(mark(m_register));
|
||||||
moveToStartOfLine();
|
moveToStartOfLine();
|
||||||
} else {
|
} else {
|
||||||
if (anchor() <= position())
|
if (anchor() <= position())
|
||||||
@@ -2469,10 +2479,10 @@ void FakeVimHandler::Private::updateSelection()
|
|||||||
for (MarksIterator it(m_marks); it.hasNext(); ) {
|
for (MarksIterator it(m_marks); it.hasNext(); ) {
|
||||||
it.next();
|
it.next();
|
||||||
QTextEdit::ExtraSelection sel;
|
QTextEdit::ExtraSelection sel;
|
||||||
const int pos = it.value().position();
|
|
||||||
sel.cursor = cursor();
|
sel.cursor = cursor();
|
||||||
sel.cursor.setPosition(pos, MoveAnchor);
|
setCursorPosition(&sel.cursor, it.value());
|
||||||
sel.cursor.setPosition(pos + 1, KeepAnchor);
|
sel.cursor.setPosition(sel.cursor.position(), MoveAnchor);
|
||||||
|
sel.cursor.movePosition(Right, KeepAnchor);
|
||||||
sel.format = cursor().blockCharFormat();
|
sel.format = cursor().blockCharFormat();
|
||||||
sel.format.setForeground(Qt::blue);
|
sel.format.setForeground(Qt::blue);
|
||||||
sel.format.setBackground(Qt::green);
|
sel.format.setBackground(Qt::green);
|
||||||
@@ -2636,17 +2646,17 @@ EventResult FakeVimHandler::Private::handleCommandSubSubMode(const Input &input)
|
|||||||
resetCommandMode();
|
resetCommandMode();
|
||||||
}
|
}
|
||||||
} else if (m_subsubmode == MarkSubSubMode) {
|
} else if (m_subsubmode == MarkSubSubMode) {
|
||||||
setMark(input.asChar().unicode(), position());
|
setMark(input.asChar().unicode(), CursorPosition(cursor()));
|
||||||
m_subsubmode = NoSubSubMode;
|
m_subsubmode = NoSubSubMode;
|
||||||
} else if (m_subsubmode == BackTickSubSubMode
|
} else if (m_subsubmode == BackTickSubSubMode
|
||||||
|| m_subsubmode == TickSubSubMode) {
|
|| m_subsubmode == TickSubSubMode) {
|
||||||
ushort markChar = input.asChar().unicode();
|
ushort markChar = input.asChar().unicode();
|
||||||
int m = mark(markChar);
|
CursorPosition m = mark(markChar);
|
||||||
if (m != -1) {
|
if (m.isValid()) {
|
||||||
if (markChar == '\'' && !m_jumpListUndo.isEmpty())
|
if (markChar == '\'' && !m_jumpListUndo.isEmpty())
|
||||||
m_jumpListUndo.pop();
|
m_jumpListUndo.pop();
|
||||||
recordJump();
|
recordJump();
|
||||||
setPosition(m);
|
setCursorPosition(m);
|
||||||
if (m_subsubmode == TickSubSubMode)
|
if (m_subsubmode == TickSubSubMode)
|
||||||
moveToFirstNonBlankOnLine();
|
moveToFirstNonBlankOnLine();
|
||||||
finishMovement();
|
finishMovement();
|
||||||
@@ -2770,29 +2780,20 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
|
|||||||
} else if (m_submode == ZSubMode) {
|
} else if (m_submode == ZSubMode) {
|
||||||
//qDebug() << "Z_MODE " << cursorLine() << linesOnScreen();
|
//qDebug() << "Z_MODE " << cursorLine() << linesOnScreen();
|
||||||
bool foldMaybeClosed = false;
|
bool foldMaybeClosed = false;
|
||||||
if (input.isReturn() || input.is('t')) {
|
if (input.isReturn() || input.is('t')
|
||||||
// Cursor line to top of window.
|
|| input.is('-') || input.is('b')
|
||||||
if (!m_mvcount.isEmpty())
|
|| input.is('.') || input.is('z')) {
|
||||||
setPosition(firstPositionInLine(count()));
|
// Cursor line to top/center/bottom of window.
|
||||||
scrollUp(- cursorLineOnScreen());
|
Qt::AlignmentFlag align;
|
||||||
if (input.isReturn())
|
if (input.isReturn() || input.is('t'))
|
||||||
moveToFirstNonBlankOnLine();
|
align = Qt::AlignTop;
|
||||||
finishMovement();
|
else if (input.is('.') || input.is('z'))
|
||||||
} else if (input.is('.') || input.is('z')) {
|
align = Qt::AlignBottom;
|
||||||
// Cursor line to center of window.
|
else
|
||||||
if (!m_mvcount.isEmpty())
|
align = Qt::AlignVCenter;
|
||||||
setPosition(firstPositionInLine(count()));
|
const bool moveToNonBlank = (input.is('.') || input.isReturn() || input.is('-'));
|
||||||
scrollUp(linesOnScreen() / 2 - cursorLineOnScreen());
|
const int line = m_mvcount.isEmpty() ? -1 : firstPositionInLine(count());
|
||||||
if (input.is('.'))
|
alignViewportToCursor(align, line, moveToNonBlank);
|
||||||
moveToFirstNonBlankOnLine();
|
|
||||||
finishMovement();
|
|
||||||
} else if (input.is('-') || input.is('b')) {
|
|
||||||
// Cursor line to bottom of window.
|
|
||||||
if (!m_mvcount.isEmpty())
|
|
||||||
setPosition(firstPositionInLine(count()));
|
|
||||||
scrollUp(linesOnScreen() - cursorLineOnScreen());
|
|
||||||
if (input.is('-'))
|
|
||||||
moveToFirstNonBlankOnLine();
|
|
||||||
finishMovement();
|
finishMovement();
|
||||||
} else if (input.is('o') || input.is('c')) {
|
} else if (input.is('o') || input.is('c')) {
|
||||||
// Open/close current fold.
|
// Open/close current fold.
|
||||||
@@ -3013,6 +3014,7 @@ EventResult FakeVimHandler::Private::handleCommandMode1(const Input &input)
|
|||||||
leaveVisualMode();
|
leaveVisualMode();
|
||||||
}
|
}
|
||||||
} else if (input.is('%')) {
|
} else if (input.is('%')) {
|
||||||
|
recordJump();
|
||||||
if (count() == 1) {
|
if (count() == 1) {
|
||||||
moveToMatchingParanthesis();
|
moveToMatchingParanthesis();
|
||||||
} else {
|
} else {
|
||||||
@@ -3191,6 +3193,7 @@ EventResult FakeVimHandler::Private::handleCommandMode1(const Input &input)
|
|||||||
m_gflag = true;
|
m_gflag = true;
|
||||||
} else if (input.is('g') || input.is('G')) {
|
} else if (input.is('g') || input.is('G')) {
|
||||||
QString dotCommand = QString("%1G").arg(count());
|
QString dotCommand = QString("%1G").arg(count());
|
||||||
|
recordJump();
|
||||||
if (input.is('G') && m_mvcount.isEmpty())
|
if (input.is('G') && m_mvcount.isEmpty())
|
||||||
dotCommand = QString(QLatin1Char('G'));
|
dotCommand = QString(QLatin1Char('G'));
|
||||||
if (input.is('g'))
|
if (input.is('g'))
|
||||||
@@ -3455,9 +3458,13 @@ EventResult FakeVimHandler::Private::handleCommandMode2(const Input &input)
|
|||||||
scrollToLine(cursorLine() - sline);
|
scrollToLine(cursorLine() - sline);
|
||||||
finishMovement();
|
finishMovement();
|
||||||
} else if (m_gflag && input.is('v')) {
|
} else if (m_gflag && input.is('v')) {
|
||||||
if (m_lastSelectionCursor.hasSelection()) {
|
CursorPosition from = mark('<');
|
||||||
|
CursorPosition to = mark('>');
|
||||||
|
if (from.isValid() && to.isValid()) {
|
||||||
toggleVisualMode(m_lastSelectionMode);
|
toggleVisualMode(m_lastSelectionMode);
|
||||||
setCursor(m_lastSelectionCursor);
|
setCursorPosition(from);
|
||||||
|
setAnchor();
|
||||||
|
setCursorPosition(to);
|
||||||
}
|
}
|
||||||
} else if (input.is('v')) {
|
} else if (input.is('v')) {
|
||||||
toggleVisualMode(VisualCharMode);
|
toggleVisualMode(VisualCharMode);
|
||||||
@@ -3576,6 +3583,7 @@ EventResult FakeVimHandler::Private::handleCommandMode2(const Input &input)
|
|||||||
|| (m_gflag && input.is('U') && !isVisualMode())) {
|
|| (m_gflag && input.is('U') && !isVisualMode())) {
|
||||||
m_gflag = false;
|
m_gflag = false;
|
||||||
m_movetype = MoveExclusive;
|
m_movetype = MoveExclusive;
|
||||||
|
setUndoPosition();
|
||||||
if (atEndOfLine())
|
if (atEndOfLine())
|
||||||
moveLeft();
|
moveLeft();
|
||||||
setAnchor();
|
setAnchor();
|
||||||
@@ -4005,7 +4013,7 @@ EventResult FakeVimHandler::Private::handleSearchSubSubMode(const Input &input)
|
|||||||
return EventHandled;
|
return EventHandled;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This uses 1 based line counting.
|
// This uses 0 based line counting (hidden lines included).
|
||||||
int FakeVimHandler::Private::readLineCode(QString &cmd)
|
int FakeVimHandler::Private::readLineCode(QString &cmd)
|
||||||
{
|
{
|
||||||
//qDebug() << "CMD: " << cmd;
|
//qDebug() << "CMD: " << cmd;
|
||||||
@@ -4015,48 +4023,38 @@ int FakeVimHandler::Private::readLineCode(QString &cmd)
|
|||||||
cmd = cmd.mid(1);
|
cmd = cmd.mid(1);
|
||||||
if (c == '.') {
|
if (c == '.') {
|
||||||
if (cmd.isEmpty())
|
if (cmd.isEmpty())
|
||||||
return cursorLine() + 1;
|
return cursorBlockNumber();
|
||||||
QChar c1 = cmd.at(0);
|
QChar c1 = cmd.at(0);
|
||||||
if (c1 == '+' || c1 == '-') {
|
if (c1 == '+' || c1 == '-') {
|
||||||
// Repeat for things like .+4
|
// Repeat for things like .+4
|
||||||
cmd = cmd.mid(1);
|
cmd = cmd.mid(1);
|
||||||
return cursorLine() + readLineCode(cmd);
|
return cursorBlockNumber() + readLineCode(cmd);
|
||||||
}
|
}
|
||||||
return cursorLine() + 1;
|
return cursorBlockNumber();
|
||||||
}
|
}
|
||||||
if (c == '$')
|
if (c == '$')
|
||||||
return linesInDocument();
|
return document()->blockCount() - 1;
|
||||||
if (c == '\'' && !cmd.isEmpty()) {
|
if (c == '\'' && !cmd.isEmpty()) {
|
||||||
if (cmd.isEmpty()) {
|
if (cmd.isEmpty()) {
|
||||||
showMessage(MessageError, msgMarkNotSet(QString()));
|
showMessage(MessageError, msgMarkNotSet(QString()));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
int m = mark(cmd.at(0).unicode());
|
CursorPosition m = mark(cmd.at(0).unicode());
|
||||||
if (m == -1) {
|
if (!m.isValid()) {
|
||||||
showMessage(MessageError, msgMarkNotSet(cmd.at(0)));
|
showMessage(MessageError, msgMarkNotSet(cmd.at(0)));
|
||||||
cmd = cmd.mid(1);
|
cmd = cmd.mid(1);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
cmd = cmd.mid(1);
|
cmd = cmd.mid(1);
|
||||||
return lineForPosition(m);
|
return m.line;
|
||||||
}
|
}
|
||||||
if (c == '-') {
|
if (c == '-') {
|
||||||
int n = readLineCode(cmd);
|
int n = readLineCode(cmd);
|
||||||
return cursorLine() + 1 - (n == -1 ? 1 : n);
|
return cursorBlockNumber() - (n == -1 ? 1 : n);
|
||||||
}
|
}
|
||||||
if (c == '+') {
|
if (c == '+') {
|
||||||
int n = readLineCode(cmd);
|
int n = readLineCode(cmd);
|
||||||
return cursorLine() + 1 + (n == -1 ? 1 : n);
|
return cursorBlockNumber() + (n == -1 ? 1 : n);
|
||||||
}
|
|
||||||
if (c == '\'' && !cmd.isEmpty()) {
|
|
||||||
int pos = mark(cmd.at(0).unicode());
|
|
||||||
if (pos == -1) {
|
|
||||||
showMessage(MessageError, msgMarkNotSet(cmd.at(0)));
|
|
||||||
cmd = cmd.mid(1);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
cmd = cmd.mid(1);
|
|
||||||
return lineForPosition(pos);
|
|
||||||
}
|
}
|
||||||
if (c.isDigit()) {
|
if (c.isDigit()) {
|
||||||
int n = c.unicode() - '0';
|
int n = c.unicode() - '0';
|
||||||
@@ -4068,7 +4066,7 @@ int FakeVimHandler::Private::readLineCode(QString &cmd)
|
|||||||
n = n * 10 + (c.unicode() - '0');
|
n = n * 10 + (c.unicode() - '0');
|
||||||
}
|
}
|
||||||
//qDebug() << "N: " << n;
|
//qDebug() << "N: " << n;
|
||||||
return n;
|
return n - 1;
|
||||||
}
|
}
|
||||||
// Parsing failed.
|
// Parsing failed.
|
||||||
cmd = c + cmd;
|
cmd = c + cmd;
|
||||||
@@ -4084,9 +4082,9 @@ void FakeVimHandler::Private::setCurrentRange(const Range &range)
|
|||||||
Range FakeVimHandler::Private::rangeFromCurrentLine() const
|
Range FakeVimHandler::Private::rangeFromCurrentLine() const
|
||||||
{
|
{
|
||||||
Range range;
|
Range range;
|
||||||
int line = cursorLine() + 1;
|
QTextBlock block = cursor().block();
|
||||||
range.beginPos = firstPositionInLine(line);
|
range.beginPos = block.position();
|
||||||
range.endPos = lastPositionInLine(line);
|
range.endPos = range.beginPos + block.length() - 1;
|
||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4173,34 +4171,36 @@ bool FakeVimHandler::Private::handleExSubstituteCommand(const ExCommand &cmd)
|
|||||||
m_lastSubstituteReplacement = replacement;
|
m_lastSubstituteReplacement = replacement;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lastLine = -1;
|
QTextBlock lastBlock;
|
||||||
int firstLine = -1;
|
QTextBlock firstBlock;
|
||||||
const bool global = flags.contains('g');
|
const bool global = flags.contains('g');
|
||||||
for (int a = 0; a != count; ++a) {
|
for (int a = 0; a != count; ++a) {
|
||||||
const Range range = cmd.range.endPos <= 0 ? rangeFromCurrentLine() : cmd.range;
|
const Range range = cmd.range.endPos <= 0 ? rangeFromCurrentLine() : cmd.range;
|
||||||
const int beginLine = lineForPosition(range.beginPos);
|
for (QTextBlock block = document()->findBlock(range.endPos);
|
||||||
const int endLine = lineForPosition(range.endPos);
|
block.isValid() && block.position() + block.length() > range.beginPos;
|
||||||
for (int line = endLine; line >= beginLine; --line) {
|
block = block.previous()) {
|
||||||
QString text = lineContents(line);
|
QString text = block.text();
|
||||||
if (substituteText(&text, pattern, replacement, global)) {
|
if (substituteText(&text, pattern, replacement, global)) {
|
||||||
firstLine = line;
|
firstBlock = block;
|
||||||
if (lastLine == -1) {
|
if (!lastBlock.isValid()) {
|
||||||
lastLine = line;
|
lastBlock = block;
|
||||||
beginEditBlock();
|
beginEditBlock();
|
||||||
}
|
}
|
||||||
setLineContents(line, text);
|
QTextCursor tc = cursor();
|
||||||
|
const int pos = block.position();
|
||||||
|
const int anchor = pos + block.length() - 1;
|
||||||
|
tc.setPosition(anchor);
|
||||||
|
tc.setPosition(pos, KeepAnchor);
|
||||||
|
tc.insertText(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastLine != -1) {
|
if (lastBlock.isValid()) {
|
||||||
State &state = m_undo.top();
|
State &state = m_undo.top();
|
||||||
state.line = firstLine;
|
state.position = CursorPosition(firstBlock.blockNumber(), 0);
|
||||||
state.position = firstPositionInLine(firstLine);
|
|
||||||
|
|
||||||
QTextCursor tc = cursor();
|
setPosition(lastBlock.position());
|
||||||
tc.setPosition(firstPositionInLine(lastLine));
|
|
||||||
setCursor(tc);
|
|
||||||
moveToFirstNonBlankOnLine();
|
moveToFirstNonBlankOnLine();
|
||||||
setTargetColumn();
|
setTargetColumn();
|
||||||
|
|
||||||
@@ -4712,8 +4712,8 @@ bool FakeVimHandler::Private::handleExCommandHelper(ExCommand &cmd)
|
|||||||
if (beginLine != -1 && endLine == -1)
|
if (beginLine != -1 && endLine == -1)
|
||||||
endLine = beginLine;
|
endLine = beginLine;
|
||||||
if (beginLine != -1) {
|
if (beginLine != -1) {
|
||||||
const int beginPos = firstPositionInLine(beginLine);
|
const int beginPos = firstPositionInLine(beginLine + 1, false);
|
||||||
const int endPos = lastPositionInLine(endLine);
|
const int endPos = lastPositionInLine(endLine + 1, false);
|
||||||
cmd.range = Range(beginPos, endPos, RangeLineMode);
|
cmd.range = Range(beginPos, endPos, RangeLineMode);
|
||||||
cmd.count = beginLine;
|
cmd.count = beginLine;
|
||||||
}
|
}
|
||||||
@@ -5234,6 +5234,11 @@ int FakeVimHandler::Private::cursorLine() const
|
|||||||
return lineForPosition(position()) - 1;
|
return lineForPosition(position()) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int FakeVimHandler::Private::cursorBlockNumber() const
|
||||||
|
{
|
||||||
|
return cursor().block().blockNumber();
|
||||||
|
}
|
||||||
|
|
||||||
int FakeVimHandler::Private::physicalCursorColumn() const
|
int FakeVimHandler::Private::physicalCursorColumn() const
|
||||||
{
|
{
|
||||||
return position() - block().position();
|
return position() - block().position();
|
||||||
@@ -5323,6 +5328,44 @@ void FakeVimHandler::Private::scrollUp(int count)
|
|||||||
scrollToLine(cursorLine() - cursorLineOnScreen() - count);
|
scrollToLine(cursorLine() - cursorLineOnScreen() - count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FakeVimHandler::Private::alignViewportToCursor(AlignmentFlag align, int line,
|
||||||
|
bool moveToNonBlank)
|
||||||
|
{
|
||||||
|
if (line > 0)
|
||||||
|
setPosition(firstPositionInLine(line));
|
||||||
|
if (moveToNonBlank)
|
||||||
|
moveToFirstNonBlankOnLine();
|
||||||
|
|
||||||
|
if (align == Qt::AlignTop)
|
||||||
|
scrollUp(- cursorLineOnScreen());
|
||||||
|
else if (align == Qt::AlignVCenter)
|
||||||
|
scrollUp(linesOnScreen() / 2 - cursorLineOnScreen());
|
||||||
|
else if (align == Qt::AlignBottom)
|
||||||
|
scrollUp(linesOnScreen() - cursorLineOnScreen());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FakeVimHandler::Private::setCursorPosition(const CursorPosition &p)
|
||||||
|
{
|
||||||
|
const int firstLine = firstVisibleLine();
|
||||||
|
const int firstBlock = document()->findBlockByLineNumber(firstLine).blockNumber();
|
||||||
|
const int lastBlock =
|
||||||
|
document()->findBlockByLineNumber(firstLine + linesOnScreen() - 2).blockNumber();
|
||||||
|
bool isLineVisible = firstBlock <= p.line && p.line <= lastBlock;
|
||||||
|
QTextCursor tc = cursor();
|
||||||
|
setCursorPosition(&tc, p);
|
||||||
|
setCursor(tc);
|
||||||
|
if (!isLineVisible)
|
||||||
|
alignViewportToCursor(Qt::AlignVCenter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FakeVimHandler::Private::setCursorPosition(QTextCursor *tc, const CursorPosition &p)
|
||||||
|
{
|
||||||
|
const int line = qMin(document()->blockCount() - 1, p.line);
|
||||||
|
QTextBlock block = document()->findBlockByNumber(line);
|
||||||
|
const int column = qMin(p.column, block.length() - 1);
|
||||||
|
tc->setPosition(block.position() + column, KeepAnchor);
|
||||||
|
}
|
||||||
|
|
||||||
int FakeVimHandler::Private::lastPositionInDocument(bool ignoreMode) const
|
int FakeVimHandler::Private::lastPositionInDocument(bool ignoreMode) const
|
||||||
{
|
{
|
||||||
return document()->characterCount()
|
return document()->characterCount()
|
||||||
@@ -5771,8 +5814,11 @@ int FakeVimHandler::Private::lastPositionInLine(int line, bool onlyVisibleLines)
|
|||||||
} else {
|
} else {
|
||||||
block = document()->findBlockByNumber(line - 1);
|
block = document()->findBlockByNumber(line - 1);
|
||||||
}
|
}
|
||||||
return block.position() + qMax(2, block.length())
|
|
||||||
- (isVisualMode() || m_mode == InsertMode || m_mode == ReplaceMode ? 1 : 2);
|
const int position = block.position() + block.length() - 1;
|
||||||
|
if (block.length() > 1 && !isVisualMode() && m_mode != InsertMode && m_mode != ReplaceMode)
|
||||||
|
return position - 1;
|
||||||
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FakeVimHandler::Private::lineForPosition(int pos) const
|
int FakeVimHandler::Private::lineForPosition(int pos) const
|
||||||
@@ -5800,14 +5846,14 @@ void FakeVimHandler::Private::leaveVisualMode()
|
|||||||
if (!isVisualMode())
|
if (!isVisualMode())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_lastSelectionCursor = cursor();
|
QTextCursor tc = cursor();
|
||||||
m_lastSelectionMode = m_visualMode;
|
m_lastSelectionMode = m_visualMode;
|
||||||
int from = m_lastSelectionCursor.anchor();
|
int from = tc.anchor();
|
||||||
int to = m_lastSelectionCursor.position();
|
int to = tc.position();
|
||||||
if (from > to)
|
if (from > to)
|
||||||
qSwap(from, to);
|
qSwap(from, to);
|
||||||
setMark('<', from);
|
setMark('<', CursorPosition(document(), from));
|
||||||
setMark('>', to);
|
setMark('>', CursorPosition(document(), to));
|
||||||
if (isVisualLineMode())
|
if (isVisualLineMode())
|
||||||
m_movetype = MoveLineWise;
|
m_movetype = MoveLineWise;
|
||||||
else if (isVisualCharMode())
|
else if (isVisualCharMode())
|
||||||
@@ -5867,6 +5913,7 @@ void FakeVimHandler::Private::undo()
|
|||||||
// 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.
|
||||||
|
CursorPosition lastPos(cursor());
|
||||||
const int current = revision();
|
const int current = revision();
|
||||||
EDITOR(undo());
|
EDITOR(undo());
|
||||||
const int rev = revision();
|
const int rev = revision();
|
||||||
@@ -5886,7 +5933,8 @@ void FakeVimHandler::Private::undo()
|
|||||||
if (state.revision == rev) {
|
if (state.revision == rev) {
|
||||||
m_lastChangePosition = state.position;
|
m_lastChangePosition = state.position;
|
||||||
m_marks = state.marks;
|
m_marks = state.marks;
|
||||||
setPosition(m_lastChangePosition);
|
setMark('\'', lastPos);
|
||||||
|
setCursorPosition(m_lastChangePosition);
|
||||||
state.revision = current;
|
state.revision = current;
|
||||||
m_redo.push(m_undo.pop());
|
m_redo.push(m_undo.pop());
|
||||||
}
|
}
|
||||||
@@ -5899,6 +5947,7 @@ void FakeVimHandler::Private::undo()
|
|||||||
|
|
||||||
void FakeVimHandler::Private::redo()
|
void FakeVimHandler::Private::redo()
|
||||||
{
|
{
|
||||||
|
CursorPosition lastPos(cursor());
|
||||||
const int current = revision();
|
const int current = revision();
|
||||||
EDITOR(redo());
|
EDITOR(redo());
|
||||||
const int rev = revision();
|
const int rev = revision();
|
||||||
@@ -5916,12 +5965,10 @@ void FakeVimHandler::Private::redo()
|
|||||||
if (!m_redo.empty()) {
|
if (!m_redo.empty()) {
|
||||||
State &state = m_redo.top();
|
State &state = m_redo.top();
|
||||||
if (state.revision == rev) {
|
if (state.revision == rev) {
|
||||||
int pos = qMin(document()->characterCount() - 1, state.position);
|
m_lastChangePosition = state.position;
|
||||||
if (lineForPosition(pos) != state.line)
|
|
||||||
pos = lastPositionInLine(state.line);
|
|
||||||
m_lastChangePosition = pos;
|
|
||||||
m_marks = state.marks;
|
m_marks = state.marks;
|
||||||
setPosition(m_lastChangePosition);
|
setMark('\'', lastPos);
|
||||||
|
setCursorPosition(m_lastChangePosition);
|
||||||
state.revision = current;
|
state.revision = current;
|
||||||
m_undo.push(m_redo.pop());
|
m_undo.push(m_redo.pop());
|
||||||
}
|
}
|
||||||
@@ -5977,9 +6024,9 @@ void FakeVimHandler::Private::enterExMode()
|
|||||||
|
|
||||||
void FakeVimHandler::Private::recordJump()
|
void FakeVimHandler::Private::recordJump()
|
||||||
{
|
{
|
||||||
CursorPosition pos = cursorPosition();
|
CursorPosition pos(cursor());
|
||||||
setMark('\'', pos.position);
|
setMark('\'', pos);
|
||||||
if (m_jumpListUndo.isEmpty() || m_jumpListUndo.top().position != pos.position)
|
if (m_jumpListUndo.isEmpty() || m_jumpListUndo.top() != pos)
|
||||||
m_jumpListUndo.push(pos);
|
m_jumpListUndo.push(pos);
|
||||||
m_jumpListRedo.clear();
|
m_jumpListRedo.clear();
|
||||||
UNDO_DEBUG("jumps: " << m_jumpListUndo);
|
UNDO_DEBUG("jumps: " << m_jumpListUndo);
|
||||||
@@ -5990,9 +6037,10 @@ void FakeVimHandler::Private::jump(int distance)
|
|||||||
QStack<CursorPosition> &from = (distance > 0) ? m_jumpListRedo : m_jumpListUndo;
|
QStack<CursorPosition> &from = (distance > 0) ? m_jumpListRedo : m_jumpListUndo;
|
||||||
QStack<CursorPosition> &to = (distance > 0) ? m_jumpListUndo : m_jumpListRedo;
|
QStack<CursorPosition> &to = (distance > 0) ? m_jumpListUndo : m_jumpListRedo;
|
||||||
int len = qMin(qAbs(distance), from.size());
|
int len = qMin(qAbs(distance), from.size());
|
||||||
setMark('\'', position());
|
CursorPosition m(cursor());
|
||||||
|
setMark('\'', m);
|
||||||
for (int i = 0; i < len; ++i) {
|
for (int i = 0; i < len; ++i) {
|
||||||
to.push(cursorPosition());
|
to.push(m);
|
||||||
setCursorPosition(from.top());
|
setCursorPosition(from.top());
|
||||||
from.pop();
|
from.pop();
|
||||||
}
|
}
|
||||||
@@ -6278,28 +6326,25 @@ void FakeVimHandler::Private::selectQuotedStringTextObject(bool inner,
|
|||||||
m_movetype = MoveInclusive;
|
m_movetype = MoveInclusive;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FakeVimHandler::Private::mark(int code) const
|
CursorPosition FakeVimHandler::Private::mark(int code) const
|
||||||
{
|
{
|
||||||
// FIXME: distinguish local and global marks.
|
// FIXME: distinguish local and global marks.
|
||||||
//qDebug() << "MARK: " << code << m_marks.value(code, -1) << m_marks;
|
//qDebug() << "MARK: " << code << m_marks.value(code, -1) << m_marks;
|
||||||
if (isVisualMode()) {
|
if (isVisualMode()) {
|
||||||
if (code == '<')
|
if (code == '<')
|
||||||
return position();
|
return CursorPosition(document(), qMin(anchor(), position()));
|
||||||
if (code == '>')
|
if (code == '>')
|
||||||
return anchor();
|
return CursorPosition(document(), qMax(anchor(), position()));
|
||||||
}
|
}
|
||||||
if (code == '.')
|
if (code == '.')
|
||||||
return m_lastChangePosition;
|
return m_lastChangePosition;
|
||||||
QTextCursor tc = m_marks.value(code);
|
return m_marks.value(code);
|
||||||
return tc.isNull() ? -1 : tc.position();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FakeVimHandler::Private::setMark(int code, int position)
|
void FakeVimHandler::Private::setMark(int code, CursorPosition position)
|
||||||
{
|
{
|
||||||
// FIXME: distinguish local and global marks.
|
// FIXME: distinguish local and global marks.
|
||||||
QTextCursor tc = cursor();
|
m_marks[code] = position;
|
||||||
tc.setPosition(position, MoveAnchor);
|
|
||||||
m_marks[code] = tc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RangeMode FakeVimHandler::Private::registerRangeMode(int reg) const
|
RangeMode FakeVimHandler::Private::registerRangeMode(int reg) const
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ private slots:
|
|||||||
void test_vim_search();
|
void test_vim_search();
|
||||||
void test_vim_indent();
|
void test_vim_indent();
|
||||||
void test_vim_marks();
|
void test_vim_marks();
|
||||||
|
void test_vim_jumps();
|
||||||
void test_vim_copy_paste();
|
void test_vim_copy_paste();
|
||||||
void test_vim_undo_redo();
|
void test_vim_undo_redo();
|
||||||
void test_vim_letter_case();
|
void test_vim_letter_case();
|
||||||
|
|||||||
Reference in New Issue
Block a user