/* This file is part of the KDE libraries

   Copyright (C) 2011 Kuzmich Svyatoslav
   Copyright (C) 2012 - 2013 Simon St James <kdedevel@etotheipiplusone.com>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
   */

#include <kateconfig.h>
#include <katebuffer.h>
#include "emulatedcommandbar.h"
#include <vimode/emulatedcommandbar/emulatedcommandbar.h>
#include <vimode/history.h>
#include <inputmode/kateviinputmode.h>
#include "keys.h"
#include "view.h"
#include "emulatedcommandbarsetupandteardown.h"
#include "vimode/mappings.h"
#include "vimode/globalstate.h"

#include <QLabel>
#include <QClipboard>
#include <QCompleter>
#include <QAbstractItemView>
#include <QStringListModel>

#include <KColorScheme>
#include <KActionCollection>

QTEST_MAIN(EmulatedCommandBarTest)

using namespace KTextEditor;
using namespace KateVi;


void EmulatedCommandBarTest::EmulatedCommandBarTests()
{
  // Ensure that some preconditions for these tests are setup, and (more importantly)
  // ensure that they are reverted no matter how these tests end.
  EmulatedCommandBarSetUpAndTearDown emulatedCommandBarSetUpAndTearDown(vi_input_mode, kate_view, mainWindow);


  // Verify that we can get a non-null pointer to the emulated command bar.
  EmulatedCommandBar *emulatedCommandBar = vi_input_mode->viModeEmulatedCommandBar();
  QVERIFY(emulatedCommandBar);

  // Should initially be hidden.
  QVERIFY(!emulatedCommandBar->isVisible());

  // Test that "/" invokes the emulated command bar (if we are configured to use it)
  BeginTest("");
  TestPressKey("/");
  QVERIFY(emulatedCommandBar->isVisible());
  QCOMPARE(emulatedCommandTypeIndicator()->text(), QString("/"));
  QVERIFY(emulatedCommandTypeIndicator()->isVisible());
  QVERIFY(emulatedCommandBarTextEdit());
  QVERIFY(emulatedCommandBarTextEdit()->text().isEmpty());
  // Make sure the keypresses end up changing the text.
  QVERIFY(emulatedCommandBarTextEdit()->isVisible());
  TestPressKey("foo");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo"));
  // Make sure ctrl-c dismisses it (assuming we allow Vim to steal the ctrl-c shortcut).
  TestPressKey("\\ctrl-c");
  QVERIFY(!emulatedCommandBar->isVisible());

  // Ensure that ESC dismisses it, too.
  BeginTest("");
  TestPressKey("/");
  QVERIFY(emulatedCommandBar->isVisible());
  TestPressKey("\\esc");
  QVERIFY(!emulatedCommandBar->isVisible());
  FinishTest("");

  // Ensure that Ctrl-[ dismisses it, too.
  BeginTest("");
  TestPressKey("/");
  QVERIFY(emulatedCommandBar->isVisible());
  TestPressKey("\\ctrl-[");
  QVERIFY(!emulatedCommandBar->isVisible());
  FinishTest("");

  // Ensure that Enter dismisses it, too.
  BeginTest("");
  TestPressKey("/");
  QVERIFY(emulatedCommandBar->isVisible());
  TestPressKey("\\enter");
  QVERIFY(!emulatedCommandBar->isVisible());
  FinishTest("");

  // Ensure that Return dismisses it, too.
  BeginTest("");
  TestPressKey("/");
  QVERIFY(emulatedCommandBar->isVisible());
  TestPressKey("\\return");
  QVERIFY(!emulatedCommandBar->isVisible());
  FinishTest("");

  // Ensure that text is always initially empty.
  BeginTest("");
  TestPressKey("/a\\enter");
  TestPressKey("/");
  QVERIFY(emulatedCommandBarTextEdit()->text().isEmpty());
  TestPressKey("\\enter");
  FinishTest("");

  // Check backspace works.
  BeginTest("");
  TestPressKey("/foo\\backspace");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("fo"));
  TestPressKey("\\enter");
  FinishTest("");

  // Check ctrl-h works.
  BeginTest("");
  TestPressKey("/bar\\ctrl-h");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("ba"));
  TestPressKey("\\enter");
  FinishTest("");

  // ctrl-h should dismiss bar when empty.
  BeginTest("");
  TestPressKey("/\\ctrl-h");
  QVERIFY(!emulatedCommandBar->isVisible());
  FinishTest("");

  // ctrl-h should not dismiss bar when there is stuff to the left of cursor.
  BeginTest("");
  TestPressKey("/a\\ctrl-h");
  QVERIFY(emulatedCommandBar->isVisible());
  TestPressKey("\\enter");
  FinishTest("");

  // ctrl-h should not dismiss bar when bar is not empty, even if there is nothing to the left of cursor.
  BeginTest("");
  TestPressKey("/a\\left\\ctrl-h");
  QVERIFY(emulatedCommandBar->isVisible());
  TestPressKey("\\enter");
  FinishTest("");

  // Same for backspace.
  BeginTest("");
  TestPressKey("/bar\\backspace");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("ba"));
  TestPressKey("\\enter");
  FinishTest("");
  BeginTest("");
  TestPressKey("/\\backspace");
  QVERIFY(!emulatedCommandBar->isVisible());
  FinishTest("");
  BeginTest("");
  TestPressKey("/a\\backspace");
  QVERIFY(emulatedCommandBar->isVisible());
  TestPressKey("\\enter");
  FinishTest("");
  BeginTest("");
  TestPressKey("/a\\left\\backspace");
  QVERIFY(emulatedCommandBar->isVisible());
  TestPressKey("\\enter");
  FinishTest("");

  // Check ctrl-b works.
  BeginTest("");
  TestPressKey("/bar foo xyz\\ctrl-bX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("Xbar foo xyz"));
  TestPressKey("\\enter");
  FinishTest("");

  // Check ctrl-e works.
  BeginTest("");
  TestPressKey("/bar foo xyz\\ctrl-b\\ctrl-eX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("bar foo xyzX"));
  TestPressKey("\\enter");
  FinishTest("");

  // Check ctrl-w works.
  BeginTest("");
  TestPressKey("/foo bar\\ctrl-w");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo "));
  TestPressKey("\\enter");
  FinishTest("");

  // Check ctrl-w works on empty command bar.
  BeginTest("");
  TestPressKey("/\\ctrl-w");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(""));
  TestPressKey("\\enter");
  FinishTest("");

  // Check ctrl-w works in middle of word.
  BeginTest("");
  TestPressKey("/foo bar\\left\\left\\ctrl-w");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo ar"));
  TestPressKey("\\enter");
  FinishTest("");

  // Check ctrl-w leaves the cursor in the right place when in the middle of word.
  BeginTest("");
  TestPressKey("/foo bar\\left\\left\\ctrl-wX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo Xar"));
  TestPressKey("\\enter");
  FinishTest("");

  // Check ctrl-w works when at the beginning of the text.
  BeginTest("");
  TestPressKey("/foo\\left\\left\\left\\ctrl-w");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo"));
  TestPressKey("\\enter");
  FinishTest("");

  // Check ctrl-w works when the character to the left is a space.
  BeginTest("");
  TestPressKey("/foo bar   \\ctrl-w");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo "));
  TestPressKey("\\enter");
  FinishTest("");

  // Check ctrl-w works when all characters to the left of the cursor are spaces.
  BeginTest("");
  TestPressKey("/   \\ctrl-w");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(""));
  TestPressKey("\\enter");
  FinishTest("");

  // Check ctrl-w works when all characters to the left of the cursor are non-spaces.
  BeginTest("");
  TestPressKey("/foo\\ctrl-w");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(""));
  TestPressKey("\\enter");
  FinishTest("");

  // Check ctrl-w does not continue to delete subsequent alphanumerics if the characters to the left of the cursor
  // are non-space, non-alphanumerics.
  BeginTest("");
  TestPressKey("/foo!!!\\ctrl-w");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo"));
  TestPressKey("\\enter");
  FinishTest("");
  // Check ctrl-w does not continue to delete subsequent alphanumerics if the characters to the left of the cursor
  // are non-space, non-alphanumerics.
  BeginTest("");
  TestPressKey("/foo!!!\\ctrl-w");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo"));
  TestPressKey("\\enter");
  FinishTest("");

  // Check ctrl-w deletes underscores and alphanumerics to the left of the cursor, but stops when it reaches a
  // character that is none of these.
  BeginTest("");
  TestPressKey("/foo!!!_d1\\ctrl-w");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo!!!"));
  TestPressKey("\\enter");
  FinishTest("");

  // Check ctrl-w doesn't swallow the spaces preceding the block of non-word chars.
  BeginTest("");
  TestPressKey("/foo !!!\\ctrl-w");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo "));
  TestPressKey("\\enter");
  FinishTest("");

  // Check ctrl-w doesn't swallow the spaces preceding the word.
  BeginTest("");
  TestPressKey("/foo 1d_\\ctrl-w");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo "));
  TestPressKey("\\enter");
  FinishTest("");

  // Check there is a "waiting for register" indicator, initially hidden.
  BeginTest("");
  TestPressKey("/");
  QLabel* waitingForRegisterIndicator = emulatedCommandBar->findChild<QLabel*>("waitingforregisterindicator");
  QVERIFY(waitingForRegisterIndicator);
  QVERIFY(!waitingForRegisterIndicator->isVisible());
  QCOMPARE(waitingForRegisterIndicator->text(), QString("\""));
  TestPressKey("\\enter");
  FinishTest("");

  // Test that ctrl-r causes it to become visible.  It is displayed to the right of the text edit.
  BeginTest("");
  TestPressKey("/\\ctrl-r");
  QVERIFY(waitingForRegisterIndicator->isVisible());
  QVERIFY(waitingForRegisterIndicator->x() >= emulatedCommandBarTextEdit()->x() + emulatedCommandBarTextEdit()->width());
  TestPressKey("\\ctrl-c");
  TestPressKey("\\ctrl-c");
  FinishTest("");

  // The first ctrl-c after ctrl-r (when no register entered) hides the waiting for register
  // indicator, but not the bar.
  BeginTest("");
  TestPressKey("/\\ctrl-r");
  QVERIFY(waitingForRegisterIndicator->isVisible());
  TestPressKey("\\ctrl-c");
  QVERIFY(!waitingForRegisterIndicator->isVisible());
  QVERIFY(emulatedCommandBar->isVisible());
  TestPressKey("\\ctrl-c"); // Dismiss the bar.
  FinishTest("");

  // The first ctrl-c after ctrl-r (when no register entered) aborts waiting for register.
  BeginTest("foo");
  TestPressKey("\"cyiw/\\ctrl-r\\ctrl-ca");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("a"));
  TestPressKey("\\ctrl-c"); // Dismiss the bar.
  FinishTest("foo");

  // Same as above, but for ctrl-[ instead of ctrl-c.
  BeginTest("");
  TestPressKey("/\\ctrl-r");
  QVERIFY(waitingForRegisterIndicator->isVisible());
  TestPressKey("\\ctrl-[");
  QVERIFY(!waitingForRegisterIndicator->isVisible());
  QVERIFY(emulatedCommandBar->isVisible());
  TestPressKey("\\ctrl-c"); // Dismiss the bar.
  FinishTest("");
  BeginTest("foo");
  TestPressKey("\"cyiw/\\ctrl-r\\ctrl-[a");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("a"));
  TestPressKey("\\ctrl-c"); // Dismiss the bar.
  FinishTest("foo");

  // Check ctrl-r works with registers, and hides the "waiting for register" indicator.
  BeginTest("xyz");
  TestPressKey("\"ayiw/foo\\ctrl-ra");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("fooxyz"));
  QVERIFY(!waitingForRegisterIndicator->isVisible());
  TestPressKey("\\enter");
  FinishTest("xyz");

  // Check ctrl-r inserts text at the current cursor position.
  BeginTest("xyz");
  TestPressKey("\"ayiw/foo\\left\\ctrl-ra");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foxyzo"));
  TestPressKey("\\enter");
  FinishTest("xyz");

  // Check ctrl-r ctrl-w inserts word under the cursor, and hides the "waiting for register" indicator.
  BeginTest("foo bar xyz");
  TestPressKey("w/\\left\\ctrl-r\\ctrl-w");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("bar"));
  QVERIFY(!waitingForRegisterIndicator->isVisible());
  TestPressKey("\\enter");
  FinishTest("foo bar xyz");

  // Check ctrl-r ctrl-w doesn't insert the contents of register w!
  BeginTest("foo baz xyz");
  TestPressKey("\"wyiww/\\left\\ctrl-r\\ctrl-w");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("baz"));
  TestPressKey("\\enter");
  FinishTest("foo baz xyz");

  // Check ctrl-r ctrl-w inserts at the current cursor position.
  BeginTest("foo nose xyz");
  TestPressKey("w/bar\\left\\ctrl-r\\ctrl-w");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("banoser"));
  TestPressKey("\\enter");
  FinishTest("foo nose xyz");

  // Cursor position is at the end of the inserted text after ctrl-r ctrl-w.
  BeginTest("foo nose xyz");
  TestPressKey("w/bar\\left\\ctrl-r\\ctrl-wX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("banoseXr"));
  TestPressKey("\\enter");
  FinishTest("foo nose xyz");

  // Cursor position is at the end of the inserted register contents after ctrl-r.
  BeginTest("xyz");
  TestPressKey("\"ayiw/foo\\left\\ctrl-raX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foxyzXo"));
  TestPressKey("\\enter");
  FinishTest("xyz");

  // Insert clipboard contents on ctrl-r +.  We implicitly need to test the ability to handle
  // shift key key events when waiting for register (they should be ignored).
  BeginTest("xyz");
  QApplication::clipboard()->setText("vimodetestclipboardtext");
  TestPressKey("/\\ctrl-r");
  QKeyEvent *shiftKeyDown = new QKeyEvent(QEvent::KeyPress, Qt::Key_Shift, Qt::NoModifier);
  QApplication::postEvent(emulatedCommandBarTextEdit(), shiftKeyDown);
  QApplication::sendPostedEvents();
  TestPressKey("+");
  QKeyEvent *shiftKeyUp = new QKeyEvent(QEvent::KeyPress, Qt::Key_Shift, Qt::NoModifier);
  QApplication::postEvent(emulatedCommandBarTextEdit(), shiftKeyUp);
  QApplication::sendPostedEvents();
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("vimodetestclipboardtext"));
  TestPressKey("\\enter");
  FinishTest("xyz");

  // Similarly, test that we can press "ctrl" after ctrl-r without it being taken for a register.
  BeginTest("wordundercursor");
  TestPressKey("/\\ctrl-r");
  QKeyEvent *ctrlKeyDown = new QKeyEvent(QEvent::KeyPress, Qt::Key_Control, Qt::NoModifier);
  QApplication::postEvent(emulatedCommandBarTextEdit(), ctrlKeyDown);
  QApplication::sendPostedEvents();
  QKeyEvent *ctrlKeyUp = new QKeyEvent(QEvent::KeyRelease, Qt::Key_Control, Qt::NoModifier);
  QApplication::postEvent(emulatedCommandBarTextEdit(), ctrlKeyUp);
  QApplication::sendPostedEvents();
  QVERIFY(waitingForRegisterIndicator->isVisible());
  TestPressKey("\\ctrl-w");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("wordundercursor"));
  TestPressKey("\\ctrl-c"); // Dismiss the bar.
  FinishTest("wordundercursor");

  // Begin tests for ctrl-g, which is almost identical to ctrl-r save that the contents, when added,
  // are escaped for searching.
  // Normal register contents/ word under cursor are added as normal.
  BeginTest("wordinregisterb wordundercursor");
  TestPressKey("\"byiw");
  TestPressKey("/\\ctrl-g");
  QVERIFY(waitingForRegisterIndicator->isVisible());
  QVERIFY(waitingForRegisterIndicator->x() >= emulatedCommandBarTextEdit()->x() + emulatedCommandBarTextEdit()->width());
  TestPressKey("b");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("wordinregisterb"));
  QVERIFY(!waitingForRegisterIndicator->isVisible());
  TestPressKey("\\ctrl-c\\ctrl-cw/\\ctrl-g\\ctrl-w");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("wordundercursor"));
  QVERIFY(!waitingForRegisterIndicator->isVisible());
  TestPressKey("\\ctrl-c");
  TestPressKey("\\ctrl-c");
  FinishTest("wordinregisterb wordundercursor");

  // \'s must be escaped when inserted via ctrl-g.
  DoTest("foo a\\b\\\\c\\\\\\d", "wYb/\\ctrl-g0\\enterrX", "foo X\\b\\\\c\\\\\\d");
  // $'s must be escaped when inserted via ctrl-g.
  DoTest("foo a$b", "wYb/\\ctrl-g0\\enterrX", "foo X$b");
  DoTest("foo a$b$c", "wYb/\\ctrl-g0\\enterrX", "foo X$b$c");
  DoTest("foo a\\$b\\$c", "wYb/\\ctrl-g0\\enterrX", "foo X\\$b\\$c");
  // ^'s must be escaped when inserted via ctrl-g.
  DoTest("foo a^b", "wYb/\\ctrl-g0\\enterrX", "foo X^b");
  DoTest("foo a^b^c", "wYb/\\ctrl-g0\\enterrX", "foo X^b^c");
  DoTest("foo a\\^b\\^c", "wYb/\\ctrl-g0\\enterrX", "foo X\\^b\\^c");
  // .'s must be escaped when inserted via ctrl-g.
  DoTest("foo axb a.b", "wwYgg/\\ctrl-g0\\enterrX", "foo axb X.b");
  DoTest("foo a\\xb Na\\.b", "fNlYgg/\\ctrl-g0\\enterrX", "foo a\\xb NX\\.b");
  // *'s must be escaped when inserted via ctrl-g
  DoTest("foo axxxxb ax*b", "wwYgg/\\ctrl-g0\\enterrX", "foo axxxxb Xx*b");
  DoTest("foo a\\xxxxb Na\\x*X", "fNlYgg/\\ctrl-g0\\enterrX", "foo a\\xxxxb NX\\x*X");
  // /'s must be escaped when inserted via ctrl-g.
  DoTest("foo a a/b", "wwYgg/\\ctrl-g0\\enterrX", "foo a X/b");
  DoTest("foo a a/b/c", "wwYgg/\\ctrl-g0\\enterrX", "foo a X/b/c");
  DoTest("foo a a\\/b\\/c", "wwYgg/\\ctrl-g0\\enterrX", "foo a X\\/b\\/c");
  // ['s and ]'s must be escaped when inserted via ctrl-g.
  DoTest("foo axb a[xyz]b", "wwYgg/\\ctrl-g0\\enterrX", "foo axb X[xyz]b");
  DoTest("foo a[b", "wYb/\\ctrl-g0\\enterrX", "foo X[b");
  DoTest("foo a[b[c", "wYb/\\ctrl-g0\\enterrX", "foo X[b[c");
  DoTest("foo a\\[b\\[c", "wYb/\\ctrl-g0\\enterrX", "foo X\\[b\\[c");
  DoTest("foo a]b", "wYb/\\ctrl-g0\\enterrX", "foo X]b");
  DoTest("foo a]b]c", "wYb/\\ctrl-g0\\enterrX", "foo X]b]c");
  DoTest("foo a\\]b\\]c", "wYb/\\ctrl-g0\\enterrX", "foo X\\]b\\]c");
  // Test that expressions involving {'s and }'s work when inserted via ctrl-g.
  DoTest("foo {", "wYgg/\\ctrl-g0\\enterrX", "foo X");
  DoTest("foo }", "wYgg/\\ctrl-g0\\enterrX", "foo X");
  DoTest("foo aaaaa \\aaaaa a\\{5}", "WWWYgg/\\ctrl-g0\\enterrX", "foo aaaaa \\aaaaa X\\{5}");
  DoTest("foo }", "wYgg/\\ctrl-g0\\enterrX", "foo X");
  // Transform newlines into "\\n" when inserted via ctrl-g.
  DoTest(" \nfoo\nfoo\nxyz\nbar\n123", "jjvjjllygg/\\ctrl-g0\\enterrX", " \nfoo\nXoo\nxyz\nbar\n123");
  DoTest(" \nfoo\nfoo\nxyz\nbar\n123", "jjvjjllygg/\\ctrl-g0/e\\enterrX", " \nfoo\nfoo\nxyz\nbaX\n123");
  // Don't do any escaping for ctrl-r, though.
  BeginTest("foo .*$^\\/");
  TestPressKey("wY/\\ctrl-r0");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(".*$^\\/"));
  TestPressKey("\\ctrl-c");
  TestPressKey("\\ctrl-c");
  FinishTest("foo .*$^\\/");
  // Ensure that the flag that says "next register insertion should be escaped for searching"
  // is cleared if we do ctrl-g but then abort with ctrl-c.
  DoTest("foo a$b", "/\\ctrl-g\\ctrl-c\\ctrl-cwYgg/\\ctrl-r0\\enterrX", "Xoo a$b");

   // Ensure that we actually perform a search while typing.
  BeginTest("abcd");
  TestPressKey("/c");
  verifyCursorAt(Cursor(0, 2));
  TestPressKey("\\enter");
  FinishTest("abcd");

  // Ensure that the search is from the cursor.
  BeginTest("acbcd");
  TestPressKey("ll/c");
  verifyCursorAt(Cursor(0, 3));
  TestPressKey("\\enter");
  FinishTest("acbcd");

  // Reset the cursor to the original position on Ctrl-C
  BeginTest("acbcd");
  TestPressKey("ll/c\\ctrl-crX");
  FinishTest("acXcd");

  // Reset the cursor to the original position on Ctrl-[
  BeginTest("acbcd");
  TestPressKey("ll/c\\ctrl-[rX");
  FinishTest("acXcd");

  // Reset the cursor to the original position on ESC
  BeginTest("acbcd");
  TestPressKey("ll/c\\escrX");
  FinishTest("acXcd");

  // *Do not* reset the cursor to the original position on Enter.
  BeginTest("acbcd");
  TestPressKey("ll/c\\enterrX");
  FinishTest("acbXd");

  // *Do not* reset the cursor to the original position on Return.
  BeginTest("acbcd");
  TestPressKey("ll/c\\returnrX");
  FinishTest("acbXd");

  // Should work with mappings.
  clearAllMappings();
  vi_global->mappings()->add(Mappings::NormalModeMapping, "'testmapping", "/c<enter>rX", Mappings::Recursive);
  BeginTest("acbcd");
  TestPressKey("'testmapping");
  FinishTest("aXbcd");
  clearAllMappings();
  // Don't send keys that were part of a mapping to the emulated command bar.
  vi_global->mappings()->add(Mappings::NormalModeMapping, "H", "/a", Mappings::Recursive);
  BeginTest("foo a aH");
  TestPressKey("H\\enterrX");
  FinishTest("foo X aH");
  clearAllMappings();

  // Incremental searching from the original position.
  BeginTest("foo bar foop fool food");
  TestPressKey("ll/foo");
  verifyCursorAt(Cursor(0, 8));
  TestPressKey("l");
  verifyCursorAt(Cursor(0, 13));
  TestPressKey("\\backspace");
  verifyCursorAt(Cursor(0, 8));
  TestPressKey("\\enter");
  FinishTest("foo bar foop fool food");

  // End up back at the start if no match found
  BeginTest("foo bar foop fool food");
  TestPressKey("ll/fool");
  verifyCursorAt(Cursor(0, 13));
  TestPressKey("\\backspacex");
  verifyCursorAt(Cursor(0, 2));
  TestPressKey("\\enter");
  FinishTest("foo bar foop fool food");

  // Wrap around if no match found.
  BeginTest("afoom bar foop fool food");
  TestPressKey("lll/foom");
  verifyCursorAt(Cursor(0, 1));
  TestPressKey("\\enter");
  FinishTest("afoom bar foop fool food");

  // SmartCase: match case-insensitively if the search text is all lower-case.
  DoTest("foo BaR", "ll/bar\\enterrX", "foo XaR");

  // SmartCase: match case-sensitively if the search text is mixed case.
  DoTest("foo BaR bAr", "ll/bAr\\enterrX", "foo BaR XAr");

  // Assume regex by default.
  DoTest("foo bwibblear", "ll/b.*ar\\enterrX", "foo Xwibblear");

  // Set the last search pattern.
  DoTest("foo bar", "ll/bar\\enterggnrX", "foo Xar");

  // Make sure the last search pattern is a regex, too.
  DoTest("foo bwibblear", "ll/b.*ar\\enterggnrX", "foo Xwibblear");

  // 'n' should search case-insensitively if the original search was case-insensitive.
  DoTest("foo bAR", "ll/bar\\enterggnrX", "foo XAR");

  // 'n' should search case-sensitively if the original search was case-sensitive.
  DoTest("foo bar bAR", "ll/bAR\\enterggnrX", "foo bar XAR");

  // 'N' should search case-insensitively if the original search was case-insensitive.
  DoTest("foo bAR xyz", "ll/bar\\enter$NrX", "foo XAR xyz");

  // 'N' should search case-sensitively if the original search was case-sensitive.
  DoTest("foo bAR bar", "ll/bAR\\enter$NrX", "foo XAR bar");

  // Don't forget to set the last search to case-insensitive.
  DoTest("foo bAR bar", "ll/bAR\\enter^/bar\\enter^nrX", "foo XAR bar");

  // Usage of \C for manually specifying case sensitivity.
  // Strip occurrences of "\C" from the pattern to find.
  DoTest("foo bar", "/\\\\Cba\\\\Cr\\enterrX", "foo Xar");
  // Be careful about escaping, though!
  DoTest("foo \\Cba\\Cr", "/\\\\\\\\Cb\\\\Ca\\\\\\\\C\\\\C\\\\Cr\\enterrX", "foo XCba\\Cr");
  // The item added to the search history should contain all the original \C's.
  clearSearchHistory();
  BeginTest("foo \\Cba\\Cr");
  TestPressKey("/\\\\\\\\Cb\\\\Ca\\\\\\\\C\\\\C\\\\Cr\\enterrX");
  QCOMPARE(searchHistory().first(), QString("\\\\Cb\\Ca\\\\C\\C\\Cr"));
  FinishTest("foo XCba\\Cr");
  // If there is an escaped C, assume case sensitivity.
  DoTest("foo bAr BAr bar", "/ba\\\\Cr\\enterrX", "foo bAr BAr Xar");
  // The last search pattern should be the last search with escaped C's stripped.
  DoTest("foo \\Cbar\nfoo \\Cbar", "/\\\\\\\\Cba\\\\C\\\\Cr\\enterggjnrX", "foo \\Cbar\nfoo XCbar");
  // If the last search pattern had an escaped "\C", then the next search should be case-sensitive.
  DoTest("foo bar\nfoo bAr BAr bar", "/ba\\\\Cr\\enterggjnrX", "foo bar\nfoo bAr BAr Xar");

  // Don't set the last search parameters if we abort, though.
  DoTest("foo bar xyz", "/bar\\enter/xyz\\ctrl-cggnrX", "foo Xar xyz");
  DoTest("foo bar bAr", "/bar\\enter/bA\\ctrl-cggnrX", "foo Xar bAr");
  DoTest("foo bar bar", "/bar\\enter?ba\\ctrl-cggnrX", "foo Xar bar");

  // Don't let ":" trample all over the search parameters, either.
  DoTest("foo bar xyz foo", "/bar\\entergg*:yank\\enterggnrX", "foo bar xyz Xoo");

  // Some mirror tests for "?"

  // Test that "?" summons the search bar, with empty text and with the "?" indicator.
  QVERIFY(!emulatedCommandBar->isVisible());
  BeginTest("");
  TestPressKey("?");
  QVERIFY(emulatedCommandBar->isVisible());
  QCOMPARE(emulatedCommandTypeIndicator()->text(), QString("?"));
  QVERIFY(emulatedCommandTypeIndicator()->isVisible());
  QVERIFY(emulatedCommandBarTextEdit());
  QVERIFY(emulatedCommandBarTextEdit()->text().isEmpty());
  TestPressKey("\\enter");
  FinishTest("");

  // Search backwards.
  DoTest("foo foo bar foo foo", "ww?foo\\enterrX", "foo Xoo bar foo foo");

  // Reset cursor if we find nothing.
  BeginTest("foo foo bar foo foo");
  TestPressKey("ww?foo");
  verifyCursorAt(Cursor(0, 4));
  TestPressKey("d");
  verifyCursorAt(Cursor(0, 8));
  TestPressKey("\\enter");
  FinishTest("foo foo bar foo foo");

  // Wrap to the end if we find nothing.
  DoTest("foo foo bar xyz xyz", "ww?xyz\\enterrX", "foo foo bar xyz Xyz");

  // Specify that the last was backwards when using '?'
  DoTest("foo foo bar foo foo", "ww?foo\\enter^wwnrX", "foo Xoo bar foo foo");

  // ... and make sure we do  the equivalent with "/"
  BeginTest("foo foo bar foo foo");
  TestPressKey("ww?foo\\enter^ww/foo");
  QCOMPARE(emulatedCommandTypeIndicator()->text(), QString("/"));
  TestPressKey("\\enter^wwnrX");
  FinishTest("foo foo bar Xoo foo");

  // If we are at the beginning of a word, that word is not the first match in a search
  // for that word.
  DoTest("foo foo foo", "w/foo\\enterrX", "foo foo Xoo");
  DoTest("foo foo foo", "w?foo\\enterrX", "Xoo foo foo");
  // When searching backwards, ensure we can find a match whose range includes the starting cursor position,
  // if we allow it to wrap around.
  DoTest("foo foofoofoo bar", "wlll?foofoofoo\\enterrX", "foo Xoofoofoo bar");
  // When searching backwards, ensure we can find a match whose range includes the starting cursor position,
  // even if we don't allow it to wrap around.
  DoTest("foo foofoofoo foofoofoo", "wlll?foofoofoo\\enterrX", "foo Xoofoofoo foofoofoo");
  // The same, but where we the match ends at the end of the line or document.
  DoTest("foo foofoofoo\nfoofoofoo", "wlll?foofoofoo\\enterrX", "foo Xoofoofoo\nfoofoofoo");
  DoTest("foo foofoofoo", "wlll?foofoofoo\\enterrX", "foo Xoofoofoo");

  // Searching forwards for just "/" repeats last search.
  DoTest("foo bar", "/bar\\entergg//\\enterrX", "foo Xar");
  // The "last search" can be one initiated via e.g. "*".
  DoTest("foo bar foo", "/bar\\entergg*gg//\\enterrX", "foo bar Xoo");
  // Searching backwards for just "?" repeats last search.
  DoTest("foo bar bar", "/bar\\entergg??\\enterrX", "foo bar Xar");
  // Search forwards treats "?" as a literal.
  DoTest("foo ?ba?r", "/?ba?r\\enterrX", "foo Xba?r");
  // As always, be careful with escaping!
  DoTest("foo ?ba\\?r", "/?ba\\\\\\\\\\\\?r\\enterrX", "foo Xba\\?r");
  // Searching forwards for just "?" finds literal question marks.
  DoTest("foo ??", "/?\\enterrX", "foo X?");
  // Searching backwards for just "/" finds literal forward slashes.
  DoTest("foo //", "?/\\enterrX", "foo /X");
  // Searching forwards, stuff after (and including) an unescaped "/" is ignored.
  DoTest("foo ba bar bar/xyz", "/bar/xyz\\enterrX", "foo ba Xar bar/xyz");
  // Needs to be unescaped, though!
  DoTest("foo bar bar/xyz", "/bar\\\\/xyz\\enterrX", "foo bar Xar/xyz");
  DoTest("foo bar bar\\/xyz", "/bar\\\\\\\\/xyz\\enterrX", "foo bar Xar\\/xyz");
  // Searching backwards, stuff after (and including) an unescaped "?" is ignored.
  DoTest("foo bar bar?xyz bar ba", "?bar?xyz\\enterrX", "foo bar bar?xyz Xar ba");
  // Needs to be unescaped, though!
  DoTest("foo bar bar?xyz bar ba", "?bar\\\\?xyz\\enterrX", "foo bar Xar?xyz bar ba");
  DoTest("foo bar bar\\?xyz bar ba", "?bar\\\\\\\\?xyz\\enterrX", "foo bar Xar\\?xyz bar ba");
  // If, in a forward search, the first character after the first unescaped "/" is an e, then
  // we place the cursor at the end of the word.
  DoTest("foo ba bar bar/eyz", "/bar/e\\enterrX", "foo ba baX bar/eyz");
  // Needs to be unescaped, though!
  DoTest("foo bar bar/eyz", "/bar\\\\/e\\enterrX", "foo bar Xar/eyz");
  DoTest("foo bar bar\\/xyz", "/bar\\\\\\\\/e\\enterrX", "foo bar barX/xyz");
  // If, in a backward search, the first character after the first unescaped "?" is an e, then
  // we place the cursor at the end of the word.
  DoTest("foo bar bar?eyz bar ba", "?bar?e\\enterrX", "foo bar bar?eyz baX ba");
  // Needs to be unescaped, though!
  DoTest("foo bar bar?eyz bar ba", "?bar\\\\?e\\enterrX", "foo bar Xar?eyz bar ba");
  DoTest("foo bar bar\\?eyz bar ba", "?bar\\\\\\\\?e\\enterrX", "foo bar barX?eyz bar ba");
  // Quick check that repeating the last search and placing the cursor at the end of the match works.
  DoTest("foo bar bar", "/bar\\entergg//e\\enterrX", "foo baX bar");
  DoTest("foo bar bar", "?bar\\entergg??e\\enterrX", "foo bar baX");
  // When repeating a change, don't try to convert from Vim to Qt regex again.
  DoTest("foo bar()", "/bar()\\entergg//e\\enterrX", "foo bar(X");
  DoTest("foo bar()", "?bar()\\entergg??e\\enterrX", "foo bar(X");
  // If the last search said that we should place the cursor at the end of the match, then
  // do this with n & N.
  DoTest("foo bar bar foo", "/bar/e\\enterggnrX", "foo baX bar foo");
  DoTest("foo bar bar foo", "/bar/e\\enterggNrX", "foo bar baX foo");
  // Don't do this if that search was aborted, though.
  DoTest("foo bar bar foo", "/bar\\enter/bar/e\\ctrl-cggnrX", "foo Xar bar foo");
  DoTest("foo bar bar foo", "/bar\\enter/bar/e\\ctrl-cggNrX", "foo bar Xar foo");
  // "#" and "*" reset the "place cursor at the end of the match" to false.
  DoTest("foo bar bar foo", "/bar/e\\enterggw*nrX", "foo Xar bar foo");
  DoTest("foo bar bar foo", "/bar/e\\enterggw#nrX", "foo Xar bar foo");

  // "/" and "?" should be usable as motions.
  DoTest("foo bar", "ld/bar\\enter", "fbar");
  // They are not linewise.
  DoTest("foo bar\nxyz", "ld/yz\\enter", "fyz");
  DoTest("foo bar\nxyz", "jld?oo\\enter", "fyz");
  // Should be usable in Visual Mode without aborting Visual Mode.
  DoTest("foo bar", "lv/bar\\enterd", "far");
  // Same for ?.
  DoTest("foo bar", "$hd?oo\\enter", "far");
  DoTest("foo bar", "$hv?oo\\enterd", "fr");
  DoTest("foo bar", "lv?bar\\enterd", "far");
  // If we abort the "/" / "?" motion, the command should be aborted, too.
  DoTest("foo bar", "d/bar\\esc", "foo bar");
  DoTest("foo bar", "d/bar\\ctrl-c", "foo bar");
  DoTest("foo bar", "d/bar\\ctrl-[", "foo bar");
  // We should be able to repeat a command using "/" or "?" as the motion.
  DoTest("foo bar bar bar", "d/bar\\enter.", "bar bar");
  // The "synthetic" Enter keypress should not be logged as part of the command to be repeated.
  DoTest("foo bar bar bar\nxyz", "d/bar\\enter.rX", "Xar bar\nxyz");
  // Counting.
  DoTest("foo bar bar bar", "2/bar\\enterrX", "foo bar Xar bar");
  // Counting with wraparound.
  DoTest("foo bar bar bar", "4/bar\\enterrX", "foo Xar bar bar");
  // Counting in Visual Mode.
  DoTest("foo bar bar bar", "v2/bar\\enterd", "ar bar");
  // Should update the selection in Visual Mode as we search.
  BeginTest("foo bar bbc");
  TestPressKey("vl/b");
  QCOMPARE(kate_view->selectionText(), QString("foo b"));
  TestPressKey("b");
  QCOMPARE(kate_view->selectionText(), QString("foo bar b"));
  TestPressKey("\\ctrl-h");
  QCOMPARE(kate_view->selectionText(), QString("foo b"));
  TestPressKey("notexists");
  QCOMPARE(kate_view->selectionText(), QString("fo"));
  TestPressKey("\\enter"); // Dismiss bar.
  QCOMPARE(kate_view->selectionText(), QString("fo"));
  FinishTest("foo bar bbc");
  BeginTest("foo\nxyz\nbar\nbbc");
  TestPressKey("Vj/b");
  QCOMPARE(kate_view->selectionText(), QString("foo\nxyz\nbar"));
  TestPressKey("b");
  QCOMPARE(kate_view->selectionText(), QString("foo\nxyz\nbar\nbbc"));
  TestPressKey("\\ctrl-h");
  QCOMPARE(kate_view->selectionText(), QString("foo\nxyz\nbar"));
  TestPressKey("notexists");
  QCOMPARE(kate_view->selectionText(), QString("foo\nxyz"));
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("foo\nxyz\nbar\nbbc");
  // Dismissing the search bar in visual mode should leave original selection.
  BeginTest("foo bar bbc");
  TestPressKey("vl/\\ctrl-c");
  QCOMPARE(kate_view->selectionText(), QString("fo"));
  FinishTest("foo bar bbc");
  BeginTest("foo bar bbc");
  TestPressKey("vl?\\ctrl-c");
  QCOMPARE(kate_view->selectionText(), QString("fo"));
  FinishTest("foo bar bbc");
  BeginTest("foo bar bbc");
  TestPressKey("vl/b\\ctrl-c");
  QCOMPARE(kate_view->selectionText(), QString("fo"));
  FinishTest("foo bar bbc");
  BeginTest("foo\nbar\nbbc");
  TestPressKey("Vl/b\\ctrl-c");
  QCOMPARE(kate_view->selectionText(), QString("foo"));
  FinishTest("foo\nbar\nbbc");

  // Search-highlighting tests.
  const QColor searchHighlightColour = kate_view->renderer()->config()->searchHighlightColor();
  BeginTest("foo bar xyz");
  // Sanity test.
  const QList<Kate::TextRange*> rangesInitial = rangesOnFirstLine();
  Q_ASSERT(rangesInitial.isEmpty() && "Assumptions about ranges are wrong - this test is invalid and may need updating!");
  FinishTest("foo bar xyz");

  // Test highlighting single character match.
  BeginTest("foo bar xyz");
  TestPressKey("/b");
  QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
  QCOMPARE(rangesOnFirstLine().first()->attribute()->background().color(), searchHighlightColour);
  QCOMPARE(rangesOnFirstLine().first()->start().line(), 0);
  QCOMPARE(rangesOnFirstLine().first()->start().column(), 4);
  QCOMPARE(rangesOnFirstLine().first()->end().line(), 0);
  QCOMPARE(rangesOnFirstLine().first()->end().column(), 5);
  TestPressKey("\\enter");
  FinishTest("foo bar xyz");

  // Test highlighting two character match.
  BeginTest("foo bar xyz");
  TestPressKey("/ba");
  QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
  QCOMPARE(rangesOnFirstLine().first()->start().line(), 0);
  QCOMPARE(rangesOnFirstLine().first()->start().column(), 4);
  QCOMPARE(rangesOnFirstLine().first()->end().line(), 0);
  QCOMPARE(rangesOnFirstLine().first()->end().column(), 6);
  TestPressKey("\\enter");
  FinishTest("foo bar xyz");

  // Test no highlighting if no longer a match.
  BeginTest("foo bar xyz");
  TestPressKey("/baz");
  QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
  TestPressKey("\\enter");
  FinishTest("foo bar xyz");

 // Test highlighting on wraparound.
  BeginTest(" foo bar xyz");
  TestPressKey("ww/foo");
  QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
  QCOMPARE(rangesOnFirstLine().first()->start().line(), 0);
  QCOMPARE(rangesOnFirstLine().first()->start().column(), 1);
  QCOMPARE(rangesOnFirstLine().first()->end().line(), 0);
  QCOMPARE(rangesOnFirstLine().first()->end().column(), 4);
  TestPressKey("\\enter");
  FinishTest(" foo bar xyz");

  // Test highlighting backwards
  BeginTest("foo bar xyz");
  TestPressKey("$?ba");
  QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
  QCOMPARE(rangesOnFirstLine().first()->start().line(), 0);
  QCOMPARE(rangesOnFirstLine().first()->start().column(), 4);
  QCOMPARE(rangesOnFirstLine().first()->end().line(), 0);
  QCOMPARE(rangesOnFirstLine().first()->end().column(), 6);
  TestPressKey("\\enter");
  FinishTest("foo bar xyz");

  // Test no highlighting when no match is found searching backwards
  BeginTest("foo bar xyz");
  TestPressKey("$?baz");
  QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
  TestPressKey("\\enter");
  FinishTest("foo bar xyz");

  // Test highlight when wrapping around after searching backwards.
  BeginTest("foo bar xyz");
  TestPressKey("w?xyz");
  QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
  QCOMPARE(rangesOnFirstLine().first()->start().line(), 0);
  QCOMPARE(rangesOnFirstLine().first()->start().column(), 8);
  QCOMPARE(rangesOnFirstLine().first()->end().line(), 0);
  QCOMPARE(rangesOnFirstLine().first()->end().column(), 11);
  TestPressKey("\\enter");
  FinishTest("foo bar xyz");

  // Test no highlighting when bar is dismissed.
  DoTest("foo bar xyz", "/bar\\ctrl-c", "foo bar xyz");
  QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
  DoTest("foo bar xyz", "/bar\\enter", "foo bar xyz");
  QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
  DoTest("foo bar xyz", "/bar\\ctrl-[", "foo bar xyz");
  QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
  DoTest("foo bar xyz", "/bar\\return", "foo bar xyz");
  QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
  DoTest("foo bar xyz", "/bar\\esc", "foo bar xyz");
  QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());

  // Update colour on config change.
  BeginTest("foo bar xyz");
  TestPressKey("/xyz");
  const QColor newSearchHighlightColour = QColor(255, 0, 0);
  kate_view->renderer()->config()->setSearchHighlightColor(newSearchHighlightColour);
  QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
  QCOMPARE(rangesOnFirstLine().first()->attribute()->background().color(), newSearchHighlightColour);
  TestPressKey("\\enter");
  FinishTest("foo bar xyz");

  // Set the background colour appropriately.
  KColorScheme currentColorScheme(QPalette::Normal);
  const QColor normalBackgroundColour = QPalette().brush(QPalette::Base).color();
  const QColor matchBackgroundColour =  currentColorScheme.background(KColorScheme::PositiveBackground).color();
  const QColor noMatchBackgroundColour =  currentColorScheme.background(KColorScheme::NegativeBackground).color();
  BeginTest("foo bar xyz");
  TestPressKey("/xyz");
  verifyTextEditBackgroundColour(matchBackgroundColour);
  TestPressKey("a");
  verifyTextEditBackgroundColour(noMatchBackgroundColour);
  TestPressKey("\\ctrl-w");
  verifyTextEditBackgroundColour(normalBackgroundColour);
  TestPressKey("/xyz\\enter/");
  verifyTextEditBackgroundColour(normalBackgroundColour);
  TestPressKey("\\enter");
  FinishTest("foo bar xyz");

  // Escape regex's in a Vim-ish style.
  // Unescaped ( and ) are always literals.
  DoTest("foo bar( xyz", "/bar(\\enterrX", "foo Xar( xyz");
  DoTest("foo bar) xyz", "/bar)\\enterrX", "foo Xar) xyz");
  // + is literal, unless it is already escaped.
  DoTest("foo bar+ xyz", "/bar+ \\enterrX", "foo Xar+ xyz");
  DoTest("  foo+AAAAbar", "/foo+A\\\\+bar\\enterrX", "  Xoo+AAAAbar");
  DoTest("  foo++++bar", "/foo+\\\\+bar\\enterrX", "  Xoo++++bar");
  DoTest("  foo++++bar", "/+\\enterrX", "  fooX+++bar");
  // An escaped "\" is a literal, of course.
  DoTest("foo x\\y", "/x\\\\\\\\y\\enterrX", "foo X\\y");
  // ( and ), if escaped, are not literals.
  DoTest("foo  barbarxyz", "/ \\\\(bar\\\\)\\\\+xyz\\enterrX", "foo Xbarbarxyz");
  // Handle escaping correctly if we have an escaped and unescaped bracket next to each other.
  DoTest("foo  x(A)y", "/x(\\\\(.\\\\))y\\enterrX", "foo  X(A)y");
  // |, if unescaped, is literal.
  DoTest("foo |bar", "/|\\enterrX", "foo Xbar");
  // |, if escaped, is not a literal.
  DoTest("foo xfoo\\y xbary", "/x\\\\(foo\\\\|bar\\\\)y\\enterrX", "foo xfoo\\y Xbary");
  // A single [ is a literal.
  DoTest("foo bar[", "/bar[\\enterrX", "foo Xar[");
  // A single ] is a literal.
  DoTest("foo bar]", "/bar]\\enterrX", "foo Xar]");
  // A matching [ and ] are *not* literals.
  DoTest("foo xbcay", "/x[abc]\\\\+y\\enterrX", "foo Xbcay");
  DoTest("foo xbcay", "/[abc]\\\\+y\\enterrX", "foo xXcay");
  DoTest("foo xbaadcdcy", "/x[ab]\\\\+[cd]\\\\+y\\enterrX", "foo Xbaadcdcy");
  // Need to be an unescaped match, though.
  DoTest("foo xbcay", "/x[abc\\\\]\\\\+y\\enterrX", "Xoo xbcay");
  DoTest("foo xbcay", "/x\\\\[abc]\\\\+y\\enterrX", "Xoo xbcay");
  DoTest("foo x[abc]]]]]y", "/x\\\\[abc]\\\\+y\\enterrX", "foo X[abc]]]]]y");
  // An escaped '[' between matching unescaped '[' and ']' is treated as a literal '['
  DoTest("foo xb[cay", "/x[a\\\\[bc]\\\\+y\\enterrX", "foo Xb[cay");
  // An escaped ']' between matching unescaped '[' and ']' is treated as a literal ']'
  DoTest("foo xb]cay", "/x[a\\\\]bc]\\\\+y\\enterrX", "foo Xb]cay");
  // An escaped '[' not between other square brackets is a literal.
  DoTest("foo xb[cay", "/xb\\\\[\\enterrX", "foo Xb[cay");
  DoTest("foo xb[cay", "/\\\\[ca\\enterrX", "foo xbXcay");
  // An escaped ']' not between other square brackets is a literal.
  DoTest("foo xb]cay", "/xb\\\\]\\enterrX", "foo Xb]cay");
  DoTest("foo xb]cay", "/\\\\]ca\\enterrX", "foo xbXcay");
  // An unescaped '[' not between other square brackets is a literal.
  DoTest("foo xbaba[y", "/x[ab]\\\\+[y\\enterrX", "foo Xbaba[y");
  DoTest("foo xbaba[dcdcy", "/x[ab]\\\\+[[cd]\\\\+y\\enterrX", "foo Xbaba[dcdcy");
  // An unescaped ']' not between other square brackets is a literal.
  DoTest("foo xbaba]y", "/x[ab]\\\\+]y\\enterrX", "foo Xbaba]y");
  DoTest("foo xbaba]dcdcy", "/x[ab]\\\\+][cd]\\\\+y\\enterrX", "foo Xbaba]dcdcy");
  // Be more clever about how we indentify escaping: the presence of a preceding
  // backslash is not always sufficient!
  DoTest("foo x\\babay", "/x\\\\\\\\[ab]\\\\+y\\enterrX", "foo X\\babay");
  DoTest("foo x\\[abc]]]]y", "/x\\\\\\\\\\\\[abc]\\\\+y\\enterrX", "foo X\\[abc]]]]y");
  DoTest("foo xa\\b\\c\\y", "/x[abc\\\\\\\\]\\\\+y\\enterrX", "foo Xa\\b\\c\\y");
  DoTest("foo x[abc\\]]]]y", "/x[abc\\\\\\\\\\\\]\\\\+y\\enterrX", "foo X[abc\\]]]]y");
  DoTest("foo xa[\\b\\[y", "/x[ab\\\\\\\\[]\\\\+y\\enterrX", "foo Xa[\\b\\[y");
  DoTest("foo x\\[y", "/x\\\\\\\\[y\\enterrX", "foo X\\[y");
  DoTest("foo x\\]y", "/x\\\\\\\\]y\\enterrX", "foo X\\]y");
  DoTest("foo x\\+y", "/x\\\\\\\\+y\\enterrX", "foo X\\+y");
  // A dot is not a literal, nor is a star.
  DoTest("foo bar", "/o.*b\\enterrX", "fXo bar");
  // Escaped dots and stars are literals, though.
  DoTest("foo xay x.y", "/x\\\\.y\\enterrX", "foo xay X.y");
  DoTest("foo xaaaay xa*y", "/xa\\\\*y\\enterrX", "foo xaaaay Xa*y");
  // Unescaped curly braces are literals.
  DoTest("foo x{}y", "/x{}y\\enterrX", "foo X{}y");
  // Escaped curly brackets are quantifers.
  DoTest("foo xaaaaay", "/xa\\\\{5\\\\}y\\enterrX", "foo Xaaaaay");
  // Matching curly brackets where only the first is escaped are also quantifiers.
  DoTest("foo xaaaaaybbbz", "/xa\\\\{5}yb\\\\{3}z\\enterrX", "foo Xaaaaaybbbz");
  // Make sure it really is escaped, though!
  DoTest("foo xa\\{5}", "/xa\\\\\\\\{5}\\enterrX", "foo Xa\\{5}");
  // Don't crash if the first character is a }
  DoTest("foo aaaaay", "/{\\enterrX", "Xoo aaaaay");
  // Vim's '\<' and '\>' map, roughly, to Qt's '\b'
  DoTest("foo xbar barx bar", "/bar\\\\>\\enterrX", "foo xXar barx bar");
  DoTest("foo xbar barx bar", "/\\\\<bar\\enterrX", "foo xbar Xarx bar");
  DoTest("foo xbar barx bar ", "/\\\\<bar\\\\>\\enterrX", "foo xbar barx Xar ");
  DoTest("foo xbar barx bar", "/\\\\<bar\\\\>\\enterrX", "foo xbar barx Xar");
  DoTest("foo xbar barx\nbar", "/\\\\<bar\\\\>\\enterrX", "foo xbar barx\nXar");
  // Escaped "^" and "$" are treated as literals.
  DoTest("foo x^$y", "/x\\\\^\\\\$y\\enterrX", "foo X^$y");
  // Ensure that it is the escaped version of the pattern that is recorded as the last search pattern.
  DoTest("foo bar( xyz", "/bar(\\enterggnrX", "foo Xar( xyz");

  // Don't log keypresses sent to the emulated command bar as commands to be repeated via "."!
  DoTest("foo", "/diw\\enterciwbar\\ctrl-c.", "bar");

  // Don't leave Visual mode on aborting a search.
  DoTest("foo bar", "vw/\\ctrl-cd", "ar");
  DoTest("foo bar", "vw/\\ctrl-[d", "ar");

  // Don't crash on leaving Visual Mode on aborting a search. This is perhaps the most opaque regression
  // test ever; what it's testing for is the situation where the synthetic keypress issue by the emulated
  // command bar on the "ctrl-[" is sent to the key mapper.  This in turn converts it into a weird character
  // which is then, upon not being recognised as part of a mapping, sent back around the keypress processing,
  // where it ends up being sent to the emulated command bar's text edit, which in turn issues a "text changed"
  // event where the text is still empty, which tries to move the cursor to (-1, -1), which causes a crash deep
  // within Kate. So, in a nutshell: this test ensures that the keymapper never handles the synthetic keypress :)
  DoTest("", "ifoo\\ctrl-cv/\\ctrl-[", "foo");

  // History auto-completion tests.
  clearSearchHistory();
  QVERIFY(searchHistory().isEmpty());
  vi_global->searchHistory()->append("foo");
  vi_global->searchHistory()->append("bar");
  QCOMPARE(searchHistory(), QStringList() << "foo" << "bar");
  clearSearchHistory();
  QVERIFY(searchHistory().isEmpty());

  // Ensure current search bar text is added to the history if we press enter.
  DoTest("foo bar", "/bar\\enter", "foo bar");
  DoTest("foo bar", "/xyz\\enter", "foo bar");
  QCOMPARE(searchHistory(), QStringList() << "bar" << "xyz");
  // Interesting - Vim adds the search bar text to the history even if we abort via e.g. ctrl-c, ctrl-[, etc.
  clearSearchHistory();
  DoTest("foo bar", "/baz\\ctrl-[", "foo bar");
  QCOMPARE(searchHistory(), QStringList() << "baz");
  clearSearchHistory();
  DoTest("foo bar", "/foo\\esc", "foo bar");
  QCOMPARE(searchHistory(), QStringList() << "foo");
  clearSearchHistory();
  DoTest("foo bar", "/nose\\ctrl-c", "foo bar");
  QCOMPARE(searchHistory(), QStringList() << "nose");

  clearSearchHistory();
  vi_global->searchHistory()->append("foo");
  vi_global->searchHistory()->append("bar");
  QVERIFY(emulatedCommandBarCompleter() != nullptr);
  BeginTest("foo bar");
  TestPressKey("/\\ctrl-p");
  verifyCommandBarCompletionVisible();
  // Make sure the completion appears in roughly the correct place: this is a little fragile :/
  const QPoint completerRectTopLeft = emulatedCommandBarCompleter()->popup()->mapToGlobal(emulatedCommandBarCompleter()->popup()->rect().topLeft()) ;
  const QPoint barEditBottomLeft = emulatedCommandBarTextEdit()->mapToGlobal(emulatedCommandBarTextEdit()->rect().bottomLeft());
  QCOMPARE(completerRectTopLeft.x(), barEditBottomLeft.x());
  QVERIFY(qAbs(completerRectTopLeft.y() - barEditBottomLeft.y()) <= 1);
  // Will activate the current completion item, activating the search, and dismissing the bar.
  TestPressKey("\\enter");
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  // Close the command bar.
  FinishTest("foo bar");

  // Don't show completion with an empty search bar.
  clearSearchHistory();
  vi_global->searchHistory()->append("foo");
  BeginTest("foo bar");
  TestPressKey("/");
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  TestPressKey("\\enter");
  FinishTest("foo bar");

  // Don't auto-complete, either.
  clearSearchHistory();
  vi_global->searchHistory()->append("foo");
  BeginTest("foo bar");
  TestPressKey("/f");
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  TestPressKey("\\enter");
  FinishTest("foo bar");

  clearSearchHistory();
  vi_global->searchHistory()->append("xyz");
  vi_global->searchHistory()->append("bar");
  QVERIFY(emulatedCommandBarCompleter() != nullptr);
  BeginTest("foo bar");
  TestPressKey("/\\ctrl-p");
  QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("bar"));
  TestPressKey("\\enter"); // Dismiss bar.
  FinishTest("foo bar");

  clearSearchHistory();
  vi_global->searchHistory()->append("xyz");
  vi_global->searchHistory()->append("bar");
  vi_global->searchHistory()->append("foo");
  QVERIFY(emulatedCommandBarCompleter() != nullptr);
  BeginTest("foo bar");
  TestPressKey("/\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo"));
  QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("foo"));
  QCOMPARE(emulatedCommandBarCompleter()->popup()->currentIndex().row(), 0);
  TestPressKey("\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("bar"));
  QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("bar"));
  QCOMPARE(emulatedCommandBarCompleter()->popup()->currentIndex().row(), 1);
  TestPressKey("\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("xyz"));
  QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("xyz"));
  QCOMPARE(emulatedCommandBarCompleter()->popup()->currentIndex().row(), 2);
  TestPressKey("\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo"));
  QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("foo")); // Wrap-around
  QCOMPARE(emulatedCommandBarCompleter()->popup()->currentIndex().row(), 0);
  TestPressKey("\\enter"); // Dismiss bar.
  FinishTest("foo bar");

  clearSearchHistory();
  vi_global->searchHistory()->append("xyz");
  vi_global->searchHistory()->append("bar");
  vi_global->searchHistory()->append("foo");
  QVERIFY(emulatedCommandBarCompleter() != nullptr);
  BeginTest("foo bar");
  TestPressKey("/\\ctrl-n");
  verifyCommandBarCompletionVisible();
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("xyz"));
  QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("xyz"));
  QCOMPARE(emulatedCommandBarCompleter()->popup()->currentIndex().row(), 2);
  TestPressKey("\\ctrl-n");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("bar"));
  QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("bar"));
  QCOMPARE(emulatedCommandBarCompleter()->popup()->currentIndex().row(), 1);
  TestPressKey("\\ctrl-n");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo"));
  QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("foo"));
  QCOMPARE(emulatedCommandBarCompleter()->popup()->currentIndex().row(), 0);
  TestPressKey("\\ctrl-n");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("xyz"));
  QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("xyz")); // Wrap-around.
  QCOMPARE(emulatedCommandBarCompleter()->popup()->currentIndex().row(), 2);
  TestPressKey("\\enter"); // Dismiss bar.
  FinishTest("foo bar");

  clearSearchHistory();
  vi_global->searchHistory()->append("xyz");
  vi_global->searchHistory()->append("bar");
  vi_global->searchHistory()->append("foo");
  BeginTest("foo bar");
  TestPressKey("/\\ctrl-n\\ctrl-n");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("bar"));
  TestPressKey("\\enter"); // Dismiss bar.
  FinishTest("foo bar");

  // If we add something to the history, remove any earliest occurrences (this is what Vim appears to do)
  // and append to the end.
  clearSearchHistory();
  vi_global->searchHistory()->append("bar");
  vi_global->searchHistory()->append("xyz");
  vi_global->searchHistory()->append("foo");
  vi_global->searchHistory()->append("xyz");
  QCOMPARE(searchHistory(), QStringList() << "bar" << "foo" << "xyz");

  // Push out older entries if we have too many search items in the history.
  const int HISTORY_SIZE_LIMIT = 100;
  clearSearchHistory();
  for (int i = 1; i <= HISTORY_SIZE_LIMIT; i++)
  {
    vi_global->searchHistory()->append(QString("searchhistoryitem %1").arg(i));
  }
  QCOMPARE(searchHistory().size(), HISTORY_SIZE_LIMIT);
  QCOMPARE(searchHistory().first(), QString("searchhistoryitem 1"));
  QCOMPARE(searchHistory().last(), QString("searchhistoryitem 100"));
  vi_global->searchHistory()->append(QString("searchhistoryitem %1").arg(HISTORY_SIZE_LIMIT + 1));
  QCOMPARE(searchHistory().size(), HISTORY_SIZE_LIMIT);
  QCOMPARE(searchHistory().first(), QString("searchhistoryitem 2"));
  QCOMPARE(searchHistory().last(), QString("searchhistoryitem %1").arg(HISTORY_SIZE_LIMIT + 1));



  // Don't add empty searches to the history.
  clearSearchHistory();
  DoTest("foo bar", "/\\enter", "foo bar");
  QVERIFY(searchHistory().isEmpty());

  // "*" and "#" should add the relevant word to the search history, enclosed between \< and \>
  clearSearchHistory();
  BeginTest("foo bar");
  TestPressKey("*");
  QVERIFY(!searchHistory().isEmpty());
  QCOMPARE(searchHistory().last(), QString("\\<foo\\>"));
  TestPressKey("w#");
  QCOMPARE(searchHistory().size(), 2);
  QCOMPARE(searchHistory().last(), QString("\\<bar\\>"));

  // Auto-complete words from the document on ctrl-space.
  // Test that we can actually find a single word and add it to the list of completions.
  BeginTest("foo");
  TestPressKey("/\\ctrl- ");
  verifyCommandBarCompletionVisible();
  QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("foo"));
  TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
  FinishTest("foo");

  // Count digits and underscores as being part of a word.
  BeginTest("foo_12");
  TestPressKey("/\\ctrl- ");
  verifyCommandBarCompletionVisible();
  QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("foo_12"));
  TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
  FinishTest("foo_12");

  // This feels a bit better to me, usability-wise: in the special case of completion from document, where
  // the completion list is manually summoned, allow one to press Enter without the bar being dismissed
  // (just dismiss the completion list instead).
  BeginTest("foo_12");
  TestPressKey("/\\ctrl- \\ctrl-p\\enter");
  QVERIFY(emulatedCommandBar->isVisible());
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  TestPressKey("\\enter"); // Dismiss bar.
  FinishTest("foo_12");

  // Check that we can find multiple words on one line.
  BeginTest("bar (foo) [xyz]");
  TestPressKey("/\\ctrl- ");
  QStringListModel *completerStringListModel = dynamic_cast<QStringListModel*>(emulatedCommandBarCompleter()->model());
  Q_ASSERT(completerStringListModel);
  QCOMPARE(completerStringListModel->stringList(), QStringList() << "bar" << "foo" << "xyz");
  TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
  FinishTest("bar (foo) [xyz]");

  // Check that we arrange the found words in case-insensitive sorted order.
  BeginTest("D c e a b f");
  TestPressKey("/\\ctrl- ");
  verifyCommandBarCompletionsMatches(QStringList() << "a" << "b" << "c" << "D" << "e" << "f");
  TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
  FinishTest("D c e a b f");

  // Check that we don't include the same word multiple times.
  BeginTest("foo bar bar bar foo");
  TestPressKey("/\\ctrl- ");
  verifyCommandBarCompletionsMatches(QStringList() << "bar" << "foo");
  TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
  FinishTest("foo bar bar bar foo");

  // Check that we search only a narrow portion of the document, around the cursor (4096 lines either
  // side, say).
  QStringList manyLines;
  for (int i = 1; i < 2 * 4096 + 3; i++)
  {
    // Pad the digits so that when sorted alphabetically, they are also sorted numerically.
    manyLines << QString("word%1").arg(i, 5, 10, QChar('0'));
  }
  QStringList allButFirstAndLastOfManyLines = manyLines;
  allButFirstAndLastOfManyLines.removeFirst();
  allButFirstAndLastOfManyLines.removeLast();

  BeginTest(manyLines.join("\n"));
  TestPressKey("4097j/\\ctrl- ");
  verifyCommandBarCompletionsMatches(allButFirstAndLastOfManyLines);
  TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
  FinishTest(manyLines.join("\n"));


  // "The current word" means the word before the cursor in the command bar, and includes numbers
  // and underscores. Make sure also that the completion prefix is set when the completion is first invoked.
  BeginTest("foo fee foa_11 foa_11b");
  // Write "bar(foa112$nose" and position cursor before the "2", then invoke completion.
  TestPressKey("/bar(foa_112$nose\\left\\left\\left\\left\\left\\left\\ctrl- ");
  verifyCommandBarCompletionsMatches(QStringList() << "foa_11" << "foa_11b");
  TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
  FinishTest("foo fee foa_11 foa_11b");

  // But don't count "-" as being part of the current word.
  BeginTest("foo_12");
  TestPressKey("/bar-foo\\ctrl- ");
  verifyCommandBarCompletionVisible();
  QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("foo_12"));
  TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
  FinishTest("foo_12");

  // Be case insensitive.
  BeginTest("foo Fo12 fOo13 FO45");
  TestPressKey("/fo\\ctrl- ");
  verifyCommandBarCompletionsMatches(QStringList() << "Fo12" << "FO45" << "foo" << "fOo13");
  TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
  FinishTest("foo Fo12 fOo13 FO45");

  // Feed the current word to complete to the completer as we type/ edit.
  BeginTest("foo fee foa foab");
  TestPressKey("/xyz|f\\ctrl- o");
  verifyCommandBarCompletionsMatches(QStringList() << "foa" << "foab" << "foo");
  TestPressKey("a");
  verifyCommandBarCompletionsMatches(QStringList() << "foa" << "foab");
  TestPressKey("\\ctrl-h");
  verifyCommandBarCompletionsMatches(QStringList() << "foa" << "foab" << "foo");
  TestPressKey("o");
  verifyCommandBarCompletionsMatches(QStringList() << "foo");
  TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
  FinishTest("foo fee foa foab");

  // Upon selecting a completion with an empty command bar, add the completed text to the command bar.
  BeginTest("foo fee fob foables");
  TestPressKey("/\\ctrl- foa\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foables"));
  verifyCommandBarCompletionVisible();
  TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
  FinishTest("foo fee fob foables");

  // If bar is non-empty, replace the word under the cursor.
  BeginTest("foo fee foa foab");
  TestPressKey("/xyz|f$nose\\left\\left\\left\\left\\left\\ctrl- oa\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("xyz|foab$nose"));
  TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
  FinishTest("foo fee foa foab");

  // Place the cursor at the end of the completed text.
  BeginTest("foo fee foa foab");
  TestPressKey("/xyz|f$nose\\left\\left\\left\\left\\left\\ctrl- oa\\ctrl-p\\enterX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("xyz|foabX$nose"));
  TestPressKey("\\ctrl-c"); // Dismiss completion, then bar.
  FinishTest("foo fee foa foab");

  // If we're completing from history, though, the entire text gets set, and the completion prefix
  // is the beginning of the entire text, not the current word before the cursor.
  clearSearchHistory();
  vi_global->searchHistory()->append("foo(bar");
  BeginTest("");
  TestPressKey("/foo(b\\ctrl-p");
  verifyCommandBarCompletionVisible();
  verifyCommandBarCompletionsMatches(QStringList() << "foo(bar");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo(bar"));
  TestPressKey("\\enter"); // Dismiss bar.
  FinishTest("");

  // If we're completing from history and we abort the completion via ctrl-c or ctrl-[, we revert the whole
  // text to the last manually typed text.
  clearSearchHistory();
  vi_global->searchHistory()->append("foo(b|ar");
  BeginTest("");
  TestPressKey("/foo(b\\ctrl-p");
  verifyCommandBarCompletionVisible();
  verifyCommandBarCompletionsMatches(QStringList() << "foo(b|ar");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo(b|ar"));
  TestPressKey("\\ctrl-c"); // Dismiss completion.
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo(b"));
  TestPressKey("\\enter"); // Dismiss bar.
  FinishTest("");

  // Scroll completion list if necessary so that currently selected completion is visible.
  BeginTest("a b c d e f g h i j k l m n o p q r s t u v w x y z");
  TestPressKey("/\\ctrl- ");
  const int lastItemRow = 25;
  const QRect initialLastCompletionItemRect = emulatedCommandBarCompleter()->popup()->visualRect(emulatedCommandBarCompleter()->popup()->model()->index(lastItemRow, 0));
  QVERIFY(!emulatedCommandBarCompleter()->popup()->rect().contains(initialLastCompletionItemRect)); // If this fails, then we have an error in the test setup: initally, the last item in the list should be outside of the bounds of the popup.
  TestPressKey("\\ctrl-n");
  QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("z"));
  const QRect lastCompletionItemRect = emulatedCommandBarCompleter()->popup()->visualRect(emulatedCommandBarCompleter()->popup()->model()->index(lastItemRow, 0));
  QVERIFY(emulatedCommandBarCompleter()->popup()->rect().contains(lastCompletionItemRect));
  TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
  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");

  // Ensure that the completion list changes size appropriately as the number of candidate completions changes.
  BeginTest("a ab abc");
  TestPressKey("/\\ctrl- ");
  const int initialPopupHeight = emulatedCommandBarCompleter()->popup()->height();
  TestPressKey("ab");
  const int popupHeightAfterEliminatingOne = emulatedCommandBarCompleter()->popup()->height();
  QVERIFY(popupHeightAfterEliminatingOne < initialPopupHeight);
  TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
  FinishTest("a ab abc");

  // Ensure that the completion list disappears when no candidate completions are found, but re-appears
  // when some are found.
  BeginTest("a ab abc");
  TestPressKey("/\\ctrl- ");
  TestPressKey("abd");
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  TestPressKey("\\ctrl-h");
  verifyCommandBarCompletionVisible();
  TestPressKey("\\enter\\enter"); // Dismiss completion, then bar.
  FinishTest("a ab abc");

  // ctrl-c and ctrl-[ when the completion list is visible should dismiss the completion list, but *not*
  // the emulated command bar. TODO - same goes for ESC, but this is harder as KateViewInternal dismisses it
  // itself.
  BeginTest("a ab abc");
  TestPressKey("/\\ctrl- \\ctrl-cdiw");
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  QVERIFY(emulatedCommandBar->isVisible());
  TestPressKey("\\enter"); // Dismiss bar.
  TestPressKey("/\\ctrl- \\ctrl-[diw");
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  QVERIFY(emulatedCommandBar->isVisible());
  TestPressKey("\\enter"); // Dismiss bar.
  FinishTest("a ab abc");

  // If we implicitly choose an element from the summoned completion list (by highlighting it, then
  // continuing to edit the text), the completion box should not re-appear unless explicitly summoned
  // again, even if the current word has a valid completion.
  BeginTest("a ab abc");
  TestPressKey("/\\ctrl- \\ctrl-p");
  TestPressKey(".a");
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  TestPressKey("\\enter"); // Dismiss bar.
  FinishTest("a ab abc");

  // If we dismiss the summoned completion list via ctrl-c or ctrl-[, it should not re-appear unless explicitly summoned
  // again, even if the current word has a valid completion.
  BeginTest("a ab abc");
  TestPressKey("/\\ctrl- \\ctrl-c");
  TestPressKey(".a");
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  TestPressKey("\\enter");
  TestPressKey("/\\ctrl- \\ctrl-[");
  TestPressKey(".a");
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  TestPressKey("\\enter"); // Dismiss bar.
  FinishTest("a ab abc");

  // If we select a completion from an empty bar, but then dismiss it via ctrl-c or ctrl-[, then we
  // should restore the empty text.
  BeginTest("foo");
  TestPressKey("/\\ctrl- \\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo"));
  TestPressKey("\\ctrl-c");
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  QVERIFY(emulatedCommandBar->isVisible());
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(""));
  TestPressKey("\\enter"); // Dismiss bar.
  FinishTest("foo");
  BeginTest("foo");
  TestPressKey("/\\ctrl- \\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("foo"));
  TestPressKey("\\ctrl-[");
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  QVERIFY(emulatedCommandBar->isVisible());
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(""));
  TestPressKey("\\enter"); // Dismiss bar.
  FinishTest("foo");

  // If we select a completion but then dismiss it via ctrl-c or ctrl-[, then we
  // should restore the last manually typed word.
  BeginTest("fooabc");
  TestPressKey("/f\\ctrl- o\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("fooabc"));
  TestPressKey("\\ctrl-c");
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  QVERIFY(emulatedCommandBar->isVisible());
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("fo"));
  TestPressKey("\\enter"); // Dismiss bar.
  FinishTest("fooabc");

  // If we select a completion but then dismiss it via ctrl-c or ctrl-[, then we
  // should restore the word currently being typed to the last manually typed word.
  BeginTest("fooabc");
  TestPressKey("/ab\\ctrl- |fo\\ctrl-p");
  TestPressKey("\\ctrl-c");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("ab|fo"));
  TestPressKey("\\enter"); // Dismiss bar.
  FinishTest("fooabc");

  // Set the completion prefix for the search history completion as soon as it is shown.
  clearSearchHistory();
  vi_global->searchHistory()->append("foo(bar");
  vi_global->searchHistory()->append("xyz");
  BeginTest("");
  TestPressKey("/f\\ctrl-p");
  verifyCommandBarCompletionVisible();
  verifyCommandBarCompletionsMatches(QStringList() << "foo(bar");
  TestPressKey("\\enter"); // Dismiss bar.
  FinishTest("");

  // Command Mode (:) tests.
  // ":" should summon the command bar, with ":" as the label.
  BeginTest("");
  TestPressKey(":");
  QVERIFY(emulatedCommandBar->isVisible());
  QCOMPARE(emulatedCommandTypeIndicator()->text(), QString(":"));
  QVERIFY(emulatedCommandTypeIndicator()->isVisible());
  QVERIFY(emulatedCommandBarTextEdit());
  QVERIFY(emulatedCommandBarTextEdit()->text().isEmpty());
  TestPressKey("\\esc");
  FinishTest("");

  // If we have a selection, it should be encoded as a range in the text edit.
  BeginTest("d\nb\na\nc");
  TestPressKey("Vjjj:");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("'<,'>"));
  TestPressKey("\\esc");
  FinishTest("d\nb\na\nc");

  // If we have a count, it should be encoded as a range in the text edit.
  BeginTest("d\nb\na\nc");
  TestPressKey("7:");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(".,.+6"));
  TestPressKey("\\esc");
  FinishTest("d\nb\na\nc");

  // Don't go doing an incremental search when we press keys!
  BeginTest("foo bar xyz");
  TestPressKey(":bar");
  QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
  TestPressKey("\\esc");
  FinishTest("foo bar xyz");

  // Execute the command on Enter.
  DoTest("d\nb\na\nc", "Vjjj:sort\\enter", "a\nb\nc\nd");

  // Don't crash if we call a non-existent command with a range.
  DoTest("123", ":42nonexistentcommand\\enter", "123");

  // Bar background should always be normal for command bar.
  BeginTest("foo");
  TestPressKey("/foo\\enter:");
  verifyTextEditBackgroundColour(normalBackgroundColour);
  TestPressKey("\\ctrl-c/bar\\enter:");
  verifyTextEditBackgroundColour(normalBackgroundColour);
  TestPressKey("\\esc");
  FinishTest("foo");


  const int commandResponseMessageTimeOutMSOverride = QString::fromLatin1(qgetenv("KATE_VIMODE_TEST_COMMANDRESPONSEMESSAGETIMEOUTMS")).toInt();
  const long commandResponseMessageTimeOutMS = (commandResponseMessageTimeOutMSOverride > 0) ? commandResponseMessageTimeOutMSOverride : 4000;
  {
  // If there is any output from the command, show it in a label for a short amount of time
  // (make sure the bar type indicator is hidden, here, as it looks messy).
  emulatedCommandBar->setCommandResponseMessageTimeout(commandResponseMessageTimeOutMS);
  BeginTest("foo bar xyz");
  const QDateTime timeJustBeforeCommandExecuted = QDateTime::currentDateTime();
  TestPressKey(":commandthatdoesnotexist\\enter");
  QVERIFY(emulatedCommandBar->isVisible());
  QVERIFY(commandResponseMessageDisplay());
  QVERIFY(commandResponseMessageDisplay()->isVisible());
  QVERIFY(!emulatedCommandBarTextEdit()->isVisible());
  QVERIFY(!emulatedCommandTypeIndicator()->isVisible());
  // Be a bit vague about the exact message, due to i18n, etc.
  QVERIFY(commandResponseMessageDisplay()->text().contains("commandthatdoesnotexist"));
  waitForEmulatedCommandBarToHide(4 * commandResponseMessageTimeOutMS);
  QVERIFY(timeJustBeforeCommandExecuted.msecsTo(QDateTime::currentDateTime()) >= commandResponseMessageTimeOutMS - 500); // "- 500" because coarse timers can fire up to 500ms *prematurely*.
  QVERIFY(!emulatedCommandBar->isVisible());
  // Piggy-back on this test, as the bug we're about to test for would actually make setting
  // up the conditions again in a separate test impossible ;)
  // When we next summon the bar, the response message should be invisible; the editor visible & editable;
  // and the bar type indicator visible again.
  TestPressKey("/");
  QVERIFY(!commandResponseMessageDisplay()->isVisible());
  QVERIFY(emulatedCommandBarTextEdit()->isVisible());
  QVERIFY(emulatedCommandBarTextEdit()->isEnabled());
  QVERIFY(emulatedCommandBar->isVisible());
  TestPressKey("\\esc"); // Dismiss the bar.
  FinishTest("foo bar xyz");
  }

  {
    // Show the same message twice in a row.
    BeginTest("foo bar xyz");
    TestPressKey(":othercommandthatdoesnotexist\\enter");
    QDateTime startWaitingForMessageToHide = QDateTime::currentDateTime();
    waitForEmulatedCommandBarToHide(4 * commandResponseMessageTimeOutMS);
    TestPressKey(":othercommandthatdoesnotexist\\enter");
    QVERIFY(commandResponseMessageDisplay()->isVisible());
    // Wait for it to disappear again, as a courtesy for the next test.
    waitForEmulatedCommandBarToHide(4 * commandResponseMessageTimeOutMS);
  }

  {
    // Emulated command bar should not steal keypresses when it is merely showing the results of an executed command.
    BeginTest("foo bar");
    TestPressKey(":commandthatdoesnotexist\\enterrX");
    Q_ASSERT_X(commandResponseMessageDisplay()->isVisible(), "running test", "Need to increase timeJustBeforeCommandExecuted!");
    FinishTest("Xoo bar");
  }

  {
    // Don't send the synthetic "enter" keypress (for making search-as-a-motion work) when we finally hide.
    BeginTest("foo bar\nbar");
    TestPressKey(":commandthatdoesnotexist\\enter");
    Q_ASSERT_X(commandResponseMessageDisplay()->isVisible(), "running test", "Need to increase timeJustBeforeCommandExecuted!");
    waitForEmulatedCommandBarToHide(commandResponseMessageTimeOutMS * 4);
    TestPressKey("rX");
    FinishTest("Xoo bar\nbar");
  }

  {
    // The timeout should be cancelled when we invoke the command bar again.
    BeginTest("");
    TestPressKey(":commandthatdoesnotexist\\enter");
    const QDateTime waitStartedTime = QDateTime::currentDateTime();
    TestPressKey(":");
    // Wait ample time for the timeout to fire.  Do not use waitForEmulatedCommandBarToHide for this!
    while(waitStartedTime.msecsTo(QDateTime::currentDateTime()) < commandResponseMessageTimeOutMS * 2)
    {
      QApplication::processEvents();
    }
    QVERIFY(emulatedCommandBar->isVisible());
    TestPressKey("\\esc"); // Dismiss the bar.
    FinishTest("");
  }

  {
    // The timeout should not cause kate_view to regain focus if we have manually taken it away.
    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.";
    BeginTest("");
    TestPressKey(":commandthatdoesnotexist\\enter");
    while (QApplication::hasPendingEvents())
    {
      // Wait for any focus changes to take effect.
      QApplication::processEvents();
    }
    const QDateTime waitStartedTime = QDateTime::currentDateTime();
    QLineEdit *dummyToFocus = new QLineEdit(QString("Sausage"), mainWindow);
    // Take focus away from kate_view by giving it to dummyToFocus.
    QApplication::setActiveWindow(mainWindow);
    kate_view->setFocus();
    mainWindowLayout->addWidget(dummyToFocus);
    dummyToFocus->show();
    dummyToFocus->setEnabled(true);
    dummyToFocus->setFocus();
    // Allow dummyToFocus to receive focus.
    while(!dummyToFocus->hasFocus())
    {
      QApplication::processEvents();
    }
    QVERIFY(dummyToFocus->hasFocus());
    // Wait ample time for the timeout to fire.  Do not use waitForEmulatedCommandBarToHide for this -
    // the bar never actually hides in this instance, and I think it would take some deep changes in
    // Kate to make it do so (the KateCommandLineBar as the same issue).
    while(waitStartedTime.msecsTo(QDateTime::currentDateTime()) < commandResponseMessageTimeOutMS * 2)
    {
      QApplication::processEvents();
    }
    QVERIFY(dummyToFocus->hasFocus());
    QVERIFY(emulatedCommandBar->isVisible());
    mainWindowLayout->removeWidget(dummyToFocus);
    // Restore focus to the kate_view.
    kate_view->setFocus();
    while(!kate_view->hasFocus())
    {
      QApplication::processEvents();
    }
    // *Now* wait for the command bar to disappear - giving kate_view focus should trigger it.
    waitForEmulatedCommandBarToHide(commandResponseMessageTimeOutMS * 4);
    FinishTest("");
  }

  {
    // No completion should be shown when the bar is first shown: this gives us an opportunity
    // to invoke command history via ctrl-p and ctrl-n.
    BeginTest("");
    TestPressKey(":");
    QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
    TestPressKey("\\ctrl-c"); // Dismiss bar
    FinishTest("");
  }

  {
    // Should be able to switch to completion from document, even when we have a completion from commands.
    BeginTest("soggy1 soggy2");
    TestPressKey(":so");
    verifyCommandBarCompletionContains(QStringList() << "sort");
    TestPressKey("\\ctrl- ");
    verifyCommandBarCompletionsMatches(QStringList() << "soggy1" << "soggy2");
    TestPressKey("\\ctrl-c"); // Dismiss completer
    TestPressKey("\\ctrl-c"); // Dismiss bar
    FinishTest("soggy1 soggy2");
  }

  {
    // If we dismiss the command completion then change the text, it should summon the completion
    // again.
    BeginTest("");
    TestPressKey(":so");
    TestPressKey("\\ctrl-c"); // Dismiss completer
    TestPressKey("r");
    verifyCommandBarCompletionVisible();
    verifyCommandBarCompletionContains(QStringList() << "sort");
    TestPressKey("\\ctrl-c"); // Dismiss completer
    TestPressKey("\\ctrl-c"); // Dismiss bar
    FinishTest("");
  }

  {
    // Completion should be dismissed when we are showing command response text.
    BeginTest("");
    TestPressKey(":set-au\\enter");
    QVERIFY(commandResponseMessageDisplay()->isVisible());
    QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
    waitForEmulatedCommandBarToHide(commandResponseMessageTimeOutMS * 4);
    FinishTest("");
  }

  // If we abort completion via ctrl-c or ctrl-[, we should revert the current word to the last
  // manually entered word.
  BeginTest("");
  TestPressKey(":se\\ctrl-p");
  verifyCommandBarCompletionVisible();
  QVERIFY(emulatedCommandBarTextEdit()->text() != "se");
  TestPressKey("\\ctrl-c"); // Dismiss completer
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("se"));
  TestPressKey("\\ctrl-c"); // Dismiss bar
  FinishTest("");

  // In practice, it's annoying if, as we enter ":s/se", completions pop up after the "se":
  // for now, only summon completion if we are on the first word in the text.
  BeginTest("");
  TestPressKey(":s/se");
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  TestPressKey("\\ctrl-c"); // Dismiss bar
  FinishTest("");
  BeginTest("");
  TestPressKey(":.,.+7s/se");
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  TestPressKey("\\ctrl-c"); // Dismiss bar
  FinishTest("");

  // Don't blank the text if we activate command history completion with no command history.
  BeginTest("");
  clearCommandHistory();
  TestPressKey(":s/se\\ctrl-p");
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/se"));
  TestPressKey("\\ctrl-c"); // Dismiss bar
  FinishTest("");

  // On completion, only update the command in front of the cursor.
  BeginTest("");
  clearCommandHistory();
  TestPressKey(":.,.+6s/se\\left\\left\\leftet-auto-in\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(".,.+6set-auto-indent/se"));
  TestPressKey("\\ctrl-c"); // Dismiss completer.
  TestPressKey("\\ctrl-c"); // Dismiss bar
  FinishTest("");

  // On completion, place the cursor after the new command.
  BeginTest("");
  clearCommandHistory();
  TestPressKey(":.,.+6s/fo\\left\\left\\leftet-auto-in\\ctrl-pX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(".,.+6set-auto-indentX/fo"));
  TestPressKey("\\ctrl-c"); // Dismiss completer.
  TestPressKey("\\ctrl-c"); // Dismiss bar
  FinishTest("");

  // "The current word", for Commands, can contain "-".
  BeginTest("");
  TestPressKey(":set-\\ctrl-p");
  verifyCommandBarCompletionVisible();
  QVERIFY(emulatedCommandBarTextEdit()->text() != "set-");
  QVERIFY(emulatedCommandBarCompleter()->currentCompletion().startsWith("set-"));
  QCOMPARE(emulatedCommandBarTextEdit()->text(), emulatedCommandBarCompleter()->currentCompletion());
  TestPressKey("\\ctrl-c"); // Dismiss completion.
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  {
    // Don't switch from word-from-document to command-completion just because we press a key, though!
    BeginTest("soggy1 soggy2");
    TestPressKey(":\\ctrl- s");
    TestPressKey("o");
    verifyCommandBarCompletionVisible();
    verifyCommandBarCompletionsMatches(QStringList() << "soggy1" << "soggy2");
    TestPressKey("\\ctrl-c"); // Dismiss completer
    TestPressKey("\\ctrl-c"); // Dismiss bar
    FinishTest("soggy1 soggy2");
  }

  {
    // If we're in a place where there is no command completion allowed, don't go hiding the word
    // completion as we type.
    BeginTest("soggy1 soggy2");
    TestPressKey(":s/s\\ctrl- o");
    verifyCommandBarCompletionVisible();
    verifyCommandBarCompletionsMatches(QStringList() << "soggy1" << "soggy2");
    TestPressKey("\\ctrl-c"); // Dismiss completer
    TestPressKey("\\ctrl-c"); // Dismiss bar
    FinishTest("soggy1 soggy2");
  }

  {
    // Don't show command completion before we start typing a command: we want ctrl-p/n
    // to go through command history instead (we'll test for that second part later).
    BeginTest("soggy1 soggy2");
    TestPressKey(":");
    QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
    TestPressKey("\\ctrl-cvl:");
    QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
    TestPressKey("\\ctrl-c"); // Dismiss bar
    FinishTest("soggy1 soggy2");
  }

  {
    // Aborting ":" should leave us in normal mode with no selection.
    BeginTest("foo bar");
    TestPressKey("vw:\\ctrl-[");
    QVERIFY(kate_view->selectionText().isEmpty());
    TestPressKey("wdiw");
    BeginTest("foo ");
  }

  // Command history tests.
  clearCommandHistory();
  QVERIFY(commandHistory().isEmpty());
  vi_global->commandHistory()->append("foo");
  vi_global->commandHistory()->append("bar");
  QCOMPARE(commandHistory(), QStringList() << "foo" << "bar");
  clearCommandHistory();
  QVERIFY(commandHistory().isEmpty());


  // If we add something to the history, remove any earliest occurrences (this is what Vim appears to do)
  // and append to the end.
  clearCommandHistory();
  vi_global->commandHistory()->append("bar");
  vi_global->commandHistory()->append("xyz");
  vi_global->commandHistory()->append("foo");
  vi_global->commandHistory()->append("xyz");
  QCOMPARE(commandHistory(), QStringList() << "bar" << "foo" << "xyz");

  // Push out older entries if we have too many command items in the history.
  clearCommandHistory();
  for (int i = 1; i <= HISTORY_SIZE_LIMIT; i++)
  {
    vi_global->commandHistory()->append(QString("commandhistoryitem %1").arg(i));
  }
  QCOMPARE(commandHistory().size(), HISTORY_SIZE_LIMIT);
  QCOMPARE(commandHistory().first(), QString("commandhistoryitem 1"));
  QCOMPARE(commandHistory().last(), QString("commandhistoryitem 100"));
  vi_global->commandHistory()->append(QString("commandhistoryitem %1").arg(HISTORY_SIZE_LIMIT + 1));
  QCOMPARE(commandHistory().size(), HISTORY_SIZE_LIMIT);
  QCOMPARE(commandHistory().first(), QString("commandhistoryitem 2"));
  QCOMPARE(commandHistory().last(), QString("commandhistoryitem %1").arg(HISTORY_SIZE_LIMIT + 1));

  // Don't add empty commands to the history.
  clearCommandHistory();
  DoTest("foo bar", ":\\enter", "foo bar");
  QVERIFY(commandHistory().isEmpty());

  clearCommandHistory();
  BeginTest("");
  TestPressKey(":sort\\enter");
  QCOMPARE(commandHistory(), QStringList() << "sort");
  TestPressKey(":yank\\enter");
  QCOMPARE(commandHistory(), QStringList() << "sort" << "yank");
  // Add to history immediately: don't wait for the command response display to timeout.
  TestPressKey(":commandthatdoesnotexist\\enter");
  QCOMPARE(commandHistory(), QStringList() << "sort" << "yank" << "commandthatdoesnotexist");
  // Vim adds aborted commands to the history too, oddly.
  TestPressKey(":abortedcommand\\ctrl-c");
  QCOMPARE(commandHistory(), QStringList() << "sort" << "yank" << "commandthatdoesnotexist" << "abortedcommand");
  // Only add for commands, not searches!
  TestPressKey("/donotaddme\\enter?donotaddmeeither\\enter/donotaddme\\ctrl-c?donotaddmeeither\\ctrl-c");
  QCOMPARE(commandHistory(), QStringList() << "sort" << "yank" << "commandthatdoesnotexist" << "abortedcommand");
  FinishTest("");

  // Commands should not be added to the search history!
  clearCommandHistory();
  clearSearchHistory();
  BeginTest("");
  TestPressKey(":sort\\enter");
  QVERIFY(searchHistory().isEmpty());
  FinishTest("");

  // With an empty command bar, ctrl-p / ctrl-n should go through history.
  clearCommandHistory();
  vi_global->commandHistory()->append("command1");
  vi_global->commandHistory()->append("command2");
  BeginTest("");
  TestPressKey(":\\ctrl-p");
  verifyCommandBarCompletionVisible();
  QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("command2"));
  QCOMPARE(emulatedCommandBarTextEdit()->text(), emulatedCommandBarCompleter()->currentCompletion());
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar
  FinishTest("");
  clearCommandHistory();
  vi_global->commandHistory()->append("command1");
  vi_global->commandHistory()->append("command2");
  BeginTest("");
  TestPressKey(":\\ctrl-n");
  verifyCommandBarCompletionVisible();
  QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("command1"));
  QCOMPARE(emulatedCommandBarTextEdit()->text(), emulatedCommandBarCompleter()->currentCompletion());
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar
  FinishTest("");

  // If we're at a place where command completions are not allowed, ctrl-p/n should go through history.
  clearCommandHistory();
  vi_global->commandHistory()->append("s/command1");
  vi_global->commandHistory()->append("s/command2");
  BeginTest("");
  TestPressKey(":s/\\ctrl-p");
  verifyCommandBarCompletionVisible();
  QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("s/command2"));
  QCOMPARE(emulatedCommandBarTextEdit()->text(), emulatedCommandBarCompleter()->currentCompletion());
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar
  FinishTest("");
  clearCommandHistory();
  vi_global->commandHistory()->append("s/command1");
  vi_global->commandHistory()->append("s/command2");
  BeginTest("");
  TestPressKey(":s/\\ctrl-n");
  verifyCommandBarCompletionVisible();
  QCOMPARE(emulatedCommandBarCompleter()->currentCompletion(), QString("s/command1"));
  QCOMPARE(emulatedCommandBarTextEdit()->text(), emulatedCommandBarCompleter()->currentCompletion());
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar
  FinishTest("");

  // Cancelling word-from-document completion should revert the whole text to what it was before.
  BeginTest("sausage bacon");
  TestPressKey(":s/b\\ctrl- \\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/bacon"));
  verifyCommandBarCompletionVisible();
  TestPressKey("\\ctrl-c"); // Dismiss completer
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/b"));
  TestPressKey("\\ctrl-c"); // Dismiss bar
  FinishTest("sausage bacon");

  // "Replace" history tests.
  clearReplaceHistory();
  QVERIFY(replaceHistory().isEmpty());
  vi_global->replaceHistory()->append("foo");
  vi_global->replaceHistory()->append("bar");
  QCOMPARE(replaceHistory(), QStringList() << "foo" << "bar");
  clearReplaceHistory();
  QVERIFY(replaceHistory().isEmpty());

  // If we add something to the history, remove any earliest occurrences (this is what Vim appears to do)
  // and append to the end.
  clearReplaceHistory();
  vi_global->replaceHistory()->append("bar");
  vi_global->replaceHistory()->append("xyz");
  vi_global->replaceHistory()->append("foo");
  vi_global->replaceHistory()->append("xyz");
  QCOMPARE(replaceHistory(), QStringList() << "bar" << "foo" << "xyz");

  // Push out older entries if we have too many replace items in the history.
  clearReplaceHistory();
  for (int i = 1; i <= HISTORY_SIZE_LIMIT; i++)
  {
    vi_global->replaceHistory()->append(QString("replacehistoryitem %1").arg(i));
  }
  QCOMPARE(replaceHistory().size(), HISTORY_SIZE_LIMIT);
  QCOMPARE(replaceHistory().first(), QString("replacehistoryitem 1"));
  QCOMPARE(replaceHistory().last(), QString("replacehistoryitem 100"));
  vi_global->replaceHistory()->append(QString("replacehistoryitem %1").arg(HISTORY_SIZE_LIMIT + 1));
  QCOMPARE(replaceHistory().size(), HISTORY_SIZE_LIMIT);
  QCOMPARE(replaceHistory().first(), QString("replacehistoryitem 2"));
  QCOMPARE(replaceHistory().last(), QString("replacehistoryitem %1").arg(HISTORY_SIZE_LIMIT + 1));

  // Don't add empty replaces to the history.
  clearReplaceHistory();
  vi_global->replaceHistory()->append("");
  QVERIFY(replaceHistory().isEmpty());

  // Some misc SedReplace tests.
  DoTest("x\\/y", ":s/\\\\//replace/g\\enter", "x\\replacey");
  DoTest("x\\/y", ":s/\\\\\\\\\\\\//replace/g\\enter", "xreplacey");
  DoTest("x\\/y", ":s:/:replace:g\\enter", "x\\replacey");
  DoTest("foo\nbar\nxyz", ":%delete\\enter", "");
  DoTest("foo\nbar\nxyz\nbaz", "jVj:delete\\enter", "foo\nbaz");
  DoTest("foo\nbar\nxyz\nbaz", "j2:delete\\enter", "foo\nbaz");
  // On ctrl-d, delete the "search" term in a s/search/replace/xx
  BeginTest("foo bar");
  TestPressKey(":s/x\\\\\\\\\\\\/yz/rep\\\\\\\\\\\\/lace/g\\ctrl-d");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s//rep\\\\\\/lace/g"));
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("foo bar");
  // Move cursor to position of deleted search term.
  BeginTest("foo bar");
  TestPressKey(":s/x\\\\\\\\\\\\/yz/rep\\\\\\\\\\\\/lace/g\\ctrl-dX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/X/rep\\\\\\/lace/g"));
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("foo bar");
  // Do nothing on ctrl-d in search mode.
  BeginTest("foo bar");
  TestPressKey("/s/search/replace/g\\ctrl-d");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/search/replace/g"));
  TestPressKey("\\ctrl-c?s/searchbackwards/replace/g\\ctrl-d");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/searchbackwards/replace/g"));
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("foo bar");
  // On ctrl-f, delete "replace" term in a s/search/replace/xx
  BeginTest("foo bar");
  TestPressKey(":s/a\\\\\\\\\\\\/bc/rep\\\\\\\\\\\\/lace/g\\ctrl-f");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/a\\\\\\/bc//g"));
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("foo bar");
  // Move cursor to position of deleted replace term.
  BeginTest("foo bar");
  TestPressKey(":s:a/bc:replace:g\\ctrl-fX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s:a/bc:X:g"));
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("foo bar");
  // Do nothing on ctrl-d in search mode.
  BeginTest("foo bar");
  TestPressKey("/s/search/replace/g\\ctrl-f");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/search/replace/g"));
  TestPressKey("\\ctrl-c?s/searchbackwards/replace/g\\ctrl-f");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/searchbackwards/replace/g"));
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("foo bar");
  // Do nothing on ctrl-d / ctrl-f if the current expression is not a sed expression.
  BeginTest("foo bar");
  TestPressKey(":s/notasedreplaceexpression::gi\\ctrl-f\\ctrl-dX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/notasedreplaceexpression::giX"));
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("foo bar");
  // Need to convert Vim-style regex's to Qt one's in Sed Replace.
  DoTest("foo xbacba(boo)|[y", ":s/x[abc]\\\\+(boo)|[y/boo/g\\enter", "foo boo");
  DoTest("foo xbacba(boo)|[y\nfoo xbacba(boo)|[y", "Vj:s/x[abc]\\\\+(boo)|[y/boo/g\\enter", "foo boo\nfoo boo");
  // Just convert the search term, please :)
  DoTest("foo xbacba(boo)|[y", ":s/x[abc]\\\\+(boo)|[y/boo()/g\\enter", "foo boo()");
  // With an empty search expression, ctrl-d should still position the cursor correctly.
  BeginTest("foo bar");
  TestPressKey(":s//replace/g\\ctrl-dX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/X/replace/g"));
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  TestPressKey(":s::replace:g\\ctrl-dX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s:X:replace:g"));
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("foo bar");
  // With an empty replace expression, ctrl-f should still position the cursor correctly.
  BeginTest("foo bar");
  TestPressKey(":s/sear\\\\/ch//g\\ctrl-fX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/sear\\/ch/X/g"));
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  TestPressKey(":s:sear\\\\:ch::g\\ctrl-fX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s:sear\\:ch:X:g"));
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("foo bar");
  // With both empty search *and* replace expressions, ctrl-f should still position the cursor correctly.
  BeginTest("foo bar");
  TestPressKey(":s///g\\ctrl-fX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s//X/g"));
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  TestPressKey(":s:::g\\ctrl-fX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s::X:g"));
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("foo bar");
  // Should be able to undo ctrl-f or ctrl-d.
  BeginTest("foo bar");
  TestPressKey(":s/find/replace/g\\ctrl-d");
  emulatedCommandBarTextEdit()->undo();
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/find/replace/g"));
  TestPressKey("\\ctrl-f");
  emulatedCommandBarTextEdit()->undo();
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/find/replace/g"));
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("foo bar");
  // ctrl-f / ctrl-d should cleanly finish sed find/ replace history completion.
  clearReplaceHistory();
  clearSearchHistory();
  vi_global->searchHistory()->append("searchxyz");
  vi_global->replaceHistory()->append("replacexyz");
  TestPressKey(":s///g\\ctrl-d\\ctrl-p");
  QVERIFY(emulatedCommandBarCompleter()->popup()->isVisible());
  TestPressKey("\\ctrl-f");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/searchxyz//g"));
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  TestPressKey("\\ctrl-p");
  QVERIFY(emulatedCommandBarCompleter()->popup()->isVisible());
  TestPressKey("\\ctrl-d");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s//replacexyz/g"));
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("foo bar");
  // Don't hang if we execute a sed replace with empty search term.
  DoTest("foo bar", ":s//replace/g\\enter", "foo bar");

  // ctrl-f & ctrl-d should work even when there is a range expression at the beginning of the sed replace.
  BeginTest("foo bar");
  TestPressKey(":'<,'>s/search/replace/g\\ctrl-d");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("'<,'>s//replace/g"));
  TestPressKey("\\ctrl-c:.,.+6s/search/replace/g\\ctrl-f");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(".,.+6s/search//g"));
  TestPressKey("\\ctrl-c:%s/search/replace/g\\ctrl-f");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("%s/search//g"));
  // Place the cursor in the right place even when there is a range expression.
  TestPressKey("\\ctrl-c:.,.+6s/search/replace/g\\ctrl-fX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(".,.+6s/search/X/g"));
  TestPressKey("\\ctrl-c:%s/search/replace/g\\ctrl-fX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("%s/search/X/g"));
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("foo bar");
  // Don't crash on ctrl-f/d if we have an empty command.
  DoTest("", ":\\ctrl-f\\ctrl-d\\ctrl-c", "");
  // Parser regression test: Don't crash on ctrl-f/d with ".,.+".
  DoTest("", ":.,.+\\ctrl-f\\ctrl-d\\ctrl-c", "");

  // Command-completion should be invoked on the command being typed even when preceded by a range expression.
  BeginTest("");
  TestPressKey(":0,'>so");
  verifyCommandBarCompletionVisible();
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // Command-completion should ignore the range expression.
  BeginTest("");
  TestPressKey(":.,.+6so");
  verifyCommandBarCompletionVisible();
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // A sed-replace should immediately add the search term to the search history.
  clearSearchHistory();
  BeginTest("");
  TestPressKey(":s/search/replace/g\\enter");
  QCOMPARE(searchHistory(), QStringList() << "search");
  FinishTest("");

  // An aborted sed-replace should not add the search term to the search history.
  clearSearchHistory();
  BeginTest("");
  TestPressKey(":s/search/replace/g\\ctrl-c");
  QCOMPARE(searchHistory(), QStringList());
  FinishTest("");

  // A non-sed-replace should leave the search history unchanged.
  clearSearchHistory();
  BeginTest("");
  TestPressKey(":s,search/replace/g\\enter");
  QCOMPARE(searchHistory(), QStringList());
  FinishTest("");

  // A sed-replace should immediately add the replace term to the replace history.
  clearReplaceHistory();
  BeginTest("");
  TestPressKey(":s/search/replace/g\\enter");
  QCOMPARE(replaceHistory(), QStringList() << "replace");
  clearReplaceHistory();
  TestPressKey(":'<,'>s/search/replace1/g\\enter");
  QCOMPARE(replaceHistory(), QStringList() << "replace1");
  FinishTest("");

  // An aborted sed-replace should not add the replace term to the replace history.
  clearReplaceHistory();
  BeginTest("");
  TestPressKey(":s/search/replace/g\\ctrl-c");
  QCOMPARE(replaceHistory(), QStringList());
  FinishTest("");

  // A non-sed-replace should leave the replace history unchanged.
  clearReplaceHistory();
  BeginTest("");
  TestPressKey(":s,search/replace/g\\enter");
  QCOMPARE(replaceHistory(), QStringList());
  FinishTest("");

  // Misc tests for sed replace.  These are for the *generic* Kate sed replace; they should all
  // use EmulatedCommandBarTests' built-in command execution stuff (\\:<commandtoexecute>\\\) rather than
  // invoking a EmulatedCommandBar and potentially doing some Vim-specific transforms to
  // the command.
  DoTest("foo foo foo", "\\:s/foo/bar/\\", "bar foo foo");
  DoTest("foo foo xyz foo", "\\:s/foo/bar/g\\", "bar bar xyz bar");
  DoTest("foofooxyzfoo", "\\:s/foo/bar/g\\", "barbarxyzbar");
  DoTest("foofooxyzfoo", "\\:s/foo/b/g\\", "bbxyzb");
  DoTest("ffxyzf", "\\:s/f/b/g\\", "bbxyzb");
  DoTest("ffxyzf", "\\:s/f/bar/g\\", "barbarxyzbar");
  DoTest("foo Foo fOO FOO foo", "\\:s/foo/bar/\\", "bar Foo fOO FOO foo");
  DoTest("Foo foo fOO FOO foo", "\\:s/foo/bar/\\", "Foo bar fOO FOO foo");
  DoTest("Foo foo fOO FOO foo", "\\:s/foo/bar/g\\", "Foo bar fOO FOO bar");
  DoTest("foo Foo fOO FOO foo", "\\:s/foo/bar/i\\", "bar Foo fOO FOO foo");
  DoTest("Foo foo fOO FOO foo", "\\:s/foo/bar/i\\", "bar foo fOO FOO foo");
  DoTest("Foo foo fOO FOO foo", "\\:s/foo/bar/gi\\", "bar bar bar bar bar");
  DoTest("Foo foo fOO FOO foo", "\\:s/foo/bar/ig\\", "bar bar bar bar bar");
  // There are some oddities to do with how EmulatedCommandBarTest's "execute command directly" stuff works with selected ranges:
  // basically, we need to do our selection in Visual mode, then exit back to Normal mode before running the
  //command.
  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");
  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");
  DoTest("Foo foo fOO FOO foo", "\\:s/foo/barfoo/g\\", "Foo barfoo fOO FOO barfoo");
  DoTest("Foo foo fOO FOO foo", "\\:s/foo/foobar/g\\", "Foo foobar fOO FOO foobar");
  DoTest("axyzb", "\\:s/a(.*)b/d\\\\1f/\\", "dxyzf");
  DoTest("ayxzzyxzfddeefdb", "\\:s/a([xyz]+)([def]+)b/<\\\\1|\\\\2>/\\", "<yxzzyxz|fddeefd>");
  DoTest("foo", "\\:s/.*//g\\", "");
  DoTest("foo", "\\:s/.*/f/g\\", "f");
  DoTest("foo/bar", "\\:s/foo\\\\/bar/123\\\\/xyz/g\\", "123/xyz");
  DoTest("foo:bar", "\\:s:foo\\\\:bar:123\\\\:xyz:g\\", "123:xyz");
  const bool oldReplaceTabsDyn = kate_document->config()->replaceTabsDyn();
  kate_document->config()->setReplaceTabsDyn(false);
  DoTest("foo\tbar", "\\:s/foo\\\\tbar/replace/g\\", "replace");
  DoTest("foo\tbar", "\\:s/foo\\\\tbar/rep\\\\tlace/g\\", "rep\tlace");
  kate_document->config()->setReplaceTabsDyn(oldReplaceTabsDyn);
  DoTest("foo", "\\:s/foo/replaceline1\\\\nreplaceline2/g\\", "replaceline1\nreplaceline2");
  DoTest("foofoo", "\\:s/foo/replaceline1\\\\nreplaceline2/g\\", "replaceline1\nreplaceline2replaceline1\nreplaceline2");
  DoTest("foofoo\nfoo", "\\:s/foo/replaceline1\\\\nreplaceline2/g\\", "replaceline1\nreplaceline2replaceline1\nreplaceline2\nfoo");
  DoTest("fooafoob\nfooc\nfood", "Vj\\esc\\:'<,'>s/foo/replaceline1\\\\nreplaceline2/g\\", "replaceline1\nreplaceline2areplaceline1\nreplaceline2b\nreplaceline1\nreplaceline2c\nfood");
  DoTest("fooafoob\nfooc\nfood", "Vj\\esc\\:'<,'>s/foo/replaceline1\\\\nreplaceline2/\\", "replaceline1\nreplaceline2afoob\nreplaceline1\nreplaceline2c\nfood");
  DoTest("fooafoob\nfooc\nfood", "Vj\\esc\\:'<,'>s/foo/replaceline1\\\\nreplaceline2\\\\nreplaceline3/g\\", "replaceline1\nreplaceline2\nreplaceline3areplaceline1\nreplaceline2\nreplaceline3b\nreplaceline1\nreplaceline2\nreplaceline3c\nfood");
  DoTest("foofoo", "\\:s/foo/replace\\\\nfoo/g\\", "replace\nfooreplace\nfoo");
  DoTest("foofoo", "\\:s/foo/replacefoo\\\\nfoo/g\\", "replacefoo\nfooreplacefoo\nfoo");
  DoTest("foofoo", "\\:s/foo/replacefoo\\\\n/g\\", "replacefoo\nreplacefoo\n");
  DoTest("ff", "\\:s/f/f\\\\nf/g\\", "f\nff\nf");
  DoTest("ff", "\\:s/f/f\\\\n/g\\", "f\nf\n");
  DoTest("foo\nbar", "\\:s/foo\\\\n//g\\", "bar");
  DoTest("foo\n\n\nbar", "\\:s/foo(\\\\n)*bar//g\\", "");
  DoTest("foo\n\n\nbar", "\\:s/foo(\\\\n*)bar/123\\\\1456/g\\", "123\n\n\n456");
  DoTest("xAbCy", "\\:s/x(.)(.)(.)y/\\\\L\\\\1\\\\U\\\\2\\\\3/g\\", "aBC");
  DoTest("foo", "\\:s/foo/\\\\a/g\\", "\x07");
  // End "generic" (i.e. not involving any Vi mode tricks/ transformations) sed replace tests: the remaining
  // ones should go via the EmulatedCommandBar.
  BeginTest("foo foo\nxyz\nfoo");
  TestPressKey(":%s/foo/bar/g\\enter");
  verifyShowsNumberOfReplacementsAcrossNumberOfLines(3, 2);
  FinishTest("bar bar\nxyz\nbar");

  // ctrl-p on the first character of the search term in a sed-replace should
  // invoke search history completion.
  clearSearchHistory();
  vi_global->searchHistory()->append("search");
  BeginTest("");
  TestPressKey(":s/search/replace/g\\ctrl-b\\right\\right\\ctrl-p");
  verifyCommandBarCompletionVisible();
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  TestPressKey(":'<,'>s/search/replace/g\\ctrl-b\\right\\right\\right\\right\\right\\right\\right\\ctrl-p");
  verifyCommandBarCompletionVisible();
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // ctrl-p on the last character of the search term in a sed-replace should
  // invoke search history completion.
  clearSearchHistory();
  vi_global->searchHistory()->append("xyz");
  BeginTest("");
  TestPressKey(":s/xyz/replace/g\\ctrl-b\\right\\right\\right\\right\\ctrl-p");
  verifyCommandBarCompletionVisible();
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  QVERIFY(!emulatedCommandBar->isVisible());
  TestPressKey(":'<,'>s/xyz/replace/g\\ctrl-b\\right\\right\\right\\right\\right\\right\\right\\right\\right\\ctrl-p");
  verifyCommandBarCompletionVisible();
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // ctrl-p on some arbitrary character of the search term in a sed-replace should
  // invoke search history completion.
  clearSearchHistory();
  vi_global->searchHistory()->append("xyzaaaaaa");
  BeginTest("");
  TestPressKey(":s/xyzaaaaaa/replace/g\\ctrl-b\\right\\right\\right\\right\\right\\right\\right\\ctrl-p");
  verifyCommandBarCompletionVisible();
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  TestPressKey(":'<,'>s/xyzaaaaaa/replace/g\\ctrl-b\\right\\right\\right\\right\\right\\right\\right\\right\\right\\right\\right\\right\\ctrl-p");
  verifyCommandBarCompletionVisible();
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // ctrl-p on some character *after" the search term should
  // *not* invoke search history completion.
  // Note: in s/xyz/replace/g, the "/" after the "z" is counted as part of the find term;
  // this allows us to do xyz<ctrl-p> and get completions.
  clearSearchHistory();
  clearCommandHistory();
  clearReplaceHistory();
  vi_global->searchHistory()->append("xyz");
  BeginTest("");
  TestPressKey(":s/xyz/replace/g\\ctrl-b\\right\\right\\right\\right\\right\\right\\ctrl-p");
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  clearSearchHistory();
  clearCommandHistory();
  TestPressKey(":'<,'>s/xyz/replace/g\\ctrl-b\\right\\right\\right\\right\\right\\right\\right\\right\\right\\right\\right\\right\\ctrl-p");
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.

  // Make sure it's the search history we're invoking.
  clearSearchHistory();
  vi_global->searchHistory()->append("xyzaaaaaa");
  BeginTest("");
  TestPressKey(":s//replace/g\\ctrl-b\\right\\right\\ctrl-p");
  verifyCommandBarCompletionVisible();
  verifyCommandBarCompletionsMatches(QStringList() << "xyzaaaaaa");
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  TestPressKey(":.,.+6s//replace/g\\ctrl-b\\right\\right\\right\\right\\right\\right\\right\\ctrl-p");
  verifyCommandBarCompletionsMatches(QStringList() << "xyzaaaaaa");
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // (Search history should be reversed).
  clearSearchHistory();
  vi_global->searchHistory()->append("xyzaaaaaa");
  vi_global->searchHistory()->append("abc");
  vi_global->searchHistory()->append("def");
  BeginTest("");
  TestPressKey(":s//replace/g\\ctrl-b\\right\\right\\ctrl-p");
  verifyCommandBarCompletionVisible();
  verifyCommandBarCompletionsMatches(QStringList()  << "def" << "abc" << "xyzaaaaaa");
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // Completion prefix is the current find term.
  clearSearchHistory();
  vi_global->searchHistory()->append("xy:zaaaaaa");
  vi_global->searchHistory()->append("abc");
  vi_global->searchHistory()->append("def");
  vi_global->searchHistory()->append("xy:zbaaaaa");
  vi_global->searchHistory()->append("xy:zcaaaaa");
  BeginTest("");
  TestPressKey(":s//replace/g\\ctrl-dxy:z\\ctrl-p");
  verifyCommandBarCompletionVisible();
  verifyCommandBarCompletionsMatches(QStringList()  << "xy:zcaaaaa" << "xy:zbaaaaa" << "xy:zaaaaaa");
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // Replace entire search term with completion.
  clearSearchHistory();
  vi_global->searchHistory()->append("ab,cd");
  vi_global->searchHistory()->append("ab,xy");
  BeginTest("");
  TestPressKey(":s//replace/g\\ctrl-dab,\\ctrl-p\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/ab,cd/replace/g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  TestPressKey(":'<,'>s//replace/g\\ctrl-dab,\\ctrl-p\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("'<,'>s/ab,cd/replace/g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // Place the cursor at the end of find term.
  clearSearchHistory();
  vi_global->searchHistory()->append("ab,xy");
  BeginTest("");
  TestPressKey(":s//replace/g\\ctrl-dab,\\ctrl-pX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/ab,xyX/replace/g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  TestPressKey(":.,.+7s//replace/g\\ctrl-dab,\\ctrl-pX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(".,.+7s/ab,xyX/replace/g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // Leave find term unchanged if there is no search history.
  clearSearchHistory();
  BeginTest("");
  TestPressKey(":s/nose/replace/g\\ctrl-b\\right\\right\\right\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/nose/replace/g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // Leave cursor position unchanged if there is no search history.
  clearSearchHistory();
  BeginTest("");
  TestPressKey(":s/nose/replace/g\\ctrl-b\\right\\right\\right\\ctrl-pX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/nXose/replace/g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // ctrl-p on the first character of the replace term in a sed-replace should
  // invoke replace history completion.
  clearSearchHistory();
  clearReplaceHistory();
  clearCommandHistory();
  vi_global->replaceHistory()->append("replace");
  BeginTest("");
  TestPressKey(":s/search/replace/g\\left\\left\\left\\left\\left\\left\\left\\left\\left\\ctrl-p");
  verifyCommandBarCompletionVisible();
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  TestPressKey(":'<,'>s/search/replace/g\\left\\left\\left\\left\\left\\left\\left\\left\\left\\ctrl-p");
  verifyCommandBarCompletionVisible();
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // ctrl-p on the last character of the replace term in a sed-replace should
  // invoke replace history completion.
  clearReplaceHistory();
  vi_global->replaceHistory()->append("replace");
  BeginTest("");
  TestPressKey(":s/xyz/replace/g\\left\\left\\ctrl-p");
  verifyCommandBarCompletionVisible();
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  TestPressKey(":'<,'>s/xyz/replace/g\\left\\left\\ctrl-p");
  verifyCommandBarCompletionVisible();
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // ctrl-p on some arbitrary character of the search term in a sed-replace should
  // invoke search history completion.
  clearReplaceHistory();
  vi_global->replaceHistory()->append("replaceaaaaaa");
  BeginTest("");
  TestPressKey(":s/xyzaaaaaa/replace/g\\left\\left\\left\\left\\ctrl-p");
  verifyCommandBarCompletionVisible();
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  TestPressKey(":'<,'>s/xyzaaaaaa/replace/g\\left\\left\\left\\left\\ctrl-p");
  verifyCommandBarCompletionVisible();
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // ctrl-p on some character *after" the replace term should
  // *not* invoke replace history completion.
  // Note: in s/xyz/replace/g, the "/" after the "e" is counted as part of the replace term;
  // this allows us to do replace<ctrl-p> and get completions.
  clearSearchHistory();
  clearCommandHistory();
  clearReplaceHistory();
  vi_global->replaceHistory()->append("xyz");
  BeginTest("");
  TestPressKey(":s/xyz/replace/g\\left\\ctrl-p");
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  clearSearchHistory();
  clearCommandHistory();
  TestPressKey(":'<,'>s/xyz/replace/g\\left\\ctrl-p");
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.

  // (Replace history should be reversed).
  clearReplaceHistory();
  vi_global->replaceHistory()->append("xyzaaaaaa");
  vi_global->replaceHistory()->append("abc");
  vi_global->replaceHistory()->append("def");
  BeginTest("");
  TestPressKey(":s/search//g\\left\\left\\ctrl-p");
  verifyCommandBarCompletionVisible();
  verifyCommandBarCompletionsMatches(QStringList()  << "def" << "abc" << "xyzaaaaaa");
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // Completion prefix is the current replace term.
  clearReplaceHistory();
  vi_global->replaceHistory()->append("xy:zaaaaaa");
  vi_global->replaceHistory()->append("abc");
  vi_global->replaceHistory()->append("def");
  vi_global->replaceHistory()->append("xy:zbaaaaa");
  vi_global->replaceHistory()->append("xy:zcaaaaa");
  BeginTest("");
  TestPressKey(":'<,'>s/replace/search/g\\ctrl-fxy:z\\ctrl-p");
  verifyCommandBarCompletionVisible();
  verifyCommandBarCompletionsMatches(QStringList()  << "xy:zcaaaaa" << "xy:zbaaaaa" << "xy:zaaaaaa");
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // Replace entire search term with completion.
  clearReplaceHistory();
  clearSearchHistory();
  vi_global->replaceHistory()->append("ab,cd");
  vi_global->replaceHistory()->append("ab,xy");
  BeginTest("");
  TestPressKey(":s/search//g\\ctrl-fab,\\ctrl-p\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/search/ab,cd/g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  TestPressKey(":'<,'>s/search//g\\ctrl-fab,\\ctrl-p\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("'<,'>s/search/ab,cd/g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // Place the cursor at the end of replace term.
  clearReplaceHistory();
  vi_global->replaceHistory()->append("ab,xy");
  BeginTest("");
  TestPressKey(":s/search//g\\ctrl-fab,\\ctrl-pX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/search/ab,xyX/g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  TestPressKey(":.,.+7s/search//g\\ctrl-fab,\\ctrl-pX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString(".,.+7s/search/ab,xyX/g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // Leave replace term unchanged if there is no replace history.
  clearReplaceHistory();
  BeginTest("");
  TestPressKey(":s/nose/replace/g\\left\\left\\left\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/nose/replace/g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // Leave cursor position unchanged if there is no replace history.
  clearSearchHistory();
  BeginTest("");
  TestPressKey(":s/nose/replace/g\\left\\left\\left\\left\\ctrl-pX");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/nose/replaXce/g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // Invoke replacement history even when the "find" term is empty.
  BeginTest("");
  clearReplaceHistory();
  clearSearchHistory();
  vi_global->replaceHistory()->append("ab,xy");
  vi_global->searchHistory()->append("whoops");
  TestPressKey(":s///g\\ctrl-f\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s//ab,xy/g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // Move the cursor back to the last manual edit point when aborting completion.
  BeginTest("");
  clearSearchHistory();
  vi_global->searchHistory()->append("xyzaaaaa");
  TestPressKey(":s/xyz/replace/g\\ctrl-b\\right\\right\\right\\right\\righta\\ctrl-p\\ctrl-[X");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/xyzaX/replace/g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // Don't blank the "find" term if there is no search history that begins with the
  // current "find" term.
  BeginTest("");
  clearSearchHistory();
  vi_global->searchHistory()->append("doesnothavexyzasaprefix");
  TestPressKey(":s//replace/g\\ctrl-dxyz\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/xyz/replace/g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // Escape the delimiter if it occurs in a search history term - searching for it likely won't
  // work, but at least it won't crash!
  BeginTest("");
  clearSearchHistory();
  vi_global->searchHistory()->append("search");
  vi_global->searchHistory()->append("aa/aa\\/a");
  vi_global->searchHistory()->append("ss/ss");
  TestPressKey(":s//replace/g\\ctrl-d\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/ss\\/ss/replace/g"));
  TestPressKey("\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/aa\\/aa\\/a/replace/g"));
  TestPressKey("\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/search/replace/g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  clearSearchHistory(); // Now do the same, but with a different delimiter.
  vi_global->searchHistory()->append("search");
  vi_global->searchHistory()->append("aa:aa\\:a");
  vi_global->searchHistory()->append("ss:ss");
  TestPressKey(":s::replace:g\\ctrl-d\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s:ss\\:ss:replace:g"));
  TestPressKey("\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s:aa\\:aa\\:a:replace:g"));
  TestPressKey("\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s:search:replace:g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // Remove \C if occurs in search history.
  BeginTest("");
  clearSearchHistory();
  vi_global->searchHistory()->append("s\\Cear\\\\Cch");
  TestPressKey(":s::replace:g\\ctrl-d\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s:sear\\\\Cch:replace:g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // Don't blank the "replace" term if there is no search history that begins with the
  // current "replace" term.
  BeginTest("");
  clearReplaceHistory();
  vi_global->replaceHistory()->append("doesnothavexyzasaprefix");
  TestPressKey(":s/search//g\\ctrl-fxyz\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/search/xyz/g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // Escape the delimiter if it occurs in a replace history term - searching for it likely won't
  // work, but at least it won't crash!
  BeginTest("");
  clearReplaceHistory();
  vi_global->replaceHistory()->append("replace");
  vi_global->replaceHistory()->append("aa/aa\\/a");
  vi_global->replaceHistory()->append("ss/ss");
  TestPressKey(":s/search//g\\ctrl-f\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/search/ss\\/ss/g"));
  TestPressKey("\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/search/aa\\/aa\\/a/g"));
  TestPressKey("\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/search/replace/g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  clearReplaceHistory(); // Now do the same, but with a different delimiter.
  vi_global->replaceHistory()->append("replace");
  vi_global->replaceHistory()->append("aa:aa\\:a");
  vi_global->replaceHistory()->append("ss:ss");
  TestPressKey(":s:search::g\\ctrl-f\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s:search:ss\\:ss:g"));
  TestPressKey("\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s:search:aa\\:aa\\:a:g"));
  TestPressKey("\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s:search:replace:g"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // In search mode, don't blank current text on completion if there is no item in the search history which
  // has the current text as a prefix.
  BeginTest("");
  clearSearchHistory();
  vi_global->searchHistory()->append("doesnothavexyzasaprefix");
  TestPressKey("/xyz\\ctrl-p");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("xyz"));
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // Don't dismiss the command completion just because the cursor ends up *temporarily* at a place where
  // command completion is disallowed when cycling through completions.
  BeginTest("");
  TestPressKey(":set/se\\left\\left\\left-\\ctrl-p");
  verifyCommandBarCompletionVisible();
  TestPressKey("\\ctrl-c"); // Dismiss completer
  TestPressKey("\\ctrl-c"); // Dismiss bar.
  FinishTest("");

  // Don't expand mappings meant for Normal mode in the emulated command bar.
  clearAllMappings();
  vi_global->mappings()->add(Mappings::NormalModeMapping, "foo", "xyz", Mappings::NonRecursive);
  DoTest("bar foo xyz", "/foo\\enterrX", "bar Xoo xyz");
  clearAllMappings();

  // Incremental search and replace.
  QLabel* interactiveSedReplaceLabel = emulatedCommandBar->findChild<QLabel*>("interactivesedreplace");
  QVERIFY(interactiveSedReplaceLabel);

  BeginTest("foo");
  TestPressKey(":s/foo/bar/c\\enter");
  QVERIFY(interactiveSedReplaceLabel->isVisible());
  QVERIFY(!commandResponseMessageDisplay()->isVisible());
  QVERIFY(!emulatedCommandBarTextEdit()->isVisible());
  QVERIFY(!emulatedCommandTypeIndicator()->isVisible());
  TestPressKey("\\ctrl-c"); // Dismiss search and replace.
  QVERIFY(!emulatedCommandBar->isVisible());
  FinishTest("foo");

  // Clear the flag that stops the command response from being shown after an incremental search and
  // replace, and also make sure that the edit and bar type indicator are not forcibly hidden.
  BeginTest("foo");
  TestPressKey(":s/foo/bar/c\\enter\\ctrl-c");
  TestPressKey(":s/foo/bar/");
  QVERIFY(emulatedCommandBarTextEdit()->isVisible());
  QVERIFY(emulatedCommandTypeIndicator()->isVisible());
  TestPressKey("\\enter");
  QVERIFY(commandResponseMessageDisplay()->isVisible());
  FinishTest("bar");

  // Hide the incremental search and replace label when we show the bar.
  BeginTest("foo");
  TestPressKey(":s/foo/bar/c\\enter\\ctrl-c");
  TestPressKey(":");
  QVERIFY(!interactiveSedReplaceLabel->isVisible());
  TestPressKey("\\ctrl-c");
  FinishTest("foo");

  // The "c" marker can be anywhere in the three chars following the delimiter.
  BeginTest("foo");
  TestPressKey(":s/foo/bar/cgi\\enter");
  QVERIFY(interactiveSedReplaceLabel->isVisible());
  TestPressKey("\\ctrl-c");
  FinishTest("foo");
  BeginTest("foo");
  TestPressKey(":s/foo/bar/igc\\enter");
  QVERIFY(interactiveSedReplaceLabel->isVisible());
  TestPressKey("\\ctrl-c");
  FinishTest("foo");
  BeginTest("foo");
  TestPressKey(":s/foo/bar/icg\\enter");
  QVERIFY(interactiveSedReplaceLabel->isVisible());
  TestPressKey("\\ctrl-c");
  FinishTest("foo");
  BeginTest("foo");
  TestPressKey(":s/foo/bar/ic\\enter");
  QVERIFY(interactiveSedReplaceLabel->isVisible());
  TestPressKey("\\ctrl-c");
  FinishTest("foo");
  BeginTest("foo");
  TestPressKey(":s/foo/bar/ci\\enter");
  QVERIFY(interactiveSedReplaceLabel->isVisible());
  TestPressKey("\\ctrl-c");
  FinishTest("foo");

  // Emulated command bar is still active during an incremental search and replace.
  BeginTest("foo");
  TestPressKey(":s/foo/bar/c\\enter");
  TestPressKey("idef\\esc");
  FinishTest("foo");

  // Emulated command bar text is not edited during an incremental search and replace.
  BeginTest("foo");
  TestPressKey(":s/foo/bar/c\\enter");
  TestPressKey("def");
  QCOMPARE(emulatedCommandBarTextEdit()->text(), QString("s/foo/bar/c"));
  TestPressKey("\\ctrl-c");
  FinishTest("foo");

  // Pressing "n" when there is only a single  change we can make aborts incremental search
  // and replace.
  BeginTest("foo");
  TestPressKey(":s/foo/bar/c\\enter");
  TestPressKey("n");
  QVERIFY(!interactiveSedReplaceLabel->isVisible());
  TestPressKey("ixyz\\esc");
  FinishTest("xyzfoo");

  // Pressing "n" when there is only a single  change we can make aborts incremental search
  // and replace, and shows the no replacements on no lines.
  BeginTest("foo");
  TestPressKey(":s/foo/bar/c\\enter");
  TestPressKey("n");
  QVERIFY(commandResponseMessageDisplay()->isVisible());
  verifyShowsNumberOfReplacementsAcrossNumberOfLines(0, 0);
  FinishTest("foo");

  // First possible match is highlighted when we start an incremental search and replace, and
  // cleared if we press 'n'.
  BeginTest(" xyz  123 foo bar");
  TestPressKey(":s/foo/bar/gc\\enter");
  QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
  QCOMPARE(rangesOnFirstLine().first()->start().line(), 0);
  QCOMPARE(rangesOnFirstLine().first()->start().column(), 10);
  QCOMPARE(rangesOnFirstLine().first()->end().line(), 0);
  QCOMPARE(rangesOnFirstLine().first()->end().column(), 13);
  TestPressKey("n");
  QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
  FinishTest(" xyz  123 foo bar");

  // Second possible match highlighted if we start incremental search and replace and press 'n',
  // cleared if we press 'n' again.
  BeginTest(" xyz  123 foo foo bar");
  TestPressKey(":s/foo/bar/gc\\enter");
  TestPressKey("n");
  QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
  QCOMPARE(rangesOnFirstLine().first()->start().line(), 0);
  QCOMPARE(rangesOnFirstLine().first()->start().column(), 14);
  QCOMPARE(rangesOnFirstLine().first()->end().line(), 0);
  QCOMPARE(rangesOnFirstLine().first()->end().column(), 17);
  TestPressKey("n");
  QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
  FinishTest(" xyz  123 foo foo bar");

  // Perform replacement if we press 'y' on the first match.
  BeginTest(" xyz  foo 123 foo bar");
  TestPressKey(":s/foo/bar/gc\\enter");
  TestPressKey("y");
  TestPressKey("\\ctrl-c");
  FinishTest(" xyz  bar 123 foo bar");

  // Replacement uses grouping, etc.
  BeginTest(" xyz  def 123 foo bar");
  TestPressKey(":s/d\\\\(e\\\\)\\\\(f\\\\)/x\\\\1\\\\U\\\\2/gc\\enter");
  TestPressKey("y");
  TestPressKey("\\ctrl-c");
  FinishTest(" xyz  xeF 123 foo bar");

  // On replacement, highlight next match.
  BeginTest(" xyz  foo 123 foo bar");
  TestPressKey(":s/foo/bar/cg\\enter");
  TestPressKey("y");
  QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
  QCOMPARE(rangesOnFirstLine().first()->start().line(), 0);
  QCOMPARE(rangesOnFirstLine().first()->start().column(), 14);
  QCOMPARE(rangesOnFirstLine().first()->end().line(), 0);
  QCOMPARE(rangesOnFirstLine().first()->end().column(), 17);
  TestPressKey("\\ctrl-c");
  FinishTest(" xyz  bar 123 foo bar");

  // On replacement, if there is no further match, abort incremental search and replace.
  BeginTest(" xyz  foo 123 foa bar");
  TestPressKey(":s/foo/bar/cg\\enter");
  TestPressKey("y");
  QVERIFY(commandResponseMessageDisplay()->isVisible());
  TestPressKey("ggidone\\esc");
  FinishTest("done xyz  bar 123 foa bar");

  // After replacement, the next match is sought after the end of the replacement text.
  BeginTest("foofoo");
  TestPressKey(":s/foo/barfoo/cg\\enter");
  TestPressKey("y");
  QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
  QCOMPARE(rangesOnFirstLine().first()->start().line(), 0);
  QCOMPARE(rangesOnFirstLine().first()->start().column(), 6);
  QCOMPARE(rangesOnFirstLine().first()->end().line(), 0);
  QCOMPARE(rangesOnFirstLine().first()->end().column(), 9);
  TestPressKey("\\ctrl-c");
  FinishTest("barfoofoo");
  BeginTest("xffy");
  TestPressKey(":s/f/bf/cg\\enter");
  TestPressKey("yy");
  FinishTest("xbfbfy");

  // Make sure the incremental search bar label contains the "instruction" keypresses.
  const QString interactiveSedReplaceShortcuts = "(y/n/a/q/l)";
  BeginTest("foofoo");
  TestPressKey(":s/foo/barfoo/cg\\enter");
  QVERIFY(interactiveSedReplaceLabel->text().contains(interactiveSedReplaceShortcuts));
  TestPressKey("\\ctrl-c");
  FinishTest("foofoo");

  // Make sure the incremental search bar label contains a reference to the text we're going to
  // replace with.
  // We're going to be a bit vague about the precise text due to localisation issues.
  BeginTest("fabababbbar");
  TestPressKey(":s/f\\\\([ab]\\\\+\\\\)/1\\\\U\\\\12/c\\enter");
  QVERIFY(interactiveSedReplaceLabel->text().contains("1ABABABBBA2"));
  TestPressKey("\\ctrl-c");
  FinishTest("fabababbbar");

  // Replace newlines in the "replace?" message with "\\n"
  BeginTest("foo");
  TestPressKey(":s/foo/bar\\\\nxyz\\\\n123/c\\enter");
  QVERIFY(interactiveSedReplaceLabel->text().contains("bar\\nxyz\\n123"));
  TestPressKey("\\ctrl-c");
  FinishTest("foo");

  // Update the "confirm replace?" message on pressing "y".
  BeginTest("fabababbbar fabbb");
  TestPressKey(":s/f\\\\([ab]\\\\+\\\\)/1\\\\U\\\\12/gc\\enter");
  TestPressKey("y");
  QVERIFY(interactiveSedReplaceLabel->text().contains("1ABBB2"));
  QVERIFY(interactiveSedReplaceLabel->text().contains(interactiveSedReplaceShortcuts));
  TestPressKey("\\ctrl-c");
  FinishTest("1ABABABBBA2r fabbb");

  // Update the "confirm replace?" message on pressing "n".
  BeginTest("fabababbbar fabab");
  TestPressKey(":s/f\\\\([ab]\\\\+\\\\)/1\\\\U\\\\12/gc\\enter");
  TestPressKey("n");
  QVERIFY(interactiveSedReplaceLabel->text().contains("1ABAB2"));
  QVERIFY(interactiveSedReplaceLabel->text().contains(interactiveSedReplaceShortcuts));
  TestPressKey("\\ctrl-c");
  FinishTest("fabababbbar fabab");

  // Cursor is placed at the beginning of first match.
  BeginTest("  foo foo foo");
  TestPressKey(":s/foo/bar/c\\enter");
  verifyCursorAt(Cursor(0, 2));
  TestPressKey("\\ctrl-c");
  FinishTest("  foo foo foo");

  // "y" and "n" update the cursor pos.
  BeginTest("  foo   foo foo");
  TestPressKey(":s/foo/bar/cg\\enter");
  TestPressKey("y");
  verifyCursorAt(Cursor(0, 8));
  TestPressKey("n");
  verifyCursorAt(Cursor(0, 12));
  TestPressKey("\\ctrl-c");
  FinishTest("  bar   foo foo");

  // If we end due to a "y" or "n" on the final match, leave the cursor at the beginning of the final match.
  BeginTest("  foo");
  TestPressKey(":s/foo/bar/c\\enter");
  TestPressKey("y");
  verifyCursorAt(Cursor(0, 2));
  FinishTest("  bar");
  BeginTest("  foo");
  TestPressKey(":s/foo/bar/c\\enter");
  TestPressKey("n");
  verifyCursorAt(Cursor(0, 2));
  FinishTest("  foo");

  // Respect ranges.
  BeginTest("foo foo\nfoo foo\nfoo foo\nfoo foo\n");
  TestPressKey("jVj:s/foo/bar/gc\\enter");
  TestPressKey("ynny");
  QVERIFY(commandResponseMessageDisplay()->isVisible());
  TestPressKey("ggidone \\ctrl-c");
  FinishTest("done foo foo\nbar foo\nfoo bar\nfoo foo\n");
  BeginTest("foo foo\nfoo foo\nfoo foo\nfoo foo\n");
  TestPressKey("jVj:s/foo/bar/gc\\enter");
  TestPressKey("nyyn");
  QVERIFY(commandResponseMessageDisplay()->isVisible());
  TestPressKey("ggidone \\ctrl-c");
  FinishTest("done foo foo\nfoo bar\nbar foo\nfoo foo\n");
  BeginTest("foo foo\nfoo foo\nfoo foo\nfoo foo\n");
  TestPressKey("j:s/foo/bar/gc\\enter");
  TestPressKey("ny");
  QVERIFY(commandResponseMessageDisplay()->isVisible());
  TestPressKey("ggidone \\ctrl-c");
  FinishTest("done foo foo\nfoo bar\nfoo foo\nfoo foo\n");
  BeginTest("foo foo\nfoo foo\nfoo foo\nfoo foo\n");
  TestPressKey("j:s/foo/bar/gc\\enter");
  TestPressKey("yn");
  QVERIFY(commandResponseMessageDisplay()->isVisible());
  TestPressKey("ggidone \\ctrl-c");
  FinishTest("done foo foo\nbar foo\nfoo foo\nfoo foo\n");

  // If no initial match can be found, abort and show a "no replacements" message.
  // The cursor position should remain unnchanged.
  BeginTest("fab");
  TestPressKey("l:s/fee/bar/c\\enter");
  QVERIFY(commandResponseMessageDisplay()->isVisible());
  verifyShowsNumberOfReplacementsAcrossNumberOfLines(0, 0);
  QVERIFY(!interactiveSedReplaceLabel->isVisible());
  TestPressKey("rX");
  BeginTest("fXb");

  // Case-sensitive by default.
  BeginTest("foo Foo FOo foo foO");
  TestPressKey(":s/foo/bar/cg\\enter");
  TestPressKey("yyggidone\\esc");
  FinishTest("donebar Foo FOo bar foO");

  // Case-insensitive if "i" flag is used.
  BeginTest("foo Foo FOo foo foO");
  TestPressKey(":s/foo/bar/icg\\enter");
  TestPressKey("yyyyyggidone\\esc");
  FinishTest("donebar bar bar bar bar");

  // Only one replacement per-line unless "g" flag is used.
  BeginTest("boo foo 123 foo\nxyz foo foo\nfoo foo foo\nxyz\nfoo foo\nfoo 123 foo");
  TestPressKey("jVjjj:s/foo/bar/c\\enter");
  TestPressKey("yynggidone\\esc");
  FinishTest("doneboo foo 123 foo\nxyz bar foo\nbar foo foo\nxyz\nfoo foo\nfoo 123 foo");
  BeginTest("boo foo 123 foo\nxyz foo foo\nfoo foo foo\nxyz\nfoo foo\nfoo 123 foo");
  TestPressKey("jVjjj:s/foo/bar/c\\enter");
  TestPressKey("nnyggidone\\esc");
  FinishTest("doneboo foo 123 foo\nxyz foo foo\nfoo foo foo\nxyz\nbar foo\nfoo 123 foo");

  // If replacement contains new lines, adjust the end line down.
  BeginTest("foo\nfoo1\nfoo2\nfoo3");
  TestPressKey("jVj:s/foo/bar\\\\n/gc\\enter");
  TestPressKey("yyggidone\\esc");
  FinishTest("donefoo\nbar\n1\nbar\n2\nfoo3");
  BeginTest("foo\nfoo1\nfoo2\nfoo3");
  TestPressKey("jVj:s/foo/bar\\\\nboo\\\\n/gc\\enter");
  TestPressKey("yyggidone\\esc");
  FinishTest("donefoo\nbar\nboo\n1\nbar\nboo\n2\nfoo3");

  // With "g" and a replacement that involves multiple lines, resume search from the end of the last line added.
  BeginTest("foofoo");
  TestPressKey(":s/foo/bar\\\\n/gc\\enter");
  TestPressKey("yyggidone\\esc");
  FinishTest("donebar\nbar\n");
  BeginTest("foofoo");
  TestPressKey(":s/foo/bar\\\\nxyz\\\\nfoo/gc\\enter");
  TestPressKey("yyggidone\\esc");
  FinishTest("donebar\nxyz\nfoobar\nxyz\nfoo");

  // Without "g" and with a replacement that involves multiple lines, resume search from the line after the line just added.
  BeginTest("foofoo1\nfoo2\nfoo3");
  TestPressKey("Vj:s/foo/bar\\\\nxyz\\\\nfoo/c\\enter");
  TestPressKey("yyggidone\\esc");
  FinishTest("donebar\nxyz\nfoofoo1\nbar\nxyz\nfoo2\nfoo3");

  // Regression test: handle 'g' when it occurs before 'i' and 'c'.
  BeginTest("foo fOo");
  TestPressKey(":s/foo/bar/gci\\enter");
  TestPressKey("yyggidone\\esc");
  FinishTest("donebar bar");

  // When the search terms swallows several lines, move the endline up accordingly.
  BeginTest("foo\nfoo1\nfoo\nfoo2\nfoo\nfoo3");
  TestPressKey("V3j:s/foo\\\\nfoo/bar/cg\\enter");
  TestPressKey("yyggidone\\esc");
  FinishTest("donebar1\nbar2\nfoo\nfoo3");
  BeginTest("foo\nfoo\nfoo1\nfoo\nfoo\nfoo2\nfoo\nfoo\nfoo3");
  TestPressKey("V5j:s/foo\\\\nfoo\\\\nfoo/bar/cg\\enter");
  TestPressKey("yyggidone\\esc");
  FinishTest("donebar1\nbar2\nfoo\nfoo\nfoo3");
  // Make sure we still adjust endline down if the replacement text has '\n's.
  BeginTest("foo\nfoo\nfoo1\nfoo\nfoo\nfoo2\nfoo\nfoo\nfoo3");
  TestPressKey("V5j:s/foo\\\\nfoo\\\\nfoo/bar\\\\n/cg\\enter");
  TestPressKey("yyggidone\\esc");
  FinishTest("donebar\n1\nbar\n2\nfoo\nfoo\nfoo3");

  // Status reports.
  BeginTest("foo");
  TestPressKey(":s/foo/bar/c\\enter");
  TestPressKey("y");
  verifyShowsNumberOfReplacementsAcrossNumberOfLines(1, 1);
  FinishTest("bar");
  BeginTest("foo foo foo");
  TestPressKey(":s/foo/bar/gc\\enter");
  TestPressKey("yyy");
  verifyShowsNumberOfReplacementsAcrossNumberOfLines(3, 1);
  FinishTest("bar bar bar");
  BeginTest("foo foo foo");
  TestPressKey(":s/foo/bar/gc\\enter");
  TestPressKey("yny");
  verifyShowsNumberOfReplacementsAcrossNumberOfLines(2, 1);
  FinishTest("bar foo bar");
  BeginTest("foo\nfoo");
  TestPressKey(":%s/foo/bar/gc\\enter");
  TestPressKey("yy");
  verifyShowsNumberOfReplacementsAcrossNumberOfLines(2, 2);
  FinishTest("bar\nbar");
  BeginTest("foo foo\nfoo foo\nfoo foo");
  TestPressKey(":%s/foo/bar/gc\\enter");
  TestPressKey("yynnyy");
  verifyShowsNumberOfReplacementsAcrossNumberOfLines(4, 2);
  FinishTest("bar bar\nfoo foo\nbar bar");
  BeginTest("foofoo");
  TestPressKey(":s/foo/bar\\\\nxyz/gc\\enter");
  TestPressKey("yy");
  verifyShowsNumberOfReplacementsAcrossNumberOfLines(2, 1);
  FinishTest("bar\nxyzbar\nxyz");
  BeginTest("foofoofoo");
  TestPressKey(":s/foo/bar\\\\nxyz\\\\nboo/gc\\enter");
  TestPressKey("yyy");
  verifyShowsNumberOfReplacementsAcrossNumberOfLines(3, 1);
  FinishTest("bar\nxyz\nboobar\nxyz\nboobar\nxyz\nboo");
  // Tricky one: how many lines are "touched" if a single replacement
  // swallows multiple lines? I'm going to say the number of lines swallowed.
  BeginTest("foo\nfoo\nfoo");
  TestPressKey(":s/foo\\\\nfoo\\\\nfoo/bar/c\\enter");
  TestPressKey("y");
  verifyShowsNumberOfReplacementsAcrossNumberOfLines(1, 3);
  FinishTest("bar");
  BeginTest("foo\nfoo\nfoo\n");
  TestPressKey(":s/foo\\\\nfoo\\\\nfoo\\\\n/bar/c\\enter");
  TestPressKey("y");
  verifyShowsNumberOfReplacementsAcrossNumberOfLines(1, 4);
  FinishTest("bar");

  // "Undo" undoes last replacement.
  BeginTest("foo foo foo foo");
  TestPressKey(":s/foo/bar/cg\\enter");
  TestPressKey("nyynu");
  FinishTest("foo bar foo foo");

  // "l" does the current replacement then exits.
  BeginTest("foo foo foo foo foo foo");
  TestPressKey(":s/foo/bar/cg\\enter");
  TestPressKey("nnl");
  verifyShowsNumberOfReplacementsAcrossNumberOfLines(1, 1);
  FinishTest("foo foo bar foo foo foo");

  // "q" just exits.
  BeginTest("foo foo foo foo foo foo");
  TestPressKey(":s/foo/bar/cg\\enter");
  TestPressKey("yyq");
  verifyShowsNumberOfReplacementsAcrossNumberOfLines(2, 1);
  FinishTest("bar bar foo foo foo foo");

  // "a" replaces all remaining, then exits.
  BeginTest("foo foo foo foo foo foo");
  TestPressKey(":s/foo/bar/cg\\enter");
  TestPressKey("nna");
  verifyShowsNumberOfReplacementsAcrossNumberOfLines(4, 1);
  FinishTest("foo foo bar bar bar bar");

  // The results of "a" can be undone in one go.
  BeginTest("foo foo foo foo foo foo");
  TestPressKey(":s/foo/bar/cg\\enter");
  TestPressKey("ya");
  verifyShowsNumberOfReplacementsAcrossNumberOfLines(6, 1);
  TestPressKey("u");
  FinishTest("bar foo foo foo foo foo");
#if 0
    // XXX - as of Qt 5.5, simply replaying the correct QKeyEvents does *not* cause shortcuts
    // to be triggered, so these tests cannot pass.
    // It's possible that a solution involving QTestLib will be workable in the future, though.

  {
    // Test the test suite: ensure that shortcuts are still being sent and received correctly.
    // The test shortcut chosen should be one that does not conflict with built-in Kate ones.
    FailsIfSlotNotCalled failsIfActionNotTriggered;
    QAction *dummyAction = kate_view->actionCollection()->addAction("Woo");
    dummyAction->setShortcut(QKeySequence("Ctrl+]"));
    QVERIFY(connect(dummyAction, SIGNAL(triggered()), &failsIfActionNotTriggered, SLOT(slot())));
    DoTest("foo", "\\ctrl-]", "foo");
    // Processing shortcuts seems to require events to be processed.
    while (QApplication::hasPendingEvents())
    {
      QApplication::processEvents();
    }
    delete dummyAction;
  }
  {
    // Test that shortcuts involving ctrl+<digit> work correctly.
    FailsIfSlotNotCalled failsIfActionNotTriggered;
    QAction *dummyAction = kate_view->actionCollection()->addAction("Woo");
    dummyAction->setShortcut(QKeySequence("Ctrl+1"));
    QVERIFY(connect(dummyAction, SIGNAL(triggered()), &failsIfActionNotTriggered, SLOT(slot())));
    DoTest("foo", "\\ctrl-1", "foo");
    // Processing shortcuts seems to require events to be processed.
    while (QApplication::hasPendingEvents())
    {
      QApplication::processEvents();
    }
    delete dummyAction;
  }
  {
    // Test that shortcuts involving alt+<digit> work correctly.
    FailsIfSlotNotCalled failsIfActionNotTriggered;
    QAction *dummyAction = kate_view->actionCollection()->addAction("Woo");
    dummyAction->setShortcut(QKeySequence("Alt+1"));
    QVERIFY(connect(dummyAction, SIGNAL(triggered()), &failsIfActionNotTriggered, SLOT(slot())));
    DoTest("foo", "\\alt-1", "foo");
    // Processing shortcuts seems to require events to be processed.
    while (QApplication::hasPendingEvents())
    {
      QApplication::processEvents();
    }
    delete dummyAction;
  }
#endif

  // Find the "Print" action for later use.
  QAction *printAction = nullptr;
  foreach(QAction* action, kate_view->actionCollection()->actions())
  {
    if (action->shortcut() == QKeySequence("Ctrl+p"))
    {
      printAction = action;
      break;
    }
  }

  // Test that we don't inadvertantly trigger shortcuts in kate_view when typing them in the
  // emulated command bar.  Requires the above test for shortcuts to be sent and received correctly
  // to pass.
  {
    QVERIFY(mainWindow->isActiveWindow());
    QVERIFY(printAction);
    FailsIfSlotCalled failsIfActionTriggered("The kate_view shortcut should not be triggered by typing it in emulated  command bar!");
    // Don't invoke Print on failure, as this hangs instead of failing.
    //disconnect(printAction, SIGNAL(triggered(bool)), kate_document, SLOT(print()));
    connect(printAction, SIGNAL(triggered(bool)), &failsIfActionTriggered, SLOT(slot()));
    DoTest("foo bar foo bar", "/bar\\enterggd/\\ctrl-p\\enter.", "bar");
    // Processing shortcuts seems to require events to be processed.
    while (QApplication::hasPendingEvents())
    {
      QApplication::processEvents();
    }
  }

  // Test that the interactive search replace does not handle general keypresses like ctrl-p ("invoke
  // completion in emulated command bar").
  // Unfortunately, "ctrl-p" in kate_view, which is what will be triggered if this
  // test succeeds, hangs due to showing the print dialog, so we need to temporarily
  // block the Print action.
  clearCommandHistory();
  if (printAction)
  {
    printAction->blockSignals(true);
  }
  vi_global->commandHistory()->append("s/foo/bar/caa");
  BeginTest("foo");
  TestPressKey(":s/foo/bar/c\\ctrl-b\\enter\\ctrl-p");
  QVERIFY(!emulatedCommandBarCompleter()->popup()->isVisible());
  TestPressKey("\\ctrl-c");
  if (printAction)
  {
    printAction->blockSignals(false);
  }
  FinishTest("foo");

  // The interactive sed replace command is added to the history straight away.
  clearCommandHistory();
  BeginTest("foo");
  TestPressKey(":s/foo/bar/c\\enter");
  QCOMPARE(commandHistory(), QStringList() << "s/foo/bar/c");
  TestPressKey("\\ctrl-c");
  FinishTest("foo");
  clearCommandHistory();
  BeginTest("foo");
  TestPressKey(":s/notfound/bar/c\\enter");
  QCOMPARE(commandHistory(), QStringList() << "s/notfound/bar/c");
  TestPressKey("\\ctrl-c");
  FinishTest("foo");

  // Should be usable in mappings.
  clearAllMappings();
  vi_global->mappings()->add(Mappings::NormalModeMapping, "H", ":s/foo/bar/gc<enter>nnyyl", Mappings::Recursive);
  DoTest("foo foo foo foo foo foo", "H", "foo foo bar bar bar foo");
  clearAllMappings();
  vi_global->mappings()->add(Mappings::NormalModeMapping, "H", ":s/foo/bar/gc<enter>nna", Mappings::Recursive);
  DoTest("foo foo foo foo foo foo", "H", "foo foo bar bar bar bar");
  clearAllMappings();
  vi_global->mappings()->add(Mappings::NormalModeMapping, "H", ":s/foo/bar/gc<enter>nnyqggidone<esc>", Mappings::Recursive);
  DoTest("foo foo foo foo foo foo", "H", "donefoo foo bar foo foo foo");

  // Don't swallow "Ctrl+<key>" meant for the text edit.
  if (QKeySequence::keyBindings(QKeySequence::Undo).contains(QKeySequence("Ctrl+Z")))
  {
    DoTest("foo bar", "/bar\\ctrl-z\\enterrX", "Xoo bar");
  }
  else
  {
    qWarning() << "Skipped test: Ctrl+Z is not Undo on this platform";
  }

  // Don't give invalid cursor position to updateCursor in Visual Mode: it will cause a crash!
  DoTest("xyz\nfoo\nbar\n123", "/foo\\\\nbar\\\\n\\enterggv//e\\enter\\ctrl-crX", "xyz\nfoo\nbaX\n123");
  DoTest("\nfooxyz\nbar;\n" , "/foo.*\\\\n.*;\\enterggv//e\\enter\\ctrl-crX", "\nfooxyz\nbarX\n");
}

QCompleter* EmulatedCommandBarTest::emulatedCommandBarCompleter()
{
  return vi_input_mode->viModeEmulatedCommandBar()->findChild<QCompleter*>("completer");
}

void EmulatedCommandBarTest::verifyCommandBarCompletionVisible()
{
  if (!emulatedCommandBarCompleter()->popup()->isVisible())
  {
    qDebug() << "Emulated command bar completer not visible.";
    QStringListModel *completionModel = qobject_cast<QStringListModel*>(emulatedCommandBarCompleter()->model());
    Q_ASSERT(completionModel);
    QStringList allAvailableCompletions = completionModel->stringList();
    qDebug() << " Completion list: " << allAvailableCompletions;
    qDebug() << " Completion prefix: " << emulatedCommandBarCompleter()->completionPrefix();
    bool candidateCompletionFound = false;
    foreach (const QString& availableCompletion, allAvailableCompletions)
    {
      if (availableCompletion.startsWith(emulatedCommandBarCompleter()->completionPrefix()))
      {
        candidateCompletionFound = true;
        break;
      }
    }
    if (candidateCompletionFound)
    {
      qDebug() << " The current completion prefix is a prefix of one of the available completions, so either complete() was not called, or the popup was manually hidden since then";
    }
    else
    {
      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";
    }
  }
  QVERIFY(emulatedCommandBarCompleter()->popup()->isVisible());
}

void EmulatedCommandBarTest::verifyCommandBarCompletionsMatches(const QStringList& expectedCompletionList)
{
  verifyCommandBarCompletionVisible();
  QStringList actualCompletionList;
  for (int i = 0; emulatedCommandBarCompleter()->setCurrentRow(i); i++)
    actualCompletionList << emulatedCommandBarCompleter()->currentCompletion();
  if (expectedCompletionList != actualCompletionList)
  {
    qDebug() << "Actual completions:\n " << actualCompletionList << "\n\ndo not match expected:\n" << expectedCompletionList;
  }

  QCOMPARE(actualCompletionList, expectedCompletionList);
}

void EmulatedCommandBarTest::verifyCommandBarCompletionContains(const QStringList& expectedCompletionList)
{
  verifyCommandBarCompletionVisible();
  QStringList actualCompletionList;

  for (int i = 0; emulatedCommandBarCompleter()->setCurrentRow(i); i++)
  {
    actualCompletionList << emulatedCommandBarCompleter()->currentCompletion();
  }

  foreach(const QString& expectedCompletion, expectedCompletionList)
  {
    if (!actualCompletionList.contains(expectedCompletion))
    {
      qDebug() << "Whoops: " << actualCompletionList << " does not contain " << expectedCompletion;
    }
    QVERIFY(actualCompletionList.contains(expectedCompletion));
  }
}

QLabel* EmulatedCommandBarTest::emulatedCommandTypeIndicator()
{
  return emulatedCommandBar()->findChild<QLabel*>("bartypeindicator");
}

void EmulatedCommandBarTest::verifyCursorAt(const Cursor& expectedCursorPos)
{
  QCOMPARE(kate_view->cursorPosition().line(), expectedCursorPos.line());
  QCOMPARE(kate_view->cursorPosition().column(), expectedCursorPos.column());
}

void EmulatedCommandBarTest::clearSearchHistory()
{
  vi_global->searchHistory()->clear();
}

QStringList EmulatedCommandBarTest::searchHistory()
{
  return vi_global->searchHistory()->items();
}

void EmulatedCommandBarTest::clearCommandHistory()
{
  vi_global->commandHistory()->clear();
}

QStringList EmulatedCommandBarTest::commandHistory()
{
  return vi_global->commandHistory()->items();
}

void EmulatedCommandBarTest::clearReplaceHistory()
{
  vi_global->replaceHistory()->clear();
}

QStringList EmulatedCommandBarTest::replaceHistory()
{
  return vi_global->replaceHistory()->items();
}

QList<Kate::TextRange *> EmulatedCommandBarTest::rangesOnFirstLine()
{
    return kate_document->buffer().rangesForLine(0, kate_view, true);
}

void EmulatedCommandBarTest::verifyTextEditBackgroundColour(const QColor& expectedBackgroundColour)
{
  QCOMPARE(emulatedCommandBarTextEdit()->palette().brush(QPalette::Base).color(), expectedBackgroundColour);
}

QLabel* EmulatedCommandBarTest::commandResponseMessageDisplay()
{
  QLabel* commandResponseMessageDisplay = emulatedCommandBar()->findChild<QLabel*>("commandresponsemessage");
  Q_ASSERT(commandResponseMessageDisplay);
  return commandResponseMessageDisplay;
}

void EmulatedCommandBarTest::waitForEmulatedCommandBarToHide(long int timeout)
{
  const QDateTime waitStartedTime = QDateTime::currentDateTime();
  while(emulatedCommandBar()->isVisible() && waitStartedTime.msecsTo(QDateTime::currentDateTime()) < timeout)
  {
    QApplication::processEvents();
  }
  QVERIFY(!emulatedCommandBar()->isVisible());
}

void EmulatedCommandBarTest::verifyShowsNumberOfReplacementsAcrossNumberOfLines(int numReplacements, int acrossNumLines)
{
  QVERIFY(commandResponseMessageDisplay()->isVisible());
  QVERIFY(!emulatedCommandTypeIndicator()->isVisible());
  const QString commandMessageResponseText = commandResponseMessageDisplay()->text();
  const QString expectedNumReplacementsAsString = QString::number(numReplacements);
  const QString expectedAcrossNumLinesAsString = QString::number(acrossNumLines);
  // Be a bit vague about the actual contents due to e.g. localisation.
  // TODO - see if we can insist that en_US is available on the Kate Jenkins server and
  // insist that we use it ... ?
  QRegExp numReplacementsMessageRegex("^.*(\\d+).*(\\d+).*$");
  QVERIFY(numReplacementsMessageRegex.exactMatch(commandMessageResponseText));
  const QString actualNumReplacementsAsString = numReplacementsMessageRegex.cap(1);
  const QString actualAcrossNumLinesAsString = numReplacementsMessageRegex.cap(2);
  QCOMPARE(actualNumReplacementsAsString, expectedNumReplacementsAsString);
  QCOMPARE(actualAcrossNumLinesAsString, expectedAcrossNumLinesAsString);
}

FailsIfSlotNotCalled::FailsIfSlotNotCalled(): QObject(), m_slotWasCalled(false)
{

}

FailsIfSlotNotCalled::~FailsIfSlotNotCalled()
{
  QVERIFY(m_slotWasCalled);
}

void FailsIfSlotNotCalled::slot()
{
  m_slotWasCalled = true;
}

FailsIfSlotCalled::FailsIfSlotCalled(const QString& failureMessage): QObject(), m_failureMessage(failureMessage)
{

}

void FailsIfSlotCalled::slot()
{
  QFAIL(qPrintable(m_failureMessage.toLatin1()));
}
