Merge branch 'master' of ssh://codereview.qt-project.org:29418/qt-creator/qt-creator

This commit is contained in:
Eike Ziller
2012-10-18 10:40:26 +02:00
4 changed files with 408 additions and 203 deletions

View File

@@ -215,6 +215,22 @@ void FakeVimPlugin::test_vim_movement()
KEYS("e", "123" N "45" "." "6" N "" N " " N "78" X "9"); KEYS("e", "123" N "45" "." "6" N "" N " " N "78" X "9");
KEYS("ge", "123" N "45" "." "6" N X "" N " " N "789"); KEYS("ge", "123" N "45" "." "6" N X "" N " " N "789");
KEYS("2ge", "123" N "45" X "." "6" N "" N " " N "789"); KEYS("2ge", "123" N "45" X "." "6" N "" N " " N "789");
// do not move behind end of line in normal mode
data.setText("abc def" N "ghi");
KEYS("$h", "abc d" X "ef" N "ghi");
data.setText("abc def" N "ghi");
KEYS("4e", "abc def" N "gh" X "i");
data.setText("abc def" N "ghi");
KEYS("$i", "abc de" X "f" N "ghi");
// move behind end of line in insert mode
data.setText("abc def" N "ghi");
KEYS("i<end>", "abc def" X N "ghi");
data.setText("abc def" N "ghi");
KEYS("A", "abc def" X N "ghi");
data.setText("abc def" N "ghi");
KEYS("$a", "abc def" X N "ghi");
} }
void FakeVimPlugin::test_vim_fFtT() void FakeVimPlugin::test_vim_fFtT()
@@ -314,6 +330,18 @@ void FakeVimPlugin::test_vim_delete()
KEYS("3dw", X "jkl"); KEYS("3dw", X "jkl");
data.setText("abc " N " def" N " ghi" N "jkl"); data.setText("abc " N " def" N " ghi" N "jkl");
KEYS("d3w", X "jkl"); KEYS("d3w", X "jkl");
// delete empty line
data.setText("a" N X "" N " b");
KEYS("dd", "a" N " " X "b");
// delete on an empty line
data.setText("a" N X "" N " b");
KEYS("d$", "a" N X "" N " b");
// delete in empty document
data.setText("");
KEYS("dd", X);
} }
void FakeVimPlugin::test_vim_delete_inner_word() void FakeVimPlugin::test_vim_delete_inner_word()
@@ -426,6 +454,60 @@ void FakeVimPlugin::test_vim_change_a_word()
KEYS("3caw#", "#" X N " jkl"); KEYS("3caw#", "#" X N " jkl");
} }
void FakeVimPlugin::test_vim_change_replace()
{
TestData data;
setup(&data);
// preserve lines in replace mode
data.setText("abc" N "def");
KEYS("llvjhrX", "ab" X "X" N "XXf");
// change empty line
data.setText("a" N X "" N " b");
KEYS("ccABC", "a" N "ABC" X N " b");
// change on empty line
data.setText("a" N X "" N " b");
KEYS("c$ABC<esc>", "a" N "AB" X "C" N " b");
KEYS("u", "a" N X "" N " b");
KEYS("rA", "a" N X "" N " b");
// change in empty document
data.setText("");
KEYS("ccABC", "ABC" X);
KEYS("u", "");
KEYS("SABC", "ABC" X);
KEYS("u", "");
KEYS("sABC", "ABC" X);
KEYS("u", "");
KEYS("rA", "" X);
// indentation with change
data.doCommand("set expandtab");
data.doCommand("set shiftwidth=2");
data.setText("int main()" N
"{" N
" " X " return 0;" N
"}" N
"");
KEYS("cc" "int i = 0;",
"int main()" N
"{" N
" int i = 0;" X N
"}" N
"");
KEYS("uS" "int i = 0;" N "int j = 1;",
"int main()" N
"{" N
" int i = 0;" N
" int j = 1;" X N
"}" N
"");
}
void FakeVimPlugin::test_vim_block_selection() void FakeVimPlugin::test_vim_block_selection()
{ {
TestData data; TestData data;
@@ -458,6 +540,11 @@ void FakeVimPlugin::test_vim_block_selection()
data.setText("{" N " { " N " } " N "}"); data.setText("{" N " { " N " } " N "}");
KEYS("di{", "{" N "}"); KEYS("di{", "{" N "}");
data.setText("(" X "())");
KEYS("di(", "((" X "))");
data.setText("\"\"");
KEYS("di\"", "\"" X "\"");
} }
void FakeVimPlugin::test_vim_repeat() void FakeVimPlugin::test_vim_repeat()
@@ -817,6 +904,92 @@ void FakeVimPlugin::test_vim_undo_redo()
KEYS("u", "abc" N " " X "def" N "ghi"); KEYS("u", "abc" N " " X "def" N "ghi");
} }
void FakeVimPlugin::test_vim_letter_case()
{
TestData data;
setup(&data);
// upper- and lower-case
data.setText("abc DEF");
KEYS("lv3l~", "a" X "BC dEF");
KEYS("v4lU", "a" X "BC DEF");
KEYS("v4$u", "a" X "bc def");
KEYS("v4$gU", "a" X "BC DEF");
KEYS("gu$", "a" X "bc def");
KEYS("lg~~", X "ABC DEF");
KEYS(".", X "abc def");
KEYS("gUiw", X "ABC def");
data.setText(" ab" X "c" N "def");
KEYS("2gUU", " " X "ABC" N "DEF");
KEYS("u", " " X "abc" N "def");
KEYS("<c-r>", " " X "ABC" N "DEF");
}
void FakeVimPlugin::test_vim_code_autoindent()
{
TestData data;
setup(&data);
data.doCommand("set expandtab");
data.doCommand("set shiftwidth=3");
data.setText("int main()" N
X "{" N
"}" N
"");
KEYS("o" "return 0;",
"int main()" N
"{" N
" return 0;" X N
"}" N
"");
KEYS("O" "int i = 0;",
"int main()" N
"{" N
" int i = 0;" X N
" return 0;" N
"}" N
"");
KEYS("ddO" "int i = 0;" N "int j = 0;",
"int main()" N
"{" N
" int i = 0;" N
" int j = 0;" X N
" return 0;" N
"}" N
"");
KEYS("^i" "int x = 1;" N,
"int main()" N
"{" N
" int i = 0;" N
" int x = 1;" N
" " X "int j = 0;" N
" return 0;" N
"}" N
"");
KEYS("c2k" "if (true) {" N ";" N "}",
"int main()" N
"{" N
" if (true) {" N
" ;" N
" }" X N
" return 0;" N
"}" N
"");
KEYS("jci{" "return 1;",
"int main()" N
"{" N
" return 1;" X N
"}" N
"");
KEYS("di{",
"int main()" N
"{" N
X "}" N
"");
}
void FakeVimPlugin::test_vim_code_folding() void FakeVimPlugin::test_vim_code_folding()
{ {
TestData data; TestData data;

View File

@@ -165,7 +165,9 @@ enum SubMode
RegisterSubMode, // Used for " RegisterSubMode, // Used for "
ShiftLeftSubMode, // Used for < ShiftLeftSubMode, // Used for <
ShiftRightSubMode, // Used for > ShiftRightSubMode, // Used for >
TransformSubMode, // Used for ~/gu/gU InvertCaseSubMode, // Used for g~
DownCaseSubMode, // Used for gu
UpCaseSubMode, // Used for gU
WindowSubMode, // Used for Ctrl-w WindowSubMode, // Used for Ctrl-w
YankSubMode, // Used for y YankSubMode, // Used for y
ZSubMode, // Used for z ZSubMode, // Used for z
@@ -185,9 +187,6 @@ enum SubSubMode
MarkSubSubMode, // Used for m. MarkSubSubMode, // Used for m.
BackTickSubSubMode, // Used for `. BackTickSubSubMode, // Used for `.
TickSubSubMode, // Used for '. TickSubSubMode, // Used for '.
InvertCaseSubSubMode, // Used for ~.
DownCaseSubSubMode, // Used for gu.
UpCaseSubSubMode, // Used for gU.
TextObjectSubSubMode, // Used for thing like iw, aW, as etc. TextObjectSubSubMode, // Used for thing like iw, aW, as etc.
SearchSubSubMode SearchSubSubMode
}; };
@@ -601,7 +600,7 @@ public:
bool isReturn() const bool isReturn() const
{ {
return m_key == Key_Return || m_key == Key_Enter; return m_key == '\n' || m_key == Key_Return || m_key == Key_Enter;
} }
bool isEscape() const bool isEscape() const
@@ -1156,6 +1155,7 @@ public:
EventResult handleCloseSquareSubMode(const Input &); EventResult handleCloseSquareSubMode(const Input &);
EventResult handleSearchSubSubMode(const Input &); EventResult handleSearchSubSubMode(const Input &);
EventResult handleCommandSubSubMode(const Input &); EventResult handleCommandSubSubMode(const Input &);
void fixSelection(); // Fix selection according to current range, move and command modes.
void finishMovement(const QString &dotCommandMovement = QString()); void finishMovement(const QString &dotCommandMovement = QString());
void finishMovement(const QString &dotCommandMovement, int count); void finishMovement(const QString &dotCommandMovement, int count);
void resetCommandMode(); void resetCommandMode();
@@ -1175,7 +1175,7 @@ public:
bool atBlockStart() const { return cursor().atBlockStart(); } bool atBlockStart() const { return cursor().atBlockStart(); }
bool atBlockEnd() const { return cursor().atBlockEnd(); } bool atBlockEnd() const { return cursor().atBlockEnd(); }
bool atEndOfLine() const { return atBlockEnd() && block().length() > 1; } bool atEndOfLine() const { return atBlockEnd() && block().length() > 1; }
bool atDocumentEnd() const { return cursor().atEnd(); } bool atDocumentEnd() const { return position() >= lastPositionInDocument(); }
bool atDocumentStart() const { return cursor().atStart(); } bool atDocumentStart() const { return cursor().atStart(); }
bool atEmptyLine(const QTextCursor &tc = QTextCursor()) const; bool atEmptyLine(const QTextCursor &tc = QTextCursor()) const;
@@ -1186,7 +1186,7 @@ public:
bool atWordEnd(bool simple, const QTextCursor &tc = QTextCursor()) const; bool atWordEnd(bool simple, const QTextCursor &tc = QTextCursor()) const;
bool isFirstNonBlankOnLine(int pos); bool isFirstNonBlankOnLine(int pos);
int lastPositionInDocument() const; // Returns last valid position in doc. int lastPositionInDocument(bool ignoreMode = false) const; // Returns last valid position in doc.
int firstPositionInLine(int line, bool onlyVisibleLines = true) const; // 1 based line, 0 based pos int firstPositionInLine(int line, bool onlyVisibleLines = true) const; // 1 based line, 0 based pos
int lastPositionInLine(int line, bool onlyVisibleLines = true) const; // 1 based line, 0 based pos 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 int lineForPosition(int pos) const; // 1 based line, 0 based pos
@@ -2184,7 +2184,7 @@ void FakeVimHandler::Private::moveBehindEndOfLine()
{ {
emit q->fold(1, false); emit q->fold(1, false);
int pos = qMin(block().position() + block().length() - 1, int pos = qMin(block().position() + block().length() - 1,
lastPositionInDocument()); lastPositionInDocument() + 1);
setPosition(pos); setPosition(pos);
} }
@@ -2198,6 +2198,73 @@ void FakeVimHandler::Private::moveToStartOfLine()
#endif #endif
} }
void FakeVimHandler::Private::fixSelection()
{
if (m_movetype == MoveExclusive) {
if (anchor() != position() && atBlockStart()) {
// Exlusive motion ending at the beginning of line
// becomes inclusive and end is moved to end of previous line.
m_movetype = MoveInclusive;
moveToStartOfLine();
moveLeft();
// Exclusive motion ending at the beginning of line and
// starting at or before first non-blank on a line becomes linewise.
if (anchor() < block().position() && isFirstNonBlankOnLine(anchor())) {
m_movetype = MoveLineWise;
}
}
}
if (m_movetype == MoveLineWise)
m_rangemode = (m_submode == ChangeSubMode)
? RangeLineModeExclusive
: RangeLineMode;
if (m_movetype == MoveInclusive) {
if (anchor() <= position()) {
if (!atBlockEnd())
setPosition(position() + 1); // correction
// Omit first character in selection if it's line break on non-empty line.
int start = anchor();
int end = position();
if (document()->characterAt(start) == ParagraphSeparator
&& start > 0 && document()->characterAt(start - 1) != ParagraphSeparator) {
start = qMin(start + 1, end);
if (m_submode == DeleteSubMode && !atDocumentEnd())
setAnchorAndPosition(start, end + 1);
else
setAnchorAndPosition(start, end);
}
// If more than one line is selected and all are selected completely
// movement becomes linewise.
if (start < block().position() && isFirstNonBlankOnLine(start) && atBlockEnd()) {
if (m_submode != ChangeSubMode) {
moveRight();
if (atEmptyLine())
moveRight();
}
m_movetype = MoveLineWise;
}
} else {
setAnchorAndPosition(anchor() + 1, position());
}
}
if (m_positionPastEnd) {
const int anc = anchor();
moveBehindEndOfLine();
moveRight();
setAnchorAndPosition(anc, position());
}
if (m_anchorPastEnd) {
setAnchorAndPosition(anchor() + 1, position());
}
}
void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement, int count) void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement, int count)
{ {
finishMovement(dotCommandMovement.arg(count)); finishMovement(dotCommandMovement.arg(count));
@@ -2217,69 +2284,20 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement)
return; return;
} }
//if (isVisualMode())
// setMark('>', position());
if (m_submode == ChangeSubMode if (m_submode == ChangeSubMode
|| m_submode == DeleteSubMode || m_submode == DeleteSubMode
|| m_submode == YankSubMode || m_submode == YankSubMode
|| m_submode == TransformSubMode) { || m_submode == InvertCaseSubMode
|| m_submode == DownCaseSubMode
if (m_movetype == MoveExclusive) { || m_submode == UpCaseSubMode) {
if (anchor() != position() && atBlockStart()) {
// Exlusive motion ending at the beginning of line
// becomes inclusive and end is moved to end of previous line.
m_movetype = MoveInclusive;
moveToStartOfLine();
moveLeft();
// Exclusive motion ending at the beginning of line and
// starting at or before first non-blank on a line becomes linewise.
if (anchor() < block().position() && isFirstNonBlankOnLine(anchor())) {
m_movetype = MoveLineWise;
}
}
}
if (m_submode != YankSubMode) if (m_submode != YankSubMode)
beginEditBlock(); beginEditBlock();
if (m_movetype == MoveLineWise) fixSelection();
m_rangemode = (m_submode == ChangeSubMode)
? RangeLineModeExclusive
: RangeLineMode;
if (m_movetype == MoveInclusive) { if (m_submode != InvertCaseSubMode
if (anchor() <= position()) { && m_submode != DownCaseSubMode
if (!atBlockEnd()) && m_submode != UpCaseSubMode) {
setPosition(position() + 1); // correction
// If more than one line is selected and all are selected completely
// movement becomes linewise.
int start = anchor();
if (start < block().position() && isFirstNonBlankOnLine(start) && atBlockEnd()) {
moveRight();
if (atEmptyLine())
moveRight();
m_movetype = MoveLineWise;
}
} else {
setAnchorAndPosition(anchor() + 1, position());
}
}
if (m_positionPastEnd) {
const int anc = anchor();
moveBehindEndOfLine();
moveRight();
setAnchorAndPosition(anc, position());
}
if (m_anchorPastEnd) {
setAnchorAndPosition(anchor() + 1, position());
}
if (m_submode != TransformSubMode) {
yankText(currentRange(), m_register); yankText(currentRange(), m_register);
if (m_movetype == MoveLineWise) if (m_movetype == MoveLineWise)
setRegister(m_register, registerContents(m_register), RangeLineMode); setRegister(m_register, registerContents(m_register), RangeLineMode);
@@ -2288,33 +2306,29 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement)
m_positionPastEnd = m_anchorPastEnd = false; m_positionPastEnd = m_anchorPastEnd = false;
} }
QString dotCommand;
if (m_submode == ChangeSubMode) { if (m_submode == ChangeSubMode) {
if (m_rangemode == RangeLineMode) if (m_rangemode == RangeLineMode)
m_rangemode = RangeLineModeExclusive; m_rangemode = RangeLineModeExclusive;
removeText(currentRange()); removeText(currentRange());
if (!dotCommandMovement.isEmpty()) dotCommand = QString('c');
setDotCommand(QLatin1Char('c') + dotCommandMovement);
if (m_movetype == MoveLineWise) if (m_movetype == MoveLineWise)
insertAutomaticIndentation(true); insertAutomaticIndentation(true);
endEditBlock(); endEditBlock();
enterInsertMode(); enterInsertMode();
m_submode = NoSubMode;
} else if (m_submode == DeleteSubMode) { } else if (m_submode == DeleteSubMode) {
setUndoPosition(); setUndoPosition();
Range range = currentRange(); Range range = currentRange();
removeText(range); removeText(range);
if (!dotCommandMovement.isEmpty()) dotCommand = QString('d');
setDotCommand(QLatin1Char('d') + dotCommandMovement);
if (m_movetype == MoveLineWise) if (m_movetype == MoveLineWise)
handleStartOfLine(); handleStartOfLine();
m_submode = NoSubMode;
if (atEndOfLine()) if (atEndOfLine())
moveLeft(); moveLeft();
else else
setTargetColumn(); setTargetColumn();
endEditBlock(); endEditBlock();
} else if (m_submode == YankSubMode) { } else if (m_submode == YankSubMode) {
m_submode = NoSubMode;
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 != '"') {
@@ -2326,54 +2340,49 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement)
} }
if (la != lp) if (la != lp)
showMessage(MessageInfo, QString("%1 lines yanked").arg(qAbs(la - lp) + 1)); showMessage(MessageInfo, QString("%1 lines yanked").arg(qAbs(la - lp) + 1));
} else if (m_submode == TransformSubMode) { } else if (m_submode == InvertCaseSubMode
if (m_subsubmode == InvertCaseSubSubMode) { || m_submode == UpCaseSubMode
|| m_submode == DownCaseSubMode) {
if (m_submode == InvertCaseSubMode) {
invertCase(currentRange()); invertCase(currentRange());
if (!dotCommandMovement.isEmpty()) dotCommand = QString("g~");
setDotCommand(QLatin1Char('~') + dotCommandMovement); } else if (m_submode == DownCaseSubMode) {
} else if (m_subsubmode == UpCaseSubSubMode) {
upCase(currentRange());
if (!dotCommandMovement.isEmpty())
setDotCommand("gU" + dotCommandMovement);
} else if (m_subsubmode == DownCaseSubSubMode) {
downCase(currentRange()); downCase(currentRange());
if (!dotCommandMovement.isEmpty()) dotCommand = QString("gu");
setDotCommand("gu" + dotCommandMovement); } else if (m_submode == UpCaseSubMode) {
upCase(currentRange());
dotCommand = QString("gU");
} }
m_submode = NoSubMode;
m_subsubmode = NoSubSubMode;
setPosition(qMin(anchor(), position()));
if (m_movetype == MoveLineWise) if (m_movetype == MoveLineWise)
handleStartOfLine(); handleStartOfLine();
endEditBlock(); endEditBlock();
} else if (m_submode == IndentSubMode) { } else if (m_submode == IndentSubMode
|| m_submode == ShiftRightSubMode
|| m_submode == ShiftLeftSubMode) {
recordJump(); recordJump();
setUndoPosition(); setUndoPosition();
indentSelectedText(); if (m_submode == IndentSubMode) {
m_submode = NoSubMode; indentSelectedText();
if (!dotCommandMovement.isEmpty()) dotCommand = QString('=');
setDotCommand('=' + dotCommandMovement); } else if (m_submode == ShiftRightSubMode) {
} else if (m_submode == ShiftRightSubMode) { shiftRegionRight(1);
recordJump(); dotCommand = QString('>');
setUndoPosition(); } else if (m_submode == ShiftLeftSubMode) {
shiftRegionRight(1); shiftRegionLeft(1);
m_submode = NoSubMode; dotCommand = QString('<');
if (!dotCommandMovement.isEmpty()) }
setDotCommand('>' + dotCommandMovement);
} else if (m_submode == ShiftLeftSubMode) {
recordJump();
setUndoPosition();
shiftRegionLeft(1);
m_submode = NoSubMode;
if (!dotCommandMovement.isEmpty())
setDotCommand('<' + dotCommandMovement);
} }
if (!dotCommand.isEmpty() && !dotCommandMovement.isEmpty())
setDotCommand(dotCommand + dotCommandMovement);
resetCommandMode(); resetCommandMode();
} }
void FakeVimHandler::Private::resetCommandMode() void FakeVimHandler::Private::resetCommandMode()
{ {
m_subsubmode = NoSubSubMode;
m_submode = NoSubMode;
m_movetype = MoveInclusive; m_movetype = MoveInclusive;
m_mvcount.clear(); m_mvcount.clear();
m_opcount.clear(); m_opcount.clear();
@@ -2504,7 +2513,10 @@ static bool subModeCanUseTextObjects(int submode)
|| submode == ChangeSubMode || submode == ChangeSubMode
|| submode == IndentSubMode || submode == IndentSubMode
|| submode == ShiftLeftSubMode || submode == ShiftLeftSubMode
|| submode == ShiftRightSubMode; || submode == ShiftRightSubMode
|| submode == InvertCaseSubMode
|| submode == DownCaseSubMode
|| submode == UpCaseSubMode;
} }
EventResult FakeVimHandler::Private::handleCommandSubSubMode(const Input &input) EventResult FakeVimHandler::Private::handleCommandSubSubMode(const Input &input)
@@ -2545,10 +2557,14 @@ EventResult FakeVimHandler::Private::handleCommandSubSubMode(const Input &input)
else if (input.is('"') || input.is('\'') || input.is('`')) else if (input.is('"') || input.is('\'') || input.is('`'))
selectQuotedStringTextObject(m_subsubdata.is('i'), input.asChar()); selectQuotedStringTextObject(m_subsubdata.is('i'), input.asChar());
m_subsubmode = NoSubSubMode; m_subsubmode = NoSubSubMode;
finishMovement(QString("%1%2%3") if (cursor().hasSelection()) {
.arg(count()) finishMovement(QString("%1%2%3")
.arg(m_subsubdata.text()) .arg(count())
.arg(input.text())); .arg(m_subsubdata.text())
.arg(input.text()));
} else {
resetCommandMode();
}
} else if (m_subsubmode == MarkSubSubMode) { } else if (m_subsubmode == MarkSubSubMode) {
setMark(input.asChar().unicode(), position()); setMark(input.asChar().unicode(), position());
m_subsubmode = NoSubSubMode; m_subsubmode = NoSubSubMode;
@@ -2642,10 +2658,11 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
m_rangemode = RangeCharMode; m_rangemode = RangeCharMode;
leaveVisualMode(); leaveVisualMode();
Range range = currentRange(); Range range = currentRange();
if (m_rangemode == RangeCharMode)
++range.endPos;
Transformation tr = Transformation tr =
&FakeVimHandler::Private::replaceByCharTransform; &FakeVimHandler::Private::replaceByCharTransform;
transformText(range, tr, input.asChar()); transformText(range, tr, input.asChar());
setPosition(range.beginPos);
} else if (count() <= rightDist()) { } else if (count() <= rightDist()) {
setUndoPosition(); setUndoPosition();
setAnchor(); setAnchor();
@@ -2665,27 +2682,20 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
} }
m_submode = NoSubMode; m_submode = NoSubMode;
finishMovement(); finishMovement();
} else if (m_submode == ChangeSubMode && input.is('c')) { // tested } else if ((m_submode == ChangeSubMode && input.is('c'))
|| (m_submode == DeleteSubMode && input.is('d'))) { // tested
m_movetype = MoveLineWise; m_movetype = MoveLineWise;
setUndoPosition(); setUndoPosition();
const int line = cursorLine() + 1; const int line = cursorLine() + 1;
const int anc = firstPositionInLine(line); const int anc = firstPositionInLine(line);
const int pos = lastPositionInLine(line + count() - 1); const int pos = lastPositionInLine(line + count() - 1);
setAnchorAndPosition(anc, pos); setAnchorAndPosition(anc, pos);
m_lastInsertion.clear(); if (m_submode == ChangeSubMode) {
setDotCommand("%1cc", count()); m_lastInsertion.clear();
finishMovement(); setDotCommand("%1cc", count());
} else if (m_submode == DeleteSubMode && input.is('d')) { // tested } else {
m_movetype = MoveLineWise; setDotCommand("%1dd", count());
setUndoPosition(); }
int endPos = lastPositionInLine(cursorLine() + count());
Range range(position(), endPos, RangeLineMode);
yankText(range);
removeText(range);
setDotCommand("%1dd", count());
m_submode = NoSubMode;
handleStartOfLine();
setTargetColumn();
finishMovement(); finishMovement();
} else if (m_submode == ZSubMode) { } else if (m_submode == ZSubMode) {
//qDebug() << "Z_MODE " << cursorLine() << linesOnScreen(); //qDebug() << "Z_MODE " << cursorLine() << linesOnScreen();
@@ -2933,7 +2943,14 @@ EventResult FakeVimHandler::Private::handleCommandMode1(const Input &input)
leaveVisualMode(); leaveVisualMode();
} }
} else if (input.is('%')) { } else if (input.is('%')) {
moveToMatchingParanthesis(); if (count() == 1) {
moveToMatchingParanthesis();
} else {
// set cursor position in percentage - formula taken from Vim help
setPosition(firstPositionInLine((count() * linesInDocument() + 99) / 100));
moveToTargetColumn();
handleStartOfLine();
}
finishMovement(); finishMovement();
} else if ((!isVisualMode() && input.is('a')) || (isVisualMode() && input.is('A'))) { } else if ((!isVisualMode() && input.is('a')) || (isVisualMode() && input.is('A'))) {
leaveVisualMode(); leaveVisualMode();
@@ -3257,7 +3274,6 @@ EventResult FakeVimHandler::Private::handleCommandMode2(const Input &input)
bool insertAfter = input.is('o'); bool insertAfter = input.is('o');
setDotCommand(insertAfter ? "%1o" : "%1O", count()); setDotCommand(insertAfter ? "%1o" : "%1O", count());
setUndoPosition(); setUndoPosition();
breakEditBlock();
enterInsertMode(); enterInsertMode();
// Insert new line so that command can be repeated [count] times inserting new line // Insert new line so that command can be repeated [count] times inserting new line
// each time without unfolding any lines. // each time without unfolding any lines.
@@ -3270,11 +3286,12 @@ EventResult FakeVimHandler::Private::handleCommandMode2(const Input &input)
} else { } else {
setPosition(block.position()); setPosition(block.position());
} }
handleInsertMode(Input('\n')); beginEditBlock();
insertText(QString("\n"));
if (!appendLine) if (!appendLine)
moveLeft(); moveUp();
joinPreviousEditBlock();
insertAutomaticIndentation(insertAfter); insertAutomaticIndentation(insertAfter);
setTargetColumn();
endEditBlock(); endEditBlock();
} else if (input.isControl('o')) { } else if (input.isControl('o')) {
jump(-count()); jump(-count());
@@ -3353,7 +3370,7 @@ EventResult FakeVimHandler::Private::handleCommandMode2(const Input &input)
m_subsubdata = input; m_subsubdata = input;
} else if (input.isControl('t')) { } else if (input.isControl('t')) {
handleExCommand("pop"); handleExCommand("pop");
} else if (!m_gflag && input.is('u')) { } else if (!m_gflag && input.is('u') && !isVisualMode() && m_submode == NoSubMode) {
int repeat = count(); int repeat = count();
while (--repeat >= 0) while (--repeat >= 0)
undo(); undo();
@@ -3376,29 +3393,20 @@ EventResult FakeVimHandler::Private::handleCommandMode2(const Input &input)
toggleVisualMode(VisualLineMode); toggleVisualMode(VisualLineMode);
} else if (input.isControl('v')) { } else if (input.isControl('v')) {
toggleVisualMode(VisualBlockMode); toggleVisualMode(VisualBlockMode);
} else if (input.is('w')) { // tested } else if (input.is('w') || input.is('W')) { // tested
// Special case: "cw" and "cW" work the same as "ce" and "cE" if the // Special case: "cw" and "cW" work the same as "ce" and "cE" if the
// cursor is on a non-blank - except if the cursor is on the last // cursor is on a non-blank - except if the cursor is on the last
// character of a word: only the current word will be changed // character of a word: only the current word will be changed
bool simple = input.is('W');
if (m_submode == ChangeSubMode) { if (m_submode == ChangeSubMode) {
moveToWordEnd(count(), false, true); moveToWordEnd(count(), simple, true);
m_movetype = MoveInclusive; m_movetype = MoveInclusive;
} else { } else {
moveToNextWordStart(count(), false, true); moveToNextWordStart(count(), simple, true);
m_movetype = MoveExclusive; m_movetype = MoveExclusive;
} }
setTargetColumn(); setTargetColumn();
finishMovement("%1w", count()); finishMovement(simple ? "%1W" : "%1w", count());
} else if (input.is('W')) {
if (m_submode == ChangeSubMode) {
moveToWordEnd(count(), true, true);
m_movetype = MoveInclusive;
} else {
moveToNextWordStart(count(), true, true);
m_movetype = MoveExclusive;
}
setTargetColumn();
finishMovement("%1W", count());
} else if (input.isControl('w')) { } else if (input.isControl('w')) {
m_submode = WindowSubMode; m_submode = WindowSubMode;
} else if (input.is('x') && isNoVisualMode()) { // = "dl" } else if (input.is('x') && isNoVisualMode()) { // = "dl"
@@ -3463,22 +3471,31 @@ EventResult FakeVimHandler::Private::handleCommandMode2(const Input &input)
m_submode = ZSubMode; m_submode = ZSubMode;
} else if (input.is('Z')) { } else if (input.is('Z')) {
m_submode = CapitalZSubMode; m_submode = CapitalZSubMode;
} else if (!m_gflag && input.is('~') && !isVisualMode()) { } else if ((m_submode == InvertCaseSubMode && input.is('~'))
|| (m_submode == DownCaseSubMode && input.is('u'))
|| (m_submode == UpCaseSubMode && input.is('U'))) {
if (!isFirstNonBlankOnLine(position())) {
moveToStartOfLine();
moveToFirstNonBlankOnLine();
}
setTargetColumn();
setUndoPosition();
setAnchor();
setPosition(lastPositionInLine(cursorLine() + count()) + 1);
finishMovement(QString("%1%2").arg(count()).arg(input.raw()));
} else if (!m_gflag && (input.is('~') || input.is('u') || input.is('U')) && !isVisualMode()) {
m_movetype = MoveExclusive; m_movetype = MoveExclusive;
if (!atEndOfLine()) { if (!atEndOfLine()) {
beginEditBlock(); beginEditBlock();
setAnchor(); setAnchor();
moveRight(qMin(count(), rightDist())); moveRight(qMin(count(), rightDist()));
if (input.is('~')) { if (input.is('~'))
invertCase(currentRange()); invertCase(currentRange());
setDotCommand("%1~", count()); else if (input.is('u'))
} else if (input.is('u')) {
downCase(currentRange()); downCase(currentRange());
setDotCommand("%1gu", count()); else if (input.is('U'))
} else if (input.is('U')) {
upCase(currentRange()); upCase(currentRange());
setDotCommand("%1gU", count()); setDotCommand("%1" + input.raw(), count());
}
endEditBlock(); endEditBlock();
} }
finishMovement(); finishMovement();
@@ -3490,16 +3507,15 @@ EventResult FakeVimHandler::Private::handleCommandMode2(const Input &input)
if (atEndOfLine()) if (atEndOfLine())
moveLeft(); moveLeft();
setAnchor(); setAnchor();
m_submode = TransformSubMode;
if (input.is('~')) if (input.is('~'))
m_subsubmode = InvertCaseSubSubMode; m_submode = InvertCaseSubMode;
if (input.is('u')) if (input.is('u'))
m_subsubmode = DownCaseSubSubMode; m_submode = DownCaseSubMode;
else if (input.is('U')) else if (input.is('U'))
m_subsubmode = UpCaseSubSubMode; m_submode = UpCaseSubMode;
} else if ((input.is('~') && isVisualMode()) } else if ((input.is('~') && isVisualMode())
|| (m_gflag && input.is('u') && isVisualMode()) || (input.is('u') && isVisualMode())
|| (m_gflag && input.is('U') && isVisualMode())) { || (input.is('U') && isVisualMode())) {
m_gflag = false; m_gflag = false;
m_movetype = MoveExclusive; m_movetype = MoveExclusive;
if (isVisualLineMode()) if (isVisualLineMode())
@@ -3507,13 +3523,12 @@ EventResult FakeVimHandler::Private::handleCommandMode2(const Input &input)
else if (isVisualBlockMode()) else if (isVisualBlockMode())
m_rangemode = RangeBlockMode; m_rangemode = RangeBlockMode;
leaveVisualMode(); leaveVisualMode();
m_submode = TransformSubMode;
if (input.is('~')) if (input.is('~'))
m_subsubmode = InvertCaseSubSubMode; m_submode = InvertCaseSubMode;
else if (input.is('u')) else if (input.is('u'))
m_subsubmode = DownCaseSubSubMode; m_submode = DownCaseSubMode;
else if (input.is('U')) else if (input.is('U'))
m_subsubmode = UpCaseSubSubMode; m_submode = UpCaseSubMode;
finishMovement(); finishMovement();
} else if (input.is('[')) { } else if (input.is('[')) {
m_submode = OpenSquareSubMode; m_submode = OpenSquareSubMode;
@@ -4728,7 +4743,7 @@ void FakeVimHandler::Private::search(const SearchData &sd, bool showMessages)
if (tc.isNull()) { if (tc.isNull()) {
if (hasConfig(ConfigWrapScan)) { if (hasConfig(ConfigWrapScan)) {
int startPos = sd.forward ? 0 : lastPositionInDocument(); int startPos = sd.forward ? 0 : lastPositionInDocument(true);
tc = document()->find(needleExp, startPos, flags); tc = document()->find(needleExp, startPos, flags);
while (!tc.isNull() && --repeat >= 1) while (!tc.isNull() && --repeat >= 1)
tc = document()->find(needleExp, tc, flags); tc = document()->find(needleExp, tc, flags);
@@ -4842,15 +4857,11 @@ void FakeVimHandler::Private::indentSelectedText(QChar typedChar)
void FakeVimHandler::Private::indentText(const Range &range, QChar typedChar) void FakeVimHandler::Private::indentText(const Range &range, QChar typedChar)
{ {
int beginLine = lineForPosition(range.beginPos); int beginBlock = document()->findBlock(range.beginPos).blockNumber();
int endLine = lineForPosition(range.endPos); int endBlock = document()->findBlock(range.endPos).blockNumber();
if (beginLine > endLine) if (beginBlock > endBlock)
qSwap(beginLine, endLine); qSwap(beginBlock, endBlock);
emit q->indentRegion(beginBlock, endBlock, typedChar);
// LineForPosition has returned 1-based line numbers.
emit q->indentRegion(beginLine - 1, endLine - 1, typedChar);
if (beginLine != endLine)
showMessage(MessageError, "MARKS ARE OFF NOW");
} }
bool FakeVimHandler::Private::isElectricCharacter(QChar c) const bool FakeVimHandler::Private::isElectricCharacter(QChar c) const
@@ -5260,10 +5271,10 @@ void FakeVimHandler::Private::scrollUp(int count)
scrollToLine(cursorLine() - cursorLineOnScreen() - count); scrollToLine(cursorLine() - cursorLineOnScreen() - count);
} }
int FakeVimHandler::Private::lastPositionInDocument() const int FakeVimHandler::Private::lastPositionInDocument(bool ignoreMode) const
{ {
QTextBlock block = document()->lastBlock(); return document()->characterCount()
return block.position() + block.length() - 1; - (ignoreMode || isVisualMode() || m_mode == InsertMode || m_mode == ReplaceMode ? 1 : 2);
} }
QString FakeVimHandler::Private::selectText(const Range &range) const QString FakeVimHandler::Private::selectText(const Range &range) const
@@ -5279,7 +5290,7 @@ QString FakeVimHandler::Private::selectText(const Range &range) const
int firstPos = firstPositionInLine(lineForPosition(range.beginPos)); int firstPos = firstPositionInLine(lineForPosition(range.beginPos));
int lastLine = lineForPosition(range.endPos); int lastLine = lineForPosition(range.endPos);
bool endOfDoc = lastLine == lineNumber(document()->lastBlock()); bool endOfDoc = lastLine == lineNumber(document()->lastBlock());
int lastPos = endOfDoc ? lastPositionInDocument() : firstPositionInLine(lastLine + 1); int lastPos = endOfDoc ? lastPositionInDocument(true) : firstPositionInLine(lastLine + 1);
tc.setPosition(firstPos, MoveAnchor); tc.setPosition(firstPos, MoveAnchor);
tc.setPosition(lastPos, KeepAnchor); tc.setPosition(lastPos, KeepAnchor);
return tc.selection().toPlainText() + QString((endOfDoc? "\n" : "")); return tc.selection().toPlainText() + QString((endOfDoc? "\n" : ""));
@@ -5323,6 +5334,7 @@ void FakeVimHandler::Private::transformText(const Range &range,
Transformation transformFunc, const QVariant &extra) Transformation transformFunc, const QVariant &extra)
{ {
QTextCursor tc = cursor(); QTextCursor tc = cursor();
int posAfter = range.beginPos;
switch (range.rangemode) { switch (range.rangemode) {
case RangeCharMode: { case RangeCharMode: {
// This can span multiple lines. // This can span multiple lines.
@@ -5331,10 +5343,9 @@ void FakeVimHandler::Private::transformText(const Range &range,
tc.setPosition(range.endPos, KeepAnchor); tc.setPosition(range.endPos, KeepAnchor);
TransformationData td(tc.selectedText(), extra); TransformationData td(tc.selectedText(), extra);
(this->*transformFunc)(&td); (this->*transformFunc)(&td);
tc.removeSelectedText();
tc.insertText(td.to); tc.insertText(td.to);
endEditBlock(); endEditBlock();
return; break;
} }
case RangeLineMode: case RangeLineMode:
case RangeLineModeExclusive: { case RangeLineModeExclusive: {
@@ -5362,10 +5373,10 @@ void FakeVimHandler::Private::transformText(const Range &range,
} }
TransformationData td(tc.selectedText(), extra); TransformationData td(tc.selectedText(), extra);
(this->*transformFunc)(&td); (this->*transformFunc)(&td);
tc.removeSelectedText(); posAfter = tc.anchor();
tc.insertText(td.to); tc.insertText(td.to);
endEditBlock(); endEditBlock();
return; break;
} }
case RangeBlockAndTailMode: case RangeBlockAndTailMode:
case RangeBlockMode: { case RangeBlockMode: {
@@ -5386,13 +5397,16 @@ void FakeVimHandler::Private::transformText(const Range &range,
tc.setPosition(block.position() + eCol, KeepAnchor); tc.setPosition(block.position() + eCol, KeepAnchor);
TransformationData td(tc.selectedText(), extra); TransformationData td(tc.selectedText(), extra);
(this->*transformFunc)(&td); (this->*transformFunc)(&td);
tc.removeSelectedText();
tc.insertText(td.to); tc.insertText(td.to);
block = block.previous(); block = block.previous();
} }
endEditBlock(); endEditBlock();
break;
} }
} }
setPosition(posAfter);
setTargetColumn();
} }
void FakeVimHandler::Private::insertText(const Register &reg) void FakeVimHandler::Private::insertText(const Register &reg)
@@ -5459,7 +5473,13 @@ void FakeVimHandler::Private::replaceByStringTransform(TransformationData *td)
void FakeVimHandler::Private::replaceByCharTransform(TransformationData *td) void FakeVimHandler::Private::replaceByCharTransform(TransformationData *td)
{ {
td->to = QString(td->from.size(), td->extraData.toChar()); // Replace each character but preserve lines.
const int len = td->from.size();
td->to = QString(len, td->extraData.toChar());
for (int i = 0; i < len; ++i) {
if (td->from.at(i) == ParagraphSeparator)
td->to[i] = ParagraphSeparator;
}
} }
void FakeVimHandler::Private::pasteText(bool afterCursor) void FakeVimHandler::Private::pasteText(bool afterCursor)
@@ -5500,10 +5520,14 @@ void FakeVimHandler::Private::pasteText(bool afterCursor)
switch (rangeMode) { switch (rangeMode) {
case RangeCharMode: { case RangeCharMode: {
m_targetColumn = 0; m_targetColumn = 0;
const int pos = position() + 1;
if (pasteAfter && rightDist() > 0) if (pasteAfter && rightDist() > 0)
moveRight(); moveRight();
insertText(text.repeated(count())); insertText(text.repeated(count()));
moveLeft(); if (text.contains('\n'))
setPosition(pos);
else
moveLeft();
break; break;
} }
case RangeLineMode: case RangeLineMode:
@@ -5514,16 +5538,21 @@ void FakeVimHandler::Private::pasteText(bool afterCursor)
else else
moveToStartOfLine(); moveToStartOfLine();
m_targetColumn = 0; m_targetColumn = 0;
bool lastLine = false;
if (pasteAfter) { if (pasteAfter) {
bool lastLine = document()->lastBlock() == this->block(); lastLine = document()->lastBlock() == this->block();
if (lastLine) { if (lastLine) {
tc.movePosition(EndOfLine, MoveAnchor); tc.movePosition(EndOfLine, MoveAnchor);
tc.insertBlock(); tc.insertBlock();
} }
moveDown(); moveDown();
} }
const int pos = position(); const int pos = position() - lastLine;
insertText(text.repeated(count())); // do not insert empty line at the end of document
if (lastLine)
insertText(text.repeated(count()).left(text.size() * count() - 1));
else
insertText(text.repeated(count()));
setPosition(pos); setPosition(pos);
moveToFirstNonBlankOnLine(); moveToFirstNonBlankOnLine();
break; break;
@@ -5584,7 +5613,6 @@ void FakeVimHandler::Private::setLineContents(int line, const QString &contents)
const int len = block.length(); const int len = block.length();
tc.setPosition(begin); tc.setPosition(begin);
tc.setPosition(begin + len - 1, KeepAnchor); tc.setPosition(begin + len - 1, KeepAnchor);
tc.removeSelectedText();
tc.insertText(contents); tc.insertText(contents);
} }
@@ -5678,20 +5706,21 @@ int FakeVimHandler::Private::firstPositionInLine(int line, bool onlyVisibleLines
int FakeVimHandler::Private::lastPositionInLine(int line, bool onlyVisibleLines) const int FakeVimHandler::Private::lastPositionInLine(int line, bool onlyVisibleLines) const
{ {
QTextBlock block;
if (onlyVisibleLines) { if (onlyVisibleLines) {
// respect folds // respect folds
QTextBlock block = document()->findBlockByLineNumber(line); block = document()->findBlockByLineNumber(line);
if (block.isValid()) { if (block.isValid()) {
if (line > 0) if (line > 0)
block = block.previous(); block = block.previous();
} else { } else {
block = document()->lastBlock(); block = document()->lastBlock();
} }
return block.position() + block.length() - 1; } else {
block = document()->findBlockByNumber(line - 1);
} }
return block.position() + qMax(2, block.length())
QTextBlock block = document()->findBlockByNumber(line - 1); - (isVisualMode() || m_mode == InsertMode || m_mode == ReplaceMode ? 1 : 2);
return block.position() + block.length() - 1;
} }
int FakeVimHandler::Private::lineForPosition(int pos) const int FakeVimHandler::Private::lineForPosition(int pos) const
@@ -6118,7 +6147,7 @@ void FakeVimHandler::Private::selectBlockTextObject(bool inner,
if (inner) { if (inner) {
p1 += sleft.size(); p1 += sleft.size();
--p2; p2 = qMax(p1, p2 - 1);
} else { } else {
p2 -= sright.size() - 1; p2 -= sright.size() - 1;
} }
@@ -6136,7 +6165,7 @@ void FakeVimHandler::Private::changeNumberTextObject(int count)
{ {
QTextCursor tc = cursor(); QTextCursor tc = cursor();
int pos = tc.position(); int pos = tc.position();
const int n = lastPositionInLine(lineForPosition(pos)); const int n = lastPositionInLine(lineForPosition(pos)) + 1;
QTextDocument *doc = document(); QTextDocument *doc = document();
QChar c = doc->characterAt(pos); QChar c = doc->characterAt(pos);
while (!c.isNumber()) { while (!c.isNumber()) {
@@ -6160,7 +6189,7 @@ void FakeVimHandler::Private::changeNumberTextObject(int count)
value += count; value += count;
QString repl = QString::fromLatin1("%1").arg(value); QString repl = QString::fromLatin1("%1").arg(value);
replaceText(currentRange(), repl); replaceText(currentRange(), repl);
moveLeft(); setPosition(p1 + repl.size() - 1);
} }
void FakeVimHandler::Private::selectQuotedStringTextObject(bool inner, void FakeVimHandler::Private::selectQuotedStringTextObject(bool inner,
@@ -6183,7 +6212,7 @@ void FakeVimHandler::Private::selectQuotedStringTextObject(bool inner,
int p1 = tc1.position(); int p1 = tc1.position();
int p2 = tc2.position(); int p2 = tc2.position();
if (inner) { if (inner) {
p2 -= sz + 1; p2 = qMax(p1, p2 - sz - 1);
if (document()->characterAt(p1) == ParagraphSeparator) if (document()->characterAt(p1) == ParagraphSeparator)
++p1; ++p1;
} else { } else {

View File

@@ -859,7 +859,7 @@ private slots:
void highlightMatches(const QString &needle); void highlightMatches(const QString &needle);
void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor); void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
void checkForElectricCharacter(bool *result, QChar c); void checkForElectricCharacter(bool *result, QChar c);
void indentRegion(int beginLine, int endLine, QChar typedChar); void indentRegion(int beginBlock, int endBlock, QChar typedChar);
void handleExCommand(bool *handled, const ExCommand &cmd); void handleExCommand(bool *handled, const ExCommand &cmd);
void writeSettings(); void writeSettings();
@@ -1778,7 +1778,7 @@ void FakeVimPluginPrivate::moveToMatchingParenthesis(bool *moved, bool *forward,
} }
} }
void FakeVimPluginPrivate::indentRegion(int beginLine, int endLine, void FakeVimPluginPrivate::indentRegion(int beginBlock, int endBlock,
QChar typedChar) QChar typedChar)
{ {
FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender()); FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
@@ -1796,14 +1796,14 @@ void FakeVimPluginPrivate::indentRegion(int beginLine, int endLine,
? TabSettings::SpacesOnlyTabPolicy : TabSettings::TabsOnlyTabPolicy; ? TabSettings::SpacesOnlyTabPolicy : TabSettings::TabsOnlyTabPolicy;
QTextDocument *doc = bt->document(); QTextDocument *doc = bt->document();
QTextBlock startBlock = doc->findBlockByNumber(beginLine); QTextBlock startBlock = doc->findBlockByNumber(beginBlock);
// Record line lenghts for mark adjustments // Record line lenghts for mark adjustments
QVector<int> lineLengths(endLine - beginLine + 1); QVector<int> lineLengths(endBlock - beginBlock + 1);
QTextBlock block = startBlock; QTextBlock block = startBlock;
for (int i = beginLine; i <= endLine; ++i) { for (int i = beginBlock; i <= endBlock; ++i) {
lineLengths[i - beginLine] = block.text().length(); lineLengths[i - beginBlock] = block.text().length();
if (typedChar == 0 && block.text().simplified().isEmpty()) { if (typedChar == 0 && block.text().simplified().isEmpty()) {
// clear empty lines // clear empty lines
QTextCursor cursor(block); QTextCursor cursor(block);

View File

@@ -67,6 +67,7 @@ private slots:
void test_vim_delete_inner_word(); void test_vim_delete_inner_word();
void test_vim_delete_a_word(); void test_vim_delete_a_word();
void test_vim_change_a_word(); void test_vim_change_a_word();
void test_vim_change_replace();
void test_vim_block_selection(); void test_vim_block_selection();
void test_vim_repeat(); void test_vim_repeat();
void test_vim_search(); void test_vim_search();
@@ -74,6 +75,8 @@ private slots:
void test_vim_marks(); void test_vim_marks();
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_code_autoindent();
void test_vim_code_folding(); void test_vim_code_folding();
void test_advanced_commands(); void test_advanced_commands();
void test_map(); void test_map();