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