File indexing completed on 2024-05-12 11:57:20

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2014 Miquel Sabaté Solà <mikisabate@gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "completion.h"
0009 #include "vimode/globalstate.h"
0010 #include "vimode/mappings.h"
0011 #include <katecompletionwidget.h>
0012 #include <kateconfig.h>
0013 #include <katedocument.h>
0014 #include <kateglobal.h>
0015 #include <kateview.h>
0016 #include <katewordcompletion.h>
0017 #include <vimode/emulatedcommandbar/emulatedcommandbar.h>
0018 
0019 using namespace KTextEditor;
0020 using KateVi::Mappings;
0021 
0022 QTEST_MAIN(CompletionTest)
0023 
0024 // BEGIN: VimCodeCompletionTestModel
0025 
0026 VimCodeCompletionTestModel::VimCodeCompletionTestModel(KTextEditor::View *parent)
0027     : KTextEditor::CodeCompletionModel(parent)
0028 {
0029     setRowCount(3);
0030     cc()->setAutomaticInvocationEnabled(true);
0031     // It would add additional items and we don't want that in tests
0032     cc()->unregisterCompletionModel(KTextEditor::EditorPrivate::self()->wordCompletionModel());
0033     cc()->registerCompletionModel(this);
0034 }
0035 
0036 QVariant VimCodeCompletionTestModel::data(const QModelIndex &index, int role) const
0037 {
0038     // Order is important, here, as the completion widget seems to do its own sorting.
0039     const char *completions[] = {"completion1", "completion2", "completion3"};
0040     if (role == Qt::DisplayRole) {
0041         if (index.column() == Name) {
0042             return QString(completions[index.row()]);
0043         }
0044     }
0045     return QVariant();
0046 }
0047 
0048 CodeCompletionInterface *VimCodeCompletionTestModel::cc() const
0049 {
0050     return dynamic_cast<CodeCompletionInterface *>(const_cast<QObject *>(QObject::parent()));
0051 }
0052 
0053 // END: VimCodeCompletionTestModel
0054 
0055 // BEGIN: CodeCompletionInterface
0056 
0057 FailTestOnInvocationModel::FailTestOnInvocationModel(KTextEditor::View *parent)
0058     : KTextEditor::CodeCompletionModel(parent)
0059 {
0060     setRowCount(3);
0061     cc()->setAutomaticInvocationEnabled(true);
0062     // It would add additional items and we don't want that in tests.
0063     cc()->unregisterCompletionModel(EditorPrivate::self()->wordCompletionModel());
0064     cc()->registerCompletionModel(this);
0065 }
0066 
0067 QVariant FailTestOnInvocationModel::data(const QModelIndex &index, int role) const
0068 {
0069     Q_UNUSED(index);
0070     Q_UNUSED(role);
0071 
0072     failTest();
0073     return QVariant();
0074 }
0075 
0076 void FailTestOnInvocationModel::failTest() const
0077 {
0078     QFAIL("Shouldn't be invoking me!");
0079 }
0080 
0081 CodeCompletionInterface *FailTestOnInvocationModel::cc() const
0082 {
0083     return dynamic_cast<CodeCompletionInterface *>(const_cast<QObject *>(QObject::parent()));
0084 }
0085 
0086 // END: CodeCompletionInterface
0087 
0088 // BEGIN: CompletionTest
0089 
0090 void CompletionTest::FakeCodeCompletionTests()
0091 {
0092     // Test that FakeCodeCompletionTestModel behaves similar to the code-completion in e.g. KDevelop.
0093     const bool oldStealKeys = KateViewConfig::global()->viInputModeStealKeys();
0094     KateViewConfig::global()->setValue(KateViewConfig::ViInputModeStealKeys, true); // For Ctrl-P, Ctrl-N etc
0095     ensureKateViewVisible(); // KTextEditor::ViewPrivate needs to be visible for the completion widget.
0096     FakeCodeCompletionTestModel *fakeCodeCompletionModel = new FakeCodeCompletionTestModel(kate_view);
0097     kate_view->registerCompletionModel(fakeCodeCompletionModel);
0098     fakeCodeCompletionModel->setCompletions({"completionA", "completionB", "completionC"});
0099     DoTest("", "i\\ctrl-p\\enter", "completionC");
0100     DoTest("", "i\\ctrl-p\\ctrl-p\\enter", "completionB");
0101     DoTest("", "i\\ctrl-p\\ctrl-p\\ctrl-p\\enter", "completionA");
0102     DoTest("", "i\\ctrl-p\\ctrl-p\\ctrl-p\\ctrl-p\\enter", "completionC");
0103     // If no word before cursor, don't delete any text.
0104     BeginTest("");
0105     clearTrackedDocumentChanges();
0106     TestPressKey("i\\ctrl- \\enter");
0107     QCOMPARE(m_docChanges.length(), 1);
0108     FinishTest("completionA");
0109     // Apparently, we must delete the word before the cursor upon completion
0110     // (even if we replace it with identical text!)
0111     BeginTest("compl");
0112     TestPressKey("ea");
0113     clearTrackedDocumentChanges();
0114     TestPressKey("\\ctrl- \\enter");
0115     QCOMPARE(m_docChanges.size(), 2);
0116     QCOMPARE(m_docChanges[0].changeType(), DocChange::TextRemoved);
0117     QCOMPARE(m_docChanges[0].changeRange(), Range(Cursor(0, 0), Cursor(0, 5)));
0118     QCOMPARE(m_docChanges[1].changeType(), DocChange::TextInserted);
0119     QCOMPARE(m_docChanges[1].changeRange(), Range(Cursor(0, 0), Cursor(0, 11)));
0120     QCOMPARE(m_docChanges[1].newText(), QString("completionA"));
0121     FinishTest("completionA");
0122     // A "word" is currently alphanumeric, plus underscore.
0123     fakeCodeCompletionModel->setCompletions({"w_123completion"});
0124     BeginTest("(w_123");
0125     TestPressKey("ea");
0126     clearTrackedDocumentChanges();
0127     TestPressKey("\\ctrl- \\enter");
0128     QCOMPARE(m_docChanges.size(), 2);
0129     QCOMPARE(m_docChanges[0].changeType(), DocChange::TextRemoved);
0130     QCOMPARE(m_docChanges[0].changeRange(), Range(Cursor(0, 1), Cursor(0, 6)));
0131     QCOMPARE(m_docChanges[1].changeType(), DocChange::TextInserted);
0132     QCOMPARE(m_docChanges[1].changeRange(), Range(Cursor(0, 1), Cursor(0, 16)));
0133     QCOMPARE(m_docChanges[1].newText(), QString("w_123completion"));
0134     FinishTest("(w_123completion");
0135     // "Removing tail on complete" is apparently done in three stages:
0136     // delete word up to the cursor; insert new word; then delete remainder.
0137     const bool oldRemoveTailOnCompletion = KateViewConfig::global()->wordCompletionRemoveTail();
0138     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, true);
0139     fakeCodeCompletionModel->setRemoveTailOnComplete(true);
0140     BeginTest("(w_123comp");
0141     TestPressKey("6li");
0142     clearTrackedDocumentChanges();
0143     TestPressKey("\\ctrl- \\enter");
0144     FinishTest("(w_123completion");
0145 
0146     // If we don't remove tail, just delete up to the cursor and insert.
0147     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, false);
0148     fakeCodeCompletionModel->setRemoveTailOnComplete(false);
0149     BeginTest("(w_123comp");
0150     TestPressKey("6li");
0151     clearTrackedDocumentChanges();
0152     TestPressKey("\\ctrl- \\enter");
0153     FinishTest("(w_123completioncomp");
0154 
0155     // If no opening bracket after the cursor, a function taking no arguments
0156     // is added as "function()", and the cursor placed after the closing ")".
0157     // The addition of "function()" is done in two steps: first "function", then "()".
0158     BeginTest("object->");
0159     fakeCodeCompletionModel->setCompletions({"functionCall()"});
0160     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, true);
0161     fakeCodeCompletionModel->setRemoveTailOnComplete(true);
0162     clearTrackedDocumentChanges();
0163     TestPressKey("$a\\ctrl- \\enter");
0164     QCOMPARE(m_docChanges.size(), 2);
0165     QCOMPARE(m_docChanges[0].changeType(), DocChange::TextInserted);
0166     QCOMPARE(m_docChanges[0].changeRange(), Range(Cursor(0, 8), Cursor(0, 20)));
0167     QCOMPARE(m_docChanges[0].newText(), QString("functionCall"));
0168     QCOMPARE(m_docChanges[1].changeRange(), Range(Cursor(0, 20), Cursor(0, 22)));
0169     QCOMPARE(m_docChanges[1].newText(), QString("()"));
0170     TestPressKey("X");
0171     FinishTest("object->functionCall()X");
0172 
0173     // If no opening bracket after the cursor, a function taking at least one argument
0174     // is added as "function()", and the cursor placed after the opening "(".
0175     // The addition of "function()" is done in two steps: first "function", then "()".
0176     qDebug() << "Fleep";
0177     BeginTest("object->");
0178     fakeCodeCompletionModel->setCompletions({"functionCall(...)"});
0179     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, true);
0180     fakeCodeCompletionModel->setRemoveTailOnComplete(true);
0181     clearTrackedDocumentChanges();
0182     TestPressKey("$a\\ctrl- \\enter");
0183     QCOMPARE(m_docChanges.size(), 2);
0184     QCOMPARE(m_docChanges[0].changeType(), DocChange::TextInserted);
0185     QCOMPARE(m_docChanges[0].changeRange(), Range(Cursor(0, 8), Cursor(0, 20)));
0186     QCOMPARE(m_docChanges[0].newText(), QString("functionCall"));
0187     QCOMPARE(m_docChanges[1].changeType(), DocChange::TextInserted);
0188     QCOMPARE(m_docChanges[1].changeRange(), Range(Cursor(0, 20), Cursor(0, 22)));
0189     QCOMPARE(m_docChanges[1].newText(), QString("()"));
0190     TestPressKey("X");
0191     FinishTest("object->functionCall(X)");
0192 
0193     // If there is an opening bracket after the cursor, we merge the function call
0194     // with that.
0195     // Even if the function takes no arguments, we still place the cursor after the opening bracket,
0196     // in contrast to the case where there is no opening bracket after the cursor.
0197     // No brackets are added.  No removals occur if there is no word before the cursor.
0198     BeginTest("object->(");
0199     fakeCodeCompletionModel->setCompletions({"functionCall()"});
0200     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, true);
0201     fakeCodeCompletionModel->setRemoveTailOnComplete(true);
0202     clearTrackedDocumentChanges();
0203     TestPressKey("f(i\\ctrl- \\enter");
0204     QCOMPARE(m_docChanges.size(), 1);
0205     QCOMPARE(m_docChanges[0].changeType(), DocChange::TextInserted);
0206     QCOMPARE(m_docChanges[0].changeRange(), Range(Cursor(0, 8), Cursor(0, 20)));
0207     QCOMPARE(m_docChanges[0].newText(), QString("functionCall"));
0208     TestPressKey("X");
0209     FinishTest("object->functionCall(X");
0210 
0211     // There can't be any non-whitespace between cursor position and opening bracket, though!
0212     BeginTest("object->|(   (");
0213     fakeCodeCompletionModel->setCompletions({"functionCall()"});
0214     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, true);
0215     fakeCodeCompletionModel->setRemoveTailOnComplete(true);
0216     clearTrackedDocumentChanges();
0217     TestPressKey("f>a\\ctrl- \\enter");
0218     QCOMPARE(m_docChanges.size(), 2);
0219     QCOMPARE(m_docChanges[0].changeType(), DocChange::TextInserted);
0220     QCOMPARE(m_docChanges[0].changeRange(), Range(Cursor(0, 8), Cursor(0, 20)));
0221     QCOMPARE(m_docChanges[0].newText(), QString("functionCall"));
0222     QCOMPARE(m_docChanges[1].changeRange(), Range(Cursor(0, 20), Cursor(0, 22)));
0223     QCOMPARE(m_docChanges[1].newText(), QString("()"));
0224     TestPressKey("X");
0225     FinishTest("object->functionCall()X|(   (");
0226 
0227     // Whitespace before the bracket is fine, though.
0228     BeginTest("object->    (<-Cursor here!");
0229     fakeCodeCompletionModel->setCompletions({"functionCall()"});
0230     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, true);
0231     fakeCodeCompletionModel->setRemoveTailOnComplete(true);
0232     clearTrackedDocumentChanges();
0233     TestPressKey("f>a\\ctrl- \\enter");
0234     QCOMPARE(m_docChanges.size(), 1);
0235     QCOMPARE(m_docChanges[0].changeType(), DocChange::TextInserted);
0236     QCOMPARE(m_docChanges[0].changeRange(), Range(Cursor(0, 8), Cursor(0, 20)));
0237     QCOMPARE(m_docChanges[0].newText(), QString("functionCall"));
0238     TestPressKey("X");
0239     FinishTest("object->functionCall    (X<-Cursor here!");
0240 
0241     // Be careful with positioning the cursor if we delete leading text!
0242     BeginTest("object->    (<-Cursor here!");
0243     fakeCodeCompletionModel->setCompletions({"functionCall()"});
0244     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, true);
0245     fakeCodeCompletionModel->setRemoveTailOnComplete(true);
0246     clearTrackedDocumentChanges();
0247     TestPressKey("f>afunct");
0248     clearTrackedDocumentChanges();
0249     TestPressKey("\\ctrl- \\enter");
0250     QCOMPARE(m_docChanges.size(), 2);
0251     QCOMPARE(m_docChanges[0].changeType(), DocChange::TextRemoved);
0252     QCOMPARE(m_docChanges[0].changeRange(), Range(Cursor(0, 8), Cursor(0, 13)));
0253     QCOMPARE(m_docChanges[1].changeType(), DocChange::TextInserted);
0254     QCOMPARE(m_docChanges[1].changeRange(), Range(Cursor(0, 8), Cursor(0, 20)));
0255     QCOMPARE(m_docChanges[1].newText(), QString("functionCall"));
0256     TestPressKey("X");
0257     FinishTest("object->functionCall    (X<-Cursor here!");
0258 
0259     // If we're removing tail on complete, it's whether there is a suitable opening
0260     // bracket *after* the word (not the cursor) that's important.
0261     BeginTest("object->function    (<-Cursor here!");
0262     fakeCodeCompletionModel->setCompletions({"functionCall()"});
0263     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, true);
0264     fakeCodeCompletionModel->setRemoveTailOnComplete(true);
0265     clearTrackedDocumentChanges();
0266     TestPressKey("12li"); // Start inserting before the "t" in "function"
0267     clearTrackedDocumentChanges();
0268     TestPressKey("\\ctrl- \\enter");
0269     TestPressKey("X");
0270     FinishTest("object->functionCall    (X<-Cursor here!");
0271 
0272     // Repeat of bracket-merging stuff, this time for functions that take at least one argument.
0273     BeginTest("object->(");
0274     fakeCodeCompletionModel->setCompletions({"functionCall(...)"});
0275     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, true);
0276     fakeCodeCompletionModel->setRemoveTailOnComplete(true);
0277     clearTrackedDocumentChanges();
0278     TestPressKey("f(i\\ctrl- \\enter");
0279     QCOMPARE(m_docChanges.size(), 1);
0280     QCOMPARE(m_docChanges[0].changeType(), DocChange::TextInserted);
0281     qDebug() << "Range: " << m_docChanges[0].changeRange();
0282     QCOMPARE(m_docChanges[0].changeRange(), Range(Cursor(0, 8), Cursor(0, 20)));
0283     QCOMPARE(m_docChanges[0].newText(), QString("functionCall"));
0284     TestPressKey("X");
0285     FinishTest("object->functionCall(X");
0286 
0287     // There can't be any non-whitespace between cursor position and opening bracket, though!
0288     BeginTest("object->|(   (");
0289     fakeCodeCompletionModel->setCompletions({"functionCall(...)"});
0290     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, true);
0291     fakeCodeCompletionModel->setRemoveTailOnComplete(true);
0292     clearTrackedDocumentChanges();
0293     TestPressKey("f>a\\ctrl- \\enter");
0294     QCOMPARE(m_docChanges.size(), 2);
0295     QCOMPARE(m_docChanges[0].changeType(), DocChange::TextInserted);
0296     QCOMPARE(m_docChanges[0].changeRange(), Range(Cursor(0, 8), Cursor(0, 20)));
0297     QCOMPARE(m_docChanges[0].newText(), QString("functionCall"));
0298     QCOMPARE(m_docChanges[1].changeRange(), Range(Cursor(0, 20), Cursor(0, 22)));
0299     QCOMPARE(m_docChanges[1].newText(), QString("()"));
0300     TestPressKey("X");
0301     FinishTest("object->functionCall(X)|(   (");
0302 
0303     // Whitespace before the bracket is fine, though.
0304     BeginTest("object->    (<-Cursor here!");
0305     qDebug() << "NooooO";
0306     fakeCodeCompletionModel->setCompletions({"functionCall(...)"});
0307     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, true);
0308     fakeCodeCompletionModel->setRemoveTailOnComplete(true);
0309     clearTrackedDocumentChanges();
0310     TestPressKey("f>a\\ctrl- \\enter");
0311     QCOMPARE(m_docChanges.size(), 1);
0312     QCOMPARE(m_docChanges[0].changeType(), DocChange::TextInserted);
0313     QCOMPARE(m_docChanges[0].changeRange(), Range(Cursor(0, 8), Cursor(0, 20)));
0314     QCOMPARE(m_docChanges[0].newText(), QString("functionCall"));
0315     TestPressKey("X");
0316     FinishTest("object->functionCall    (X<-Cursor here!");
0317 
0318     // Be careful with positioning the cursor if we delete leading text!
0319     BeginTest("object->    (<-Cursor here!");
0320     fakeCodeCompletionModel->setCompletions({"functionCall(...)"});
0321     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, true);
0322     fakeCodeCompletionModel->setRemoveTailOnComplete(true);
0323     clearTrackedDocumentChanges();
0324     TestPressKey("f>afunct");
0325     clearTrackedDocumentChanges();
0326     TestPressKey("\\ctrl- \\enter");
0327     QCOMPARE(m_docChanges.size(), 2);
0328     QCOMPARE(m_docChanges[0].changeType(), DocChange::TextRemoved);
0329     QCOMPARE(m_docChanges[0].changeRange(), Range(Cursor(0, 8), Cursor(0, 13)));
0330     QCOMPARE(m_docChanges[1].changeType(), DocChange::TextInserted);
0331     QCOMPARE(m_docChanges[1].changeRange(), Range(Cursor(0, 8), Cursor(0, 20)));
0332     QCOMPARE(m_docChanges[1].newText(), QString("functionCall"));
0333     TestPressKey("X");
0334     FinishTest("object->functionCall    (X<-Cursor here!");
0335 
0336     // If we're removing tail on complete, it's whether there is a suitable opening
0337     // bracket *after* the word (not the cursor) that's important.
0338     BeginTest("object->function    (<-Cursor here!");
0339     fakeCodeCompletionModel->setCompletions({"functionCall(...)"});
0340     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, true);
0341     fakeCodeCompletionModel->setRemoveTailOnComplete(true);
0342     clearTrackedDocumentChanges();
0343     TestPressKey("12li"); // Start inserting before the "t" in "function"
0344     clearTrackedDocumentChanges();
0345     TestPressKey("\\ctrl- \\enter");
0346     TestPressKey("X");
0347     FinishTest("object->functionCall    (X<-Cursor here!");
0348 
0349     // Deal with function completions which add a ";".
0350     BeginTest("");
0351     fakeCodeCompletionModel->setCompletions({"functionCall();"});
0352     clearTrackedDocumentChanges();
0353     TestPressKey("ifun");
0354     clearTrackedDocumentChanges();
0355     TestPressKey("\\ctrl- \\enter");
0356     QCOMPARE(m_docChanges.size(), 3);
0357     QCOMPARE(m_docChanges[0].changeType(), DocChange::TextRemoved);
0358     QCOMPARE(m_docChanges[0].changeRange(), Range(Cursor(0, 0), Cursor(0, 3)));
0359     QCOMPARE(m_docChanges[1].changeType(), DocChange::TextInserted);
0360     QCOMPARE(m_docChanges[1].changeRange(), Range(Cursor(0, 0), Cursor(0, 12)));
0361     QCOMPARE(m_docChanges[1].newText(), QString("functionCall"));
0362     QCOMPARE(m_docChanges[2].changeType(), DocChange::TextInserted);
0363     QCOMPARE(m_docChanges[2].changeRange(), Range(Cursor(0, 12), Cursor(0, 15)));
0364     QCOMPARE(m_docChanges[2].newText(), QString("();"));
0365     FinishTest("functionCall();");
0366 
0367     BeginTest("");
0368     fakeCodeCompletionModel->setCompletions({"functionCall();"});
0369     TestPressKey("ifun\\ctrl- \\enterX");
0370     FinishTest("functionCall();X");
0371 
0372     BeginTest("");
0373     fakeCodeCompletionModel->setCompletions({"functionCall(...);"});
0374     clearTrackedDocumentChanges();
0375     TestPressKey("ifun");
0376     clearTrackedDocumentChanges();
0377     TestPressKey("\\ctrl- \\enter");
0378     QCOMPARE(m_docChanges.size(), 3);
0379     QCOMPARE(m_docChanges[0].changeType(), DocChange::TextRemoved);
0380     QCOMPARE(m_docChanges[0].changeRange(), Range(Cursor(0, 0), Cursor(0, 3)));
0381     QCOMPARE(m_docChanges[1].changeType(), DocChange::TextInserted);
0382     QCOMPARE(m_docChanges[1].changeRange(), Range(Cursor(0, 0), Cursor(0, 12)));
0383     QCOMPARE(m_docChanges[1].newText(), QString("functionCall"));
0384     QCOMPARE(m_docChanges[2].changeType(), DocChange::TextInserted);
0385     QCOMPARE(m_docChanges[2].changeRange(), Range(Cursor(0, 12), Cursor(0, 15)));
0386     QCOMPARE(m_docChanges[2].newText(), QString("();"));
0387     FinishTest("functionCall();");
0388 
0389     BeginTest("");
0390     fakeCodeCompletionModel->setCompletions({"functionCall(...);"});
0391     TestPressKey("ifun\\ctrl- \\enterX");
0392     FinishTest("functionCall(X);");
0393 
0394     // Completions ending with ";" do not participate in bracket merging.
0395     BeginTest("(<-old bracket");
0396     fakeCodeCompletionModel->setCompletions({"functionCall();"});
0397     TestPressKey("ifun\\ctrl- \\enterX");
0398     FinishTest("functionCall();X(<-old bracket");
0399     BeginTest("(<-old bracket");
0400     fakeCodeCompletionModel->setCompletions({"functionCall(...);"});
0401     TestPressKey("ifun\\ctrl- \\enterX");
0402     FinishTest("functionCall(X);(<-old bracket");
0403 
0404     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, oldRemoveTailOnCompletion);
0405     KateViewConfig::global()->setValue(KateViewConfig::ViInputModeStealKeys, oldStealKeys);
0406     kate_view->hide();
0407     mainWindow->hide();
0408     kate_view->unregisterCompletionModel(fakeCodeCompletionModel);
0409     delete fakeCodeCompletionModel;
0410 }
0411 
0412 void CompletionTest::CompletionTests()
0413 {
0414     const bool oldRemoveTailOnCompletion = KateViewConfig::global()->wordCompletionRemoveTail();
0415     // For these tests, assume we don't swallow the tail on completion.
0416     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, false);
0417 
0418     KateViewConfig::global()->setValue(KateViewConfig::ViInputModeStealKeys, true); // For Ctrl-P, Ctrl-N etc
0419     ensureKateViewVisible(); // KTextEditor::ViewPrivate needs to be visible for the completion widget.
0420     VimCodeCompletionTestModel *testModel = new VimCodeCompletionTestModel(kate_view);
0421 
0422     BeginTest("");
0423     TestPressKey("i\\ctrl-p");
0424     waitForCompletionWidgetToActivate();
0425     TestPressKey("\\return");
0426     FinishTest("completion3");
0427 
0428     BeginTest("");
0429     TestPressKey("i\\ctrl- ");
0430     waitForCompletionWidgetToActivate();
0431     TestPressKey("\\return");
0432     FinishTest("completion1");
0433 
0434     BeginTest("");
0435     TestPressKey("i\\ctrl-n");
0436     waitForCompletionWidgetToActivate();
0437     TestPressKey("\\return");
0438     FinishTest("completion1");
0439 
0440     // Test wraps around from top to bottom.
0441     BeginTest("");
0442     TestPressKey("i\\ctrl- \\ctrl-p");
0443     waitForCompletionWidgetToActivate();
0444     TestPressKey("\\return");
0445     FinishTest("completion3");
0446 
0447     // Test wraps around from bottom to top.
0448     BeginTest("");
0449     TestPressKey("i\\ctrl- \\ctrl-n\\ctrl-n\\ctrl-n");
0450     waitForCompletionWidgetToActivate();
0451     TestPressKey("\\return");
0452     FinishTest("completion1");
0453 
0454     // Test does not re-invoke completion when doing a "." repeat.
0455     BeginTest("");
0456     TestPressKey("i\\ctrl- ");
0457     waitForCompletionWidgetToActivate();
0458     TestPressKey("\\return\\ctrl-c");
0459     kate_view->unregisterCompletionModel(testModel);
0460     FailTestOnInvocationModel *failsTestOnInvocation = new FailTestOnInvocationModel(kate_view);
0461     TestPressKey("gg.");
0462     FinishTest("completion1completion1");
0463     kate_view->unregisterCompletionModel(failsTestOnInvocation);
0464     kate_view->registerCompletionModel(testModel);
0465 
0466     // Test that the full completion is repeated when repeat an insert that uses completion,
0467     // where the completion list was not manually invoked.
0468     BeginTest("");
0469     TestPressKey("i");
0470     // Simulate "automatic" invoking of completion.
0471     kate_view->completionWidget()->userInvokedCompletion();
0472     waitForCompletionWidgetToActivate();
0473     TestPressKey("\\return\\ctrl-cgg.");
0474     FinishTest("completion1completion1");
0475 
0476     clearAllMappings();
0477     // Make sure the "Enter"/ "Return" used when invoking completions is not swallowed before being
0478     // passed to the key mapper.
0479     kate_view->registerCompletionModel(testModel);
0480     vi_global->mappings()->add(Mappings::InsertModeMapping, "cb", "mapped-shouldntbehere", Mappings::Recursive);
0481     BeginTest("");
0482     TestPressKey("ic");
0483     kate_view->userInvokedCompletion();
0484     waitForCompletionWidgetToActivate();
0485     QVERIFY(kate_view->completionWidget()->isCompletionActive());
0486     TestPressKey("\\enterb");
0487     FinishTest("completion1b");
0488     BeginTest("");
0489     TestPressKey("ic");
0490     kate_view->userInvokedCompletion();
0491     waitForCompletionWidgetToActivate();
0492     QVERIFY(kate_view->completionWidget()->isCompletionActive());
0493     TestPressKey("\\returnb");
0494     FinishTest("completion1b");
0495 
0496     // Make sure the completion widget is dismissed on ESC, ctrl-c and ctrl-[.
0497     BeginTest("");
0498     TestPressKey("ic");
0499     kate_view->userInvokedCompletion();
0500     waitForCompletionWidgetToActivate();
0501     QVERIFY(kate_view->completionWidget()->isCompletionActive());
0502     TestPressKey("\\esc");
0503     QVERIFY(!kate_view->completionWidget()->isCompletionActive());
0504     FinishTest("c");
0505     BeginTest("");
0506     TestPressKey("ic");
0507     kate_view->userInvokedCompletion();
0508     waitForCompletionWidgetToActivate();
0509     QVERIFY(kate_view->completionWidget()->isCompletionActive());
0510     TestPressKey("\\ctrl-c");
0511     QVERIFY(!kate_view->completionWidget()->isCompletionActive());
0512     FinishTest("c");
0513     BeginTest("");
0514     TestPressKey("ic");
0515     kate_view->userInvokedCompletion();
0516     waitForCompletionWidgetToActivate();
0517     QVERIFY(kate_view->completionWidget()->isCompletionActive());
0518     TestPressKey("\\ctrl-[");
0519     QVERIFY(!kate_view->completionWidget()->isCompletionActive());
0520     FinishTest("c");
0521     kate_view->unregisterCompletionModel(testModel);
0522 
0523     // Check that the repeat-last-change handles Completions in the same way as Macros do
0524     // i.e. fairly intelligently :)
0525     FakeCodeCompletionTestModel *fakeCodeCompletionModel = new FakeCodeCompletionTestModel(kate_view);
0526     fakeCodeCompletionModel->setRemoveTailOnComplete(true);
0527     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, true);
0528     kate_view->registerCompletionModel(fakeCodeCompletionModel);
0529     clearTrackedDocumentChanges();
0530     clearAllMacros();
0531     BeginTest("funct\nnoa\ncomtail\ncomtail");
0532     fakeCodeCompletionModel->setCompletions({"completionA", "functionwithargs(...)", "noargfunction()"});
0533     fakeCodeCompletionModel->setFailTestOnInvocation(false);
0534     // Record 'a'.
0535     TestPressKey("i\\right\\right\\right\\right\\right\\ctrl- \\enterfirstArg"); // Function with args.
0536     TestPressKey("\\home\\down\\right\\right\\right\\ctrl- \\enter"); // Function no args.
0537     fakeCodeCompletionModel->setRemoveTailOnComplete(true);
0538     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, true);
0539     TestPressKey("\\home\\down\\right\\right\\right\\ctrl- \\enter"); // Cut off tail.
0540     fakeCodeCompletionModel->setRemoveTailOnComplete(false);
0541     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, false);
0542     TestPressKey("\\home\\down\\right\\right\\right\\ctrl- \\enter\\ctrl-c"); // Don't cut off tail.
0543     fakeCodeCompletionModel->setRemoveTailOnComplete(true);
0544     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, true);
0545     FinishTest("functionwithargs(firstArg)\nnoargfunction()\ncompletionA\ncompletionAtail");
0546 
0547     // Replay.
0548     fakeCodeCompletionModel->setFailTestOnInvocation(true);
0549     kate_document->setText("funct\nnoa\ncomtail\ncomtail");
0550     clearTrackedDocumentChanges();
0551     TestPressKey("gg.");
0552     FinishTest("functionwithargs(firstArg)\nnoargfunction()\ncompletionA\ncompletionAtail");
0553 
0554     // Clear our log of completions for each change.
0555     BeginTest("");
0556     fakeCodeCompletionModel->setCompletions({"completionA"});
0557     fakeCodeCompletionModel->setFailTestOnInvocation(false);
0558     TestPressKey("ciw\\ctrl- \\enter\\ctrl-c");
0559     fakeCodeCompletionModel->setCompletions({"completionB"});
0560     TestPressKey("ciw\\ctrl- \\enter\\ctrl-c");
0561     fakeCodeCompletionModel->setFailTestOnInvocation(true);
0562     TestPressKey(".");
0563     FinishTest("completionB");
0564 
0565     kate_view->unregisterCompletionModel(fakeCodeCompletionModel);
0566     delete fakeCodeCompletionModel;
0567     KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, oldRemoveTailOnCompletion);
0568 
0569     // Hide the kate_view for subsequent tests.
0570     kate_view->hide();
0571     mainWindow->hide();
0572 }
0573 
0574 void CompletionTest::waitForCompletionWidgetToActivate()
0575 {
0576     BaseTest::waitForCompletionWidgetToActivate(kate_view);
0577 }
0578 
0579 void CompletionTest::clearTrackedDocumentChanges()
0580 {
0581     m_docChanges.clear();
0582 }
0583 
0584 // END: CompletionTest
0585 
0586 #include "moc_completion.cpp"