File indexing completed on 2024-04-21 03:57:12
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2010 Milian Wolff <mail@milianw.de> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kateview_test.h" 0009 #include "moc_kateview_test.cpp" 0010 0011 #include <katebuffer.h> 0012 #include <kateconfig.h> 0013 #include <katedocument.h> 0014 #include <kateglobal.h> 0015 #include <kateview.h> 0016 #include <kateviewinternal.h> 0017 #include <ktexteditor/message.h> 0018 #include <ktexteditor/movingcursor.h> 0019 0020 #include <QScrollBar> 0021 #include <QTemporaryFile> 0022 #include <QtTestWidgets> 0023 0024 #define testNewRow() (QTest::newRow(QStringLiteral("line %1").arg(__LINE__).toLatin1().data())) 0025 0026 using namespace KTextEditor; 0027 0028 QTEST_MAIN(KateViewTest) 0029 0030 KateViewTest::KateViewTest() 0031 : QObject() 0032 { 0033 KTextEditor::EditorPrivate::enableUnitTestMode(); 0034 } 0035 0036 KateViewTest::~KateViewTest() 0037 { 0038 } 0039 0040 void KateViewTest::testCoordinatesToCursor() 0041 { 0042 KTextEditor::DocumentPrivate doc(false, false); 0043 doc.setText(QStringLiteral("Hi World!\nHi\n")); 0044 0045 KTextEditor::View *view1 = static_cast<KTextEditor::View *>(doc.createView(nullptr)); 0046 view1->resize(400, 300); 0047 view1->show(); 0048 0049 QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(KTextEditor::Cursor(0, 2))), KTextEditor::Cursor(0, 2)); 0050 QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(KTextEditor::Cursor(1, 1))), KTextEditor::Cursor(1, 1)); 0051 // behind end of line should give an invalid cursor 0052 QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(KTextEditor::Cursor(1, 5))), KTextEditor::Cursor::invalid()); 0053 QCOMPARE(view1->cursorToCoordinate(KTextEditor::Cursor(3, 1)), QPoint(-1, -1)); 0054 0055 // check consistency between cursorToCoordinate(view->cursorPosition() and cursorPositionCoordinates() 0056 // random position 0057 view1->setCursorPosition(KTextEditor::Cursor(0, 3)); 0058 QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(view1->cursorPosition())), KTextEditor::Cursor(0, 3)); 0059 QCOMPARE(view1->coordinatesToCursor(view1->cursorPositionCoordinates()), KTextEditor::Cursor(0, 3)); 0060 // end of line 0061 view1->setCursorPosition(KTextEditor::Cursor(0, 9)); 0062 QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(KTextEditor::Cursor(0, 9))), KTextEditor::Cursor(0, 9)); 0063 QCOMPARE(view1->coordinatesToCursor(view1->cursorPositionCoordinates()), KTextEditor::Cursor(0, 9)); 0064 // empty line 0065 view1->setCursorPosition(KTextEditor::Cursor(2, 0)); 0066 QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(KTextEditor::Cursor(2, 0))), KTextEditor::Cursor(2, 0)); 0067 QCOMPARE(view1->coordinatesToCursor(view1->cursorPositionCoordinates()), KTextEditor::Cursor(2, 0)); 0068 0069 // same test again, but with message widget on top visible 0070 KTextEditor::Message *message = new KTextEditor::Message(QStringLiteral("Jo World!"), KTextEditor::Message::Information); 0071 doc.postMessage(message); 0072 0073 // wait 500ms until show animation is finished, so the message widget is visible 0074 QTest::qWait(500); 0075 0076 QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(KTextEditor::Cursor(0, 2))), KTextEditor::Cursor(0, 2)); 0077 QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(KTextEditor::Cursor(1, 1))), KTextEditor::Cursor(1, 1)); 0078 // behind end of line should give an invalid cursor 0079 QCOMPARE(view1->coordinatesToCursor(view1->cursorToCoordinate(KTextEditor::Cursor(1, 5))), KTextEditor::Cursor::invalid()); 0080 QCOMPARE(view1->cursorToCoordinate(KTextEditor::Cursor(3, 1)), QPoint(-1, -1)); 0081 } 0082 0083 void KateViewTest::testCursorToCoordinates() 0084 { 0085 KTextEditor::DocumentPrivate doc(false, false); 0086 doc.setText(QStringLiteral("int a;")); 0087 0088 KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); 0089 view->config()->setDynWordWrap(true); 0090 view->show(); 0091 0092 // don't crash, see https://bugs.kde.org/show_bug.cgi?id=337863 0093 view->cursorToCoordinate(Cursor(0, 0)); 0094 view->cursorToCoordinate(Cursor(1, 0)); 0095 view->cursorToCoordinate(Cursor(-1, 0)); 0096 } 0097 0098 void KateViewTest::testReloadMultipleViews() 0099 { 0100 QTemporaryFile file(QStringLiteral("XXXXXX.cpp")); 0101 file.open(); 0102 QTextStream stream(&file); 0103 const QString line = QStringLiteral("const char* foo = \"asdf\"\n"); 0104 for (int i = 0; i < 200; ++i) { 0105 stream << line; 0106 } 0107 stream << Qt::flush; 0108 file.close(); 0109 0110 KTextEditor::DocumentPrivate doc; 0111 QVERIFY(doc.openUrl(QUrl::fromLocalFile(file.fileName()))); 0112 QCOMPARE(doc.highlightingMode(), QStringLiteral("C++")); 0113 0114 KTextEditor::ViewPrivate *view1 = new KTextEditor::ViewPrivate(&doc, nullptr); 0115 KTextEditor::ViewPrivate *view2 = new KTextEditor::ViewPrivate(&doc, nullptr); 0116 view1->show(); 0117 view2->show(); 0118 QCOMPARE(doc.views().count(), 2); 0119 0120 QVERIFY(doc.documentReload()); 0121 } 0122 0123 void KateViewTest::testTabCursorOnReload() 0124 { 0125 // testcase for https://bugs.kde.org/show_bug.cgi?id=258480 0126 QTemporaryFile file(QStringLiteral("XXXXXX.cpp")); 0127 file.open(); 0128 QTextStream stream(&file); 0129 stream << "\tfoo\n"; 0130 file.close(); 0131 0132 KTextEditor::DocumentPrivate doc; 0133 QVERIFY(doc.openUrl(QUrl::fromLocalFile(file.fileName()))); 0134 0135 KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); 0136 const KTextEditor::Cursor cursor(0, 4); 0137 view->setCursorPosition(cursor); 0138 QCOMPARE(view->cursorPosition(), cursor); 0139 QVERIFY(doc.documentReload()); 0140 QCOMPARE(view->cursorPosition(), cursor); 0141 } 0142 0143 void KateViewTest::testLowerCaseBlockSelection() 0144 { 0145 // testcase for https://bugs.kde.org/show_bug.cgi?id=258480 0146 KTextEditor::DocumentPrivate doc; 0147 doc.setText(QStringLiteral("nY\nnYY\n")); 0148 0149 KTextEditor::ViewPrivate *view1 = new KTextEditor::ViewPrivate(&doc, nullptr); 0150 view1->setBlockSelection(true); 0151 view1->setSelection(Range(0, 1, 1, 3)); 0152 view1->lowercase(); 0153 0154 QCOMPARE(doc.text(), QStringLiteral("ny\nnyy\n")); 0155 } 0156 0157 namespace 0158 { 0159 QWidget *findViewInternal(KTextEditor::View *view) 0160 { 0161 for (QObject *child : view->children()) { 0162 if (child->metaObject()->className() == QByteArrayLiteral("KateViewInternal")) { 0163 return qobject_cast<QWidget *>(child); 0164 } 0165 } 0166 return nullptr; 0167 } 0168 } 0169 0170 void KateViewTest::testSelection() 0171 { 0172 // see also: https://bugs.kde.org/show_bug.cgi?id=277422 0173 // wrong behavior before: 0174 // Open file with text 0175 // click at end of some line (A) and drag to right, i.e. without selecting anything 0176 // click somewhere else (B) 0177 // shift click to another place (C) 0178 // => expected: selection from B to C 0179 // => actual: selection from A to C 0180 0181 QTemporaryFile file(QStringLiteral("XXXXXX.txt")); 0182 file.open(); 0183 QTextStream stream(&file); 0184 stream << "A\n" 0185 << "B\n" 0186 << "C"; 0187 stream << Qt::flush; 0188 file.close(); 0189 0190 KTextEditor::DocumentPrivate doc; 0191 QVERIFY(doc.openUrl(QUrl::fromLocalFile(file.fileName()))); 0192 0193 KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); 0194 view->resize(100, 200); 0195 view->show(); 0196 0197 auto *internalView = findViewInternal(view); 0198 QVERIFY(internalView); 0199 0200 const QPoint afterA = view->cursorToCoordinate(Cursor(0, 1)); 0201 const QPoint afterB = view->cursorToCoordinate(Cursor(1, 1)); 0202 const QPoint afterC = view->cursorToCoordinate(Cursor(2, 1)); 0203 0204 // click after A 0205 auto me = QMouseEvent(QEvent::MouseButtonPress, afterA, internalView->mapToGlobal(afterA), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); 0206 QCoreApplication::sendEvent(internalView, &me); 0207 0208 auto me1 = QMouseEvent(QEvent::MouseButtonRelease, afterA, internalView->mapToGlobal(afterA), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); 0209 QCoreApplication::sendEvent(internalView, &me1); 0210 QCOMPARE(view->cursorPosition(), Cursor(0, 1)); 0211 0212 // drag to right 0213 auto me2 = QMouseEvent(QEvent::MouseButtonPress, afterA, internalView->mapToGlobal(afterA), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); 0214 QCoreApplication::sendEvent(internalView, &me2); 0215 0216 auto me3 = QMouseEvent(QEvent::MouseMove, 0217 afterA + QPoint(50, 0), 0218 internalView->mapToGlobal(afterA + QPoint(50, 0)), 0219 Qt::LeftButton, 0220 Qt::LeftButton, 0221 Qt::NoModifier); 0222 QCoreApplication::sendEvent(internalView, &me3); 0223 0224 auto me4 = QMouseEvent(QEvent::MouseButtonRelease, 0225 afterA + QPoint(50, 0), 0226 internalView->mapToGlobal(afterA + QPoint(50, 0)), 0227 Qt::LeftButton, 0228 Qt::LeftButton, 0229 Qt::NoModifier); 0230 QCoreApplication::sendEvent(internalView, &me4); 0231 0232 QCOMPARE(view->cursorPosition(), Cursor(0, 1)); 0233 QVERIFY(!view->selection()); 0234 0235 // click after C 0236 auto me5 = QMouseEvent(QEvent::MouseButtonPress, afterC, internalView->mapToGlobal(afterC), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); 0237 QCoreApplication::sendEvent(internalView, &me5); 0238 0239 auto me6 = QMouseEvent(QEvent::MouseButtonRelease, afterC, internalView->mapToGlobal(afterC), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); 0240 QCoreApplication::sendEvent(internalView, &me6); 0241 0242 QCOMPARE(view->cursorPosition(), Cursor(2, 1)); 0243 // shift+click after B 0244 auto me7 = QMouseEvent(QEvent::MouseButtonPress, afterB, internalView->mapToGlobal(afterB), Qt::LeftButton, Qt::LeftButton, Qt::ShiftModifier); 0245 QCoreApplication::sendEvent(internalView, &me7); 0246 0247 auto me8 = QMouseEvent(QEvent::MouseButtonRelease, afterB, internalView->mapToGlobal(afterB), Qt::LeftButton, Qt::LeftButton, Qt::ShiftModifier); 0248 QCoreApplication::sendEvent(internalView, &me8); 0249 0250 QCOMPARE(view->cursorPosition(), Cursor(1, 1)); 0251 QCOMPARE(view->selectionRange(), Range(1, 1, 2, 1)); 0252 } 0253 0254 void KateViewTest::testDeselectByArrowKeys_data() 0255 { 0256 QTest::addColumn<QString>("text"); 0257 0258 testNewRow() << QStringLiteral("foobarhaz"); 0259 testNewRow() << QStringLiteral("كلسشمن يتبكسب"); // We all win, translates Google 0260 } 0261 0262 void KateViewTest::testDeselectByArrowKeys() 0263 { 0264 QFETCH(QString, text); 0265 0266 KTextEditor::DocumentPrivate doc; 0267 doc.setText(text); 0268 KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); 0269 KTextEditor::Cursor cur1(0, 3); // Start of bar: foo|barhaz 0270 KTextEditor::Cursor cur2(0, 6); // End of bar: foobar|haz 0271 KTextEditor::Cursor curDelta(0, 1); 0272 Range range(cur1, cur2); // Select "bar" 0273 0274 // RTL drives me nuts! 0275 KTextEditor::Cursor help; 0276 if (text.isRightToLeft()) { 0277 help = cur1; 0278 cur1 = cur2; 0279 cur2 = help; 0280 } 0281 0282 view->setSelection(range); 0283 view->setCursorPositionInternal(cur1); 0284 view->cursorLeft(); 0285 QCOMPARE(view->cursorPosition(), cur1); // Be at begin: foo|barhaz 0286 QCOMPARE(view->selection(), false); 0287 0288 view->setSelection(range); 0289 view->setCursorPositionInternal(cur1); 0290 view->cursorRight(); 0291 QCOMPARE(view->cursorPosition(), cur2); // Be at end: foobar|haz 0292 QCOMPARE(view->selection(), false); 0293 0294 view->config()->setValue(KateViewConfig::PersistentSelection, true); 0295 0296 view->setSelection(range); 0297 view->setCursorPositionInternal(cur1); 0298 view->cursorLeft(); 0299 // RTL drives me nuts! 0300 help = text.isRightToLeft() ? (cur1 + curDelta) : (cur1 - curDelta); 0301 QCOMPARE(view->cursorPosition(), help); // Be one left: fo|obarhaz 0302 QCOMPARE(view->selection(), true); 0303 0304 view->setSelection(range); 0305 view->setCursorPositionInternal(cur1); 0306 view->cursorRight(); 0307 // RTL drives me nuts! 0308 help = text.isRightToLeft() ? (cur1 - curDelta) : (cur1 + curDelta); 0309 QCOMPARE(view->cursorPosition(), help); // Be one right: foob|arhaz 0310 QCOMPARE(view->selection(), true); 0311 } 0312 0313 void KateViewTest::testKillline() 0314 { 0315 KTextEditor::DocumentPrivate doc; 0316 doc.insertLines(0, {QStringLiteral("foo"), QStringLiteral("bar"), QStringLiteral("baz")}); 0317 0318 KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); 0319 0320 view->setCursorPositionInternal(KTextEditor::Cursor(1, 2)); 0321 view->killLine(); 0322 0323 QCOMPARE(doc.text(), QLatin1String("foo\nbaz\n")); 0324 0325 doc.clear(); 0326 QVERIFY(doc.isEmpty()); 0327 0328 doc.insertLines(0, {QStringLiteral("foo"), QStringLiteral("bar"), QStringLiteral("baz"), QStringLiteral("xxx")}); 0329 0330 view->setCursorPositionInternal(KTextEditor::Cursor(1, 2)); 0331 view->shiftDown(); 0332 view->killLine(); 0333 0334 QCOMPARE(doc.text(), QLatin1String("foo\nxxx\n")); 0335 } 0336 0337 void KateViewTest::testKeyDeleteBlockSelection() 0338 { 0339 KTextEditor::DocumentPrivate doc; 0340 doc.insertLines(0, {QStringLiteral("foo"), QStringLiteral("12345"), QStringLiteral("bar"), QStringLiteral("baz")}); 0341 0342 KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); 0343 0344 view->setBlockSelection(true); 0345 view->setSelection(Range(0, 1, 2, 1)); 0346 QCOMPARE(view->selectionRange(), Range(0, 1, 2, 1)); 0347 0348 view->keyDelete(); 0349 QCOMPARE(doc.text(), QLatin1String("fo\n1345\nbr\nbaz\n")); 0350 view->keyDelete(); 0351 QCOMPARE(doc.text(), QLatin1String("f\n145\nb\nbaz\n")); 0352 view->keyDelete(); 0353 QCOMPARE(doc.text(), QLatin1String("f\n15\nb\nbaz\n")); 0354 view->keyDelete(); 0355 QCOMPARE(doc.text(), QLatin1String("f\n1\nb\nbaz\n")); 0356 view->keyDelete(); 0357 QCOMPARE(doc.text(), QLatin1String("f\n1\nb\nbaz\n")); 0358 0359 doc.clear(); 0360 QVERIFY(doc.isEmpty()); 0361 0362 doc.insertLines(0, {QStringLiteral("foo"), QStringLiteral("12345"), QStringLiteral("bar"), QStringLiteral("baz")}); 0363 0364 view->setSelection(Range(0, 1, 2, 3)); 0365 0366 view->keyDelete(); 0367 QCOMPARE(doc.text(), QLatin1String("f\n145\nb\nbaz\n")); 0368 } 0369 0370 void KateViewTest::testScrollPastEndOfDocument() 0371 { 0372 // bug 306745 0373 KTextEditor::DocumentPrivate doc; 0374 doc.setText( 0375 QStringLiteral("0000000000\n" 0376 "1111111111\n" 0377 "2222222222\n" 0378 "3333333333\n" 0379 "4444444444")); 0380 QCOMPARE(doc.lines(), 5); 0381 0382 KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); 0383 view->setCursorPosition({3, 5}); 0384 view->resize(400, 300); 0385 view->show(); 0386 0387 // enable "[x] Scroll past end of document" 0388 view->config()->setValue(KateViewConfig::ScrollPastEnd, true); 0389 QCOMPARE(view->config()->scrollPastEnd(), true); 0390 0391 // disable dynamic word wrap 0392 view->config()->setDynWordWrap(false); 0393 QCOMPARE(view->config()->dynWordWrap(), false); 0394 0395 view->scrollDown(); 0396 view->scrollDown(); 0397 view->scrollDown(); 0398 // at this point, only lines 3333333333 and 4444444444 are visible. 0399 view->down(); 0400 QCOMPARE(view->cursorPosition(), KTextEditor::Cursor(4, 5)); 0401 // verify, that only lines 3333333333 and 4444444444 are still visible. 0402 QCOMPARE(view->firstDisplayedLineInternal(KTextEditor::View::RealLine), 3); 0403 } 0404 0405 void KateViewTest::testFoldFirstLine() 0406 { 0407 QTemporaryFile file(QStringLiteral("XXXXXX.cpp")); 0408 file.open(); 0409 QTextStream stream(&file); 0410 stream << "/**\n" 0411 << " * foo\n" 0412 << " */\n" 0413 << "\n" 0414 << "int main() {}\n"; 0415 file.close(); 0416 0417 KTextEditor::DocumentPrivate doc; 0418 QVERIFY(doc.openUrl(QUrl::fromLocalFile(file.fileName()))); 0419 QCOMPARE(doc.highlightingMode(), QStringLiteral("C++")); 0420 0421 KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); 0422 view->config()->setValue(KateViewConfig::FoldFirstLine, false); 0423 view->setCursorPosition({4, 0}); 0424 0425 // initially, nothing is folded 0426 QVERIFY(view->textFolding().isLineVisible(1)); 0427 0428 // now change the config, and expect the header to be folded 0429 view->config()->setValue(KateViewConfig::FoldFirstLine, true); 0430 qint64 foldedRangeId = 0; 0431 QVERIFY(!view->textFolding().isLineVisible(1, &foldedRangeId)); 0432 0433 // now unfold the range 0434 QVERIFY(view->textFolding().unfoldRange(foldedRangeId)); 0435 QVERIFY(view->textFolding().isLineVisible(1)); 0436 0437 // and save the file, we do not expect the folding to change then 0438 doc.setModified(true); 0439 doc.saveFile(); 0440 QVERIFY(view->textFolding().isLineVisible(1)); 0441 0442 // now reload the document, nothing should change 0443 doc.setModified(false); 0444 QVERIFY(doc.documentReload()); 0445 QVERIFY(view->textFolding().isLineVisible(1)); 0446 } 0447 0448 // test for bug https://bugs.kde.org/374163 0449 void KateViewTest::testDragAndDrop() 0450 { 0451 // mouse move only on X11 0452 if (qApp->platformName() != QLatin1String("xcb")) { 0453 QSKIP("mouse moving only on X11"); 0454 } 0455 0456 KTextEditor::DocumentPrivate doc(false, false); 0457 doc.setText( 0458 QStringLiteral("line0\n" 0459 "line1\n" 0460 "line2\n" 0461 "\n" 0462 "line4")); 0463 0464 KTextEditor::View *view = static_cast<KTextEditor::View *>(doc.createView(nullptr)); 0465 view->show(); 0466 view->resize(400, 300); 0467 0468 QWidget *internalView = findViewInternal(view); 0469 QVERIFY(internalView); 0470 0471 // select "line1\n" 0472 view->setSelection(Range(1, 0, 2, 0)); 0473 QCOMPARE(view->selectionRange(), Range(1, 0, 2, 0)); 0474 0475 (void)QTest::qWaitForWindowExposed(view); 0476 QTest::qWait(100); // For whatever reason needed 0477 0478 const QPoint startDragPos = internalView->mapFrom(view, view->cursorToCoordinate(KTextEditor::Cursor(1, 2))); 0479 const QPoint endDragPos = internalView->mapFrom(view, view->cursorToCoordinate(KTextEditor::Cursor(3, 0))); 0480 const QPoint gStartDragPos = internalView->mapToGlobal(startDragPos); 0481 const QPoint gEndDragPos = internalView->mapToGlobal(endDragPos); 0482 0483 // now drag and drop selected text to Cursor(3, 0) 0484 QMouseEvent pressEvent(QEvent::MouseButtonPress, startDragPos, gStartDragPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); 0485 QCoreApplication::sendEvent(internalView, &pressEvent); 0486 0487 // ugly workaround: Drag & Drop has own blocking event queue. Therefore, we need a single-shot timer to 0488 // break out of the blocking event queue, see (*) 0489 QTimer::singleShot(50, [&]() { 0490 QMouseEvent moveEvent(QEvent::MouseMove, endDragPos + QPoint(5, 0), gEndDragPos + QPoint(5, 0), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); 0491 QMouseEvent releaseEvent(QEvent::MouseButtonRelease, endDragPos, gEndDragPos, Qt::LeftButton, Qt::NoButton, Qt::NoModifier); 0492 QCoreApplication::sendEvent(internalView, &moveEvent); 0493 QCoreApplication::sendEvent(internalView, &releaseEvent); 0494 }); 0495 0496 // (*) this somehow blocks... 0497 QMouseEvent moveEvent1(QEvent::MouseMove, endDragPos + QPoint(10, 0), gEndDragPos + QPoint(10, 0), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); 0498 QCoreApplication::sendEvent(internalView, &moveEvent1); 0499 0500 QTest::qWait(100); 0501 0502 // final tests of dragged text 0503 QCOMPARE(doc.text(), 0504 QStringLiteral("line0\n" 0505 "line2\n" 0506 "line1\n" 0507 "\n" 0508 "line4")); 0509 0510 QCOMPARE(view->cursorPosition(), KTextEditor::Cursor(3, 0)); 0511 QCOMPARE(view->selectionRange(), Range(2, 0, 3, 0)); 0512 } 0513 0514 // test for bug https://bugs.kde.org/402594 0515 void KateViewTest::testGotoMatchingBracket() 0516 { 0517 KTextEditor::DocumentPrivate doc(false, false); 0518 doc.setText(QStringLiteral("foo(bar)baz[]")); 0519 // 0123456789 0520 0521 KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); 0522 const KTextEditor::Cursor cursor1(0, 3); // Starting point on open ( 0523 const KTextEditor::Cursor cursor2(0, 8); // Insert Mode differ slightly from... 0524 const KTextEditor::Cursor cursor3(0, 7); // Overwrite Mode 0525 const KTextEditor::Cursor cursor4(0, 11); // Test adjacents brackets (start at [) 0526 0527 doc.config()->setOvr(false); // Insert Mode 0528 0529 view->setCursorPosition(cursor1); 0530 view->toMatchingBracket(); 0531 QCOMPARE(view->cursorPosition(), cursor2); 0532 view->toMatchingBracket(); 0533 QCOMPARE(view->cursorPosition(), cursor1); 0534 0535 // Currently has it in Insert Mode also to work when the cursor is placed inside the parentheses 0536 view->setCursorPosition(cursor1 + KTextEditor::Cursor(0, 1)); 0537 view->toMatchingBracket(); 0538 QCOMPARE(view->cursorPosition(), cursor2); 0539 view->setCursorPosition(cursor2 + KTextEditor::Cursor(0, -1)); 0540 view->toMatchingBracket(); 0541 QCOMPARE(view->cursorPosition(), cursor1); 0542 0543 view->setCursorPosition(cursor4 + KTextEditor::Cursor(0, 1)); 0544 view->toMatchingBracket(); 0545 QCOMPARE(view->cursorPosition(), cursor4); 0546 view->toMatchingBracket(); 0547 view->setCursorPosition(view->cursorPosition() + KTextEditor::Cursor(0, 1)); 0548 view->toMatchingBracket(); 0549 QCOMPARE(view->cursorPosition(), cursor4); 0550 0551 doc.config()->setOvr(true); // Overwrite Mode 0552 0553 view->setCursorPosition(cursor1); 0554 view->toMatchingBracket(); 0555 QCOMPARE(view->cursorPosition(), cursor3); 0556 view->toMatchingBracket(); 0557 QCOMPARE(view->cursorPosition(), cursor1); 0558 } 0559 0560 void KateViewTest::testFindSelected() 0561 { 0562 KTextEditor::DocumentPrivate doc(false, false); 0563 doc.setText( 0564 QStringLiteral("foo\n" 0565 "bar\n" 0566 "foo\n" 0567 "bar\n")); 0568 // 0123456789 0569 0570 KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); 0571 const KTextEditor::Cursor cursor1(0, 0); // before 1. foo 0572 const KTextEditor::Cursor cursor2(0, 3); // after 1. foo 0573 const KTextEditor::Range range1({0, 0}, {0, 3}); // 1. foo range 0574 const KTextEditor::Cursor cursor3(2, 3); // after 2. foo 0575 const KTextEditor::Range range2({2, 0}, {2, 3}); // 2. foo range 0576 0577 QVERIFY(!view->selection()); 0578 QCOMPARE(view->cursorPosition(), cursor1); 0579 0580 // first time we call this without a selection, just select the word under the cursor 0581 view->findSelectedForwards(); 0582 QVERIFY(view->selection()); 0583 QCOMPARE(view->selectionRange(), range1); 0584 // cursor jumps to the end of the word 0585 QCOMPARE(view->cursorPosition(), cursor2); 0586 0587 // second time we call this it actually jumps to the next occurance 0588 view->findSelectedForwards(); 0589 QCOMPARE(view->selectionRange(), range2); 0590 QCOMPARE(view->cursorPosition(), cursor3); 0591 0592 // wrap around 0593 view->findSelectedForwards(); 0594 QCOMPARE(view->selectionRange(), range1); 0595 QCOMPARE(view->cursorPosition(), cursor2); 0596 0597 // search backwards, wrap again 0598 view->findSelectedBackwards(); 0599 QCOMPARE(view->selectionRange(), range2); 0600 QCOMPARE(view->cursorPosition(), cursor3); 0601 } 0602 0603 void KateViewTest::testTransposeWord() 0604 { 0605 KTextEditor::DocumentPrivate doc(false, false); 0606 doc.setText( 0607 QStringLiteral("swaps forward\n" 0608 "wordAbove\n" 0609 "wordLeft (_skips]Spaces.__And___}Sym_bols)))) wordRight)\n" 0610 "wordBelow anotherWord yetAnotherWord\n")); 0611 0612 KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); 0613 const KTextEditor::Cursor swaps(0, 2); // swa|ps 0614 const KTextEditor::Cursor wordAbove(1, 4); // wordA|bove 0615 const KTextEditor::Cursor wordLeft(2, 1); // wo|rdLeft 0616 const KTextEditor::Cursor skips(2, 10); // |_skips 0617 const KTextEditor::Cursor And(2, 27); // __A|nd___ 0618 const KTextEditor::Cursor wordBelow(3, 0); // w|ordBelow 0619 0620 view->setCursorPosition(swaps); 0621 QCOMPARE(view->cursorPosition(), swaps); 0622 QCOMPARE(view->doc()->characterAt(view->cursorPosition()), QLatin1Char('a')); 0623 view->transposeWord(); 0624 QCOMPARE(view->cursorPosition(), swaps + KTextEditor::Cursor(0, 8)); // " forward" has 8 characters 0625 QCOMPARE(view->doc()->characterAt(view->cursorPosition()), QLatin1Char('a')); // retain relative position inside the word 0626 0627 view->transposeWord(); 0628 QCOMPARE(view->cursorPosition(), swaps); // when the word is already last in line, swap backwards instead 0629 0630 view->setCursorPosition(wordAbove); 0631 view->transposeWord(); 0632 QCOMPARE(view->cursorPosition(), wordAbove); // when there is no other word in the line, do nothing 0633 QCOMPARE(view->doc()->characterAt(view->cursorPosition()), QLatin1Char('A')); 0634 0635 view->setCursorPosition(wordLeft); 0636 view->transposeWord(); 0637 QCOMPARE(view->cursorPosition(), wordLeft); // when next word is invalid (made up of only symbols, in this case "(") do nothing 0638 QCOMPARE(view->doc()->characterAt(view->cursorPosition()), QLatin1Char('o')); 0639 0640 view->setCursorPosition(skips); 0641 view->transposeWord(); 0642 QCOMPARE(view->cursorPosition(), skips + KTextEditor::Cursor(0, 7)); // transpose word beginning with a symbol 0643 QCOMPARE(view->doc()->characterAt(view->cursorPosition()), QLatin1Char('_')); 0644 0645 view->setCursorPosition(And); 0646 view->transposeWord(); 0647 // skip multiple symbols if there is no space between current word and the symbols (in contrast to the case of wordLeft). 0648 // Can be useful for e.g. transposing function arguments 0649 QCOMPARE(view->cursorPosition(), And + KTextEditor::Cursor(0, 9)); 0650 0651 view->transposeWord(); 0652 QCOMPARE(view->cursorPosition(), And + KTextEditor::Cursor(0, 9 + 17)); // next "word" is invalid as it is only a single "(", so do nothing 0653 0654 view->setCursorPosition(wordBelow); 0655 view->transposeWord(); 0656 QCOMPARE(view->cursorPosition(), wordBelow + KTextEditor::Cursor(0, 12)); 0657 view->transposeWord(); 0658 QCOMPARE(view->cursorPosition(), wordBelow + KTextEditor::Cursor(0, 12 + 15)); 0659 view->transposeWord(); 0660 QCOMPARE(view->cursorPosition(), wordBelow + KTextEditor::Cursor(0, 12)); // end of line, transpose backwards instead 0661 } 0662 0663 void KateViewTest::testFindMatchingFoldingMarker() 0664 { 0665 KTextEditor::DocumentPrivate doc(false, false); 0666 doc.setMode(QStringLiteral("Bash")); 0667 doc.setHighlightingMode(QStringLiteral("Bash")); 0668 0669 doc.setText( 0670 QStringLiteral("for i in 0 1 2; do\n" 0671 " if [[ i -lt 1 ]]; then echo $i; fi\n" 0672 " if [[ i -eq 2 ]]; then\n" 0673 " echo 'hello :)'\n" 0674 " fi\n" 0675 "done\n")); 0676 0677 KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); 0678 KateViewInternal *viewInternal = view->getViewInternal(); 0679 0680 const auto ifvalue = doc.buffer().computeFoldings(1)[0].foldingRegion; 0681 const auto dovalue = doc.buffer().computeFoldings(0)[0].foldingRegion; 0682 0683 const KTextEditor::Range firstDo(0, 16, 0, 18); 0684 const KTextEditor::Range firstDoMatching(5, 0, 5, 4); 0685 const KTextEditor::Range firstIf(1, 4, 1, 6); 0686 const KTextEditor::Range firstIfMatching(1, 36, 1, 38); 0687 0688 // first test the do folding marker with cursor above first position of the word. 0689 QCOMPARE(viewInternal->findMatchingFoldingMarker(firstDo.start(), dovalue, 2000), firstDoMatching); 0690 // with cursor above last position of the word 0691 QCOMPARE(viewInternal->findMatchingFoldingMarker(firstDo.end(), dovalue, 2000), firstDoMatching); 0692 // now to test the maxLines param. 0693 QCOMPARE(viewInternal->findMatchingFoldingMarker(firstDo.start(), dovalue, 2), KTextEditor::Range::invalid()); 0694 0695 // it must work from end folding to start folding too. 0696 QCOMPARE(viewInternal->findMatchingFoldingMarker(firstDoMatching.start(), dovalue.sibling(), 2000), firstDo); 0697 QCOMPARE(viewInternal->findMatchingFoldingMarker(firstDoMatching.start(), dovalue.sibling(), 2), KTextEditor::Range::invalid()); 0698 0699 // folding in the same line 0700 QCOMPARE(viewInternal->findMatchingFoldingMarker(firstIf.start(), ifvalue, 2000), firstIfMatching); 0701 QCOMPARE(viewInternal->findMatchingFoldingMarker(firstIfMatching.start(), ifvalue.sibling(), 2000), firstIf); 0702 } 0703 0704 void KateViewTest::testUpdateFoldingMarkersHighlighting() 0705 { 0706 KTextEditor::DocumentPrivate doc(false, false); 0707 doc.setMode(QStringLiteral("Bash")); 0708 doc.setHighlightingMode(QStringLiteral("Bash")); 0709 0710 doc.setText( 0711 QStringLiteral("for i in 0 1 2; do\n" 0712 " if [[ i -lt 1 ]]; then echo $i; fi\n" 0713 " if [[ i -eq 2 ]]; then\n" 0714 " echo 'hello :)'\n" 0715 " fi\n" 0716 "done\n")); 0717 0718 KTextEditor::ViewPrivate *view = new KTextEditor::ViewPrivate(&doc, nullptr); 0719 KateViewInternal *viewInternal = view->getViewInternal(); 0720 0721 const KTextEditor::Cursor positionWithoutMarker(0, 4); 0722 const KTextEditor::Range firstDo(0, 16, 0, 18); 0723 const KTextEditor::Range firstDoMatching(5, 0, 5, 4); 0724 0725 KTextEditor::MovingRange *foldingMarkerStart = viewInternal->m_fmStart.get(); 0726 KTextEditor::MovingRange *foldingMarkerEnd = viewInternal->m_fmEnd.get(); 0727 0728 // If the cursor is not above any folding marker, the highlighting range is invalid 0729 view->editSetCursor(positionWithoutMarker); 0730 viewInternal->updateFoldingMarkersHighlighting(); 0731 QCOMPARE(foldingMarkerStart->toRange(), KTextEditor::Range::invalid()); 0732 QCOMPARE(foldingMarkerEnd->toRange(), KTextEditor::Range::invalid()); 0733 0734 // If the cursor is above a opening folding marker, the highlighting range is the range of both opening and end folding markers words 0735 view->editSetCursor(firstDo.start()); 0736 viewInternal->updateFoldingMarkersHighlighting(); 0737 QCOMPARE(foldingMarkerStart->toRange(), firstDo); 0738 QCOMPARE(foldingMarkerEnd->toRange(), firstDoMatching); 0739 0740 // If the cursor is above a ending folding marker, then same rule above 0741 view->editSetCursor(firstDoMatching.start()); 0742 viewInternal->updateFoldingMarkersHighlighting(); 0743 QCOMPARE(foldingMarkerStart->toRange(), firstDo); 0744 QCOMPARE(foldingMarkerEnd->toRange(), firstDoMatching); 0745 } 0746 0747 // kate: indent-mode cstyle; indent-width 4; replace-tabs on;