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

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