File indexing completed on 2024-04-28 03:57:09

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