File indexing completed on 2024-05-12 15:45:11
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"