File indexing completed on 2024-05-12 15:45:13

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2011 Kuzmich Svyatoslav
0004     SPDX-FileCopyrightText: 2012-2013 Simon St James <kdedevel@etotheipiplusone.com>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "emulatedcommandbar.h"
0010 #include "emulatedcommandbarsetupandteardown.h"
0011 #include "keys.h"
0012 #include "view.h"
0013 #include "vimode/globalstate.h"
0014 #include "vimode/mappings.h"
0015 #include <inputmode/kateviinputmode.h>
0016 #include <katebuffer.h>
0017 #include <kateconfig.h>
0018 #include <katedocument.h>
0019 #include <kateview.h>
0020 #include <vimode/emulatedcommandbar/emulatedcommandbar.h>
0021 #include <vimode/history.h>
0022 
0023 #include <QAbstractItemView>
0024 #include <QClipboard>
0025 #include <QCompleter>
0026 #include <QLabel>
0027 #include <QStringListModel>
0028 
0029 #include <KActionCollection>
0030 #include <KColorScheme>
0031 
0032 QTEST_MAIN(EmulatedCommandBarTest)
0033 
0034 using namespace KTextEditor;
0035 using namespace KateVi;
0036 
0037 void EmulatedCommandBarTest::EmulatedCommandBarTests()
0038 {
0039     // Ensure that some preconditions for these tests are setup, and (more importantly)
0040     // ensure that they are reverted no matter how these tests end.
0041     EmulatedCommandBarSetUpAndTearDown emulatedCommandBarSetUpAndTearDown(vi_input_mode, kate_view, mainWindow);
0042 
0043     // Verify that we can get a non-null pointer to the emulated command bar.
0044     EmulatedCommandBar *emulatedCommandBar = vi_input_mode->viModeEmulatedCommandBar();
0045     QVERIFY(emulatedCommandBar);
0046 
0047     // Should initially be hidden.
0048     QVERIFY(!emulatedCommandBar->isVisible());
0049 
0050     // Test that "/" invokes the emulated command bar (if we are configured to use it)
0051     BeginTest("");
0052     TestPressKey("/");
0053     QVERIFY(emulatedCommandBar->isVisible());
0054     QCOMPARE(emulatedCommandTypeIndicator()->text(), QString("/"));
0055     QVERIFY(emulatedCommandTypeIndicator()->isVisible());
0056     QVERIFY(emulatedCommandBarTextEdit());
0057     QVERIFY(emulatedCommandBarTextEdit()->text().isEmpty());
0058     // Make sure the keypresses end up changing the text.
0059     QVERIFY(emulatedCommandBarTextEdit()->isVisible());
0060     TestPressKey("foo");
0061     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo"));
0062     // Make sure ctrl-c dismisses it (assuming we allow Vim to steal the ctrl-c shortcut).
0063     TestPressKey("\\ctrl-c");
0064     QVERIFY(!emulatedCommandBar->isVisible());
0065 
0066     // Ensure that ESC dismisses it, too.
0067     BeginTest("");
0068     TestPressKey("/");
0069     QVERIFY(emulatedCommandBar->isVisible());
0070     TestPressKey("\\esc");
0071     QVERIFY(!emulatedCommandBar->isVisible());
0072     FinishTest("");
0073 
0074     // Ensure that Ctrl-[ dismisses it, too.
0075     BeginTest("");
0076     TestPressKey("/");
0077     QVERIFY(emulatedCommandBar->isVisible());
0078     TestPressKey("\\ctrl-[");
0079     QVERIFY(!emulatedCommandBar->isVisible());
0080     FinishTest("");
0081 
0082     // Ensure that Enter dismisses it, too.
0083     BeginTest("");
0084     TestPressKey("/");
0085     QVERIFY(emulatedCommandBar->isVisible());
0086     TestPressKey("\\enter");
0087     QVERIFY(!emulatedCommandBar->isVisible());
0088     FinishTest("");
0089 
0090     // Ensure that Return dismisses it, too.
0091     BeginTest("");
0092     TestPressKey("/");
0093     QVERIFY(emulatedCommandBar->isVisible());
0094     TestPressKey("\\return");
0095     QVERIFY(!emulatedCommandBar->isVisible());
0096     FinishTest("");
0097 
0098     // Ensure that text is always initially empty.
0099     BeginTest("");
0100     TestPressKey("/a\\enter");
0101     TestPressKey("/");
0102     QVERIFY(emulatedCommandBarTextEdit()->text().isEmpty());
0103     TestPressKey("\\enter");
0104     FinishTest("");
0105 
0106     // Check backspace works.
0107     BeginTest("");
0108     TestPressKey("/foo\\backspace");
0109     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("fo"));
0110     TestPressKey("\\enter");
0111     FinishTest("");
0112 
0113     // Check ctrl-h works.
0114     BeginTest("");
0115     TestPressKey("/bar\\ctrl-h");
0116     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("ba"));
0117     TestPressKey("\\enter");
0118     FinishTest("");
0119 
0120     // ctrl-h should dismiss bar when empty.
0121     BeginTest("");
0122     TestPressKey("/\\ctrl-h");
0123     QVERIFY(!emulatedCommandBar->isVisible());
0124     FinishTest("");
0125 
0126     // ctrl-h should not dismiss bar when there is stuff to the left of cursor.
0127     BeginTest("");
0128     TestPressKey("/a\\ctrl-h");
0129     QVERIFY(emulatedCommandBar->isVisible());
0130     TestPressKey("\\enter");
0131     FinishTest("");
0132 
0133     // ctrl-h should not dismiss bar when bar is not empty, even if there is nothing to the left of cursor.
0134     BeginTest("");
0135     TestPressKey("/a\\left\\ctrl-h");
0136     QVERIFY(emulatedCommandBar->isVisible());
0137     TestPressKey("\\enter");
0138     FinishTest("");
0139 
0140     // Same for backspace.
0141     BeginTest("");
0142     TestPressKey("/bar\\backspace");
0143     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("ba"));
0144     TestPressKey("\\enter");
0145     FinishTest("");
0146     BeginTest("");
0147     TestPressKey("/\\backspace");
0148     QVERIFY(!emulatedCommandBar->isVisible());
0149     FinishTest("");
0150     BeginTest("");
0151     TestPressKey("/a\\backspace");
0152     QVERIFY(emulatedCommandBar->isVisible());
0153     TestPressKey("\\enter");
0154     FinishTest("");
0155     BeginTest("");
0156     TestPressKey("/a\\left\\backspace");
0157     QVERIFY(emulatedCommandBar->isVisible());
0158     TestPressKey("\\enter");
0159     FinishTest("");
0160 
0161     // Check ctrl-b works.
0162     BeginTest("");
0163     TestPressKey("/bar foo xyz\\ctrl-bX");
0164     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("Xbar foo xyz"));
0165     TestPressKey("\\enter");
0166     FinishTest("");
0167 
0168     // Check ctrl-e works.
0169     BeginTest("");
0170     TestPressKey("/bar foo xyz\\ctrl-b\\ctrl-eX");
0171     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("bar foo xyzX"));
0172     TestPressKey("\\enter");
0173     FinishTest("");
0174 
0175     // Check ctrl-w works.
0176     BeginTest("");
0177     TestPressKey("/foo bar\\ctrl-w");
0178     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo "));
0179     TestPressKey("\\enter");
0180     FinishTest("");
0181 
0182     // Check ctrl-w works on empty command bar.
0183     BeginTest("");
0184     TestPressKey("/\\ctrl-w");
0185     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(""));
0186     TestPressKey("\\enter");
0187     FinishTest("");
0188 
0189     // Check ctrl-w works in middle of word.
0190     BeginTest("");
0191     TestPressKey("/foo bar\\left\\left\\ctrl-w");
0192     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo ar"));
0193     TestPressKey("\\enter");
0194     FinishTest("");
0195 
0196     // Check ctrl-w leaves the cursor in the right place when in the middle of word.
0197     BeginTest("");
0198     TestPressKey("/foo bar\\left\\left\\ctrl-wX");
0199     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo Xar"));
0200     TestPressKey("\\enter");
0201     FinishTest("");
0202 
0203     // Check ctrl-w works when at the beginning of the text.
0204     BeginTest("");
0205     TestPressKey("/foo\\left\\left\\left\\ctrl-w");
0206     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo"));
0207     TestPressKey("\\enter");
0208     FinishTest("");
0209 
0210     // Check ctrl-w works when the character to the left is a space.
0211     BeginTest("");
0212     TestPressKey("/foo bar   \\ctrl-w");
0213     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo "));
0214     TestPressKey("\\enter");
0215     FinishTest("");
0216 
0217     // Check ctrl-w works when all characters to the left of the cursor are spaces.
0218     BeginTest("");
0219     TestPressKey("/   \\ctrl-w");
0220     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(""));
0221     TestPressKey("\\enter");
0222     FinishTest("");
0223 
0224     // Check ctrl-w works when all characters to the left of the cursor are non-spaces.
0225     BeginTest("");
0226     TestPressKey("/foo\\ctrl-w");
0227     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(""));
0228     TestPressKey("\\enter");
0229     FinishTest("");
0230 
0231     // Check ctrl-w does not continue to delete subsequent alphanumerics if the characters to the left of the cursor
0232     // are non-space, non-alphanumerics.
0233     BeginTest("");
0234     TestPressKey("/foo!!!\\ctrl-w");
0235     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo"));
0236     TestPressKey("\\enter");
0237     FinishTest("");
0238     // Check ctrl-w does not continue to delete subsequent alphanumerics if the characters to the left of the cursor
0239     // are non-space, non-alphanumerics.
0240     BeginTest("");
0241     TestPressKey("/foo!!!\\ctrl-w");
0242     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo"));
0243     TestPressKey("\\enter");
0244     FinishTest("");
0245 
0246     // Check ctrl-w deletes underscores and alphanumerics to the left of the cursor, but stops when it reaches a
0247     // character that is none of these.
0248     BeginTest("");
0249     TestPressKey("/foo!!!_d1\\ctrl-w");
0250     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo!!!"));
0251     TestPressKey("\\enter");
0252     FinishTest("");
0253 
0254     // Check ctrl-w doesn't swallow the spaces preceding the block of non-word chars.
0255     BeginTest("");
0256     TestPressKey("/foo !!!\\ctrl-w");
0257     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo "));
0258     TestPressKey("\\enter");
0259     FinishTest("");
0260 
0261     // Check ctrl-w doesn't swallow the spaces preceding the word.
0262     BeginTest("");
0263     TestPressKey("/foo 1d_\\ctrl-w");
0264     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo "));
0265     TestPressKey("\\enter");
0266     FinishTest("");
0267 
0268     // Check there is a "waiting for register" indicator, initially hidden.
0269     BeginTest("");
0270     TestPressKey("/");
0271     QLabel *waitingForRegisterIndicator = emulatedCommandBar->findChild<QLabel *>("waitingforregisterindicator");
0272     QVERIFY(waitingForRegisterIndicator);
0273     QVERIFY(!waitingForRegisterIndicator->isVisible());
0274     QCOMPARE(waitingForRegisterIndicator->text(), QString("\""));
0275     TestPressKey("\\enter");
0276     FinishTest("");
0277 
0278     // Test that ctrl-r causes it to become visible.  It is displayed to the right of the text edit.
0279     BeginTest("");
0280     TestPressKey("/\\ctrl-r");
0281     QVERIFY(waitingForRegisterIndicator->isVisible());
0282     QVERIFY(waitingForRegisterIndicator->x() >= emulatedCommandBarTextEdit()->x() + emulatedCommandBarTextEdit()->width());
0283     TestPressKey("\\ctrl-c");
0284     TestPressKey("\\ctrl-c");
0285     FinishTest("");
0286 
0287     // The first ctrl-c after ctrl-r (when no register entered) hides the waiting for register
0288     // indicator, but not the bar.
0289     BeginTest("");
0290     TestPressKey("/\\ctrl-r");
0291     QVERIFY(waitingForRegisterIndicator->isVisible());
0292     TestPressKey("\\ctrl-c");
0293     QVERIFY(!waitingForRegisterIndicator->isVisible());
0294     QVERIFY(emulatedCommandBar->isVisible());
0295     TestPressKey("\\ctrl-c"); // Dismiss the bar.
0296     FinishTest("");
0297 
0298     // The first ctrl-c after ctrl-r (when no register entered) aborts waiting for register.
0299     BeginTest("foo");
0300     TestPressKey("\"cyiw/\\ctrl-r\\ctrl-ca");
0301     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("a"));
0302     TestPressKey("\\ctrl-c"); // Dismiss the bar.
0303     FinishTest("foo");
0304 
0305     // Same as above, but for ctrl-[ instead of ctrl-c.
0306     BeginTest("");
0307     TestPressKey("/\\ctrl-r");
0308     QVERIFY(waitingForRegisterIndicator->isVisible());
0309     TestPressKey("\\ctrl-[");
0310     QVERIFY(!waitingForRegisterIndicator->isVisible());
0311     QVERIFY(emulatedCommandBar->isVisible());
0312     TestPressKey("\\ctrl-c"); // Dismiss the bar.
0313     FinishTest("");
0314     BeginTest("foo");
0315     TestPressKey("\"cyiw/\\ctrl-r\\ctrl-[a");
0316     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("a"));
0317     TestPressKey("\\ctrl-c"); // Dismiss the bar.
0318     FinishTest("foo");
0319 
0320     // Check ctrl-r works with registers, and hides the "waiting for register" indicator.
0321     BeginTest("xyz");
0322     TestPressKey("\"ayiw/foo\\ctrl-ra");
0323     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("fooxyz"));
0324     QVERIFY(!waitingForRegisterIndicator->isVisible());
0325     TestPressKey("\\enter");
0326     FinishTest("xyz");
0327 
0328     // Check ctrl-r inserts text at the current cursor position.
0329     BeginTest("xyz");
0330     TestPressKey("\"ayiw/foo\\left\\ctrl-ra");
0331     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foxyzo"));
0332     TestPressKey("\\enter");
0333     FinishTest("xyz");
0334 
0335     // Check ctrl-r ctrl-w inserts word under the cursor, and hides the "waiting for register" indicator.
0336     BeginTest("foo bar xyz");
0337     TestPressKey("w/\\left\\ctrl-r\\ctrl-w");
0338     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("bar"));
0339     QVERIFY(!waitingForRegisterIndicator->isVisible());
0340     TestPressKey("\\enter");
0341     FinishTest("foo bar xyz");
0342 
0343     // Check ctrl-r ctrl-w doesn't insert the contents of register w!
0344     BeginTest("foo baz xyz");
0345     TestPressKey("\"wyiww/\\left\\ctrl-r\\ctrl-w");
0346     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("baz"));
0347     TestPressKey("\\enter");
0348     FinishTest("foo baz xyz");
0349 
0350     // Check ctrl-r ctrl-w inserts at the current cursor position.
0351     BeginTest("foo nose xyz");
0352     TestPressKey("w/bar\\left\\ctrl-r\\ctrl-w");
0353     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("banoser"));
0354     TestPressKey("\\enter");
0355     FinishTest("foo nose xyz");
0356 
0357     // Cursor position is at the end of the inserted text after ctrl-r ctrl-w.
0358     BeginTest("foo nose xyz");
0359     TestPressKey("w/bar\\left\\ctrl-r\\ctrl-wX");
0360     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("banoseXr"));
0361     TestPressKey("\\enter");
0362     FinishTest("foo nose xyz");
0363 
0364     // Cursor position is at the end of the inserted register contents after ctrl-r.
0365     BeginTest("xyz");
0366     TestPressKey("\"ayiw/foo\\left\\ctrl-raX");
0367     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foxyzXo"));
0368     TestPressKey("\\enter");
0369     FinishTest("xyz");
0370 
0371     // Insert clipboard contents on ctrl-r +.  We implicitly need to test the ability to handle
0372     // shift key key events when waiting for register (they should be ignored).
0373     BeginTest("xyz");
0374     QApplication::clipboard()->setText("vimodetestclipboardtext");
0375     TestPressKey("/\\ctrl-r");
0376     QKeyEvent *shiftKeyDown = new QKeyEvent(QEvent::KeyPress, Qt::Key_Shift, Qt::NoModifier);
0377     QApplication::postEvent(emulatedCommandBarTextEdit(), shiftKeyDown);
0378     QApplication::sendPostedEvents();
0379     TestPressKey("+");
0380     QKeyEvent *shiftKeyUp = new QKeyEvent(QEvent::KeyPress, Qt::Key_Shift, Qt::NoModifier);
0381     QApplication::postEvent(emulatedCommandBarTextEdit(), shiftKeyUp);
0382     QApplication::sendPostedEvents();
0383     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("vimodetestclipboardtext"));
0384     TestPressKey("\\enter");
0385     FinishTest("xyz");
0386 
0387     // Similarly, test that we can press "ctrl" after ctrl-r without it being taken for a register.
0388     BeginTest("wordundercursor");
0389     TestPressKey("/\\ctrl-r");
0390     QKeyEvent *ctrlKeyDown = new QKeyEvent(QEvent::KeyPress, Qt::Key_Control, Qt::NoModifier);
0391     QApplication::postEvent(emulatedCommandBarTextEdit(), ctrlKeyDown);
0392     QApplication::sendPostedEvents();
0393     QKeyEvent *ctrlKeyUp = new QKeyEvent(QEvent::KeyRelease, Qt::Key_Control, Qt::NoModifier);
0394     QApplication::postEvent(emulatedCommandBarTextEdit(), ctrlKeyUp);
0395     QApplication::sendPostedEvents();
0396     QVERIFY(waitingForRegisterIndicator->isVisible());
0397     TestPressKey("\\ctrl-w");
0398     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("wordundercursor"));
0399     TestPressKey("\\ctrl-c"); // Dismiss the bar.
0400     FinishTest("wordundercursor");
0401 
0402     // Begin tests for ctrl-g, which is almost identical to ctrl-r save that the contents, when added,
0403     // are escaped for searching.
0404     // Normal register contents/ word under cursor are added as normal.
0405     BeginTest("wordinregisterb wordundercursor");
0406     TestPressKey("\"byiw");
0407     TestPressKey("/\\ctrl-g");
0408     QVERIFY(waitingForRegisterIndicator->isVisible());
0409     QVERIFY(waitingForRegisterIndicator->x() >= emulatedCommandBarTextEdit()->x() + emulatedCommandBarTextEdit()->width());
0410     TestPressKey("b");
0411     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("wordinregisterb"));
0412     QVERIFY(!waitingForRegisterIndicator->isVisible());
0413     TestPressKey("\\ctrl-c\\ctrl-cw/\\ctrl-g\\ctrl-w");
0414     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("wordundercursor"));
0415     QVERIFY(!waitingForRegisterIndicator->isVisible());
0416     TestPressKey("\\ctrl-c");
0417     TestPressKey("\\ctrl-c");
0418     FinishTest("wordinregisterb wordundercursor");
0419 
0420     // \'s must be escaped when inserted via ctrl-g.
0421     DoTest("foo a\\b\\\\c\\\\\\d", "wYb/\\ctrl-g0\\enterrX", "foo X\\b\\\\c\\\\\\d");
0422     // $'s must be escaped when inserted via ctrl-g.
0423     DoTest("foo a$b", "wYb/\\ctrl-g0\\enterrX", "foo X$b");
0424     DoTest("foo a$b$c", "wYb/\\ctrl-g0\\enterrX", "foo X$b$c");
0425     DoTest("foo a\\$b\\$c", "wYb/\\ctrl-g0\\enterrX", "foo X\\$b\\$c");
0426     // ^'s must be escaped when inserted via ctrl-g.
0427     DoTest("foo a^b", "wYb/\\ctrl-g0\\enterrX", "foo X^b");
0428     DoTest("foo a^b^c", "wYb/\\ctrl-g0\\enterrX", "foo X^b^c");
0429     DoTest("foo a\\^b\\^c", "wYb/\\ctrl-g0\\enterrX", "foo X\\^b\\^c");
0430     // .'s must be escaped when inserted via ctrl-g.
0431     DoTest("foo axb a.b", "wwYgg/\\ctrl-g0\\enterrX", "foo axb X.b");
0432     DoTest("foo a\\xb Na\\.b", "fNlYgg/\\ctrl-g0\\enterrX", "foo a\\xb NX\\.b");
0433     // *'s must be escaped when inserted via ctrl-g
0434     DoTest("foo axxxxb ax*b", "wwYgg/\\ctrl-g0\\enterrX", "foo axxxxb Xx*b");
0435     DoTest("foo a\\xxxxb Na\\x*X", "fNlYgg/\\ctrl-g0\\enterrX", "foo a\\xxxxb NX\\x*X");
0436     // /'s must be escaped when inserted via ctrl-g.
0437     DoTest("foo a a/b", "wwYgg/\\ctrl-g0\\enterrX", "foo a X/b");
0438     DoTest("foo a a/b/c", "wwYgg/\\ctrl-g0\\enterrX", "foo a X/b/c");
0439     DoTest("foo a a\\/b\\/c", "wwYgg/\\ctrl-g0\\enterrX", "foo a X\\/b\\/c");
0440     // ['s and ]'s must be escaped when inserted via ctrl-g.
0441     DoTest("foo axb a[xyz]b", "wwYgg/\\ctrl-g0\\enterrX", "foo axb X[xyz]b");
0442     DoTest("foo a[b", "wYb/\\ctrl-g0\\enterrX", "foo X[b");
0443     DoTest("foo a[b[c", "wYb/\\ctrl-g0\\enterrX", "foo X[b[c");
0444     DoTest("foo a\\[b\\[c", "wYb/\\ctrl-g0\\enterrX", "foo X\\[b\\[c");
0445     DoTest("foo a]b", "wYb/\\ctrl-g0\\enterrX", "foo X]b");
0446     DoTest("foo a]b]c", "wYb/\\ctrl-g0\\enterrX", "foo X]b]c");
0447     DoTest("foo a\\]b\\]c", "wYb/\\ctrl-g0\\enterrX", "foo X\\]b\\]c");
0448     // Test that expressions involving {'s and }'s work when inserted via ctrl-g.
0449     DoTest("foo {", "wYgg/\\ctrl-g0\\enterrX", "foo X");
0450     DoTest("foo }", "wYgg/\\ctrl-g0\\enterrX", "foo X");
0451     DoTest("foo aaaaa \\aaaaa a\\{5}", "WWWYgg/\\ctrl-g0\\enterrX", "foo aaaaa \\aaaaa X\\{5}");
0452     DoTest("foo }", "wYgg/\\ctrl-g0\\enterrX", "foo X");
0453     // Transform newlines into "\\n" when inserted via ctrl-g.
0454     DoTest(" \nfoo\nfoo\nxyz\nbar\n123", "jjvjjllygg/\\ctrl-g0\\enterrX", " \nfoo\nXoo\nxyz\nbar\n123");
0455     DoTest(" \nfoo\nfoo\nxyz\nbar\n123", "jjvjjllygg/\\ctrl-g0/e\\enterrX", " \nfoo\nfoo\nxyz\nbaX\n123");
0456     // Don't do any escaping for ctrl-r, though.
0457     BeginTest("foo .*$^\\/");
0458     TestPressKey("wY/\\ctrl-r0");
0459     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(".*$^\\/"));
0460     TestPressKey("\\ctrl-c");
0461     TestPressKey("\\ctrl-c");
0462     FinishTest("foo .*$^\\/");
0463     // Ensure that the flag that says "next register insertion should be escaped for searching"
0464     // is cleared if we do ctrl-g but then abort with ctrl-c.
0465     DoTest("foo a$b", "/\\ctrl-g\\ctrl-c\\ctrl-cwYgg/\\ctrl-r0\\enterrX", "Xoo a$b");
0466 
0467     // Ensure that we actually perform a search while typing.
0468     BeginTest("abcd");
0469     TestPressKey("/c");
0470     verifyCursorAt(Cursor(0, 2));
0471     TestPressKey("\\enter");
0472     FinishTest("abcd");
0473 
0474     // Ensure that the search is from the cursor.
0475     BeginTest("acbcd");
0476     TestPressKey("ll/c");
0477     verifyCursorAt(Cursor(0, 3));
0478     TestPressKey("\\enter");
0479     FinishTest("acbcd");
0480 
0481     // Reset the cursor to the original position on Ctrl-C
0482     BeginTest("acbcd");
0483     TestPressKey("ll/c\\ctrl-crX");
0484     FinishTest("acXcd");
0485 
0486     // Reset the cursor to the original position on Ctrl-[
0487     BeginTest("acbcd");
0488     TestPressKey("ll/c\\ctrl-[rX");
0489     FinishTest("acXcd");
0490 
0491     // Reset the cursor to the original position on ESC
0492     BeginTest("acbcd");
0493     TestPressKey("ll/c\\escrX");
0494     FinishTest("acXcd");
0495 
0496     // *Do not* reset the cursor to the original position on Enter.
0497     BeginTest("acbcd");
0498     TestPressKey("ll/c\\enterrX");
0499     FinishTest("acbXd");
0500 
0501     // *Do not* reset the cursor to the original position on Return.
0502     BeginTest("acbcd");
0503     TestPressKey("ll/c\\returnrX");
0504     FinishTest("acbXd");
0505 
0506     // Should work with mappings.
0507     clearAllMappings();
0508     vi_global->mappings()->add(Mappings::NormalModeMapping, "'testmapping", "/c<enter>rX", Mappings::Recursive);
0509     BeginTest("acbcd");
0510     TestPressKey("'testmapping");
0511     FinishTest("aXbcd");
0512     clearAllMappings();
0513     // Don't send keys that were part of a mapping to the emulated command bar.
0514     vi_global->mappings()->add(Mappings::NormalModeMapping, "H", "/a", Mappings::Recursive);
0515     BeginTest("foo a aH");
0516     TestPressKey("H\\enterrX");
0517     FinishTest("foo X aH");
0518     clearAllMappings();
0519 
0520     // Incremental searching from the original position.
0521     BeginTest("foo bar foop fool food");
0522     TestPressKey("ll/foo");
0523     verifyCursorAt(Cursor(0, 8));
0524     TestPressKey("l");
0525     verifyCursorAt(Cursor(0, 13));
0526     TestPressKey("\\backspace");
0527     verifyCursorAt(Cursor(0, 8));
0528     TestPressKey("\\enter");
0529     FinishTest("foo bar foop fool food");
0530 
0531     // End up back at the start if no match found
0532     BeginTest("foo bar foop fool food");
0533     TestPressKey("ll/fool");
0534     verifyCursorAt(Cursor(0, 13));
0535     TestPressKey("\\backspacex");
0536     verifyCursorAt(Cursor(0, 2));
0537     TestPressKey("\\enter");
0538     FinishTest("foo bar foop fool food");
0539 
0540     // Wrap around if no match found.
0541     BeginTest("afoom bar foop fool food");
0542     TestPressKey("lll/foom");
0543     verifyCursorAt(Cursor(0, 1));
0544     TestPressKey("\\enter");
0545     FinishTest("afoom bar foop fool food");
0546 
0547     // SmartCase: match case-insensitively if the search text is all lower-case.
0548     DoTest("foo BaR", "ll/bar\\enterrX", "foo XaR");
0549 
0550     // SmartCase: match case-sensitively if the search text is mixed case.
0551     DoTest("foo BaR bAr", "ll/bAr\\enterrX", "foo BaR XAr");
0552 
0553     // Assume regex by default.
0554     DoTest("foo bwibblear", "ll/b.*ar\\enterrX", "foo Xwibblear");
0555 
0556     // Set the last search pattern.
0557     DoTest("foo bar", "ll/bar\\enterggnrX", "foo Xar");
0558 
0559     // Make sure the last search pattern is a regex, too.
0560     DoTest("foo bwibblear", "ll/b.*ar\\enterggnrX", "foo Xwibblear");
0561 
0562     // 'n' should search case-insensitively if the original search was case-insensitive.
0563     DoTest("foo bAR", "ll/bar\\enterggnrX", "foo XAR");
0564 
0565     // 'n' should search case-sensitively if the original search was case-sensitive.
0566     DoTest("foo bar bAR", "ll/bAR\\enterggnrX", "foo bar XAR");
0567 
0568     // 'N' should search case-insensitively if the original search was case-insensitive.
0569     DoTest("foo bAR xyz", "ll/bar\\enter$NrX", "foo XAR xyz");
0570 
0571     // 'N' should search case-sensitively if the original search was case-sensitive.
0572     DoTest("foo bAR bar", "ll/bAR\\enter$NrX", "foo XAR bar");
0573 
0574     // Don't forget to set the last search to case-insensitive.
0575     DoTest("foo bAR bar", "ll/bAR\\enter^/bar\\enter^nrX", "foo XAR bar");
0576 
0577     // Usage of \C for manually specifying case sensitivity.
0578     // Strip occurrences of "\C" from the pattern to find.
0579     DoTest("foo bar", "/\\\\Cba\\\\Cr\\enterrX", "foo Xar");
0580     // Be careful about escaping, though!
0581     DoTest("foo \\Cba\\Cr", "/\\\\\\\\Cb\\\\Ca\\\\\\\\C\\\\C\\\\Cr\\enterrX", "foo XCba\\Cr");
0582     // The item added to the search history should contain all the original \C's.
0583     clearSearchHistory();
0584     BeginTest("foo \\Cba\\Cr");
0585     TestPressKey("/\\\\\\\\Cb\\\\Ca\\\\\\\\C\\\\C\\\\Cr\\enterrX");
0586     QCOMPARE(searchHistory().first(), QString("\\\\Cb\\Ca\\\\C\\C\\Cr"));
0587     FinishTest("foo XCba\\Cr");
0588     // If there is an escaped C, assume case sensitivity.
0589     DoTest("foo bAr BAr bar", "/ba\\\\Cr\\enterrX", "foo bAr BAr Xar");
0590     // The last search pattern should be the last search with escaped C's stripped.
0591     DoTest("foo \\Cbar\nfoo \\Cbar", "/\\\\\\\\Cba\\\\C\\\\Cr\\enterggjnrX", "foo \\Cbar\nfoo XCbar");
0592     // If the last search pattern had an escaped "\C", then the next search should be case-sensitive.
0593     DoTest("foo bar\nfoo bAr BAr bar", "/ba\\\\Cr\\enterggjnrX", "foo bar\nfoo bAr BAr Xar");
0594 
0595     // Don't set the last search parameters if we abort, though.
0596     DoTest("foo bar xyz", "/bar\\enter/xyz\\ctrl-cggnrX", "foo Xar xyz");
0597     DoTest("foo bar bAr", "/bar\\enter/bA\\ctrl-cggnrX", "foo Xar bAr");
0598     DoTest("foo bar bar", "/bar\\enter?ba\\ctrl-cggnrX", "foo Xar bar");
0599 
0600     // Don't let ":" trample all over the search parameters, either.
0601     DoTest("foo bar xyz foo", "/bar\\entergg*:yank\\enterggnrX", "foo bar xyz Xoo");
0602 
0603     // Some mirror tests for "?"
0604 
0605     // Test that "?" summons the search bar, with empty text and with the "?" indicator.
0606     QVERIFY(!emulatedCommandBar->isVisible());
0607     BeginTest("");
0608     TestPressKey("?");
0609     QVERIFY(emulatedCommandBar->isVisible());
0610     QCOMPARE(emulatedCommandTypeIndicator()->text(), QString("?"));
0611     QVERIFY(emulatedCommandTypeIndicator()->isVisible());
0612     QVERIFY(emulatedCommandBarTextEdit());
0613     QVERIFY(emulatedCommandBarTextEdit()->text().isEmpty());
0614     TestPressKey("\\enter");
0615     FinishTest("");
0616 
0617     // Search backwards.
0618     DoTest("foo foo bar foo foo", "ww?foo\\enterrX", "foo Xoo bar foo foo");
0619 
0620     // Reset cursor if we find nothing.
0621     BeginTest("foo foo bar foo foo");
0622     TestPressKey("ww?foo");
0623     verifyCursorAt(Cursor(0, 4));
0624     TestPressKey("d");
0625     verifyCursorAt(Cursor(0, 8));
0626     TestPressKey("\\enter");
0627     FinishTest("foo foo bar foo foo");
0628 
0629     // Wrap to the end if we find nothing.
0630     DoTest("foo foo bar xyz xyz", "ww?xyz\\enterrX", "foo foo bar xyz Xyz");
0631 
0632     // Specify that the last was backwards when using '?'
0633     DoTest("foo foo bar foo foo", "ww?foo\\enter^wwnrX", "foo Xoo bar foo foo");
0634 
0635     // ... and make sure we do  the equivalent with "/"
0636     BeginTest("foo foo bar foo foo");
0637     TestPressKey("ww?foo\\enter^ww/foo");
0638     QCOMPARE(emulatedCommandTypeIndicator()->text(), QString("/"));
0639     TestPressKey("\\enter^wwnrX");
0640     FinishTest("foo foo bar Xoo foo");
0641 
0642     // If we are at the beginning of a word, that word is not the first match in a search
0643     // for that word.
0644     DoTest("foo foo foo", "w/foo\\enterrX", "foo foo Xoo");
0645     DoTest("foo foo foo", "w?foo\\enterrX", "Xoo foo foo");
0646     // When searching backwards, ensure we can find a match whose range includes the starting cursor position,
0647     // if we allow it to wrap around.
0648     DoTest("foo foofoofoo bar", "wlll?foofoofoo\\enterrX", "foo Xoofoofoo bar");
0649     // When searching backwards, ensure we can find a match whose range includes the starting cursor position,
0650     // even if we don't allow it to wrap around.
0651     DoTest("foo foofoofoo foofoofoo", "wlll?foofoofoo\\enterrX", "foo Xoofoofoo foofoofoo");
0652     // The same, but where we the match ends at the end of the line or document.
0653     DoTest("foo foofoofoo\nfoofoofoo", "wlll?foofoofoo\\enterrX", "foo Xoofoofoo\nfoofoofoo");
0654     DoTest("foo foofoofoo", "wlll?foofoofoo\\enterrX", "foo Xoofoofoo");
0655 
0656     // Searching forwards for just "/" repeats last search.
0657     DoTest("foo bar", "/bar\\entergg//\\enterrX", "foo Xar");
0658     // The "last search" can be one initiated via e.g. "*".
0659     DoTest("foo bar foo", "/bar\\entergg*gg//\\enterrX", "foo bar Xoo");
0660     // Searching backwards for just "?" repeats last search.
0661     DoTest("foo bar bar", "/bar\\entergg??\\enterrX", "foo bar Xar");
0662     // Search forwards treats "?" as a literal.
0663     DoTest("foo ?ba?r", "/?ba?r\\enterrX", "foo Xba?r");
0664     // As always, be careful with escaping!
0665     DoTest("foo ?ba\\?r", "/?ba\\\\\\\\\\\\?r\\enterrX", "foo Xba\\?r");
0666     // Searching forwards for just "?" finds literal question marks.
0667     DoTest("foo ??", "/?\\enterrX", "foo X?");
0668     // Searching backwards for just "/" finds literal forward slashes.
0669     DoTest("foo //", "?/\\enterrX", "foo /X");
0670     // Searching forwards, stuff after (and including) an unescaped "/" is ignored.
0671     DoTest("foo ba bar bar/xyz", "/bar/xyz\\enterrX", "foo ba Xar bar/xyz");
0672     // Needs to be unescaped, though!
0673     DoTest("foo bar bar/xyz", "/bar\\\\/xyz\\enterrX", "foo bar Xar/xyz");
0674     DoTest("foo bar bar\\/xyz", "/bar\\\\\\\\/xyz\\enterrX", "foo bar Xar\\/xyz");
0675     // Searching backwards, stuff after (and including) an unescaped "?" is ignored.
0676     DoTest("foo bar bar?xyz bar ba", "?bar?xyz\\enterrX", "foo bar bar?xyz Xar ba");
0677     // Needs to be unescaped, though!
0678     DoTest("foo bar bar?xyz bar ba", "?bar\\\\?xyz\\enterrX", "foo bar Xar?xyz bar ba");
0679     DoTest("foo bar bar\\?xyz bar ba", "?bar\\\\\\\\?xyz\\enterrX", "foo bar Xar\\?xyz bar ba");
0680     // If, in a forward search, the first character after the first unescaped "/" is an e, then
0681     // we place the cursor at the end of the word.
0682     DoTest("foo ba bar bar/eyz", "/bar/e\\enterrX", "foo ba baX bar/eyz");
0683     // Needs to be unescaped, though!
0684     DoTest("foo bar bar/eyz", "/bar\\\\/e\\enterrX", "foo bar Xar/eyz");
0685     DoTest("foo bar bar\\/xyz", "/bar\\\\\\\\/e\\enterrX", "foo bar barX/xyz");
0686     // If, in a backward search, the first character after the first unescaped "?" is an e, then
0687     // we place the cursor at the end of the word.
0688     DoTest("foo bar bar?eyz bar ba", "?bar?e\\enterrX", "foo bar bar?eyz baX ba");
0689     // Needs to be unescaped, though!
0690     DoTest("foo bar bar?eyz bar ba", "?bar\\\\?e\\enterrX", "foo bar Xar?eyz bar ba");
0691     DoTest("foo bar bar\\?eyz bar ba", "?bar\\\\\\\\?e\\enterrX", "foo bar barX?eyz bar ba");
0692     // Quick check that repeating the last search and placing the cursor at the end of the match works.
0693     DoTest("foo bar bar", "/bar\\entergg//e\\enterrX", "foo baX bar");
0694     DoTest("foo bar bar", "?bar\\entergg??e\\enterrX", "foo bar baX");
0695     // When repeating a change, don't try to convert from Vim to Qt regex again.
0696     DoTest("foo bar()", "/bar()\\entergg//e\\enterrX", "foo bar(X");
0697     DoTest("foo bar()", "?bar()\\entergg??e\\enterrX", "foo bar(X");
0698     // If the last search said that we should place the cursor at the end of the match, then
0699     // do this with n & N.
0700     DoTest("foo bar bar foo", "/bar/e\\enterggnrX", "foo baX bar foo");
0701     DoTest("foo bar bar foo", "/bar/e\\enterggNrX", "foo bar baX foo");
0702     // Don't do this if that search was aborted, though.
0703     DoTest("foo bar bar foo", "/bar\\enter/bar/e\\ctrl-cggnrX", "foo Xar bar foo");
0704     DoTest("foo bar bar foo", "/bar\\enter/bar/e\\ctrl-cggNrX", "foo bar Xar foo");
0705     // "#" and "*" reset the "place cursor at the end of the match" to false.
0706     DoTest("foo bar bar foo", "/bar/e\\enterggw*nrX", "foo Xar bar foo");
0707     DoTest("foo bar bar foo", "/bar/e\\enterggw#nrX", "foo Xar bar foo");
0708 
0709     // "/" and "?" should be usable as motions.
0710     DoTest("foo bar", "ld/bar\\enter", "fbar");
0711     // They are not linewise.
0712     DoTest("foo bar\nxyz", "ld/yz\\enter", "fyz");
0713     DoTest("foo bar\nxyz", "jld?oo\\enter", "fyz");
0714     // Should be usable in Visual Mode without aborting Visual Mode.
0715     DoTest("foo bar", "lv/bar\\enterd", "far");
0716     // Same for ?.
0717     DoTest("foo bar", "$hd?oo\\enter", "far");
0718     DoTest("foo bar", "$hv?oo\\enterd", "fr");
0719     DoTest("foo bar", "lv?bar\\enterd", "far");
0720     // If we abort the "/" / "?" motion, the command should be aborted, too.
0721     DoTest("foo bar", "d/bar\\esc", "foo bar");
0722     DoTest("foo bar", "d/bar\\ctrl-c", "foo bar");
0723     DoTest("foo bar", "d/bar\\ctrl-[", "foo bar");
0724     // We should be able to repeat a command using "/" or "?" as the motion.
0725     DoTest("foo bar bar bar", "d/bar\\enter.", "bar bar");
0726     // The "synthetic" Enter keypress should not be logged as part of the command to be repeated.
0727     DoTest("foo bar bar bar\nxyz", "d/bar\\enter.rX", "Xar bar\nxyz");
0728     // Counting.
0729     DoTest("foo bar bar bar", "2/bar\\enterrX", "foo bar Xar bar");
0730     // Counting with wraparound.
0731     DoTest("foo bar bar bar", "4/bar\\enterrX", "foo Xar bar bar");
0732     // Counting in Visual Mode.
0733     DoTest("foo bar bar bar", "v2/bar\\enterd", "ar bar");
0734     // Should update the selection in Visual Mode as we search.
0735     BeginTest("foo bar bbc");
0736     TestPressKey("vl/b");
0737     QCOMPARE(kate_view->selectionText(), QString("foo b"));
0738     TestPressKey("b");
0739     QCOMPARE(kate_view->selectionText(), QString("foo bar b"));
0740     TestPressKey("\\ctrl-h");
0741     QCOMPARE(kate_view->selectionText(), QString("foo b"));
0742     TestPressKey("notexists");
0743     QCOMPARE(kate_view->selectionText(), QString("fo"));
0744     TestPressKey("\\enter"); // Dismiss bar.
0745     QCOMPARE(kate_view->selectionText(), QString("fo"));
0746     FinishTest("foo bar bbc");
0747     BeginTest("foo\nxyz\nbar\nbbc");
0748     TestPressKey("Vj/b");
0749     QCOMPARE(kate_view->selectionText(), QString("foo\nxyz\nbar"));
0750     TestPressKey("b");
0751     QCOMPARE(kate_view->selectionText(), QString("foo\nxyz\nbar\nbbc"));
0752     TestPressKey("\\ctrl-h");
0753     QCOMPARE(kate_view->selectionText(), QString("foo\nxyz\nbar"));
0754     TestPressKey("notexists");
0755     QCOMPARE(kate_view->selectionText(), QString("foo\nxyz"));
0756     TestPressKey("\\ctrl-c"); // Dismiss bar.
0757     FinishTest("foo\nxyz\nbar\nbbc");
0758     // Dismissing the search bar in visual mode should leave original selection.
0759     BeginTest("foo bar bbc");
0760     TestPressKey("vl/\\ctrl-c");
0761     QCOMPARE(kate_view->selectionText(), QString("fo"));
0762     FinishTest("foo bar bbc");
0763     BeginTest("foo bar bbc");
0764     TestPressKey("vl?\\ctrl-c");
0765     QCOMPARE(kate_view->selectionText(), QString("fo"));
0766     FinishTest("foo bar bbc");
0767     BeginTest("foo bar bbc");
0768     TestPressKey("vl/b\\ctrl-c");
0769     QCOMPARE(kate_view->selectionText(), QString("fo"));
0770     FinishTest("foo bar bbc");
0771     BeginTest("foo\nbar\nbbc");
0772     TestPressKey("Vl/b\\ctrl-c");
0773     QCOMPARE(kate_view->selectionText(), QString("foo"));
0774     FinishTest("foo\nbar\nbbc");
0775 
0776     // Search-highlighting tests.
0777     const QColor searchHighlightColour = kate_view->renderer()->config()->searchHighlightColor();
0778     BeginTest("foo bar xyz");
0779     // Sanity test.
0780     const QVector<Kate::TextRange *> rangesInitial = rangesOnFirstLine();
0781     Q_ASSERT(rangesInitial.isEmpty() && "Assumptions about ranges are wrong - this test is invalid and may need updating!");
0782     FinishTest("foo bar xyz");
0783 
0784     // Test highlighting single character match.
0785     BeginTest("foo bar xyz");
0786     TestPressKey("/b");
0787     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
0788     QCOMPARE(rangesOnFirstLine().first()->attribute()->background().color(), searchHighlightColour);
0789     QCOMPARE(rangesOnFirstLine().first()->start().line(), 0);
0790     QCOMPARE(rangesOnFirstLine().first()->start().column(), 4);
0791     QCOMPARE(rangesOnFirstLine().first()->end().line(), 0);
0792     QCOMPARE(rangesOnFirstLine().first()->end().column(), 5);
0793     TestPressKey("\\enter");
0794     FinishTest("foo bar xyz");
0795 
0796     // Test highlighting two character match.
0797     BeginTest("foo bar xyz");
0798     TestPressKey("/ba");
0799     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
0800     QCOMPARE(rangesOnFirstLine().first()->start().line(), 0);
0801     QCOMPARE(rangesOnFirstLine().first()->start().column(), 4);
0802     QCOMPARE(rangesOnFirstLine().first()->end().line(), 0);
0803     QCOMPARE(rangesOnFirstLine().first()->end().column(), 6);
0804     TestPressKey("\\enter");
0805     FinishTest("foo bar xyz");
0806 
0807     // Test no highlighting if no longer a match.
0808     BeginTest("foo bar xyz");
0809     TestPressKey("/baz");
0810     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
0811     TestPressKey("\\enter");
0812     FinishTest("foo bar xyz");
0813 
0814     // Test highlighting on wraparound.
0815     BeginTest(" foo bar xyz");
0816     TestPressKey("ww/foo");
0817     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
0818     QCOMPARE(rangesOnFirstLine().first()->start().line(), 0);
0819     QCOMPARE(rangesOnFirstLine().first()->start().column(), 1);
0820     QCOMPARE(rangesOnFirstLine().first()->end().line(), 0);
0821     QCOMPARE(rangesOnFirstLine().first()->end().column(), 4);
0822     TestPressKey("\\enter");
0823     FinishTest(" foo bar xyz");
0824 
0825     // Test highlighting backwards
0826     BeginTest("foo bar xyz");
0827     TestPressKey("$?ba");
0828     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
0829     QCOMPARE(rangesOnFirstLine().first()->start().line(), 0);
0830     QCOMPARE(rangesOnFirstLine().first()->start().column(), 4);
0831     QCOMPARE(rangesOnFirstLine().first()->end().line(), 0);
0832     QCOMPARE(rangesOnFirstLine().first()->end().column(), 6);
0833     TestPressKey("\\enter");
0834     FinishTest("foo bar xyz");
0835 
0836     // Test no highlighting when no match is found searching backwards
0837     BeginTest("foo bar xyz");
0838     TestPressKey("$?baz");
0839     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
0840     TestPressKey("\\enter");
0841     FinishTest("foo bar xyz");
0842 
0843     // Test highlight when wrapping around after searching backwards.
0844     BeginTest("foo bar xyz");
0845     TestPressKey("w?xyz");
0846     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
0847     QCOMPARE(rangesOnFirstLine().first()->start().line(), 0);
0848     QCOMPARE(rangesOnFirstLine().first()->start().column(), 8);
0849     QCOMPARE(rangesOnFirstLine().first()->end().line(), 0);
0850     QCOMPARE(rangesOnFirstLine().first()->end().column(), 11);
0851     TestPressKey("\\enter");
0852     FinishTest("foo bar xyz");
0853 
0854     // Test no highlighting when bar is dismissed.
0855     DoTest("foo bar xyz", "/bar\\ctrl-c", "foo bar xyz");
0856     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
0857     DoTest("foo bar xyz", ":set-nohls\\enter/bar\\enter", "foo bar xyz");
0858     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
0859     DoTest("foo bar xyz", "/bar\\ctrl-[", "foo bar xyz");
0860     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
0861     DoTest("foo bar xyz", ":set-nohls\\enter/bar\\return", "foo bar xyz");
0862     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
0863     DoTest("foo bar xyz", "/bar\\esc", "foo bar xyz");
0864     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
0865 
0866     // Update colour on config change.
0867     BeginTest("foo bar xyz");
0868     TestPressKey("/xyz");
0869     const QColor newSearchHighlightColour = QColor(255, 0, 0);
0870     kate_view->renderer()->config()->setSearchHighlightColor(newSearchHighlightColour);
0871     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
0872     QCOMPARE(rangesOnFirstLine().first()->attribute()->background().color(), newSearchHighlightColour);
0873     TestPressKey("\\enter");
0874     FinishTest("foo bar xyz");
0875 
0876     // Set the background colour appropriately.
0877     KColorScheme currentColorScheme(QPalette::Normal);
0878     const QColor normalBackgroundColour = QPalette().brush(QPalette::Base).color();
0879     const QColor matchBackgroundColour = currentColorScheme.background(KColorScheme::PositiveBackground).color();
0880     const QColor noMatchBackgroundColour = currentColorScheme.background(KColorScheme::NegativeBackground).color();
0881     BeginTest("foo bar xyz");
0882     TestPressKey("/xyz");
0883     verifyTextEditBackgroundColour(matchBackgroundColour);
0884     TestPressKey("a");
0885     verifyTextEditBackgroundColour(noMatchBackgroundColour);
0886     TestPressKey("\\ctrl-w");
0887     verifyTextEditBackgroundColour(normalBackgroundColour);
0888     TestPressKey("/xyz\\enter/");
0889     verifyTextEditBackgroundColour(normalBackgroundColour);
0890     TestPressKey("\\enter");
0891     FinishTest("foo bar xyz");
0892 
0893     // Escape regex's in a Vim-ish style.
0894     // Unescaped ( and ) are always literals.
0895     DoTest("foo bar( xyz", "/bar(\\enterrX", "foo Xar( xyz");
0896     DoTest("foo bar) xyz", "/bar)\\enterrX", "foo Xar) xyz");
0897     // + is literal, unless it is already escaped.
0898     DoTest("foo bar+ xyz", "/bar+ \\enterrX", "foo Xar+ xyz");
0899     DoTest("  foo+AAAAbar", "/foo+A\\\\+bar\\enterrX", "  Xoo+AAAAbar");
0900     DoTest("  foo++++bar", "/foo+\\\\+bar\\enterrX", "  Xoo++++bar");
0901     DoTest("  foo++++bar", "/+\\enterrX", "  fooX+++bar");
0902     // An escaped "\" is a literal, of course.
0903     DoTest("foo x\\y", "/x\\\\\\\\y\\enterrX", "foo X\\y");
0904     // ( and ), if escaped, are not literals.
0905     DoTest("foo  barbarxyz", "/ \\\\(bar\\\\)\\\\+xyz\\enterrX", "foo Xbarbarxyz");
0906     // Handle escaping correctly if we have an escaped and unescaped bracket next to each other.
0907     DoTest("foo  x(A)y", "/x(\\\\(.\\\\))y\\enterrX", "foo  X(A)y");
0908     // |, if unescaped, is literal.
0909     DoTest("foo |bar", "/|\\enterrX", "foo Xbar");
0910     // |, if escaped, is not a literal.
0911     DoTest("foo xfoo\\y xbary", "/x\\\\(foo\\\\|bar\\\\)y\\enterrX", "foo xfoo\\y Xbary");
0912     // A single [ is a literal.
0913     DoTest("foo bar[", "/bar[\\enterrX", "foo Xar[");
0914     // A single ] is a literal.
0915     DoTest("foo bar]", "/bar]\\enterrX", "foo Xar]");
0916     // A matching [ and ] are *not* literals.
0917     DoTest("foo xbcay", "/x[abc]\\\\+y\\enterrX", "foo Xbcay");
0918     DoTest("foo xbcay", "/[abc]\\\\+y\\enterrX", "foo xXcay");
0919     DoTest("foo xbaadcdcy", "/x[ab]\\\\+[cd]\\\\+y\\enterrX", "foo Xbaadcdcy");
0920     // Need to be an unescaped match, though.
0921     DoTest("foo xbcay", "/x[abc\\\\]\\\\+y\\enterrX", "Xoo xbcay");
0922     DoTest("foo xbcay", "/x\\\\[abc]\\\\+y\\enterrX", "Xoo xbcay");
0923     DoTest("foo x[abc]]]]]y", "/x\\\\[abc]\\\\+y\\enterrX", "foo X[abc]]]]]y");
0924     // An escaped '[' between matching unescaped '[' and ']' is treated as a literal '['
0925     DoTest("foo xb[cay", "/x[a\\\\[bc]\\\\+y\\enterrX", "foo Xb[cay");
0926     // An escaped ']' between matching unescaped '[' and ']' is treated as a literal ']'
0927     DoTest("foo xb]cay", "/x[a\\\\]bc]\\\\+y\\enterrX", "foo Xb]cay");
0928     // An escaped '[' not between other square brackets is a literal.
0929     DoTest("foo xb[cay", "/xb\\\\[\\enterrX", "foo Xb[cay");
0930     DoTest("foo xb[cay", "/\\\\[ca\\enterrX", "foo xbXcay");
0931     // An escaped ']' not between other square brackets is a literal.
0932     DoTest("foo xb]cay", "/xb\\\\]\\enterrX", "foo Xb]cay");
0933     DoTest("foo xb]cay", "/\\\\]ca\\enterrX", "foo xbXcay");
0934     // An unescaped '[' not between other square brackets is a literal.
0935     DoTest("foo xbaba[y", "/x[ab]\\\\+[y\\enterrX", "foo Xbaba[y");
0936     DoTest("foo xbaba[dcdcy", "/x[ab]\\\\+[[cd]\\\\+y\\enterrX", "foo Xbaba[dcdcy");
0937     // An unescaped ']' not between other square brackets is a literal.
0938     DoTest("foo xbaba]y", "/x[ab]\\\\+]y\\enterrX", "foo Xbaba]y");
0939     DoTest("foo xbaba]dcdcy", "/x[ab]\\\\+][cd]\\\\+y\\enterrX", "foo Xbaba]dcdcy");
0940     // Be more clever about how we identify escaping: the presence of a preceding
0941     // backslash is not always sufficient!
0942     DoTest("foo x\\babay", "/x\\\\\\\\[ab]\\\\+y\\enterrX", "foo X\\babay");
0943     DoTest("foo x\\[abc]]]]y", "/x\\\\\\\\\\\\[abc]\\\\+y\\enterrX", "foo X\\[abc]]]]y");
0944     DoTest("foo xa\\b\\c\\y", "/x[abc\\\\\\\\]\\\\+y\\enterrX", "foo Xa\\b\\c\\y");
0945     DoTest("foo x[abc\\]]]]y", "/x[abc\\\\\\\\\\\\]\\\\+y\\enterrX", "foo X[abc\\]]]]y");
0946     DoTest("foo xa[\\b\\[y", "/x[ab\\\\\\\\[]\\\\+y\\enterrX", "foo Xa[\\b\\[y");
0947     DoTest("foo x\\[y", "/x\\\\\\\\[y\\enterrX", "foo X\\[y");
0948     DoTest("foo x\\]y", "/x\\\\\\\\]y\\enterrX", "foo X\\]y");
0949     DoTest("foo x\\+y", "/x\\\\\\\\+y\\enterrX", "foo X\\+y");
0950     // A dot is not a literal, nor is a star.
0951     DoTest("foo bar", "/o.*b\\enterrX", "fXo bar");
0952     // Escaped dots and stars are literals, though.
0953     DoTest("foo xay x.y", "/x\\\\.y\\enterrX", "foo xay X.y");
0954     DoTest("foo xaaaay xa*y", "/xa\\\\*y\\enterrX", "foo xaaaay Xa*y");
0955     // Unescaped curly braces are literals.
0956     DoTest("foo x{}y", "/x{}y\\enterrX", "foo X{}y");
0957     // Escaped curly brackets are quantifers.
0958     DoTest("foo xaaaaay", "/xa\\\\{5\\\\}y\\enterrX", "foo Xaaaaay");
0959     // Matching curly brackets where only the first is escaped are also quantifiers.
0960     DoTest("foo xaaaaaybbbz", "/xa\\\\{5}yb\\\\{3}z\\enterrX", "foo Xaaaaaybbbz");
0961     // Make sure it really is escaped, though!
0962     DoTest("foo xa\\{5}", "/xa\\\\\\\\{5}\\enterrX", "foo Xa\\{5}");
0963     // Don't crash if the first character is a }
0964     DoTest("foo aaaaay", "/{\\enterrX", "Xoo aaaaay");
0965     // Vim's '\<' and '\>' map, roughly, to Qt's '\b'
0966     DoTest("foo xbar barx bar", "/bar\\\\>\\enterrX", "foo xXar barx bar");
0967     DoTest("foo xbar barx bar", "/\\\\<bar\\enterrX", "foo xbar Xarx bar");
0968     DoTest("foo xbar barx bar ", "/\\\\<bar\\\\>\\enterrX", "foo xbar barx Xar ");
0969     DoTest("foo xbar barx bar", "/\\\\<bar\\\\>\\enterrX", "foo xbar barx Xar");
0970     DoTest("foo xbar barx\nbar", "/\\\\<bar\\\\>\\enterrX", "foo xbar barx\nXar");
0971     // Escaped "^" and "$" are treated as literals.
0972     DoTest("foo x^$y", "/x\\\\^\\\\$y\\enterrX", "foo X^$y");
0973     // Ensure that it is the escaped version of the pattern that is recorded as the last search pattern.
0974     DoTest("foo bar( xyz", "/bar(\\enterggnrX", "foo Xar( xyz");
0975 
0976     // Don't log keypresses sent to the emulated command bar as commands to be repeated via "."!
0977     DoTest("foo", "/diw\\enterciwbar\\ctrl-c.", "bar");
0978 
0979     // Don't leave Visual mode on aborting a search.
0980     DoTest("foo bar", "vw/\\ctrl-cd", "ar");
0981     DoTest("foo bar", "vw/\\ctrl-[d", "ar");
0982 
0983     // Don't crash on leaving Visual Mode on aborting a search. This is perhaps the most opaque regression
0984     // test ever; what it's testing for is the situation where the synthetic keypress issue by the emulated
0985     // command bar on the "ctrl-[" is sent to the key mapper.  This in turn converts it into a weird character
0986     // which is then, upon not being recognised as part of a mapping, sent back around the keypress processing,
0987     // where it ends up being sent to the emulated command bar's text edit, which in turn issues a "text changed"
0988     // event where the text is still empty, which tries to move the cursor to (-1, -1), which causes a crash deep
0989     // within Kate. So, in a nutshell: this test ensures that the keymapper never handles the synthetic keypress :)
0990     DoTest("", "ifoo\\ctrl-cv/\\ctrl-[", "foo");
0991 
0992     // History auto-completion tests.
0993     clearSearchHistory();
0994     QVERIFY(searchHistory().isEmpty());
0995     vi_global->searchHistory()->append("foo");
0996     vi_global->searchHistory()->append("bar");
0997     QCOMPARE(searchHistory(),
0998              QStringList() << "foo"
0999                            << "bar");
1000     clearSearchHistory();
1001     QVERIFY(searchHistory().isEmpty());
1002 
1003     // Ensure current search bar text is added to the history if we press enter.
1004     DoTest("foo bar", "/bar\\enter", "foo bar");
1005     DoTest("foo bar", "/xyz\\enter", "foo bar");
1006     QCOMPARE(searchHistory(),
1007              QStringList() << "bar"
1008                            << "xyz");
1009     // Interesting - Vim adds the search bar text to the history even if we abort via e.g. ctrl-c, ctrl-[, etc.
1010     clearSearchHistory();
1011     DoTest("foo bar", "/baz\\ctrl-[", "foo bar");
1012     QCOMPARE(searchHistory(), QStringList() << "baz");
1013     clearSearchHistory();
1014     DoTest("foo bar", "/foo\\esc", "foo bar");
1015     QCOMPARE(searchHistory(), QStringList() << "foo");
1016     clearSearchHistory();
1017     DoTest("foo bar", "/nose\\ctrl-c", "foo bar");
1018     QCOMPARE(searchHistory(), QStringList() << "nose");
1019 
1020     clearSearchHistory();
1021     vi_global->searchHistory()->append("foo");
1022     vi_global->searchHistory()->append("bar");
1023     QVERIFY(emulatedCommandBarCompleter() != nullptr);
1024     BeginTest("foo bar");
1025     TestPressKey("/\\ctrl-p");
1026     verifyCommandBarCompletionVisible();
1027     // Make sure the completion appears in roughly the correct place: this is a little fragile :/
1028     const QPoint completerRectTopLeft = emulatedCommandBarCompleter()->popup()->mapToGlobal(emulatedCommandBarCompleter()->popup()->rect().topLeft());
1029     const QPoint barEditBottomLeft = emulatedCommandBarTextEdit()->mapToGlobal(emulatedCommandBarTextEdit()->rect().bottomLeft());
1030     QCOMPARE(completerRectTopLeft.x(), barEditBottomLeft.x());
1031     QVERIFY(qAbs(completerRectTopLeft.y() - barEditBottomLeft.y()) <= 1);
1032     // Will activate the current completion item, activating the search, and dismissing the bar.
1033     TestPressKey("\\enter");
1034     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
1035     // Close the command bar.
1036     FinishTest("foo bar");
1037 
1038     // Don't show completion with an empty search bar.
1039     clearSearchHistory();
1040     vi_global->searchHistory()->append("foo");
1041     BeginTest("foo bar");
1042     TestPressKey("/");
1043     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
1044     TestPressKey("\\enter");
1045     FinishTest("foo bar");
1046 
1047     // Don't auto-complete, either.
1048     clearSearchHistory();
1049     vi_global->searchHistory()->append("foo");
1050     BeginTest("foo bar");
1051     TestPressKey("/f");
1052     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
1053     TestPressKey("\\enter");
1054     FinishTest("foo bar");
1055 
1056     clearSearchHistory();
1057     vi_global->searchHistory()->append("xyz");
1058     vi_global->searchHistory()->append("bar");
1059     QVERIFY(emulatedCommandBarCompleter() != nullptr);
1060     BeginTest("foo bar");
1061     TestPressKey("/\\ctrl-p");
1062     QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("bar"));
1063     TestPressKey("\\enter"); // Dismiss bar.
1064     FinishTest("foo bar");
1065 
1066     clearSearchHistory();
1067     vi_global->searchHistory()->append("xyz");
1068     vi_global->searchHistory()->append("bar");
1069     vi_global->searchHistory()->append("foo");
1070     QVERIFY(emulatedCommandBarCompleter() != nullptr);
1071     BeginTest("foo bar");
1072     TestPressKey("/\\ctrl-p");
1073     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo"));
1074     QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("foo"));
1075     QCOMPARE(emulatedCommandBarCompleter()->popup()->currentIndex().row(), 0);
1076     TestPressKey("\\ctrl-p");
1077     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("bar"));
1078     QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("bar"));
1079     QCOMPARE(emulatedCommandBarCompleter()->popup()->currentIndex().row(), 1);
1080     TestPressKey("\\ctrl-p");
1081     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("xyz"));
1082     QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("xyz"));
1083     QCOMPARE(emulatedCommandBarCompleter()->popup()->currentIndex().row(), 2);
1084     TestPressKey("\\ctrl-p");
1085     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo"));
1086     QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("foo")); // Wrap-around
1087     QCOMPARE(emulatedCommandBarCompleter()->popup()->currentIndex().row(), 0);
1088     TestPressKey("\\enter"); // Dismiss bar.
1089     FinishTest("foo bar");
1090 
1091     clearSearchHistory();
1092     vi_global->searchHistory()->append("xyz");
1093     vi_global->searchHistory()->append("bar");
1094     vi_global->searchHistory()->append("foo");
1095     QVERIFY(emulatedCommandBarCompleter() != nullptr);
1096     BeginTest("foo bar");
1097     TestPressKey("/\\ctrl-n");
1098     verifyCommandBarCompletionVisible();
1099     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("xyz"));
1100     QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("xyz"));
1101     QCOMPARE(emulatedCommandBarCompleter()->popup()->currentIndex().row(), 2);
1102     TestPressKey("\\ctrl-n");
1103     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("bar"));
1104     QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("bar"));
1105     QCOMPARE(emulatedCommandBarCompleter()->popup()->currentIndex().row(), 1);
1106     TestPressKey("\\ctrl-n");
1107     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo"));
1108     QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("foo"));
1109     QCOMPARE(emulatedCommandBarCompleter()->popup()->currentIndex().row(), 0);
1110     TestPressKey("\\ctrl-n");
1111     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("xyz"));
1112     QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("xyz")); // Wrap-around.
1113     QCOMPARE(emulatedCommandBarCompleter()->popup()->currentIndex().row(), 2);
1114     TestPressKey("\\enter"); // Dismiss bar.
1115     FinishTest("foo bar");
1116 
1117     clearSearchHistory();
1118     vi_global->searchHistory()->append("xyz");
1119     vi_global->searchHistory()->append("bar");
1120     vi_global->searchHistory()->append("foo");
1121     BeginTest("foo bar");
1122     TestPressKey("/\\ctrl-n\\ctrl-n");
1123     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("bar"));
1124     TestPressKey("\\enter"); // Dismiss bar.
1125     FinishTest("foo bar");
1126 
1127     // If we add something to the history, remove any earliest occurrences (this is what Vim appears to do)
1128     // and append to the end.
1129     clearSearchHistory();
1130     vi_global->searchHistory()->append("bar");
1131     vi_global->searchHistory()->append("xyz");
1132     vi_global->searchHistory()->append("foo");
1133     vi_global->searchHistory()->append("xyz");
1134     QCOMPARE(searchHistory(),
1135              QStringList() << "bar"
1136                            << "foo"
1137                            << "xyz");
1138 
1139     // Push out older entries if we have too many search items in the history.
1140     const int HISTORY_SIZE_LIMIT = 100;
1141     clearSearchHistory();
1142     for (int i = 1; i <= HISTORY_SIZE_LIMIT; i++) {
1143         vi_global->searchHistory()->append(QString("searchhistoryitem %1").arg(i));
1144     }
1145     QCOMPARE(searchHistory().size(), HISTORY_SIZE_LIMIT);
1146     QCOMPARE(searchHistory().first(), QString("searchhistoryitem 1"));
1147     QCOMPARE(searchHistory().last(), QString("searchhistoryitem 100"));
1148     vi_global->searchHistory()->append(QString("searchhistoryitem %1").arg(HISTORY_SIZE_LIMIT + 1));
1149     QCOMPARE(searchHistory().size(), HISTORY_SIZE_LIMIT);
1150     QCOMPARE(searchHistory().first(), QString("searchhistoryitem 2"));
1151     QCOMPARE(searchHistory().last(), QString("searchhistoryitem %1").arg(HISTORY_SIZE_LIMIT + 1));
1152 
1153     // Don't add empty searches to the history.
1154     clearSearchHistory();
1155     DoTest("foo bar", "/\\enter", "foo bar");
1156     QVERIFY(searchHistory().isEmpty());
1157 
1158     // "*" and "#" should add the relevant word to the search history, enclosed between \< and \>
1159     clearSearchHistory();
1160     BeginTest("foo bar");
1161     TestPressKey("*");
1162     QVERIFY(!searchHistory().isEmpty());
1163     QCOMPARE(searchHistory().last(), QString("\\<foo\\>"));
1164     TestPressKey("w#");
1165     QCOMPARE(searchHistory().size(), 2);
1166     QCOMPARE(searchHistory().last(), QString("\\<bar\\>"));
1167 
1168     // Auto-complete words from the document on ctrl-space.
1169     // Test that we can actually find a single word and add it to the list of completions.
1170     BeginTest("foo");
1171     TestPressKey("/\\ctrl- ");
1172     verifyCommandBarCompletionVisible();
1173     QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("foo"));
1174     TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
1175     FinishTest("foo");
1176 
1177     // Count digits and underscores as being part of a word.
1178     BeginTest("foo_12");
1179     TestPressKey("/\\ctrl- ");
1180     verifyCommandBarCompletionVisible();
1181     QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("foo_12"));
1182     TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
1183     FinishTest("foo_12");
1184 
1185     // This feels a bit better to me, usability-wise: in the special case of completion from document, where
1186     // the completion list is manually summoned, allow one to press Enter without the bar being dismissed
1187     // (just dismiss the completion list instead).
1188     BeginTest("foo_12");
1189     TestPressKey("/\\ctrl- \\ctrl-p\\enter");
1190     QVERIFY(emulatedCommandBar->isVisible());
1191     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
1192     TestPressKey("\\enter"); // Dismiss bar.
1193     FinishTest("foo_12");
1194 
1195     // Check that we can find multiple words on one line.
1196     BeginTest("bar (foo) [xyz]");
1197     TestPressKey("/\\ctrl- ");
1198     QStringListModel *completerStringListModel = dynamic_cast<QStringListModel *>(emulatedCommandBarCompleter()->model());
1199     Q_ASSERT(completerStringListModel);
1200     QCOMPARE(completerStringListModel->stringList(),
1201              QStringList() << "bar"
1202                            << "foo"
1203                            << "xyz");
1204     TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
1205     FinishTest("bar (foo) [xyz]");
1206 
1207     // Check that we arrange the found words in case-insensitive sorted order.
1208     BeginTest("D c e a b f");
1209     TestPressKey("/\\ctrl- ");
1210     verifyCommandBarCompletionsMatches(QStringList() << "a"
1211                                                      << "b"
1212                                                      << "c"
1213                                                      << "D"
1214                                                      << "e"
1215                                                      << "f");
1216     TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
1217     FinishTest("D c e a b f");
1218 
1219     // Check that we don't include the same word multiple times.
1220     BeginTest("foo bar bar bar foo");
1221     TestPressKey("/\\ctrl- ");
1222     verifyCommandBarCompletionsMatches(QStringList() << "bar"
1223                                                      << "foo");
1224     TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
1225     FinishTest("foo bar bar bar foo");
1226 
1227     // Check that we search only a narrow portion of the document, around the cursor (4096 lines either
1228     // side, say).
1229     QStringList manyLines;
1230     for (int i = 1; i < 2 * 4096 + 3; i++) {
1231         // Pad the digits so that when sorted alphabetically, they are also sorted numerically.
1232         manyLines << QString("word%1").arg(i, 5, 10, QChar('0'));
1233     }
1234     QStringList allButFirstAndLastOfManyLines = manyLines;
1235     allButFirstAndLastOfManyLines.removeFirst();
1236     allButFirstAndLastOfManyLines.removeLast();
1237 
1238     BeginTest(manyLines.join("\n"));
1239     TestPressKey("4097j/\\ctrl- ");
1240     verifyCommandBarCompletionsMatches(allButFirstAndLastOfManyLines);
1241     TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
1242     FinishTest(manyLines.join("\n"));
1243 
1244     // "The current word" means the word before the cursor in the command bar, and includes numbers
1245     // and underscores. Make sure also that the completion prefix is set when the completion is first invoked.
1246     BeginTest("foo fee foa_11 foa_11b");
1247     // Write "bar(foa112$nose" and position cursor before the "2", then invoke completion.
1248     TestPressKey("/bar(foa_112$nose\\left\\left\\left\\left\\left\\left\\ctrl- ");
1249     verifyCommandBarCompletionsMatches(QStringList() << "foa_11"
1250                                                      << "foa_11b");
1251     TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
1252     FinishTest("foo fee foa_11 foa_11b");
1253 
1254     // But don't count "-" as being part of the current word.
1255     BeginTest("foo_12");
1256     TestPressKey("/bar-foo\\ctrl- ");
1257     verifyCommandBarCompletionVisible();
1258     QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("foo_12"));
1259     TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
1260     FinishTest("foo_12");
1261 
1262     // Be case insensitive.
1263     BeginTest("foo Fo12 fOo13 FO45");
1264     TestPressKey("/fo\\ctrl- ");
1265     verifyCommandBarCompletionsMatches(QStringList() << "Fo12"
1266                                                      << "FO45"
1267                                                      << "foo"
1268                                                      << "fOo13");
1269     TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
1270     FinishTest("foo Fo12 fOo13 FO45");
1271 
1272     // Feed the current word to complete to the completer as we type/ edit.
1273     BeginTest("foo fee foa foab");
1274     TestPressKey("/xyz|f\\ctrl- o");
1275     verifyCommandBarCompletionsMatches(QStringList() << "foa"
1276                                                      << "foab"
1277                                                      << "foo");
1278     TestPressKey("a");
1279     verifyCommandBarCompletionsMatches(QStringList() << "foa"
1280                                                      << "foab");
1281     TestPressKey("\\ctrl-h");
1282     verifyCommandBarCompletionsMatches(QStringList() << "foa"
1283                                                      << "foab"
1284                                                      << "foo");
1285     TestPressKey("o");
1286     verifyCommandBarCompletionsMatches(QStringList() << "foo");
1287     TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
1288     FinishTest("foo fee foa foab");
1289 
1290     // Upon selecting a completion with an empty command bar, add the completed text to the command bar.
1291     BeginTest("foo fee fob foables");
1292     TestPressKey("/\\ctrl- foa\\ctrl-p");
1293     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foables"));
1294     verifyCommandBarCompletionVisible();
1295     TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
1296     FinishTest("foo fee fob foables");
1297 
1298     // If bar is non-empty, replace the word under the cursor.
1299     BeginTest("foo fee foa foab");
1300     TestPressKey("/xyz|f$nose\\left\\left\\left\\left\\left\\ctrl- oa\\ctrl-p");
1301     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("xyz|foab$nose"));
1302     TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
1303     FinishTest("foo fee foa foab");
1304 
1305     // Place the cursor at the end of the completed text.
1306     BeginTest("foo fee foa foab");
1307     TestPressKey("/xyz|f$nose\\left\\left\\left\\left\\left\\ctrl- oa\\ctrl-p\\enterX");
1308     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("xyz|foabX$nose"));
1309     TestPressKey("\\ctrl-c"); // Dismiss completion, then bar.
1310     FinishTest("foo fee foa foab");
1311 
1312     // If we're completing from history, though, the entire text gets set, and the completion prefix
1313     // is the beginning of the entire text, not the current word before the cursor.
1314     clearSearchHistory();
1315     vi_global->searchHistory()->append("foo(bar");
1316     BeginTest("");
1317     TestPressKey("/foo(b\\ctrl-p");
1318     verifyCommandBarCompletionVisible();
1319     verifyCommandBarCompletionsMatches(QStringList() << "foo(bar");
1320     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo(bar"));
1321     TestPressKey("\\enter"); // Dismiss bar.
1322     FinishTest("");
1323 
1324     // If we're completing from history and we abort the completion via ctrl-c or ctrl-[, we revert the whole
1325     // text to the last manually typed text.
1326     clearSearchHistory();
1327     vi_global->searchHistory()->append("foo(b|ar");
1328     BeginTest("");
1329     TestPressKey("/foo(b\\ctrl-p");
1330     verifyCommandBarCompletionVisible();
1331     verifyCommandBarCompletionsMatches(QStringList() << "foo(b|ar");
1332     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo(b|ar"));
1333     TestPressKey("\\ctrl-c"); // Dismiss completion.
1334     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo(b"));
1335     TestPressKey("\\enter"); // Dismiss bar.
1336     FinishTest("");
1337 
1338     // Scroll completion list if necessary so that currently selected completion is visible.
1339     BeginTest("a b c d e f g h i j k l m n o p q r s t u v w x y z");
1340     TestPressKey("/\\ctrl- ");
1341     const int lastItemRow = 25;
1342     const QRect initialLastCompletionItemRect =
1343         emulatedCommandBarCompleter()->popup()->visualRect(emulatedCommandBarCompleter()->popup()->model()->index(lastItemRow, 0));
1344     QVERIFY(!emulatedCommandBarCompleter()->popup()->rect().contains(
1345         initialLastCompletionItemRect)); // If this fails, then we have an error in the test setup: initially, the last item in the list should be outside of
1346                                          // the bounds of the popup.
1347     TestPressKey("\\ctrl-n");
1348     QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("z"));
1349     const QRect lastCompletionItemRect =
1350         emulatedCommandBarCompleter()->popup()->visualRect(emulatedCommandBarCompleter()->popup()->model()->index(lastItemRow, 0));
1351     QVERIFY(emulatedCommandBarCompleter()->popup()->rect().contains(lastCompletionItemRect));
1352     TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
1353     FinishTest("a b c d e f g h i j k l m n o p q r s t u v w x y z");
1354 
1355     // Ensure that the completion list changes size appropriately as the number of candidate completions changes.
1356     BeginTest("a ab abc");
1357     TestPressKey("/\\ctrl- ");
1358     const int initialPopupHeight = emulatedCommandBarCompleter()->popup()->height();
1359     TestPressKey("ab");
1360     const int popupHeightAfterEliminatingOne = emulatedCommandBarCompleter()->popup()->height();
1361     QVERIFY(popupHeightAfterEliminatingOne < initialPopupHeight);
1362     TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
1363     FinishTest("a ab abc");
1364 
1365     // Ensure that the completion list disappears when no candidate completions are found, but re-appears
1366     // when some are found.
1367     BeginTest("a ab abc");
1368     TestPressKey("/\\ctrl- ");
1369     TestPressKey("abd");
1370     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
1371     TestPressKey("\\ctrl-h");
1372     verifyCommandBarCompletionVisible();
1373     TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
1374     FinishTest("a ab abc");
1375 
1376     // ctrl-c and ctrl-[ when the completion list is visible should dismiss the completion list, but *not*
1377     // the emulated command bar. TODO - same goes for ESC, but this is harder as KateViewInternal dismisses it
1378     // itself.
1379     BeginTest("a ab abc");
1380     TestPressKey("/\\ctrl- \\ctrl-cdiw");
1381     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
1382     QVERIFY(emulatedCommandBar->isVisible());
1383     TestPressKey("\\enter"); // Dismiss bar.
1384     TestPressKey("/\\ctrl- \\ctrl-[diw");
1385     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
1386     QVERIFY(emulatedCommandBar->isVisible());
1387     TestPressKey("\\enter"); // Dismiss bar.
1388     FinishTest("a ab abc");
1389 
1390     // If we implicitly choose an element from the summoned completion list (by highlighting it, then
1391     // continuing to edit the text), the completion box should not re-appear unless explicitly summoned
1392     // again, even if the current word has a valid completion.
1393     BeginTest("a ab abc");
1394     TestPressKey("/\\ctrl- \\ctrl-p");
1395     TestPressKey(".a");
1396     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
1397     TestPressKey("\\enter"); // Dismiss bar.
1398     FinishTest("a ab abc");
1399 
1400     // If we dismiss the summoned completion list via ctrl-c or ctrl-[, it should not re-appear unless explicitly summoned
1401     // again, even if the current word has a valid completion.
1402     BeginTest("a ab abc");
1403     TestPressKey("/\\ctrl- \\ctrl-c");
1404     TestPressKey(".a");
1405     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
1406     TestPressKey("\\enter");
1407     TestPressKey("/\\ctrl- \\ctrl-[");
1408     TestPressKey(".a");
1409     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
1410     TestPressKey("\\enter"); // Dismiss bar.
1411     FinishTest("a ab abc");
1412 
1413     // If we select a completion from an empty bar, but then dismiss it via ctrl-c or ctrl-[, then we
1414     // should restore the empty text.
1415     BeginTest("foo");
1416     TestPressKey("/\\ctrl- \\ctrl-p");
1417     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo"));
1418     TestPressKey("\\ctrl-c");
1419     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
1420     QVERIFY(emulatedCommandBar->isVisible());
1421     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(""));
1422     TestPressKey("\\enter"); // Dismiss bar.
1423     FinishTest("foo");
1424     BeginTest("foo");
1425     TestPressKey("/\\ctrl- \\ctrl-p");
1426     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo"));
1427     TestPressKey("\\ctrl-[");
1428     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
1429     QVERIFY(emulatedCommandBar->isVisible());
1430     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(""));
1431     TestPressKey("\\enter"); // Dismiss bar.
1432     FinishTest("foo");
1433 
1434     // If we select a completion but then dismiss it via ctrl-c or ctrl-[, then we
1435     // should restore the last manually typed word.
1436     BeginTest("fooabc");
1437     TestPressKey("/f\\ctrl- o\\ctrl-p");
1438     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("fooabc"));
1439     TestPressKey("\\ctrl-c");
1440     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
1441     QVERIFY(emulatedCommandBar->isVisible());
1442     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("fo"));
1443     TestPressKey("\\enter"); // Dismiss bar.
1444     FinishTest("fooabc");
1445 
1446     // If we select a completion but then dismiss it via ctrl-c or ctrl-[, then we
1447     // should restore the word currently being typed to the last manually typed word.
1448     BeginTest("fooabc");
1449     TestPressKey("/ab\\ctrl- |fo\\ctrl-p");
1450     TestPressKey("\\ctrl-c");
1451     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("ab|fo"));
1452     TestPressKey("\\enter"); // Dismiss bar.
1453     FinishTest("fooabc");
1454 
1455     // Set the completion prefix for the search history completion as soon as it is shown.
1456     clearSearchHistory();
1457     vi_global->searchHistory()->append("foo(bar");
1458     vi_global->searchHistory()->append("xyz");
1459     BeginTest("");
1460     TestPressKey("/f\\ctrl-p");
1461     verifyCommandBarCompletionVisible();
1462     verifyCommandBarCompletionsMatches(QStringList() << "foo(bar");
1463     TestPressKey("\\enter"); // Dismiss bar.
1464     FinishTest("");
1465 
1466     // Command Mode (:) tests.
1467     // ":" should summon the command bar, with ":" as the label.
1468     BeginTest("");
1469     TestPressKey(":");
1470     QVERIFY(emulatedCommandBar->isVisible());
1471     QCOMPARE(emulatedCommandTypeIndicator()->text(), QString(":"));
1472     QVERIFY(emulatedCommandTypeIndicator()->isVisible());
1473     QVERIFY(emulatedCommandBarTextEdit());
1474     QVERIFY(emulatedCommandBarTextEdit()->text().isEmpty());
1475     TestPressKey("\\esc");
1476     FinishTest("");
1477 
1478     // If we have a selection, it should be encoded as a range in the text edit.
1479     BeginTest("d\nb\na\nc");
1480     TestPressKey("Vjjj:");
1481     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("'<,'>"));
1482     TestPressKey("\\esc");
1483     FinishTest("d\nb\na\nc");
1484 
1485     // If we have a count, it should be encoded as a range in the text edit.
1486     BeginTest("d\nb\na\nc");
1487     TestPressKey("7:");
1488     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(".,.+6"));
1489     TestPressKey("\\esc");
1490     FinishTest("d\nb\na\nc");
1491 
1492     // Don't go doing an incremental search when we press keys!
1493     BeginTest("foo bar xyz");
1494     TestPressKey(":bar");
1495     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
1496     TestPressKey("\\esc");
1497     FinishTest("foo bar xyz");
1498 
1499     // Execute the command on Enter.
1500     DoTest("d\nb\na\nc", "Vjjj:sort\\enter", "a\nb\nc\nd");
1501 
1502     // Don't crash if we call a non-existent command with a range.
1503     DoTest("123", ":42nonexistentcommand\\enter", "123");
1504 
1505     // Bar background should always be normal for command bar.
1506     BeginTest("foo");
1507     TestPressKey("/foo\\enter:");
1508     verifyTextEditBackgroundColour(normalBackgroundColour);
1509     TestPressKey("\\ctrl-c/bar\\enter:");
1510     verifyTextEditBackgroundColour(normalBackgroundColour);
1511     TestPressKey("\\esc");
1512     FinishTest("foo");
1513 
1514     const int commandResponseMessageTimeOutMSOverride = QString::fromLatin1(qgetenv("KATE_VIMODE_TEST_COMMANDRESPONSEMESSAGETIMEOUTMS")).toInt();
1515     const long commandResponseMessageTimeOutMS = (commandResponseMessageTimeOutMSOverride > 0) ? commandResponseMessageTimeOutMSOverride : 2000;
1516     {
1517         // If there is any output from the command, show it in a label for a short amount of time
1518         // (make sure the bar type indicator is hidden, here, as it looks messy).
1519         emulatedCommandBar->setCommandResponseMessageTimeout(commandResponseMessageTimeOutMS);
1520         BeginTest("foo bar xyz");
1521         const QDateTime timeJustBeforeCommandExecuted = QDateTime::currentDateTime();
1522         TestPressKey(":commandthatdoesnotexist\\enter");
1523         QVERIFY(emulatedCommandBar->isVisible());
1524         QVERIFY(commandResponseMessageDisplay());
1525         QVERIFY(commandResponseMessageDisplay()->isVisible());
1526         QVERIFY(!emulatedCommandBarTextEdit()->isVisible());
1527         QVERIFY(!emulatedCommandTypeIndicator()->isVisible());
1528         // Be a bit vague about the exact message, due to i18n, etc.
1529         QVERIFY(commandResponseMessageDisplay()->text().contains("commandthatdoesnotexist"));
1530         waitForEmulatedCommandBarToHide(4 * commandResponseMessageTimeOutMS);
1531         QVERIFY(timeJustBeforeCommandExecuted.msecsTo(QDateTime::currentDateTime())
1532                 >= commandResponseMessageTimeOutMS - 500); // "- 500" because coarse timers can fire up to 500ms *prematurely*.
1533         QVERIFY(!emulatedCommandBar->isVisible());
1534         // Piggy-back on this test, as the bug we're about to test for would actually make setting
1535         // up the conditions again in a separate test impossible ;)
1536         // When we next summon the bar, the response message should be invisible; the editor visible & editable;
1537         // and the bar type indicator visible again.
1538         TestPressKey("/");
1539         QVERIFY(!commandResponseMessageDisplay()->isVisible());
1540         QVERIFY(emulatedCommandBarTextEdit()->isVisible());
1541         QVERIFY(emulatedCommandBarTextEdit()->isEnabled());
1542         QVERIFY(emulatedCommandBar->isVisible());
1543         TestPressKey("\\esc"); // Dismiss the bar.
1544         FinishTest("foo bar xyz");
1545     }
1546 
1547     {
1548         // Show the same message twice in a row.
1549         BeginTest("foo bar xyz");
1550         TestPressKey(":othercommandthatdoesnotexist\\enter");
1551         QDateTime startWaitingForMessageToHide = QDateTime::currentDateTime();
1552         waitForEmulatedCommandBarToHide(4 * commandResponseMessageTimeOutMS);
1553         TestPressKey(":othercommandthatdoesnotexist\\enter");
1554         QVERIFY(commandResponseMessageDisplay()->isVisible());
1555         // Wait for it to disappear again, as a courtesy for the next test.
1556         waitForEmulatedCommandBarToHide(4 * commandResponseMessageTimeOutMS);
1557     }
1558 
1559     {
1560         // Emulated command bar should not steal keypresses when it is merely showing the results of an executed command.
1561         BeginTest("foo bar");
1562         TestPressKey(":commandthatdoesnotexist\\enterrX");
1563         Q_ASSERT_X(commandResponseMessageDisplay()->isVisible(), "running test", "Need to increase timeJustBeforeCommandExecuted!");
1564         FinishTest("Xoo bar");
1565     }
1566 
1567     {
1568         // Don't send the synthetic "enter" keypress (for making search-as-a-motion work) when we finally hide.
1569         BeginTest("foo bar\nbar");
1570         TestPressKey(":commandthatdoesnotexist\\enter");
1571         Q_ASSERT_X(commandResponseMessageDisplay()->isVisible(), "running test", "Need to increase timeJustBeforeCommandExecuted!");
1572         waitForEmulatedCommandBarToHide(commandResponseMessageTimeOutMS * 4);
1573         TestPressKey("rX");
1574         FinishTest("Xoo bar\nbar");
1575     }
1576 
1577     {
1578         // The timeout should be cancelled when we invoke the command bar again.
1579         BeginTest("");
1580         TestPressKey(":commandthatdoesnotexist\\enter");
1581         TestPressKey(":");
1582         // Wait ample time for the timeout to fire.  Do not use waitForEmulatedCommandBarToHide for this!
1583         QTRY_VERIFY_WITH_TIMEOUT(emulatedCommandBar->isVisible(), commandResponseMessageTimeOutMS * 2);
1584         TestPressKey("\\esc"); // Dismiss the bar.
1585         FinishTest("");
1586     }
1587 
1588     {
1589         // The timeout should not cause kate_view to regain focus if we have manually taken it away.
1590         qDebug() << " NOTE: this test is weirdly fragile, so if it starts failing, comment it out and e-mail me:  it may well be more trouble that it's worth.";
1591         BeginTest("");
1592         TestPressKey(":commandthatdoesnotexist\\enter");
1593         // Wait for any focus changes to take effect.
1594         QApplication::processEvents();
1595         QLineEdit *dummyToFocus = new QLineEdit(QString("Sausage"), mainWindow);
1596         // Take focus away from kate_view by giving it to dummyToFocus.
1597         QApplication::setActiveWindow(mainWindow);
1598         kate_view->setFocus();
1599         mainWindowLayout->addWidget(dummyToFocus);
1600         dummyToFocus->show();
1601         dummyToFocus->setEnabled(true);
1602         dummyToFocus->setFocus();
1603         // Allow dummyToFocus to receive focus.
1604         QTRY_VERIFY(dummyToFocus->hasFocus());
1605         // Wait ample time for the timeout to fire.  Do not use waitForEmulatedCommandBarToHide for this -
1606         // the bar never actually hides in this instance, and I think it would take some deep changes in
1607         // Kate to make it do so (the KateCommandLineBar as the same issue).
1608         QTest::qWait(commandResponseMessageTimeOutMS * 2);
1609         QVERIFY(dummyToFocus->hasFocus());
1610         QVERIFY(emulatedCommandBar->isVisible());
1611         mainWindowLayout->removeWidget(dummyToFocus);
1612         // Restore focus to the kate_view.
1613         kate_view->setFocus();
1614         QTRY_VERIFY(kate_view->hasFocus());
1615         // *Now* wait for the command bar to disappear - giving kate_view focus should trigger it.
1616         waitForEmulatedCommandBarToHide(commandResponseMessageTimeOutMS * 4);
1617         FinishTest("");
1618     }
1619 
1620     {
1621         // No completion should be shown when the bar is first shown: this gives us an opportunity
1622         // to invoke command history via ctrl-p and ctrl-n.
1623         BeginTest("");
1624         TestPressKey(":");
1625         QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
1626         TestPressKey("\\ctrl-c"); // Dismiss bar
1627         FinishTest("");
1628     }
1629 
1630     {
1631         // Should be able to switch to completion from document, even when we have a completion from commands.
1632         BeginTest("soggy1 soggy2");
1633         TestPressKey(":so");
1634         verifyCommandBarCompletionContains(QStringList() << "sort");
1635         TestPressKey("\\ctrl- ");
1636         verifyCommandBarCompletionsMatches(QStringList() << "soggy1"
1637                                                          << "soggy2");
1638         TestPressKey("\\ctrl-c"); // Dismiss completer
1639         TestPressKey("\\ctrl-c"); // Dismiss bar
1640         FinishTest("soggy1 soggy2");
1641     }
1642 
1643     {
1644         // If we dismiss the command completion then change the text, it should summon the completion
1645         // again.
1646         BeginTest("");
1647         TestPressKey(":so");
1648         TestPressKey("\\ctrl-c"); // Dismiss completer
1649         TestPressKey("r");
1650         verifyCommandBarCompletionVisible();
1651         verifyCommandBarCompletionContains(QStringList() << "sort");
1652         TestPressKey("\\ctrl-c"); // Dismiss completer
1653         TestPressKey("\\ctrl-c"); // Dismiss bar
1654         FinishTest("");
1655     }
1656 
1657     {
1658         // Completion should be dismissed when we are showing command response text.
1659         BeginTest("");
1660         TestPressKey(":set-au\\enter");
1661         QVERIFY(commandResponseMessageDisplay()->isVisible());
1662         QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
1663         waitForEmulatedCommandBarToHide(commandResponseMessageTimeOutMS * 4);
1664         FinishTest("");
1665     }
1666 
1667     // If we abort completion via ctrl-c or ctrl-[, we should revert the current word to the last
1668     // manually entered word.
1669     BeginTest("");
1670     TestPressKey(":se\\ctrl-p");
1671     verifyCommandBarCompletionVisible();
1672     QVERIFY(emulatedCommandBarTextEdit()->text() != "se");
1673     TestPressKey("\\ctrl-c"); // Dismiss completer
1674     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("se"));
1675     TestPressKey("\\ctrl-c"); // Dismiss bar
1676     FinishTest("");
1677 
1678     // In practice, it's annoying if, as we enter ":s/se", completions pop up after the "se":
1679     // for now, only summon completion if we are on the first word in the text.
1680     BeginTest("");
1681     TestPressKey(":s/se");
1682     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
1683     TestPressKey("\\ctrl-c"); // Dismiss bar
1684     FinishTest("");
1685     BeginTest("");
1686     TestPressKey(":.,.+7s/se");
1687     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
1688     TestPressKey("\\ctrl-c"); // Dismiss bar
1689     FinishTest("");
1690 
1691     // Don't blank the text if we activate command history completion with no command history.
1692     BeginTest("");
1693     clearCommandHistory();
1694     TestPressKey(":s/se\\ctrl-p");
1695     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
1696     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/se"));
1697     TestPressKey("\\ctrl-c"); // Dismiss bar
1698     FinishTest("");
1699 
1700     // On completion, only update the command in front of the cursor.
1701     BeginTest("");
1702     clearCommandHistory();
1703     TestPressKey(":.,.+6s/se\\left\\left\\leftet-auto-in\\ctrl-p");
1704     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(".,.+6set-auto-indent/se"));
1705     TestPressKey("\\ctrl-c"); // Dismiss completer.
1706     TestPressKey("\\ctrl-c"); // Dismiss bar
1707     FinishTest("");
1708 
1709     // On completion, place the cursor after the new command.
1710     BeginTest("");
1711     clearCommandHistory();
1712     TestPressKey(":.,.+6s/fo\\left\\left\\leftet-auto-in\\ctrl-pX");
1713     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(".,.+6set-auto-indentX/fo"));
1714     TestPressKey("\\ctrl-c"); // Dismiss completer.
1715     TestPressKey("\\ctrl-c"); // Dismiss bar
1716     FinishTest("");
1717 
1718     // "The current word", for Commands, can contain "-".
1719     BeginTest("");
1720     TestPressKey(":set-\\ctrl-p");
1721     verifyCommandBarCompletionVisible();
1722     QVERIFY(emulatedCommandBarTextEdit()->text() != "set-");
1723     QVERIFY(emulatedCommandBarCompleter()->currentCompletion().startsWith(QLatin1String("set-")));
1724     QCOMPARE(emulatedCommandBarTextEdit()->text(), emulatedCommandBarCompleter()->currentCompletion());
1725     TestPressKey("\\ctrl-c"); // Dismiss completion.
1726     TestPressKey("\\ctrl-c"); // Dismiss bar.
1727     FinishTest("");
1728 
1729     {
1730         // Don't switch from word-from-document to command-completion just because we press a key, though!
1731         BeginTest("soggy1 soggy2");
1732         TestPressKey(":\\ctrl- s");
1733         TestPressKey("o");
1734         verifyCommandBarCompletionVisible();
1735         verifyCommandBarCompletionsMatches(QStringList() << "soggy1"
1736                                                          << "soggy2");
1737         TestPressKey("\\ctrl-c"); // Dismiss completer
1738         TestPressKey("\\ctrl-c"); // Dismiss bar
1739         FinishTest("soggy1 soggy2");
1740     }
1741 
1742     {
1743         // If we're in a place where there is no command completion allowed, don't go hiding the word
1744         // completion as we type.
1745         BeginTest("soggy1 soggy2");
1746         TestPressKey(":s/s\\ctrl- o");
1747         verifyCommandBarCompletionVisible();
1748         verifyCommandBarCompletionsMatches(QStringList() << "soggy1"
1749                                                          << "soggy2");
1750         TestPressKey("\\ctrl-c"); // Dismiss completer
1751         TestPressKey("\\ctrl-c"); // Dismiss bar
1752         FinishTest("soggy1 soggy2");
1753     }
1754 
1755     {
1756         // Don't show command completion before we start typing a command: we want ctrl-p/n
1757         // to go through command history instead (we'll test for that second part later).
1758         BeginTest("soggy1 soggy2");
1759         TestPressKey(":");
1760         QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
1761         TestPressKey("\\ctrl-cvl:");
1762         QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
1763         TestPressKey("\\ctrl-c"); // Dismiss bar
1764         FinishTest("soggy1 soggy2");
1765     }
1766 
1767     {
1768         // Aborting ":" should leave us in normal mode with no selection.
1769         BeginTest("foo bar");
1770         TestPressKey("vw:\\ctrl-[");
1771         QVERIFY(kate_view->selectionText().isEmpty());
1772         TestPressKey("wdiw");
1773         BeginTest("foo ");
1774     }
1775 
1776     // Command history tests.
1777     clearCommandHistory();
1778     QVERIFY(commandHistory().isEmpty());
1779     vi_global->commandHistory()->append("foo");
1780     vi_global->commandHistory()->append("bar");
1781     QCOMPARE(commandHistory(),
1782              QStringList() << "foo"
1783                            << "bar");
1784     clearCommandHistory();
1785     QVERIFY(commandHistory().isEmpty());
1786 
1787     // If we add something to the history, remove any earliest occurrences (this is what Vim appears to do)
1788     // and append to the end.
1789     clearCommandHistory();
1790     vi_global->commandHistory()->append("bar");
1791     vi_global->commandHistory()->append("xyz");
1792     vi_global->commandHistory()->append("foo");
1793     vi_global->commandHistory()->append("xyz");
1794     QCOMPARE(commandHistory(),
1795              QStringList() << "bar"
1796                            << "foo"
1797                            << "xyz");
1798 
1799     // Push out older entries if we have too many command items in the history.
1800     clearCommandHistory();
1801     for (int i = 1; i <= HISTORY_SIZE_LIMIT; i++) {
1802         vi_global->commandHistory()->append(QString("commandhistoryitem %1").arg(i));
1803     }
1804     QCOMPARE(commandHistory().size(), HISTORY_SIZE_LIMIT);
1805     QCOMPARE(commandHistory().first(), QString("commandhistoryitem 1"));
1806     QCOMPARE(commandHistory().last(), QString("commandhistoryitem 100"));
1807     vi_global->commandHistory()->append(QString("commandhistoryitem %1").arg(HISTORY_SIZE_LIMIT + 1));
1808     QCOMPARE(commandHistory().size(), HISTORY_SIZE_LIMIT);
1809     QCOMPARE(commandHistory().first(), QString("commandhistoryitem 2"));
1810     QCOMPARE(commandHistory().last(), QString("commandhistoryitem %1").arg(HISTORY_SIZE_LIMIT + 1));
1811 
1812     // Don't add empty commands to the history.
1813     clearCommandHistory();
1814     DoTest("foo bar", ":\\enter", "foo bar");
1815     QVERIFY(commandHistory().isEmpty());
1816 
1817     clearCommandHistory();
1818     BeginTest("");
1819     TestPressKey(":sort\\enter");
1820     QCOMPARE(commandHistory(), QStringList() << "sort");
1821     TestPressKey(":yank\\enter");
1822     QCOMPARE(commandHistory(),
1823              QStringList() << "sort"
1824                            << "yank");
1825     // Add to history immediately: don't wait for the command response display to timeout.
1826     TestPressKey(":commandthatdoesnotexist\\enter");
1827     QCOMPARE(commandHistory(),
1828              QStringList() << "sort"
1829                            << "yank"
1830                            << "commandthatdoesnotexist");
1831     // Vim adds aborted commands to the history too, oddly.
1832     TestPressKey(":abortedcommand\\ctrl-c");
1833     QCOMPARE(commandHistory(),
1834              QStringList() << "sort"
1835                            << "yank"
1836                            << "commandthatdoesnotexist"
1837                            << "abortedcommand");
1838     // Only add for commands, not searches!
1839     TestPressKey("/donotaddme\\enter?donotaddmeeither\\enter/donotaddme\\ctrl-c?donotaddmeeither\\ctrl-c");
1840     QCOMPARE(commandHistory(),
1841              QStringList() << "sort"
1842                            << "yank"
1843                            << "commandthatdoesnotexist"
1844                            << "abortedcommand");
1845     FinishTest("");
1846 
1847     // Commands should not be added to the search history!
1848     clearCommandHistory();
1849     clearSearchHistory();
1850     BeginTest("");
1851     TestPressKey(":sort\\enter");
1852     QVERIFY(searchHistory().isEmpty());
1853     FinishTest("");
1854 
1855     // With an empty command bar, ctrl-p / ctrl-n should go through history.
1856     clearCommandHistory();
1857     vi_global->commandHistory()->append("command1");
1858     vi_global->commandHistory()->append("command2");
1859     BeginTest("");
1860     TestPressKey(":\\ctrl-p");
1861     verifyCommandBarCompletionVisible();
1862     QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("command2"));
1863     QCOMPARE(emulatedCommandBarTextEdit()->text(), emulatedCommandBarCompleter()->currentCompletion());
1864     TestPressKey("\\ctrl-c"); // Dismiss completer
1865     TestPressKey("\\ctrl-c"); // Dismiss bar
1866     FinishTest("");
1867     clearCommandHistory();
1868     vi_global->commandHistory()->append("command1");
1869     vi_global->commandHistory()->append("command2");
1870     BeginTest("");
1871     TestPressKey(":\\ctrl-n");
1872     verifyCommandBarCompletionVisible();
1873     QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("command1"));
1874     QCOMPARE(emulatedCommandBarTextEdit()->text(), emulatedCommandBarCompleter()->currentCompletion());
1875     TestPressKey("\\ctrl-c"); // Dismiss completer
1876     TestPressKey("\\ctrl-c"); // Dismiss bar
1877     FinishTest("");
1878 
1879     // If we're at a place where command completions are not allowed, ctrl-p/n should go through history.
1880     clearCommandHistory();
1881     vi_global->commandHistory()->append("s/command1");
1882     vi_global->commandHistory()->append("s/command2");
1883     BeginTest("");
1884     TestPressKey(":s/\\ctrl-p");
1885     verifyCommandBarCompletionVisible();
1886     QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("s/command2"));
1887     QCOMPARE(emulatedCommandBarTextEdit()->text(), emulatedCommandBarCompleter()->currentCompletion());
1888     TestPressKey("\\ctrl-c"); // Dismiss completer
1889     TestPressKey("\\ctrl-c"); // Dismiss bar
1890     FinishTest("");
1891     clearCommandHistory();
1892     vi_global->commandHistory()->append("s/command1");
1893     vi_global->commandHistory()->append("s/command2");
1894     BeginTest("");
1895     TestPressKey(":s/\\ctrl-n");
1896     verifyCommandBarCompletionVisible();
1897     QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("s/command1"));
1898     QCOMPARE(emulatedCommandBarTextEdit()->text(), emulatedCommandBarCompleter()->currentCompletion());
1899     TestPressKey("\\ctrl-c"); // Dismiss completer
1900     TestPressKey("\\ctrl-c"); // Dismiss bar
1901     FinishTest("");
1902 
1903     // Cancelling word-from-document completion should revert the whole text to what it was before.
1904     BeginTest("sausage bacon");
1905     TestPressKey(":s/b\\ctrl- \\ctrl-p");
1906     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/bacon"));
1907     verifyCommandBarCompletionVisible();
1908     TestPressKey("\\ctrl-c"); // Dismiss completer
1909     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/b"));
1910     TestPressKey("\\ctrl-c"); // Dismiss bar
1911     FinishTest("sausage bacon");
1912 
1913     // "Replace" history tests.
1914     clearReplaceHistory();
1915     QVERIFY(replaceHistory().isEmpty());
1916     vi_global->replaceHistory()->append("foo");
1917     vi_global->replaceHistory()->append("bar");
1918     QCOMPARE(replaceHistory(),
1919              QStringList() << "foo"
1920                            << "bar");
1921     clearReplaceHistory();
1922     QVERIFY(replaceHistory().isEmpty());
1923 
1924     // If we add something to the history, remove any earliest occurrences (this is what Vim appears to do)
1925     // and append to the end.
1926     clearReplaceHistory();
1927     vi_global->replaceHistory()->append("bar");
1928     vi_global->replaceHistory()->append("xyz");
1929     vi_global->replaceHistory()->append("foo");
1930     vi_global->replaceHistory()->append("xyz");
1931     QCOMPARE(replaceHistory(),
1932              QStringList() << "bar"
1933                            << "foo"
1934                            << "xyz");
1935 
1936     // Push out older entries if we have too many replace items in the history.
1937     clearReplaceHistory();
1938     for (int i = 1; i <= HISTORY_SIZE_LIMIT; i++) {
1939         vi_global->replaceHistory()->append(QString("replacehistoryitem %1").arg(i));
1940     }
1941     QCOMPARE(replaceHistory().size(), HISTORY_SIZE_LIMIT);
1942     QCOMPARE(replaceHistory().first(), QString("replacehistoryitem 1"));
1943     QCOMPARE(replaceHistory().last(), QString("replacehistoryitem 100"));
1944     vi_global->replaceHistory()->append(QString("replacehistoryitem %1").arg(HISTORY_SIZE_LIMIT + 1));
1945     QCOMPARE(replaceHistory().size(), HISTORY_SIZE_LIMIT);
1946     QCOMPARE(replaceHistory().first(), QString("replacehistoryitem 2"));
1947     QCOMPARE(replaceHistory().last(), QString("replacehistoryitem %1").arg(HISTORY_SIZE_LIMIT + 1));
1948 
1949     // Don't add empty replaces to the history.
1950     clearReplaceHistory();
1951     vi_global->replaceHistory()->append("");
1952     QVERIFY(replaceHistory().isEmpty());
1953 
1954     // Some misc SedReplace tests.
1955     DoTest("x\\/y", ":s/\\\\//replace/g\\enter", "x\\replacey");
1956     DoTest("x\\/y", ":s/\\\\\\\\\\\\//replace/g\\enter", "xreplacey");
1957     DoTest("x\\/y", ":s:/:replace:g\\enter", "x\\replacey");
1958     DoTest("foo\nbar\nxyz", ":%delete\\enter", "");
1959     DoTest("foo\nbar\nxyz\nbaz", "jVj:delete\\enter", "foo\nbaz");
1960     DoTest("foo\nbar\nxyz\nbaz", "j2:delete\\enter", "foo\nbaz");
1961 
1962     // Test that 0 is accepted as a line index (and treated as 1) in a range specifier
1963     DoTest("bar\nbar\nbar", ":0,$s/bar/foo/g\\enter", "foo\nfoo\nfoo");
1964     DoTest("bar\nbar\nbar", ":1,$s/bar/foo/g\\enter", "foo\nfoo\nfoo");
1965     DoTest("bar\nbar\nbar", ":0,2s/bar/foo/g\\enter", "foo\nfoo\nbar");
1966 
1967     // On ctrl-d, delete the "search" term in a s/search/replace/xx
1968     BeginTest("foo bar");
1969     TestPressKey(":s/x\\\\\\\\\\\\/yz/rep\\\\\\\\\\\\/lace/g\\ctrl-d");
1970     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s//rep\\\\\\/lace/g"));
1971     TestPressKey("\\ctrl-c"); // Dismiss bar.
1972     FinishTest("foo bar");
1973     // Move cursor to position of deleted search term.
1974     BeginTest("foo bar");
1975     TestPressKey(":s/x\\\\\\\\\\\\/yz/rep\\\\\\\\\\\\/lace/g\\ctrl-dX");
1976     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/X/rep\\\\\\/lace/g"));
1977     TestPressKey("\\ctrl-c"); // Dismiss bar.
1978     FinishTest("foo bar");
1979     // Do nothing on ctrl-d in search mode.
1980     BeginTest("foo bar");
1981     TestPressKey("/s/search/replace/g\\ctrl-d");
1982     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/search/replace/g"));
1983     TestPressKey("\\ctrl-c?s/searchbackwards/replace/g\\ctrl-d");
1984     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/searchbackwards/replace/g"));
1985     TestPressKey("\\ctrl-c"); // Dismiss bar.
1986     FinishTest("foo bar");
1987     // On ctrl-f, delete "replace" term in a s/search/replace/xx
1988     BeginTest("foo bar");
1989     TestPressKey(":s/a\\\\\\\\\\\\/bc/rep\\\\\\\\\\\\/lace/g\\ctrl-f");
1990     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/a\\\\\\/bc//g"));
1991     TestPressKey("\\ctrl-c"); // Dismiss bar.
1992     FinishTest("foo bar");
1993     // Move cursor to position of deleted replace term.
1994     BeginTest("foo bar");
1995     TestPressKey(":s:a/bc:replace:g\\ctrl-fX");
1996     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s:a/bc:X:g"));
1997     TestPressKey("\\ctrl-c"); // Dismiss bar.
1998     FinishTest("foo bar");
1999     // Do nothing on ctrl-d in search mode.
2000     BeginTest("foo bar");
2001     TestPressKey("/s/search/replace/g\\ctrl-f");
2002     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/search/replace/g"));
2003     TestPressKey("\\ctrl-c?s/searchbackwards/replace/g\\ctrl-f");
2004     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/searchbackwards/replace/g"));
2005     TestPressKey("\\ctrl-c"); // Dismiss bar.
2006     FinishTest("foo bar");
2007     // Do nothing on ctrl-d / ctrl-f if the current expression is not a sed expression.
2008     BeginTest("foo bar");
2009     TestPressKey(":s/notasedreplaceexpression::gi\\ctrl-f\\ctrl-dX");
2010     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/notasedreplaceexpression::giX"));
2011     TestPressKey("\\ctrl-c"); // Dismiss bar.
2012     FinishTest("foo bar");
2013     // Need to convert Vim-style regex's to Qt one's in Sed Replace.
2014     DoTest("foo xbacba(boo)|[y", ":s/x[abc]\\\\+(boo)|[y/boo/g\\enter", "foo boo");
2015     DoTest("foo xbacba(boo)|[y\nfoo xbacba(boo)|[y", "Vj:s/x[abc]\\\\+(boo)|[y/boo/g\\enter", "foo boo\nfoo boo");
2016     // Just convert the search term, please :)
2017     DoTest("foo xbacba(boo)|[y", ":s/x[abc]\\\\+(boo)|[y/boo()/g\\enter", "foo boo()");
2018     // With an empty search expression, ctrl-d should still position the cursor correctly.
2019     BeginTest("foo bar");
2020     TestPressKey(":s//replace/g\\ctrl-dX");
2021     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/X/replace/g"));
2022     TestPressKey("\\ctrl-c"); // Dismiss bar.
2023     TestPressKey(":s::replace:g\\ctrl-dX");
2024     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s:X:replace:g"));
2025     TestPressKey("\\ctrl-c"); // Dismiss bar.
2026     FinishTest("foo bar");
2027     // With an empty replace expression, ctrl-f should still position the cursor correctly.
2028     BeginTest("foo bar");
2029     TestPressKey(":s/sear\\\\/ch//g\\ctrl-fX");
2030     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/sear\\/ch/X/g"));
2031     TestPressKey("\\ctrl-c"); // Dismiss bar.
2032     TestPressKey(":s:sear\\\\:ch::g\\ctrl-fX");
2033     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s:sear\\:ch:X:g"));
2034     TestPressKey("\\ctrl-c"); // Dismiss bar.
2035     FinishTest("foo bar");
2036     // With both empty search *and* replace expressions, ctrl-f should still position the cursor correctly.
2037     BeginTest("foo bar");
2038     TestPressKey(":s///g\\ctrl-fX");
2039     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s//X/g"));
2040     TestPressKey("\\ctrl-c"); // Dismiss bar.
2041     TestPressKey(":s:::g\\ctrl-fX");
2042     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s::X:g"));
2043     TestPressKey("\\ctrl-c"); // Dismiss bar.
2044     FinishTest("foo bar");
2045     // Should be able to undo ctrl-f or ctrl-d.
2046     BeginTest("foo bar");
2047     TestPressKey(":s/find/replace/g\\ctrl-d");
2048     emulatedCommandBarTextEdit()->undo();
2049     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/find/replace/g"));
2050     TestPressKey("\\ctrl-f");
2051     emulatedCommandBarTextEdit()->undo();
2052     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/find/replace/g"));
2053     TestPressKey("\\ctrl-c"); // Dismiss bar.
2054     FinishTest("foo bar");
2055     // ctrl-f / ctrl-d should cleanly finish sed find/ replace history completion.
2056     clearReplaceHistory();
2057     clearSearchHistory();
2058     vi_global->searchHistory()->append("searchxyz");
2059     vi_global->replaceHistory()->append("replacexyz");
2060     TestPressKey(":s///g\\ctrl-d\\ctrl-p");
2061     QVERIFY(emulatedCommandBarCompleter()->popup()->isVisible());
2062     TestPressKey("\\ctrl-f");
2063     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/searchxyz//g"));
2064     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
2065     TestPressKey("\\ctrl-p");
2066     QVERIFY(emulatedCommandBarCompleter()->popup()->isVisible());
2067     TestPressKey("\\ctrl-d");
2068     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s//replacexyz/g"));
2069     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
2070     TestPressKey("\\ctrl-c"); // Dismiss bar.
2071     FinishTest("foo bar");
2072     // Don't hang if we execute a sed replace with empty search term.
2073     DoTest("foo bar", ":s//replace/g\\enter", "foo bar");
2074 
2075     // ctrl-f & ctrl-d should work even when there is a range expression at the beginning of the sed replace.
2076     BeginTest("foo bar");
2077     TestPressKey(":'<,'>s/search/replace/g\\ctrl-d");
2078     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("'<,'>s//replace/g"));
2079     TestPressKey("\\ctrl-c:.,.+6s/search/replace/g\\ctrl-f");
2080     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(".,.+6s/search//g"));
2081     TestPressKey("\\ctrl-c:%s/search/replace/g\\ctrl-f");
2082     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("%s/search//g"));
2083     // Place the cursor in the right place even when there is a range expression.
2084     TestPressKey("\\ctrl-c:.,.+6s/search/replace/g\\ctrl-fX");
2085     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(".,.+6s/search/X/g"));
2086     TestPressKey("\\ctrl-c:%s/search/replace/g\\ctrl-fX");
2087     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("%s/search/X/g"));
2088     TestPressKey("\\ctrl-c"); // Dismiss bar.
2089     FinishTest("foo bar");
2090     // Don't crash on ctrl-f/d if we have an empty command.
2091     DoTest("", ":\\ctrl-f\\ctrl-d\\ctrl-c", "");
2092     // Parser regression test: Don't crash on ctrl-f/d with ".,.+".
2093     DoTest("", ":.,.+\\ctrl-f\\ctrl-d\\ctrl-c", "");
2094 
2095     // Command-completion should be invoked on the command being typed even when preceded by a range expression.
2096     BeginTest("");
2097     TestPressKey(":0,'>so");
2098     verifyCommandBarCompletionVisible();
2099     TestPressKey("\\ctrl-c"); // Dismiss completer
2100     TestPressKey("\\ctrl-c"); // Dismiss bar.
2101     FinishTest("");
2102 
2103     // Command-completion should ignore the range expression.
2104     BeginTest("");
2105     TestPressKey(":.,.+6so");
2106     verifyCommandBarCompletionVisible();
2107     TestPressKey("\\ctrl-c"); // Dismiss completer
2108     TestPressKey("\\ctrl-c"); // Dismiss bar.
2109     FinishTest("");
2110 
2111     // A sed-replace should immediately add the search term to the search history.
2112     clearSearchHistory();
2113     BeginTest("");
2114     TestPressKey(":s/search/replace/g\\enter");
2115     QCOMPARE(searchHistory(), QStringList() << "search");
2116     FinishTest("");
2117 
2118     // An aborted sed-replace should not add the search term to the search history.
2119     clearSearchHistory();
2120     BeginTest("");
2121     TestPressKey(":s/search/replace/g\\ctrl-c");
2122     QCOMPARE(searchHistory(), QStringList());
2123     FinishTest("");
2124 
2125     // A non-sed-replace should leave the search history unchanged.
2126     clearSearchHistory();
2127     BeginTest("");
2128     TestPressKey(":s,search/replace/g\\enter");
2129     QCOMPARE(searchHistory(), QStringList());
2130     FinishTest("");
2131 
2132     // A sed-replace should immediately add the replace term to the replace history.
2133     clearReplaceHistory();
2134     BeginTest("");
2135     TestPressKey(":s/search/replace/g\\enter");
2136     QCOMPARE(replaceHistory(), QStringList() << "replace");
2137     clearReplaceHistory();
2138     TestPressKey(":'<,'>s/search/replace1/g\\enter");
2139     QCOMPARE(replaceHistory(), QStringList() << "replace1");
2140     FinishTest("");
2141 
2142     // An aborted sed-replace should not add the replace term to the replace history.
2143     clearReplaceHistory();
2144     BeginTest("");
2145     TestPressKey(":s/search/replace/g\\ctrl-c");
2146     QCOMPARE(replaceHistory(), QStringList());
2147     FinishTest("");
2148 
2149     // A non-sed-replace should leave the replace history unchanged.
2150     clearReplaceHistory();
2151     BeginTest("");
2152     TestPressKey(":s,search/replace/g\\enter");
2153     QCOMPARE(replaceHistory(), QStringList());
2154     FinishTest("");
2155 
2156     // Misc tests for sed replace.  These are for the *generic* Kate sed replace; they should all
2157     // use EmulatedCommandBarTests' built-in command execution stuff (\\:<commandtoexecute>\\\) rather than
2158     // invoking a EmulatedCommandBar and potentially doing some Vim-specific transforms to
2159     // the command.
2160     DoTest("foo foo foo", "\\:s/foo/bar/\\", "bar foo foo");
2161     DoTest("foo foo xyz foo", "\\:s/foo/bar/g\\", "bar bar xyz bar");
2162     DoTest("foofooxyzfoo", "\\:s/foo/bar/g\\", "barbarxyzbar");
2163     DoTest("foofooxyzfoo", "\\:s/foo/b/g\\", "bbxyzb");
2164     DoTest("ffxyzf", "\\:s/f/b/g\\", "bbxyzb");
2165     DoTest("ffxyzf", "\\:s/f/bar/g\\", "barbarxyzbar");
2166     DoTest("foo Foo fOO FOO foo", "\\:s/foo/bar/\\", "bar Foo fOO FOO foo");
2167     DoTest("Foo foo fOO FOO foo", "\\:s/foo/bar/\\", "Foo bar fOO FOO foo");
2168     DoTest("Foo foo fOO FOO foo", "\\:s/foo/bar/g\\", "Foo bar fOO FOO bar");
2169     DoTest("foo Foo fOO FOO foo", "\\:s/foo/bar/i\\", "bar Foo fOO FOO foo");
2170     DoTest("Foo foo fOO FOO foo", "\\:s/foo/bar/i\\", "bar foo fOO FOO foo");
2171     DoTest("Foo foo fOO FOO foo", "\\:s/foo/bar/gi\\", "bar bar bar bar bar");
2172     DoTest("Foo foo fOO FOO foo", "\\:s/foo/bar/ig\\", "bar bar bar bar bar");
2173     // There are some oddities to do with how EmulatedCommandBarTest's "execute command directly" stuff works with selected ranges:
2174     // basically, we need to do our selection in Visual mode, then exit back to Normal mode before running the
2175     // command.
2176     DoTest("foo foo\nbar foo foo\nxyz foo foo\nfoo bar foo", "jVj\\esc\\:'<,'>s/foo/bar/\\", "foo foo\nbar bar foo\nxyz bar foo\nfoo bar foo");
2177     DoTest("foo foo\nbar foo foo\nxyz foo foo\nfoo bar foo", "jVj\\esc\\:'<,'>s/foo/bar/g\\", "foo foo\nbar bar bar\nxyz bar bar\nfoo bar foo");
2178     DoTest("Foo foo fOO FOO foo", "\\:s/foo/barfoo/g\\", "Foo barfoo fOO FOO barfoo");
2179     DoTest("Foo foo fOO FOO foo", "\\:s/foo/foobar/g\\", "Foo foobar fOO FOO foobar");
2180     DoTest("axyzb", "\\:s/a(.*)b/d\\\\1f/\\", "dxyzf");
2181     DoTest("ayxzzyxzfddeefdb", "\\:s/a([xyz]+)([def]+)b/<\\\\1|\\\\2>/\\", "<yxzzyxz|fddeefd>");
2182     DoTest("foo", "\\:s/.*//g\\", "");
2183     DoTest("foo", "\\:s/.*/f/g\\", "f");
2184     DoTest("foo/bar", "\\:s/foo\\\\/bar/123\\\\/xyz/g\\", "123/xyz");
2185     DoTest("foo:bar", "\\:s:foo\\\\:bar:123\\\\:xyz:g\\", "123:xyz");
2186     const bool oldReplaceTabsDyn = kate_document->config()->replaceTabsDyn();
2187     kate_document->config()->setReplaceTabsDyn(false);
2188     DoTest("foo\tbar", "\\:s/foo\\\\tbar/replace/g\\", "replace");
2189     DoTest("foo\tbar", "\\:s/foo\\\\tbar/rep\\\\tlace/g\\", "rep\tlace");
2190     kate_document->config()->setReplaceTabsDyn(oldReplaceTabsDyn);
2191     DoTest("foo", "\\:s/foo/replaceline1\\\\nreplaceline2/g\\", "replaceline1\nreplaceline2");
2192     DoTest("foofoo", "\\:s/foo/replaceline1\\\\nreplaceline2/g\\", "replaceline1\nreplaceline2replaceline1\nreplaceline2");
2193     DoTest("foofoo\nfoo", "\\:s/foo/replaceline1\\\\nreplaceline2/g\\", "replaceline1\nreplaceline2replaceline1\nreplaceline2\nfoo");
2194     DoTest("fooafoob\nfooc\nfood",
2195            "Vj\\esc\\:'<,'>s/foo/replaceline1\\\\nreplaceline2/g\\",
2196            "replaceline1\nreplaceline2areplaceline1\nreplaceline2b\nreplaceline1\nreplaceline2c\nfood");
2197     DoTest("fooafoob\nfooc\nfood",
2198            "Vj\\esc\\:'<,'>s/foo/replaceline1\\\\nreplaceline2/\\",
2199            "replaceline1\nreplaceline2afoob\nreplaceline1\nreplaceline2c\nfood");
2200     DoTest("fooafoob\nfooc\nfood",
2201            "Vj\\esc\\:'<,'>s/foo/replaceline1\\\\nreplaceline2\\\\nreplaceline3/g\\",
2202            "replaceline1\nreplaceline2\nreplaceline3areplaceline1\nreplaceline2\nreplaceline3b\nreplaceline1\nreplaceline2\nreplaceline3c\nfood");
2203     DoTest("foofoo", "\\:s/foo/replace\\\\nfoo/g\\", "replace\nfooreplace\nfoo");
2204     DoTest("foofoo", "\\:s/foo/replacefoo\\\\nfoo/g\\", "replacefoo\nfooreplacefoo\nfoo");
2205     DoTest("foofoo", "\\:s/foo/replacefoo\\\\n/g\\", "replacefoo\nreplacefoo\n");
2206     DoTest("ff", "\\:s/f/f\\\\nf/g\\", "f\nff\nf");
2207     DoTest("ff", "\\:s/f/f\\\\n/g\\", "f\nf\n");
2208     DoTest("foo\nbar", "\\:s/foo\\\\n//g\\", "bar");
2209     DoTest("foo\n\n\nbar", "\\:s/foo(\\\\n)*bar//g\\", "");
2210     DoTest("foo\n\n\nbar", "\\:s/foo(\\\\n*)bar/123\\\\1456/g\\", "123\n\n\n456");
2211     DoTest("xAbCy", "\\:s/x(.)(.)(.)y/\\\\L\\\\1\\\\U\\\\2\\\\3/g\\", "aBC");
2212     DoTest("foo", "\\:s/foo/\\\\a/g\\", "\x07");
2213     // End "generic" (i.e. not involving any Vi mode tricks/ transformations) sed replace tests: the remaining
2214     // ones should go via the EmulatedCommandBar.
2215     BeginTest("foo foo\nxyz\nfoo");
2216     TestPressKey(":%s/foo/bar/g\\enter");
2217     verifyShowsNumberOfReplacementsAcrossNumberOfLines(3, 2);
2218     FinishTest("bar bar\nxyz\nbar");
2219 
2220     // ctrl-p on the first character of the search term in a sed-replace should
2221     // invoke search history completion.
2222     clearSearchHistory();
2223     vi_global->searchHistory()->append("search");
2224     BeginTest("");
2225     TestPressKey(":s/search/replace/g\\ctrl-b\\right\\right\\ctrl-p");
2226     verifyCommandBarCompletionVisible();
2227     TestPressKey("\\ctrl-c"); // Dismiss completer
2228     TestPressKey("\\ctrl-c"); // Dismiss bar.
2229     TestPressKey(":'<,'>s/search/replace/g\\ctrl-b\\right\\right\\right\\right\\right\\right\\right\\ctrl-p");
2230     verifyCommandBarCompletionVisible();
2231     TestPressKey("\\ctrl-c"); // Dismiss completer
2232     TestPressKey("\\ctrl-c"); // Dismiss bar.
2233     FinishTest("");
2234 
2235     // ctrl-p on the last character of the search term in a sed-replace should
2236     // invoke search history completion.
2237     clearSearchHistory();
2238     vi_global->searchHistory()->append("xyz");
2239     BeginTest("");
2240     TestPressKey(":s/xyz/replace/g\\ctrl-b\\right\\right\\right\\right\\ctrl-p");
2241     verifyCommandBarCompletionVisible();
2242     TestPressKey("\\ctrl-c"); // Dismiss completer
2243     TestPressKey("\\ctrl-c"); // Dismiss bar.
2244     QVERIFY(!emulatedCommandBar->isVisible());
2245     TestPressKey(":'<,'>s/xyz/replace/g\\ctrl-b\\right\\right\\right\\right\\right\\right\\right\\right\\right\\ctrl-p");
2246     verifyCommandBarCompletionVisible();
2247     TestPressKey("\\ctrl-c"); // Dismiss completer
2248     TestPressKey("\\ctrl-c"); // Dismiss bar.
2249     FinishTest("");
2250 
2251     // ctrl-p on some arbitrary character of the search term in a sed-replace should
2252     // invoke search history completion.
2253     clearSearchHistory();
2254     vi_global->searchHistory()->append("xyzaaaaaa");
2255     BeginTest("");
2256     TestPressKey(":s/xyzaaaaaa/replace/g\\ctrl-b\\right\\right\\right\\right\\right\\right\\right\\ctrl-p");
2257     verifyCommandBarCompletionVisible();
2258     TestPressKey("\\ctrl-c"); // Dismiss completer
2259     TestPressKey("\\ctrl-c"); // Dismiss bar.
2260     TestPressKey(":'<,'>s/xyzaaaaaa/replace/g\\ctrl-b\\right\\right\\right\\right\\right\\right\\right\\right\\right\\right\\right\\right\\ctrl-p");
2261     verifyCommandBarCompletionVisible();
2262     TestPressKey("\\ctrl-c"); // Dismiss completer
2263     TestPressKey("\\ctrl-c"); // Dismiss bar.
2264     FinishTest("");
2265 
2266     // ctrl-p on some character *after" the search term should
2267     // *not* invoke search history completion.
2268     // Note: in s/xyz/replace/g, the "/" after the "z" is counted as part of the find term;
2269     // this allows us to do xyz<ctrl-p> and get completions.
2270     clearSearchHistory();
2271     clearCommandHistory();
2272     clearReplaceHistory();
2273     vi_global->searchHistory()->append("xyz");
2274     BeginTest("");
2275     TestPressKey(":s/xyz/replace/g\\ctrl-b\\right\\right\\right\\right\\right\\right\\ctrl-p");
2276     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
2277     TestPressKey("\\ctrl-c"); // Dismiss completer
2278     TestPressKey("\\ctrl-c"); // Dismiss bar.
2279     clearSearchHistory();
2280     clearCommandHistory();
2281     TestPressKey(":'<,'>s/xyz/replace/g\\ctrl-b\\right\\right\\right\\right\\right\\right\\right\\right\\right\\right\\right\\right\\ctrl-p");
2282     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
2283     TestPressKey("\\ctrl-c"); // Dismiss completer
2284     TestPressKey("\\ctrl-c"); // Dismiss bar.
2285 
2286     // Make sure it's the search history we're invoking.
2287     clearSearchHistory();
2288     vi_global->searchHistory()->append("xyzaaaaaa");
2289     BeginTest("");
2290     TestPressKey(":s//replace/g\\ctrl-b\\right\\right\\ctrl-p");
2291     verifyCommandBarCompletionVisible();
2292     verifyCommandBarCompletionsMatches(QStringList() << "xyzaaaaaa");
2293     TestPressKey("\\ctrl-c"); // Dismiss completer
2294     TestPressKey("\\ctrl-c"); // Dismiss bar.
2295     TestPressKey(":.,.+6s//replace/g\\ctrl-b\\right\\right\\right\\right\\right\\right\\right\\ctrl-p");
2296     verifyCommandBarCompletionsMatches(QStringList() << "xyzaaaaaa");
2297     TestPressKey("\\ctrl-c"); // Dismiss completer
2298     TestPressKey("\\ctrl-c"); // Dismiss bar.
2299     FinishTest("");
2300 
2301     // (Search history should be reversed).
2302     clearSearchHistory();
2303     vi_global->searchHistory()->append("xyzaaaaaa");
2304     vi_global->searchHistory()->append("abc");
2305     vi_global->searchHistory()->append("def");
2306     BeginTest("");
2307     TestPressKey(":s//replace/g\\ctrl-b\\right\\right\\ctrl-p");
2308     verifyCommandBarCompletionVisible();
2309     verifyCommandBarCompletionsMatches(QStringList() << "def"
2310                                                      << "abc"
2311                                                      << "xyzaaaaaa");
2312     TestPressKey("\\ctrl-c"); // Dismiss completer
2313     TestPressKey("\\ctrl-c"); // Dismiss bar.
2314     FinishTest("");
2315 
2316     // Completion prefix is the current find term.
2317     clearSearchHistory();
2318     vi_global->searchHistory()->append("xy:zaaaaaa");
2319     vi_global->searchHistory()->append("abc");
2320     vi_global->searchHistory()->append("def");
2321     vi_global->searchHistory()->append("xy:zbaaaaa");
2322     vi_global->searchHistory()->append("xy:zcaaaaa");
2323     BeginTest("");
2324     TestPressKey(":s//replace/g\\ctrl-dxy:z\\ctrl-p");
2325     verifyCommandBarCompletionVisible();
2326     verifyCommandBarCompletionsMatches(QStringList() << "xy:zcaaaaa"
2327                                                      << "xy:zbaaaaa"
2328                                                      << "xy:zaaaaaa");
2329     TestPressKey("\\ctrl-c"); // Dismiss completer
2330     TestPressKey("\\ctrl-c"); // Dismiss bar.
2331     FinishTest("");
2332 
2333     // Replace entire search term with completion.
2334     clearSearchHistory();
2335     vi_global->searchHistory()->append("ab,cd");
2336     vi_global->searchHistory()->append("ab,xy");
2337     BeginTest("");
2338     TestPressKey(":s//replace/g\\ctrl-dab,\\ctrl-p\\ctrl-p");
2339     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/ab,cd/replace/g"));
2340     TestPressKey("\\ctrl-c"); // Dismiss completer
2341     TestPressKey("\\ctrl-c"); // Dismiss bar.
2342     TestPressKey(":'<,'>s//replace/g\\ctrl-dab,\\ctrl-p\\ctrl-p");
2343     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("'<,'>s/ab,cd/replace/g"));
2344     TestPressKey("\\ctrl-c"); // Dismiss completer
2345     TestPressKey("\\ctrl-c"); // Dismiss bar.
2346     FinishTest("");
2347 
2348     // Place the cursor at the end of find term.
2349     clearSearchHistory();
2350     vi_global->searchHistory()->append("ab,xy");
2351     BeginTest("");
2352     TestPressKey(":s//replace/g\\ctrl-dab,\\ctrl-pX");
2353     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/ab,xyX/replace/g"));
2354     TestPressKey("\\ctrl-c"); // Dismiss completer
2355     TestPressKey("\\ctrl-c"); // Dismiss bar.
2356     TestPressKey(":.,.+7s//replace/g\\ctrl-dab,\\ctrl-pX");
2357     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(".,.+7s/ab,xyX/replace/g"));
2358     TestPressKey("\\ctrl-c"); // Dismiss completer
2359     TestPressKey("\\ctrl-c"); // Dismiss bar.
2360     FinishTest("");
2361 
2362     // Leave find term unchanged if there is no search history.
2363     clearSearchHistory();
2364     BeginTest("");
2365     TestPressKey(":s/nose/replace/g\\ctrl-b\\right\\right\\right\\ctrl-p");
2366     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/nose/replace/g"));
2367     TestPressKey("\\ctrl-c"); // Dismiss completer
2368     TestPressKey("\\ctrl-c"); // Dismiss bar.
2369     FinishTest("");
2370 
2371     // Leave cursor position unchanged if there is no search history.
2372     clearSearchHistory();
2373     BeginTest("");
2374     TestPressKey(":s/nose/replace/g\\ctrl-b\\right\\right\\right\\ctrl-pX");
2375     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/nXose/replace/g"));
2376     TestPressKey("\\ctrl-c"); // Dismiss completer
2377     TestPressKey("\\ctrl-c"); // Dismiss bar.
2378     FinishTest("");
2379 
2380     // ctrl-p on the first character of the replace term in a sed-replace should
2381     // invoke replace history completion.
2382     clearSearchHistory();
2383     clearReplaceHistory();
2384     clearCommandHistory();
2385     vi_global->replaceHistory()->append("replace");
2386     BeginTest("");
2387     TestPressKey(":s/search/replace/g\\left\\left\\left\\left\\left\\left\\left\\left\\left\\ctrl-p");
2388     verifyCommandBarCompletionVisible();
2389     TestPressKey("\\ctrl-c"); // Dismiss completer
2390     TestPressKey("\\ctrl-c"); // Dismiss bar.
2391     TestPressKey(":'<,'>s/search/replace/g\\left\\left\\left\\left\\left\\left\\left\\left\\left\\ctrl-p");
2392     verifyCommandBarCompletionVisible();
2393     TestPressKey("\\ctrl-c"); // Dismiss completer
2394     TestPressKey("\\ctrl-c"); // Dismiss bar.
2395     FinishTest("");
2396 
2397     // ctrl-p on the last character of the replace term in a sed-replace should
2398     // invoke replace history completion.
2399     clearReplaceHistory();
2400     vi_global->replaceHistory()->append("replace");
2401     BeginTest("");
2402     TestPressKey(":s/xyz/replace/g\\left\\left\\ctrl-p");
2403     verifyCommandBarCompletionVisible();
2404     TestPressKey("\\ctrl-c"); // Dismiss completer
2405     TestPressKey("\\ctrl-c"); // Dismiss bar.
2406     TestPressKey(":'<,'>s/xyz/replace/g\\left\\left\\ctrl-p");
2407     verifyCommandBarCompletionVisible();
2408     TestPressKey("\\ctrl-c"); // Dismiss completer
2409     TestPressKey("\\ctrl-c"); // Dismiss bar.
2410     FinishTest("");
2411 
2412     // ctrl-p on some arbitrary character of the search term in a sed-replace should
2413     // invoke search history completion.
2414     clearReplaceHistory();
2415     vi_global->replaceHistory()->append("replaceaaaaaa");
2416     BeginTest("");
2417     TestPressKey(":s/xyzaaaaaa/replace/g\\left\\left\\left\\left\\ctrl-p");
2418     verifyCommandBarCompletionVisible();
2419     TestPressKey("\\ctrl-c"); // Dismiss completer
2420     TestPressKey("\\ctrl-c"); // Dismiss bar.
2421     TestPressKey(":'<,'>s/xyzaaaaaa/replace/g\\left\\left\\left\\left\\ctrl-p");
2422     verifyCommandBarCompletionVisible();
2423     TestPressKey("\\ctrl-c"); // Dismiss completer
2424     TestPressKey("\\ctrl-c"); // Dismiss bar.
2425     FinishTest("");
2426 
2427     // ctrl-p on some character *after" the replace term should
2428     // *not* invoke replace history completion.
2429     // Note: in s/xyz/replace/g, the "/" after the "e" is counted as part of the replace term;
2430     // this allows us to do replace<ctrl-p> and get completions.
2431     clearSearchHistory();
2432     clearCommandHistory();
2433     clearReplaceHistory();
2434     vi_global->replaceHistory()->append("xyz");
2435     BeginTest("");
2436     TestPressKey(":s/xyz/replace/g\\left\\ctrl-p");
2437     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
2438     TestPressKey("\\ctrl-c"); // Dismiss completer
2439     TestPressKey("\\ctrl-c"); // Dismiss bar.
2440     clearSearchHistory();
2441     clearCommandHistory();
2442     TestPressKey(":'<,'>s/xyz/replace/g\\left\\ctrl-p");
2443     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
2444     TestPressKey("\\ctrl-c"); // Dismiss completer
2445     TestPressKey("\\ctrl-c"); // Dismiss bar.
2446 
2447     // (Replace history should be reversed).
2448     clearReplaceHistory();
2449     vi_global->replaceHistory()->append("xyzaaaaaa");
2450     vi_global->replaceHistory()->append("abc");
2451     vi_global->replaceHistory()->append("def");
2452     BeginTest("");
2453     TestPressKey(":s/search//g\\left\\left\\ctrl-p");
2454     verifyCommandBarCompletionVisible();
2455     verifyCommandBarCompletionsMatches(QStringList() << "def"
2456                                                      << "abc"
2457                                                      << "xyzaaaaaa");
2458     TestPressKey("\\ctrl-c"); // Dismiss completer
2459     TestPressKey("\\ctrl-c"); // Dismiss bar.
2460     FinishTest("");
2461 
2462     // Completion prefix is the current replace term.
2463     clearReplaceHistory();
2464     vi_global->replaceHistory()->append("xy:zaaaaaa");
2465     vi_global->replaceHistory()->append("abc");
2466     vi_global->replaceHistory()->append("def");
2467     vi_global->replaceHistory()->append("xy:zbaaaaa");
2468     vi_global->replaceHistory()->append("xy:zcaaaaa");
2469     BeginTest("");
2470     TestPressKey(":'<,'>s/replace/search/g\\ctrl-fxy:z\\ctrl-p");
2471     verifyCommandBarCompletionVisible();
2472     verifyCommandBarCompletionsMatches(QStringList() << "xy:zcaaaaa"
2473                                                      << "xy:zbaaaaa"
2474                                                      << "xy:zaaaaaa");
2475     TestPressKey("\\ctrl-c"); // Dismiss completer
2476     TestPressKey("\\ctrl-c"); // Dismiss bar.
2477     FinishTest("");
2478 
2479     // Replace entire search term with completion.
2480     clearReplaceHistory();
2481     clearSearchHistory();
2482     vi_global->replaceHistory()->append("ab,cd");
2483     vi_global->replaceHistory()->append("ab,xy");
2484     BeginTest("");
2485     TestPressKey(":s/search//g\\ctrl-fab,\\ctrl-p\\ctrl-p");
2486     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/search/ab,cd/g"));
2487     TestPressKey("\\ctrl-c"); // Dismiss completer
2488     TestPressKey("\\ctrl-c"); // Dismiss bar.
2489     TestPressKey(":'<,'>s/search//g\\ctrl-fab,\\ctrl-p\\ctrl-p");
2490     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("'<,'>s/search/ab,cd/g"));
2491     TestPressKey("\\ctrl-c"); // Dismiss completer
2492     TestPressKey("\\ctrl-c"); // Dismiss bar.
2493     FinishTest("");
2494 
2495     // Place the cursor at the end of replace term.
2496     clearReplaceHistory();
2497     vi_global->replaceHistory()->append("ab,xy");
2498     BeginTest("");
2499     TestPressKey(":s/search//g\\ctrl-fab,\\ctrl-pX");
2500     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/search/ab,xyX/g"));
2501     TestPressKey("\\ctrl-c"); // Dismiss completer
2502     TestPressKey("\\ctrl-c"); // Dismiss bar.
2503     TestPressKey(":.,.+7s/search//g\\ctrl-fab,\\ctrl-pX");
2504     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(".,.+7s/search/ab,xyX/g"));
2505     TestPressKey("\\ctrl-c"); // Dismiss completer
2506     TestPressKey("\\ctrl-c"); // Dismiss bar.
2507     FinishTest("");
2508 
2509     // Leave replace term unchanged if there is no replace history.
2510     clearReplaceHistory();
2511     BeginTest("");
2512     TestPressKey(":s/nose/replace/g\\left\\left\\left\\ctrl-p");
2513     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/nose/replace/g"));
2514     TestPressKey("\\ctrl-c"); // Dismiss completer
2515     TestPressKey("\\ctrl-c"); // Dismiss bar.
2516     FinishTest("");
2517 
2518     // Leave cursor position unchanged if there is no replace history.
2519     clearSearchHistory();
2520     BeginTest("");
2521     TestPressKey(":s/nose/replace/g\\left\\left\\left\\left\\ctrl-pX");
2522     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/nose/replaXce/g"));
2523     TestPressKey("\\ctrl-c"); // Dismiss completer
2524     TestPressKey("\\ctrl-c"); // Dismiss bar.
2525     FinishTest("");
2526 
2527     // Invoke replacement history even when the "find" term is empty.
2528     BeginTest("");
2529     clearReplaceHistory();
2530     clearSearchHistory();
2531     vi_global->replaceHistory()->append("ab,xy");
2532     vi_global->searchHistory()->append("whoops");
2533     TestPressKey(":s///g\\ctrl-f\\ctrl-p");
2534     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s//ab,xy/g"));
2535     TestPressKey("\\ctrl-c"); // Dismiss completer
2536     TestPressKey("\\ctrl-c"); // Dismiss bar.
2537     FinishTest("");
2538 
2539     // Move the cursor back to the last manual edit point when aborting completion.
2540     BeginTest("");
2541     clearSearchHistory();
2542     vi_global->searchHistory()->append("xyzaaaaa");
2543     TestPressKey(":s/xyz/replace/g\\ctrl-b\\right\\right\\right\\right\\righta\\ctrl-p\\ctrl-[X");
2544     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/xyzaX/replace/g"));
2545     TestPressKey("\\ctrl-c"); // Dismiss completer
2546     TestPressKey("\\ctrl-c"); // Dismiss bar.
2547     FinishTest("");
2548 
2549     // Don't blank the "find" term if there is no search history that begins with the
2550     // current "find" term.
2551     BeginTest("");
2552     clearSearchHistory();
2553     vi_global->searchHistory()->append("doesnothavexyzasaprefix");
2554     TestPressKey(":s//replace/g\\ctrl-dxyz\\ctrl-p");
2555     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/xyz/replace/g"));
2556     TestPressKey("\\ctrl-c"); // Dismiss completer
2557     TestPressKey("\\ctrl-c"); // Dismiss bar.
2558     FinishTest("");
2559 
2560     // Escape the delimiter if it occurs in a search history term - searching for it likely won't
2561     // work, but at least it won't crash!
2562     BeginTest("");
2563     clearSearchHistory();
2564     vi_global->searchHistory()->append("search");
2565     vi_global->searchHistory()->append("aa/aa\\/a");
2566     vi_global->searchHistory()->append("ss/ss");
2567     TestPressKey(":s//replace/g\\ctrl-d\\ctrl-p");
2568     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/ss\\/ss/replace/g"));
2569     TestPressKey("\\ctrl-p");
2570     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/aa\\/aa\\/a/replace/g"));
2571     TestPressKey("\\ctrl-p");
2572     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/search/replace/g"));
2573     TestPressKey("\\ctrl-c"); // Dismiss completer
2574     TestPressKey("\\ctrl-c"); // Dismiss bar.
2575     clearSearchHistory(); // Now do the same, but with a different delimiter.
2576     vi_global->searchHistory()->append("search");
2577     vi_global->searchHistory()->append("aa:aa\\:a");
2578     vi_global->searchHistory()->append("ss:ss");
2579     TestPressKey(":s::replace:g\\ctrl-d\\ctrl-p");
2580     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s:ss\\:ss:replace:g"));
2581     TestPressKey("\\ctrl-p");
2582     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s:aa\\:aa\\:a:replace:g"));
2583     TestPressKey("\\ctrl-p");
2584     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s:search:replace:g"));
2585     TestPressKey("\\ctrl-c"); // Dismiss completer
2586     TestPressKey("\\ctrl-c"); // Dismiss bar.
2587     FinishTest("");
2588 
2589     // Remove \C if occurs in search history.
2590     BeginTest("");
2591     clearSearchHistory();
2592     vi_global->searchHistory()->append("s\\Cear\\\\Cch");
2593     TestPressKey(":s::replace:g\\ctrl-d\\ctrl-p");
2594     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s:sear\\\\Cch:replace:g"));
2595     TestPressKey("\\ctrl-c"); // Dismiss completer
2596     TestPressKey("\\ctrl-c"); // Dismiss bar.
2597     FinishTest("");
2598 
2599     // Don't blank the "replace" term if there is no search history that begins with the
2600     // current "replace" term.
2601     BeginTest("");
2602     clearReplaceHistory();
2603     vi_global->replaceHistory()->append("doesnothavexyzasaprefix");
2604     TestPressKey(":s/search//g\\ctrl-fxyz\\ctrl-p");
2605     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/search/xyz/g"));
2606     TestPressKey("\\ctrl-c"); // Dismiss completer
2607     TestPressKey("\\ctrl-c"); // Dismiss bar.
2608     FinishTest("");
2609 
2610     // Escape the delimiter if it occurs in a replace history term - searching for it likely won't
2611     // work, but at least it won't crash!
2612     BeginTest("");
2613     clearReplaceHistory();
2614     vi_global->replaceHistory()->append("replace");
2615     vi_global->replaceHistory()->append("aa/aa\\/a");
2616     vi_global->replaceHistory()->append("ss/ss");
2617     TestPressKey(":s/search//g\\ctrl-f\\ctrl-p");
2618     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/search/ss\\/ss/g"));
2619     TestPressKey("\\ctrl-p");
2620     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/search/aa\\/aa\\/a/g"));
2621     TestPressKey("\\ctrl-p");
2622     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/search/replace/g"));
2623     TestPressKey("\\ctrl-c"); // Dismiss completer
2624     TestPressKey("\\ctrl-c"); // Dismiss bar.
2625     clearReplaceHistory(); // Now do the same, but with a different delimiter.
2626     vi_global->replaceHistory()->append("replace");
2627     vi_global->replaceHistory()->append("aa:aa\\:a");
2628     vi_global->replaceHistory()->append("ss:ss");
2629     TestPressKey(":s:search::g\\ctrl-f\\ctrl-p");
2630     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s:search:ss\\:ss:g"));
2631     TestPressKey("\\ctrl-p");
2632     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s:search:aa\\:aa\\:a:g"));
2633     TestPressKey("\\ctrl-p");
2634     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s:search:replace:g"));
2635     TestPressKey("\\ctrl-c"); // Dismiss completer
2636     TestPressKey("\\ctrl-c"); // Dismiss bar.
2637     FinishTest("");
2638 
2639     // In search mode, don't blank current text on completion if there is no item in the search history which
2640     // has the current text as a prefix.
2641     BeginTest("");
2642     clearSearchHistory();
2643     vi_global->searchHistory()->append("doesnothavexyzasaprefix");
2644     TestPressKey("/xyz\\ctrl-p");
2645     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("xyz"));
2646     TestPressKey("\\ctrl-c"); // Dismiss completer
2647     TestPressKey("\\ctrl-c"); // Dismiss bar.
2648     FinishTest("");
2649 
2650     // Don't dismiss the command completion just because the cursor ends up *temporarily* at a place where
2651     // command completion is disallowed when cycling through completions.
2652     BeginTest("");
2653     TestPressKey(":set/se\\left\\left\\left-\\ctrl-p");
2654     verifyCommandBarCompletionVisible();
2655     TestPressKey("\\ctrl-c"); // Dismiss completer
2656     TestPressKey("\\ctrl-c"); // Dismiss bar.
2657     FinishTest("");
2658 
2659     // Don't expand mappings meant for Normal mode in the emulated command bar.
2660     clearAllMappings();
2661     vi_global->mappings()->add(Mappings::NormalModeMapping, "foo", "xyz", Mappings::NonRecursive);
2662     DoTest("bar foo xyz", "/foo\\enterrX", "bar Xoo xyz");
2663     clearAllMappings();
2664 
2665     // Incremental search and replace.
2666     QLabel *interactiveSedReplaceLabel = emulatedCommandBar->findChild<QLabel *>("interactivesedreplace");
2667     QVERIFY(interactiveSedReplaceLabel);
2668 
2669     BeginTest("foo");
2670     TestPressKey(":s/foo/bar/c\\enter");
2671     QVERIFY(interactiveSedReplaceLabel->isVisible());
2672     QVERIFY(!commandResponseMessageDisplay()->isVisible());
2673     QVERIFY(!emulatedCommandBarTextEdit()->isVisible());
2674     QVERIFY(!emulatedCommandTypeIndicator()->isVisible());
2675     TestPressKey("\\ctrl-c"); // Dismiss search and replace.
2676     QVERIFY(!emulatedCommandBar->isVisible());
2677     FinishTest("foo");
2678 
2679     // Clear the flag that stops the command response from being shown after an incremental search and
2680     // replace, and also make sure that the edit and bar type indicator are not forcibly hidden.
2681     BeginTest("foo");
2682     TestPressKey(":s/foo/bar/c\\enter\\ctrl-c");
2683     TestPressKey(":s/foo/bar/");
2684     QVERIFY(emulatedCommandBarTextEdit()->isVisible());
2685     QVERIFY(emulatedCommandTypeIndicator()->isVisible());
2686     TestPressKey("\\enter");
2687     QVERIFY(commandResponseMessageDisplay()->isVisible());
2688     FinishTest("bar");
2689 
2690     // Hide the incremental search and replace label when we show the bar.
2691     BeginTest("foo");
2692     TestPressKey(":s/foo/bar/c\\enter\\ctrl-c");
2693     TestPressKey(":");
2694     QVERIFY(!interactiveSedReplaceLabel->isVisible());
2695     TestPressKey("\\ctrl-c");
2696     FinishTest("foo");
2697 
2698     // The "c" marker can be anywhere in the three chars following the delimiter.
2699     BeginTest("foo");
2700     TestPressKey(":s/foo/bar/cgi\\enter");
2701     QVERIFY(interactiveSedReplaceLabel->isVisible());
2702     TestPressKey("\\ctrl-c");
2703     FinishTest("foo");
2704     BeginTest("foo");
2705     TestPressKey(":s/foo/bar/igc\\enter");
2706     QVERIFY(interactiveSedReplaceLabel->isVisible());
2707     TestPressKey("\\ctrl-c");
2708     FinishTest("foo");
2709     BeginTest("foo");
2710     TestPressKey(":s/foo/bar/icg\\enter");
2711     QVERIFY(interactiveSedReplaceLabel->isVisible());
2712     TestPressKey("\\ctrl-c");
2713     FinishTest("foo");
2714     BeginTest("foo");
2715     TestPressKey(":s/foo/bar/ic\\enter");
2716     QVERIFY(interactiveSedReplaceLabel->isVisible());
2717     TestPressKey("\\ctrl-c");
2718     FinishTest("foo");
2719     BeginTest("foo");
2720     TestPressKey(":s/foo/bar/ci\\enter");
2721     QVERIFY(interactiveSedReplaceLabel->isVisible());
2722     TestPressKey("\\ctrl-c");
2723     FinishTest("foo");
2724 
2725     // Emulated command bar is still active during an incremental search and replace.
2726     BeginTest("foo");
2727     TestPressKey(":s/foo/bar/c\\enter");
2728     TestPressKey("idef\\esc");
2729     FinishTest("foo");
2730 
2731     // Emulated command bar text is not edited during an incremental search and replace.
2732     BeginTest("foo");
2733     TestPressKey(":s/foo/bar/c\\enter");
2734     TestPressKey("def");
2735     QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/foo/bar/c"));
2736     TestPressKey("\\ctrl-c");
2737     FinishTest("foo");
2738 
2739     // Pressing "n" when there is only a single  change we can make aborts incremental search
2740     // and replace.
2741     BeginTest("foo");
2742     TestPressKey(":s/foo/bar/c\\enter");
2743     TestPressKey("n");
2744     QVERIFY(!interactiveSedReplaceLabel->isVisible());
2745     TestPressKey("ixyz\\esc");
2746     FinishTest("xyzfoo");
2747 
2748     // Pressing "n" when there is only a single  change we can make aborts incremental search
2749     // and replace, and shows the no replacements on no lines.
2750     BeginTest("foo");
2751     TestPressKey(":s/foo/bar/c\\enter");
2752     TestPressKey("n");
2753     QVERIFY(commandResponseMessageDisplay()->isVisible());
2754     verifyShowsNumberOfReplacementsAcrossNumberOfLines(0, 0);
2755     FinishTest("foo");
2756 
2757     // First possible match is highlighted when we start an incremental search and replace, and
2758     // cleared if we press 'n'.
2759     BeginTest(" xyz  123 foo bar");
2760     TestPressKey(":s/foo/bar/gc\\enter");
2761     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
2762     QCOMPARE(rangesOnFirstLine().first()->start().line(), 0);
2763     QCOMPARE(rangesOnFirstLine().first()->start().column(), 10);
2764     QCOMPARE(rangesOnFirstLine().first()->end().line(), 0);
2765     QCOMPARE(rangesOnFirstLine().first()->end().column(), 13);
2766     TestPressKey("n");
2767     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
2768     FinishTest(" xyz  123 foo bar");
2769 
2770     // Second possible match highlighted if we start incremental search and replace and press 'n',
2771     // cleared if we press 'n' again.
2772     BeginTest(" xyz  123 foo foo bar");
2773     TestPressKey(":s/foo/bar/gc\\enter");
2774     TestPressKey("n");
2775     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
2776     QCOMPARE(rangesOnFirstLine().first()->start().line(), 0);
2777     QCOMPARE(rangesOnFirstLine().first()->start().column(), 14);
2778     QCOMPARE(rangesOnFirstLine().first()->end().line(), 0);
2779     QCOMPARE(rangesOnFirstLine().first()->end().column(), 17);
2780     TestPressKey("n");
2781     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
2782     FinishTest(" xyz  123 foo foo bar");
2783 
2784     // Perform replacement if we press 'y' on the first match.
2785     BeginTest(" xyz  foo 123 foo bar");
2786     TestPressKey(":s/foo/bar/gc\\enter");
2787     TestPressKey("y");
2788     TestPressKey("\\ctrl-c");
2789     FinishTest(" xyz  bar 123 foo bar");
2790 
2791     // Replacement uses grouping, etc.
2792     BeginTest(" xyz  def 123 foo bar");
2793     TestPressKey(":s/d\\\\(e\\\\)\\\\(f\\\\)/x\\\\1\\\\U\\\\2/gc\\enter");
2794     TestPressKey("y");
2795     TestPressKey("\\ctrl-c");
2796     FinishTest(" xyz  xeF 123 foo bar");
2797 
2798     // On replacement, highlight next match.
2799     BeginTest(" xyz  foo 123 foo bar");
2800     TestPressKey(":s/foo/bar/cg\\enter");
2801     TestPressKey("y");
2802     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
2803     QCOMPARE(rangesOnFirstLine().first()->start().line(), 0);
2804     QCOMPARE(rangesOnFirstLine().first()->start().column(), 14);
2805     QCOMPARE(rangesOnFirstLine().first()->end().line(), 0);
2806     QCOMPARE(rangesOnFirstLine().first()->end().column(), 17);
2807     TestPressKey("\\ctrl-c");
2808     FinishTest(" xyz  bar 123 foo bar");
2809 
2810     // On replacement, if there is no further match, abort incremental search and replace.
2811     BeginTest(" xyz  foo 123 foa bar");
2812     TestPressKey(":s/foo/bar/cg\\enter");
2813     TestPressKey("y");
2814     QVERIFY(commandResponseMessageDisplay()->isVisible());
2815     TestPressKey("ggidone\\esc");
2816     FinishTest("done xyz  bar 123 foa bar");
2817 
2818     // After replacement, the next match is sought after the end of the replacement text.
2819     BeginTest("foofoo");
2820     TestPressKey(":s/foo/barfoo/cg\\enter");
2821     TestPressKey("y");
2822     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
2823     QCOMPARE(rangesOnFirstLine().first()->start().line(), 0);
2824     QCOMPARE(rangesOnFirstLine().first()->start().column(), 6);
2825     QCOMPARE(rangesOnFirstLine().first()->end().line(), 0);
2826     QCOMPARE(rangesOnFirstLine().first()->end().column(), 9);
2827     TestPressKey("\\ctrl-c");
2828     FinishTest("barfoofoo");
2829     BeginTest("xffy");
2830     TestPressKey(":s/f/bf/cg\\enter");
2831     TestPressKey("yy");
2832     FinishTest("xbfbfy");
2833 
2834     // Make sure the incremental search bar label contains the "instruction" keypresses.
2835     const QString interactiveSedReplaceShortcuts = "(y/n/a/q/l)";
2836     BeginTest("foofoo");
2837     TestPressKey(":s/foo/barfoo/cg\\enter");
2838     QVERIFY(interactiveSedReplaceLabel->text().contains(interactiveSedReplaceShortcuts));
2839     TestPressKey("\\ctrl-c");
2840     FinishTest("foofoo");
2841 
2842     // Make sure the incremental search bar label contains a reference to the text we're going to
2843     // replace with.
2844     // We're going to be a bit vague about the precise text due to localization issues.
2845     BeginTest("fabababbbar");
2846     TestPressKey(":s/f\\\\([ab]\\\\+\\\\)/1\\\\U\\\\12/c\\enter");
2847     QVERIFY(interactiveSedReplaceLabel->text().contains("1ABABABBBA2"));
2848     TestPressKey("\\ctrl-c");
2849     FinishTest("fabababbbar");
2850 
2851     // Replace newlines in the "replace?" message with "\\n"
2852     BeginTest("foo");
2853     TestPressKey(":s/foo/bar\\\\nxyz\\\\n123/c\\enter");
2854     QVERIFY(interactiveSedReplaceLabel->text().contains("bar\\nxyz\\n123"));
2855     TestPressKey("\\ctrl-c");
2856     FinishTest("foo");
2857 
2858     // Update the "confirm replace?" message on pressing "y".
2859     BeginTest("fabababbbar fabbb");
2860     TestPressKey(":s/f\\\\([ab]\\\\+\\\\)/1\\\\U\\\\12/gc\\enter");
2861     TestPressKey("y");
2862     QVERIFY(interactiveSedReplaceLabel->text().contains("1ABBB2"));
2863     QVERIFY(interactiveSedReplaceLabel->text().contains(interactiveSedReplaceShortcuts));
2864     TestPressKey("\\ctrl-c");
2865     FinishTest("1ABABABBBA2r fabbb");
2866 
2867     // Update the "confirm replace?" message on pressing "n".
2868     BeginTest("fabababbbar fabab");
2869     TestPressKey(":s/f\\\\([ab]\\\\+\\\\)/1\\\\U\\\\12/gc\\enter");
2870     TestPressKey("n");
2871     QVERIFY(interactiveSedReplaceLabel->text().contains("1ABAB2"));
2872     QVERIFY(interactiveSedReplaceLabel->text().contains(interactiveSedReplaceShortcuts));
2873     TestPressKey("\\ctrl-c");
2874     FinishTest("fabababbbar fabab");
2875 
2876     // Cursor is placed at the beginning of first match.
2877     BeginTest("  foo foo foo");
2878     TestPressKey(":s/foo/bar/c\\enter");
2879     verifyCursorAt(Cursor(0, 2));
2880     TestPressKey("\\ctrl-c");
2881     FinishTest("  foo foo foo");
2882 
2883     // "y" and "n" update the cursor pos.
2884     BeginTest("  foo   foo foo");
2885     TestPressKey(":s/foo/bar/cg\\enter");
2886     TestPressKey("y");
2887     verifyCursorAt(Cursor(0, 8));
2888     TestPressKey("n");
2889     verifyCursorAt(Cursor(0, 12));
2890     TestPressKey("\\ctrl-c");
2891     FinishTest("  bar   foo foo");
2892 
2893     // If we end due to a "y" or "n" on the final match, leave the cursor at the beginning of the final match.
2894     BeginTest("  foo");
2895     TestPressKey(":s/foo/bar/c\\enter");
2896     TestPressKey("y");
2897     verifyCursorAt(Cursor(0, 2));
2898     FinishTest("  bar");
2899     BeginTest("  foo");
2900     TestPressKey(":s/foo/bar/c\\enter");
2901     TestPressKey("n");
2902     verifyCursorAt(Cursor(0, 2));
2903     FinishTest("  foo");
2904 
2905     // Respect ranges.
2906     BeginTest("foo foo\nfoo foo\nfoo foo\nfoo foo\n");
2907     TestPressKey("jVj:s/foo/bar/gc\\enter");
2908     TestPressKey("ynny");
2909     QVERIFY(commandResponseMessageDisplay()->isVisible());
2910     TestPressKey("ggidone \\ctrl-c");
2911     FinishTest("done foo foo\nbar foo\nfoo bar\nfoo foo\n");
2912     BeginTest("foo foo\nfoo foo\nfoo foo\nfoo foo\n");
2913     TestPressKey("jVj:s/foo/bar/gc\\enter");
2914     TestPressKey("nyyn");
2915     QVERIFY(commandResponseMessageDisplay()->isVisible());
2916     TestPressKey("ggidone \\ctrl-c");
2917     FinishTest("done foo foo\nfoo bar\nbar foo\nfoo foo\n");
2918     BeginTest("foo foo\nfoo foo\nfoo foo\nfoo foo\n");
2919     TestPressKey("j:s/foo/bar/gc\\enter");
2920     TestPressKey("ny");
2921     QVERIFY(commandResponseMessageDisplay()->isVisible());
2922     TestPressKey("ggidone \\ctrl-c");
2923     FinishTest("done foo foo\nfoo bar\nfoo foo\nfoo foo\n");
2924     BeginTest("foo foo\nfoo foo\nfoo foo\nfoo foo\n");
2925     TestPressKey("j:s/foo/bar/gc\\enter");
2926     TestPressKey("yn");
2927     QVERIFY(commandResponseMessageDisplay()->isVisible());
2928     TestPressKey("ggidone \\ctrl-c");
2929     FinishTest("done foo foo\nbar foo\nfoo foo\nfoo foo\n");
2930 
2931     // If no initial match can be found, abort and show a "no replacements" message.
2932     // The cursor position should remain unnchanged.
2933     BeginTest("fab");
2934     TestPressKey("l:s/fee/bar/c\\enter");
2935     QVERIFY(commandResponseMessageDisplay()->isVisible());
2936     verifyShowsNumberOfReplacementsAcrossNumberOfLines(0, 0);
2937     QVERIFY(!interactiveSedReplaceLabel->isVisible());
2938     TestPressKey("rX");
2939     BeginTest("fXb");
2940 
2941     // Case-sensitive by default.
2942     BeginTest("foo Foo FOo foo foO");
2943     TestPressKey(":s/foo/bar/cg\\enter");
2944     TestPressKey("yyggidone\\esc");
2945     FinishTest("donebar Foo FOo bar foO");
2946 
2947     // Case-insensitive if "i" flag is used.
2948     BeginTest("foo Foo FOo foo foO");
2949     TestPressKey(":s/foo/bar/icg\\enter");
2950     TestPressKey("yyyyyggidone\\esc");
2951     FinishTest("donebar bar bar bar bar");
2952 
2953     // Only one replacement per-line unless "g" flag is used.
2954     BeginTest("boo foo 123 foo\nxyz foo foo\nfoo foo foo\nxyz\nfoo foo\nfoo 123 foo");
2955     TestPressKey("jVjjj:s/foo/bar/c\\enter");
2956     TestPressKey("yynggidone\\esc");
2957     FinishTest("doneboo foo 123 foo\nxyz bar foo\nbar foo foo\nxyz\nfoo foo\nfoo 123 foo");
2958     BeginTest("boo foo 123 foo\nxyz foo foo\nfoo foo foo\nxyz\nfoo foo\nfoo 123 foo");
2959     TestPressKey("jVjjj:s/foo/bar/c\\enter");
2960     TestPressKey("nnyggidone\\esc");
2961     FinishTest("doneboo foo 123 foo\nxyz foo foo\nfoo foo foo\nxyz\nbar foo\nfoo 123 foo");
2962 
2963     // If replacement contains new lines, adjust the end line down.
2964     BeginTest("foo\nfoo1\nfoo2\nfoo3");
2965     TestPressKey("jVj:s/foo/bar\\\\n/gc\\enter");
2966     TestPressKey("yyggidone\\esc");
2967     FinishTest("donefoo\nbar\n1\nbar\n2\nfoo3");
2968     BeginTest("foo\nfoo1\nfoo2\nfoo3");
2969     TestPressKey("jVj:s/foo/bar\\\\nboo\\\\n/gc\\enter");
2970     TestPressKey("yyggidone\\esc");
2971     FinishTest("donefoo\nbar\nboo\n1\nbar\nboo\n2\nfoo3");
2972 
2973     // With "g" and a replacement that involves multiple lines, resume search from the end of the last line added.
2974     BeginTest("foofoo");
2975     TestPressKey(":s/foo/bar\\\\n/gc\\enter");
2976     TestPressKey("yyggidone\\esc");
2977     FinishTest("donebar\nbar\n");
2978     BeginTest("foofoo");
2979     TestPressKey(":s/foo/bar\\\\nxyz\\\\nfoo/gc\\enter");
2980     TestPressKey("yyggidone\\esc");
2981     FinishTest("donebar\nxyz\nfoobar\nxyz\nfoo");
2982 
2983     // Without "g" and with a replacement that involves multiple lines, resume search from the line after the line just added.
2984     BeginTest("foofoo1\nfoo2\nfoo3");
2985     TestPressKey("Vj:s/foo/bar\\\\nxyz\\\\nfoo/c\\enter");
2986     TestPressKey("yyggidone\\esc");
2987     FinishTest("donebar\nxyz\nfoofoo1\nbar\nxyz\nfoo2\nfoo3");
2988 
2989     // Regression test: handle 'g' when it occurs before 'i' and 'c'.
2990     BeginTest("foo fOo");
2991     TestPressKey(":s/foo/bar/gci\\enter");
2992     TestPressKey("yyggidone\\esc");
2993     FinishTest("donebar bar");
2994 
2995     // When the search terms swallows several lines, move the endline up accordingly.
2996     BeginTest("foo\nfoo1\nfoo\nfoo2\nfoo\nfoo3");
2997     TestPressKey("V3j:s/foo\\\\nfoo/bar/cg\\enter");
2998     TestPressKey("yyggidone\\esc");
2999     FinishTest("donebar1\nbar2\nfoo\nfoo3");
3000     BeginTest("foo\nfoo\nfoo1\nfoo\nfoo\nfoo2\nfoo\nfoo\nfoo3");
3001     TestPressKey("V5j:s/foo\\\\nfoo\\\\nfoo/bar/cg\\enter");
3002     TestPressKey("yyggidone\\esc");
3003     FinishTest("donebar1\nbar2\nfoo\nfoo\nfoo3");
3004     // Make sure we still adjust endline down if the replacement text has '\n's.
3005     BeginTest("foo\nfoo\nfoo1\nfoo\nfoo\nfoo2\nfoo\nfoo\nfoo3");
3006     TestPressKey("V5j:s/foo\\\\nfoo\\\\nfoo/bar\\\\n/cg\\enter");
3007     TestPressKey("yyggidone\\esc");
3008     FinishTest("donebar\n1\nbar\n2\nfoo\nfoo\nfoo3");
3009 
3010     // Status reports.
3011     BeginTest("foo");
3012     TestPressKey(":s/foo/bar/c\\enter");
3013     TestPressKey("y");
3014     verifyShowsNumberOfReplacementsAcrossNumberOfLines(1, 1);
3015     FinishTest("bar");
3016     BeginTest("foo foo foo");
3017     TestPressKey(":s/foo/bar/gc\\enter");
3018     TestPressKey("yyy");
3019     verifyShowsNumberOfReplacementsAcrossNumberOfLines(3, 1);
3020     FinishTest("bar bar bar");
3021     BeginTest("foo foo foo");
3022     TestPressKey(":s/foo/bar/gc\\enter");
3023     TestPressKey("yny");
3024     verifyShowsNumberOfReplacementsAcrossNumberOfLines(2, 1);
3025     FinishTest("bar foo bar");
3026     BeginTest("foo\nfoo");
3027     TestPressKey(":%s/foo/bar/gc\\enter");
3028     TestPressKey("yy");
3029     verifyShowsNumberOfReplacementsAcrossNumberOfLines(2, 2);
3030     FinishTest("bar\nbar");
3031     BeginTest("foo foo\nfoo foo\nfoo foo");
3032     TestPressKey(":%s/foo/bar/gc\\enter");
3033     TestPressKey("yynnyy");
3034     verifyShowsNumberOfReplacementsAcrossNumberOfLines(4, 2);
3035     FinishTest("bar bar\nfoo foo\nbar bar");
3036     BeginTest("foofoo");
3037     TestPressKey(":s/foo/bar\\\\nxyz/gc\\enter");
3038     TestPressKey("yy");
3039     verifyShowsNumberOfReplacementsAcrossNumberOfLines(2, 1);
3040     FinishTest("bar\nxyzbar\nxyz");
3041     BeginTest("foofoofoo");
3042     TestPressKey(":s/foo/bar\\\\nxyz\\\\nboo/gc\\enter");
3043     TestPressKey("yyy");
3044     verifyShowsNumberOfReplacementsAcrossNumberOfLines(3, 1);
3045     FinishTest("bar\nxyz\nboobar\nxyz\nboobar\nxyz\nboo");
3046     // Tricky one: how many lines are "touched" if a single replacement
3047     // swallows multiple lines? I'm going to say the number of lines swallowed.
3048     BeginTest("foo\nfoo\nfoo");
3049     TestPressKey(":s/foo\\\\nfoo\\\\nfoo/bar/c\\enter");
3050     TestPressKey("y");
3051     verifyShowsNumberOfReplacementsAcrossNumberOfLines(1, 3);
3052     FinishTest("bar");
3053     BeginTest("foo\nfoo\nfoo\n");
3054     TestPressKey(":s/foo\\\\nfoo\\\\nfoo\\\\n/bar/c\\enter");
3055     TestPressKey("y");
3056     verifyShowsNumberOfReplacementsAcrossNumberOfLines(1, 4);
3057     FinishTest("bar");
3058 
3059     // "Undo" undoes last replacement.
3060     BeginTest("foo foo foo foo");
3061     TestPressKey(":s/foo/bar/cg\\enter");
3062     TestPressKey("nyynu");
3063     FinishTest("foo bar foo foo");
3064 
3065     // "l" does the current replacement then exits.
3066     BeginTest("foo foo foo foo foo foo");
3067     TestPressKey(":s/foo/bar/cg\\enter");
3068     TestPressKey("nnl");
3069     verifyShowsNumberOfReplacementsAcrossNumberOfLines(1, 1);
3070     FinishTest("foo foo bar foo foo foo");
3071 
3072     // "q" just exits.
3073     BeginTest("foo foo foo foo foo foo");
3074     TestPressKey(":s/foo/bar/cg\\enter");
3075     TestPressKey("yyq");
3076     verifyShowsNumberOfReplacementsAcrossNumberOfLines(2, 1);
3077     FinishTest("bar bar foo foo foo foo");
3078 
3079     // "a" replaces all remaining, then exits.
3080     BeginTest("foo foo foo foo foo foo");
3081     TestPressKey(":s/foo/bar/cg\\enter");
3082     TestPressKey("nna");
3083     verifyShowsNumberOfReplacementsAcrossNumberOfLines(4, 1);
3084     FinishTest("foo foo bar bar bar bar");
3085 
3086     // The results of "a" can be undone in one go.
3087     BeginTest("foo foo foo foo foo foo");
3088     TestPressKey(":s/foo/bar/cg\\enter");
3089     TestPressKey("ya");
3090     verifyShowsNumberOfReplacementsAcrossNumberOfLines(6, 1);
3091     TestPressKey("u");
3092     FinishTest("bar foo foo foo foo foo");
3093 #if 0
3094     // XXX - as of Qt 5.5, simply replaying the correct QKeyEvents does *not* cause shortcuts
3095     // to be triggered, so these tests cannot pass.
3096     // It's possible that a solution involving QTestLib will be workable in the future, though.
3097 
3098   {
3099     // Test the test suite: ensure that shortcuts are still being sent and received correctly.
3100     // The test shortcut chosen should be one that does not conflict with built-in Kate ones.
3101     FailsIfSlotNotCalled failsIfActionNotTriggered;
3102     QAction *dummyAction = kate_view->actionCollection()->addAction("Woo");
3103     dummyAction->setShortcut(QKeySequence("Ctrl+]"));
3104     QVERIFY(connect(dummyAction, SIGNAL(triggered()), &failsIfActionNotTriggered, SLOT(slot())));
3105     DoTest("foo", "\\ctrl-]", "foo");
3106     // Processing shortcuts seems to require events to be processed.
3107     while (QApplication::hasPendingEvents())
3108     {
3109       QApplication::processEvents();
3110     }
3111     delete dummyAction;
3112   }
3113   {
3114     // Test that shortcuts involving ctrl+<digit> work correctly.
3115     FailsIfSlotNotCalled failsIfActionNotTriggered;
3116     QAction *dummyAction = kate_view->actionCollection()->addAction("Woo");
3117     dummyAction->setShortcut(QKeySequence("Ctrl+1"));
3118     QVERIFY(connect(dummyAction, SIGNAL(triggered()), &failsIfActionNotTriggered, SLOT(slot())));
3119     DoTest("foo", "\\ctrl-1", "foo");
3120     // Processing shortcuts seems to require events to be processed.
3121     while (QApplication::hasPendingEvents())
3122     {
3123       QApplication::processEvents();
3124     }
3125     delete dummyAction;
3126   }
3127   {
3128     // Test that shortcuts involving alt+<digit> work correctly.
3129     FailsIfSlotNotCalled failsIfActionNotTriggered;
3130     QAction *dummyAction = kate_view->actionCollection()->addAction("Woo");
3131     dummyAction->setShortcut(QKeySequence("Alt+1"));
3132     QVERIFY(connect(dummyAction, SIGNAL(triggered()), &failsIfActionNotTriggered, SLOT(slot())));
3133     DoTest("foo", "\\alt-1", "foo");
3134     // Processing shortcuts seems to require events to be processed.
3135     while (QApplication::hasPendingEvents())
3136     {
3137       QApplication::processEvents();
3138     }
3139     delete dummyAction;
3140   }
3141 #endif
3142 
3143     // Find the "Print" action for later use.
3144     QAction *printAction = nullptr;
3145     const auto viewActions = kate_view->actionCollection()->actions();
3146     for (QAction *action : viewActions) {
3147         if (action->shortcut() == QKeySequence("Ctrl+p")) {
3148             printAction = action;
3149             break;
3150         }
3151     }
3152 
3153     // Test that we don't inadvertantly trigger shortcuts in kate_view when typing them in the
3154     // emulated command bar.  Requires the above test for shortcuts to be sent and received correctly
3155     // to pass.
3156     {
3157         QVERIFY(mainWindow->isActiveWindow());
3158         QVERIFY(printAction);
3159         FailsIfSlotCalled failsIfActionTriggered("The kate_view shortcut should not be triggered by typing it in emulated  command bar!");
3160         // Don't invoke Print on failure, as this hangs instead of failing.
3161         // disconnect(printAction, SIGNAL(triggered(bool)), kate_document, SLOT(print()));
3162         connect(printAction, &QAction::triggered, &failsIfActionTriggered, &FailsIfSlotCalled::slot);
3163         DoTest("foo bar foo bar", "/bar\\enterggd/\\ctrl-p\\enter.", "bar");
3164         // Processing shortcuts seems to require events to be processed.
3165         QApplication::processEvents();
3166     }
3167 
3168     // Test that the interactive search replace does not handle general keypresses like ctrl-p ("invoke
3169     // completion in emulated command bar").
3170     // Unfortunately, "ctrl-p" in kate_view, which is what will be triggered if this
3171     // test succeeds, hangs due to showing the print dialog, so we need to temporarily
3172     // block the Print action.
3173     clearCommandHistory();
3174     if (printAction) {
3175         printAction->blockSignals(true);
3176     }
3177     vi_global->commandHistory()->append("s/foo/bar/caa");
3178     BeginTest("foo");
3179     TestPressKey(":s/foo/bar/c\\ctrl-b\\enter\\ctrl-p");
3180     QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
3181     TestPressKey("\\ctrl-c");
3182     if (printAction) {
3183         printAction->blockSignals(false);
3184     }
3185     FinishTest("foo");
3186 
3187     // The interactive sed replace command is added to the history straight away.
3188     clearCommandHistory();
3189     BeginTest("foo");
3190     TestPressKey(":s/foo/bar/c\\enter");
3191     QCOMPARE(commandHistory(), QStringList() << "s/foo/bar/c");
3192     TestPressKey("\\ctrl-c");
3193     FinishTest("foo");
3194     clearCommandHistory();
3195     BeginTest("foo");
3196     TestPressKey(":s/notfound/bar/c\\enter");
3197     QCOMPARE(commandHistory(), QStringList() << "s/notfound/bar/c");
3198     TestPressKey("\\ctrl-c");
3199     FinishTest("foo");
3200 
3201     // Should be usable in mappings.
3202     clearAllMappings();
3203     vi_global->mappings()->add(Mappings::NormalModeMapping, "H", ":s/foo/bar/gc<enter>nnyyl", Mappings::Recursive);
3204     DoTest("foo foo foo foo foo foo", "H", "foo foo bar bar bar foo");
3205     clearAllMappings();
3206     vi_global->mappings()->add(Mappings::NormalModeMapping, "H", ":s/foo/bar/gc<enter>nna", Mappings::Recursive);
3207     DoTest("foo foo foo foo foo foo", "H", "foo foo bar bar bar bar");
3208     clearAllMappings();
3209     vi_global->mappings()->add(Mappings::NormalModeMapping, "H", ":s/foo/bar/gc<enter>nnyqggidone<esc>", Mappings::Recursive);
3210     DoTest("foo foo foo foo foo foo", "H", "donefoo foo bar foo foo foo");
3211 
3212 #ifndef Q_OS_MACOS
3213     // Don't swallow "Ctrl+<key>" meant for the text edit.
3214     // On MacOS the QKeySequence for undo is defined as "Ctrl+Z" as well, however it's actually Cmd+Z...
3215     if (QKeySequence::keyBindings(QKeySequence::Undo).contains(QKeySequence("Ctrl+Z"))) {
3216         DoTest("foo bar", "/bar\\ctrl-z\\enterrX", "Xoo bar");
3217     } else {
3218         qWarning() << "Skipped test: Ctrl+Z is not Undo on this platform";
3219     }
3220 #endif
3221 
3222     // Don't give invalid cursor position to updateCursor in Visual Mode: it will cause a crash!
3223     DoTest("xyz\nfoo\nbar\n123", "/foo\\\\nbar\\\\n\\enterggv//e\\enter\\ctrl-crX", "xyz\nfoo\nbaX\n123");
3224     DoTest("\nfooxyz\nbar;\n", "/foo.*\\\\n.*;\\enterggv//e\\enter\\ctrl-crX", "\nfooxyz\nbarX\n");
3225 }
3226 
3227 QCompleter *EmulatedCommandBarTest::emulatedCommandBarCompleter()
3228 {
3229     return vi_input_mode->viModeEmulatedCommandBar()->findChild<QCompleter *>("completer");
3230 }
3231 
3232 void EmulatedCommandBarTest::verifyCommandBarCompletionVisible()
3233 {
3234     if (!emulatedCommandBarCompleter()->popup()->isVisible()) {
3235         qDebug() << "Emulated command bar completer not visible.";
3236         QStringListModel *completionModel = qobject_cast<QStringListModel *>(emulatedCommandBarCompleter()->model());
3237         Q_ASSERT(completionModel);
3238         const QStringList allAvailableCompletions = completionModel->stringList();
3239         qDebug() << " Completion list: " << allAvailableCompletions;
3240         qDebug() << " Completion prefix: " << emulatedCommandBarCompleter()->completionPrefix();
3241         bool candidateCompletionFound = false;
3242         for (const QString &availableCompletion : allAvailableCompletions) {
3243             if (availableCompletion.startsWith(emulatedCommandBarCompleter()->completionPrefix())) {
3244                 candidateCompletionFound = true;
3245                 break;
3246             }
3247         }
3248         if (candidateCompletionFound) {
3249             qDebug() << " The current completion prefix is a prefix of one of the available completions, so either complete() was not called, or the popup was "
3250                         "manually hidden since then";
3251         } else {
3252             qDebug() << " The current completion prefix is not a prefix of one of the available completions; this may or may not be why it is not visible";
3253         }
3254     }
3255     QVERIFY(emulatedCommandBarCompleter()->popup()->isVisible());
3256 }
3257 
3258 void EmulatedCommandBarTest::verifyCommandBarCompletionsMatches(const QStringList &expectedCompletionList)
3259 {
3260     verifyCommandBarCompletionVisible();
3261     QStringList actualCompletionList;
3262     for (int i = 0; emulatedCommandBarCompleter()->setCurrentRow(i); i++) {
3263         actualCompletionList << emulatedCommandBarCompleter()->currentCompletion();
3264     }
3265     if (expectedCompletionList != actualCompletionList) {
3266         qDebug() << "Actual completions:\n " << actualCompletionList << "\n\ndo not match expected:\n" << expectedCompletionList;
3267     }
3268 
3269     QCOMPARE(actualCompletionList, expectedCompletionList);
3270 }
3271 
3272 void EmulatedCommandBarTest::verifyCommandBarCompletionContains(const QStringList &expectedCompletionList)
3273 {
3274     verifyCommandBarCompletionVisible();
3275     QStringList actualCompletionList;
3276 
3277     for (int i = 0; emulatedCommandBarCompleter()->setCurrentRow(i); i++) {
3278         actualCompletionList << emulatedCommandBarCompleter()->currentCompletion();
3279     }
3280 
3281     for (const QString &expectedCompletion : expectedCompletionList) {
3282         if (!actualCompletionList.contains(expectedCompletion)) {
3283             qDebug() << "Whoops: " << actualCompletionList << " does not contain " << expectedCompletion;
3284         }
3285         QVERIFY(actualCompletionList.contains(expectedCompletion));
3286     }
3287 }
3288 
3289 QLabel *EmulatedCommandBarTest::emulatedCommandTypeIndicator()
3290 {
3291     return emulatedCommandBar()->findChild<QLabel *>("bartypeindicator");
3292 }
3293 
3294 void EmulatedCommandBarTest::verifyCursorAt(const Cursor &expectedCursorPos)
3295 {
3296     QCOMPARE(kate_view->cursorPosition().line(), expectedCursorPos.line());
3297     QCOMPARE(kate_view->cursorPosition().column(), expectedCursorPos.column());
3298 }
3299 
3300 void EmulatedCommandBarTest::clearSearchHistory()
3301 {
3302     vi_global->searchHistory()->clear();
3303 }
3304 
3305 QStringList EmulatedCommandBarTest::searchHistory()
3306 {
3307     return vi_global->searchHistory()->items();
3308 }
3309 
3310 void EmulatedCommandBarTest::clearCommandHistory()
3311 {
3312     vi_global->commandHistory()->clear();
3313 }
3314 
3315 QStringList EmulatedCommandBarTest::commandHistory()
3316 {
3317     return vi_global->commandHistory()->items();
3318 }
3319 
3320 void EmulatedCommandBarTest::clearReplaceHistory()
3321 {
3322     vi_global->replaceHistory()->clear();
3323 }
3324 
3325 QStringList EmulatedCommandBarTest::replaceHistory()
3326 {
3327     return vi_global->replaceHistory()->items();
3328 }
3329 
3330 QVector<Kate::TextRange *> EmulatedCommandBarTest::rangesOnFirstLine()
3331 {
3332     return kate_document->buffer().rangesForLine(0, kate_view, true);
3333 }
3334 
3335 void EmulatedCommandBarTest::verifyTextEditBackgroundColour(const QColor &expectedBackgroundColour)
3336 {
3337     QCOMPARE(emulatedCommandBarTextEdit()->palette().brush(QPalette::Base).color(), expectedBackgroundColour);
3338 }
3339 
3340 QLabel *EmulatedCommandBarTest::commandResponseMessageDisplay()
3341 {
3342     QLabel *commandResponseMessageDisplay = emulatedCommandBar()->findChild<QLabel *>("commandresponsemessage");
3343     Q_ASSERT(commandResponseMessageDisplay);
3344     return commandResponseMessageDisplay;
3345 }
3346 
3347 void EmulatedCommandBarTest::waitForEmulatedCommandBarToHide(long int timeout)
3348 {
3349     QTRY_VERIFY_WITH_TIMEOUT(!emulatedCommandBar()->isVisible(), timeout);
3350 }
3351 
3352 void EmulatedCommandBarTest::verifyShowsNumberOfReplacementsAcrossNumberOfLines(int numReplacements, int acrossNumLines)
3353 {
3354     QVERIFY(commandResponseMessageDisplay()->isVisible());
3355     QVERIFY(!emulatedCommandTypeIndicator()->isVisible());
3356     const QString commandMessageResponseText = commandResponseMessageDisplay()->text();
3357     const QString expectedNumReplacementsAsString = QString::number(numReplacements);
3358     const QString expectedAcrossNumLinesAsString = QString::number(acrossNumLines);
3359     // Be a bit vague about the actual contents due to e.g. localization.
3360     // TODO - see if we can insist that en_US is available on the Kate Jenkins server and
3361     // insist that we use it ... ?
3362     static const QRegularExpression numReplacementsMessageRegex("^.*(\\d+).*(\\d+).*$");
3363     const auto match = numReplacementsMessageRegex.match(commandMessageResponseText);
3364     QVERIFY(match.hasMatch());
3365     const QString actualNumReplacementsAsString = match.captured(1);
3366     const QString actualAcrossNumLinesAsString = match.captured(2);
3367     QCOMPARE(actualNumReplacementsAsString, expectedNumReplacementsAsString);
3368     QCOMPARE(actualAcrossNumLinesAsString, expectedAcrossNumLinesAsString);
3369 }
3370 
3371 FailsIfSlotNotCalled::FailsIfSlotNotCalled()
3372     : QObject()
3373 {
3374 }
3375 
3376 FailsIfSlotNotCalled::~FailsIfSlotNotCalled()
3377 {
3378     QVERIFY(m_slotWasCalled);
3379 }
3380 
3381 void FailsIfSlotNotCalled::slot()
3382 {
3383     m_slotWasCalled = true;
3384 }
3385 
3386 FailsIfSlotCalled::FailsIfSlotCalled(const QString &failureMessage)
3387     : QObject()
3388     , m_failureMessage(failureMessage)
3389 {
3390 }
3391 
3392 void FailsIfSlotCalled::slot()
3393 {
3394     QFAIL(qPrintable(m_failureMessage.toLatin1()));
3395 }
3396 
3397 #include "moc_emulatedcommandbar.cpp"