File indexing completed on 2024-04-28 03:57: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 "view.h"
0009 #include <QClipboard>
0010 #include <QFontDatabase>
0011 #include <inputmode/kateviinputmode.h>
0012 #include <katebuffer.h>
0013 #include <kateconfig.h>
0014 #include <katedocument.h>
0015 #include <kateview.h>
0016 
0017 #include <QMainWindow>
0018 #include <QTest>
0019 
0020 using namespace KTextEditor;
0021 
0022 QTEST_MAIN(ViewTest)
0023 
0024 void ViewTest::yankHighlightingTests()
0025 {
0026     const QColor yankHighlightColour = kate_view->rendererConfig()->savedLineColor();
0027 
0028     BeginTest(QStringLiteral("foo bar xyz"));
0029     const QList<Kate::TextRange *> rangesInitial = rangesOnFirstLine();
0030     Q_ASSERT(rangesInitial.isEmpty() && "Assumptions about ranges are wrong - this test is invalid and may need updating!");
0031     TestPressKey(QStringLiteral("wyiw"));
0032     {
0033         const QList<Kate::TextRange *> rangesAfterYank = rangesOnFirstLine();
0034         QCOMPARE(rangesAfterYank.size(), rangesInitial.size() + 1);
0035         QCOMPARE(rangesAfterYank.first()->attribute()->background().color(), yankHighlightColour);
0036         QCOMPARE(rangesAfterYank.first()->start().line(), 0);
0037         QCOMPARE(rangesAfterYank.first()->start().column(), 4);
0038         QCOMPARE(rangesAfterYank.first()->end().line(), 0);
0039         QCOMPARE(rangesAfterYank.first()->end().column(), 7);
0040     }
0041     FinishTest("foo bar xyz");
0042 
0043     BeginTest(QStringLiteral("foom bar xyz"));
0044     TestPressKey(QStringLiteral("wY"));
0045     {
0046         const QList<Kate::TextRange *> rangesAfterYank = rangesOnFirstLine();
0047         QCOMPARE(rangesAfterYank.size(), rangesInitial.size() + 1);
0048         QCOMPARE(rangesAfterYank.first()->attribute()->background().color(), yankHighlightColour);
0049         QCOMPARE(rangesAfterYank.first()->start().line(), 0);
0050         QCOMPARE(rangesAfterYank.first()->start().column(), 5);
0051         QCOMPARE(rangesAfterYank.first()->end().line(), 0);
0052         QCOMPARE(rangesAfterYank.first()->end().column(), 12);
0053     }
0054     FinishTest("foom bar xyz");
0055 
0056     // Unhighlight on keypress.
0057     DoTest("foo bar xyz", "yiww", "foo bar xyz");
0058     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
0059 
0060     // Update colour on config change.
0061     DoTest("foo bar xyz", "yiw", "foo bar xyz");
0062     const QColor newYankHighlightColour = QColor(255, 0, 0);
0063     kate_view->rendererConfig()->setSavedLineColor(newYankHighlightColour);
0064     QCOMPARE(rangesOnFirstLine().first()->attribute()->background().color(), newYankHighlightColour);
0065 
0066     // Visual Mode.
0067     DoTest("foo", "viwy", "foo");
0068     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
0069 
0070     // Unhighlight on keypress in Visual Mode
0071     DoTest("foo", "viwyw", "foo");
0072     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size());
0073 
0074     // Add a yank highlight and directly (i.e. without using Vim commands,
0075     // which would clear the highlight) delete all text; if this deletes the yank highlight behind our back
0076     // and we don't respond correctly to this, it will be double-deleted by KateViNormalMode.
0077     // Currently, this seems like it doesn't occur, but better safe than sorry :)
0078     BeginTest(QStringLiteral("foo bar xyz"));
0079     TestPressKey(QStringLiteral("yiw"));
0080     QCOMPARE(rangesOnFirstLine().size(), rangesInitial.size() + 1);
0081     kate_document->documentReload();
0082     kate_document->clear();
0083     vi_input_mode->reset();
0084     vi_input_mode_manager = vi_input_mode->viInputModeManager();
0085     FinishTest("");
0086 }
0087 
0088 void ViewTest::visualLineUpDownTests()
0089 {
0090     // Need to ensure we have dynamic wrap, a fixed width font, and a decent size kate_view.
0091     ensureKateViewVisible();
0092     const QFont oldFont = kate_view->rendererConfig()->baseFont();
0093     QFont fixedWidthFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
0094     kate_view->rendererConfig()->setFont(fixedWidthFont);
0095     const bool oldDynWordWrap = KateViewConfig::global()->dynWordWrap();
0096     KateViewConfig::global()->setDynWordWrap(true);
0097     const bool oldReplaceTabsDyn = kate_document->config()->replaceTabsDyn();
0098     kate_document->config()->setReplaceTabsDyn(false);
0099     const int oldTabWidth = kate_document->config()->tabWidth();
0100     const int tabWidth = 5;
0101     kate_document->config()->setTabWidth(tabWidth);
0102     KateViewConfig::global()->setValue(KateViewConfig::ShowScrollbars, KateViewConfig::ScrollbarMode::AlwaysOn);
0103 
0104     // Compute the maximum width of text before line-wrapping sets it.
0105     int textWrappingLength = 1;
0106     while (true) {
0107         QString text = QStringLiteral("X").repeated(textWrappingLength) + QLatin1Char(' ') + QLatin1Char('O');
0108         const int posOfO = text.length() - 1;
0109         kate_document->setText(text);
0110         if (kate_view->cursorToCoordinate(Cursor(0, posOfO)).y() != kate_view->cursorToCoordinate(Cursor(0, 0)).y()) {
0111             textWrappingLength++; // Number of x's, plus space.
0112             break;
0113         }
0114         textWrappingLength++;
0115     }
0116     const QString fillsLineAndEndsOnSpace = QStringLiteral("X").repeated(textWrappingLength - 1) + QLatin1Char(' ');
0117 
0118     // Create a QString consisting of enough concatenated fillsLineAndEndsOnSpace to completely
0119     // fill the viewport of the kate View.
0120     QString fillsView = fillsLineAndEndsOnSpace;
0121     while (true) {
0122         kate_document->setText(fillsView);
0123         const QString visibleText = kate_document->text(kate_view->visibleRange());
0124         if (fillsView.length() > visibleText.length() * 2) { // Overkill.
0125             break;
0126         }
0127         fillsView += fillsLineAndEndsOnSpace;
0128     }
0129     const int numVisibleLinesToFillView = fillsView.length() / fillsLineAndEndsOnSpace.length();
0130 
0131     {
0132         // gk/ gj when there is only one line.
0133         DoTest("foo", "lgkr.", "f.o");
0134         DoTest("foo", "lgjr.", "f.o");
0135     }
0136 
0137     {
0138         // gk when sticky bit is set to the end.
0139         const QString originalText = fillsLineAndEndsOnSpace.repeated(2);
0140         QString expectedText = originalText;
0141         kate_document->setText(originalText);
0142         Q_ASSERT(expectedText[textWrappingLength - 1] == QLatin1Char(' '));
0143         expectedText[textWrappingLength - 1] = QLatin1Char('.');
0144         DoTest(originalText.toUtf8().constData(), "$gkr.", expectedText.toUtf8().constData());
0145     }
0146 
0147     {
0148         // Regression test: more than fill the view up, go to end, and do gk on wrapped text (used to crash).
0149         // First work out the text that will fill up the view.
0150         QString expectedText = fillsView;
0151         Q_ASSERT(expectedText[expectedText.length() - textWrappingLength - 1] == QLatin1Char(' '));
0152         expectedText[expectedText.length() - textWrappingLength - 1] = QLatin1Char('.');
0153 
0154         DoTest(fillsView.toUtf8().constData(), "$gkr.", expectedText.toUtf8().constData());
0155     }
0156 
0157     {
0158         // Jump down a few lines all in one go, where we have some variable length lines to navigate.
0159         const int numVisualLinesOnLine[] = {3, 5, 2, 3};
0160         const int numLines = sizeof(numVisualLinesOnLine) / sizeof(int);
0161         const int startVisualLine = 2;
0162         const int numberLinesToGoDownInOneGo = 10;
0163 
0164         int totalVisualLines = 0;
0165         for (int i = 0; i < numLines; i++) {
0166             totalVisualLines += numVisualLinesOnLine[i];
0167         }
0168 
0169         QString startText;
0170         for (int i = 0; i < numLines; i++) {
0171             QString thisLine = fillsLineAndEndsOnSpace.repeated(numVisualLinesOnLine[i]);
0172             // Replace trailing space with carriage return.
0173             thisLine.chop(1);
0174             thisLine.append(QLatin1Char('\n'));
0175             startText += thisLine;
0176         }
0177         QString expectedText = startText;
0178         expectedText[((startVisualLine - 1) + numberLinesToGoDownInOneGo) * fillsLineAndEndsOnSpace.length()] = QLatin1Char('.');
0179 
0180         Q_ASSERT(numberLinesToGoDownInOneGo + startVisualLine < totalVisualLines);
0181         Q_ASSERT(numberLinesToGoDownInOneGo + startVisualLine < numVisibleLinesToFillView);
0182         DoTest(startText.toUtf8().constData(),
0183                QString(QStringLiteral("gj").repeated(startVisualLine - 1) + QString::number(numberLinesToGoDownInOneGo) + QStringLiteral("gjr."))
0184                    .toUtf8()
0185                    .constData(),
0186                expectedText.toUtf8().constData());
0187         // Now go up a few lines.
0188         const int numLinesToGoBackUp = 7;
0189         expectedText = startText;
0190         expectedText[((startVisualLine - 1) + numberLinesToGoDownInOneGo - numLinesToGoBackUp) * fillsLineAndEndsOnSpace.length()] = QLatin1Char('.');
0191         DoTest(startText.toUtf8().constData(),
0192                QString(QStringLiteral("gj").repeated(startVisualLine - 1) + QString::number(numberLinesToGoDownInOneGo) + QStringLiteral("gj")
0193                        + QString::number(numLinesToGoBackUp) + QStringLiteral("gkr."))
0194                    .toUtf8()
0195                    .constData(),
0196                expectedText.toUtf8().constData());
0197     }
0198 
0199     {
0200         // Move down enough lines in one go to disappear off the view.
0201         // About half-a-viewport past the end of the current viewport.
0202         const int numberLinesToGoDown = numVisibleLinesToFillView * 3 / 2;
0203         const int visualColumnNumber = 7;
0204         Q_ASSERT(fillsLineAndEndsOnSpace.length() > visualColumnNumber);
0205         QString expectedText = fillsView.repeated(2);
0206         Q_ASSERT(expectedText[expectedText.length() - textWrappingLength - 1] == QLatin1Char(' '));
0207         expectedText[visualColumnNumber + fillsLineAndEndsOnSpace.length() * numberLinesToGoDown] = QLatin1Char('.');
0208 
0209         DoTest(fillsView.repeated(2).toUtf8().constData(),
0210                QString(QStringLiteral("l").repeated(visualColumnNumber) + QString::number(numberLinesToGoDown) + QStringLiteral("gjr.")).toUtf8().constData(),
0211                expectedText.toUtf8().constData());
0212     }
0213 
0214     {
0215         // Deal with dynamic wrapping and indented blocks - continuations of a line are "invisibly" idented by
0216         // the same amount as the beginning of the line, and we have to subtract this indentation.
0217         const QString unindentedFirstLine = QStringLiteral("stickyhelper\n");
0218         const int numIndentationSpaces = 5;
0219         Q_ASSERT(textWrappingLength > numIndentationSpaces * 2 /* keep some wriggle room */);
0220         const QString indentedFillsLineEndsOnSpace =
0221             QStringLiteral(" ").repeated(numIndentationSpaces) + QStringLiteral("X").repeated(textWrappingLength - 1 - numIndentationSpaces) + QLatin1Char(' ');
0222         DoTest(QString(unindentedFirstLine + indentedFillsLineEndsOnSpace + QStringLiteral("LINE3")).toUtf8().constData(),
0223                QString(QStringLiteral("l").repeated(numIndentationSpaces) + QStringLiteral("jgjr.")).toUtf8().constData(),
0224                QString(unindentedFirstLine + indentedFillsLineEndsOnSpace + QStringLiteral(".INE3")).toUtf8().constData());
0225 
0226         // The first, non-wrapped portion of the line is not invisibly indented, though, so ensure we don't mess that up.
0227         QString expectedSecondLine = indentedFillsLineEndsOnSpace;
0228         expectedSecondLine[numIndentationSpaces] = QLatin1Char('.');
0229         DoTest(QString(unindentedFirstLine + indentedFillsLineEndsOnSpace + QStringLiteral("LINE3")).toUtf8().constData(),
0230                QString(QStringLiteral("l").repeated(numIndentationSpaces) + QStringLiteral("jgjgkr.")).toUtf8().constData(),
0231                (unindentedFirstLine + expectedSecondLine + QStringLiteral("LINE3")).toUtf8().constData());
0232     }
0233 
0234     {
0235         // Take into account any invisible indentation when setting the sticky column.
0236         const int numIndentationSpaces = 5;
0237         Q_ASSERT(textWrappingLength > numIndentationSpaces * 2 /* keep some wriggle room */);
0238         const QString indentedFillsLineEndsOnSpace =
0239             QStringLiteral(" ").repeated(numIndentationSpaces) + QStringLiteral("X").repeated(textWrappingLength - 1 - numIndentationSpaces) + QLatin1Char(' ');
0240         const int posInSecondWrappedLineToChange = 3;
0241         QString expectedText = indentedFillsLineEndsOnSpace + fillsLineAndEndsOnSpace;
0242         expectedText[textWrappingLength + posInSecondWrappedLineToChange] = QLatin1Char('.');
0243         DoTest((indentedFillsLineEndsOnSpace + fillsLineAndEndsOnSpace).toUtf8().constData(),
0244                (QString::number(textWrappingLength + posInSecondWrappedLineToChange) + QStringLiteral("lgkgjr.")).toUtf8().constData(),
0245                expectedText.toUtf8().constData());
0246         // Make sure we can do this more than once (i.e. clear any flags that need clearing).
0247         DoTest((indentedFillsLineEndsOnSpace + fillsLineAndEndsOnSpace).toUtf8().constData(),
0248                (QString::number(textWrappingLength + posInSecondWrappedLineToChange) + QStringLiteral("lgkgjr.")).toUtf8().constData(),
0249                expectedText.toUtf8().constData());
0250     }
0251 
0252     {
0253         // Take into account any invisible indentation when setting the sticky column as above, but use tabs.
0254         const QString indentedFillsLineEndsOnSpace = QStringLiteral("\t") + QStringLiteral("X").repeated(textWrappingLength - 1 - tabWidth) + QLatin1Char(' ');
0255         const int posInSecondWrappedLineToChange = 3;
0256         QString expectedText = indentedFillsLineEndsOnSpace + fillsLineAndEndsOnSpace;
0257         expectedText[textWrappingLength - tabWidth + posInSecondWrappedLineToChange] = QLatin1Char('.');
0258         DoTest(QString(indentedFillsLineEndsOnSpace + fillsLineAndEndsOnSpace).toUtf8().constData(),
0259                QString(QStringLiteral("fXf ") + QString::number(posInSecondWrappedLineToChange) + QStringLiteral("lgkgjr.")).toUtf8().constData(),
0260                expectedText.toUtf8().constData());
0261     }
0262 
0263     {
0264         // Deal with the fact that j/ k may set a sticky column that is impossible to adhere to in visual mode because
0265         // it is too high.
0266         // Here, we have one dummy line and one wrapped line.  We start from the beginning of the wrapped line and
0267         // move right until we wrap and end up at posInWrappedLineToChange one the second line of the wrapped line.
0268         // We then move up and down with j and k to set the sticky column to a value to large to adhere to in a
0269         // visual line, and try to move a visual line up.
0270         const QString dummyLineForUseWithK(QStringLiteral("dummylineforusewithk\n"));
0271         QString startText = dummyLineForUseWithK + fillsLineAndEndsOnSpace.repeated(2);
0272         const int posInWrappedLineToChange = 3;
0273         QString expectedText = startText;
0274         expectedText[dummyLineForUseWithK.length() + posInWrappedLineToChange] = QLatin1Char('.');
0275         DoTest(startText.toUtf8().constData(),
0276                QString(QStringLiteral("j") + QString::number(textWrappingLength + posInWrappedLineToChange) + QStringLiteral("lkjgkr.")).toUtf8().constData(),
0277                expectedText.toUtf8().constData());
0278     }
0279 
0280     {
0281         // Ensure gj works in Visual mode.
0282         Q_ASSERT(fillsLineAndEndsOnSpace.toLower() != fillsLineAndEndsOnSpace);
0283         QString expectedText = fillsLineAndEndsOnSpace.toLower() + fillsLineAndEndsOnSpace;
0284         expectedText[textWrappingLength] = expectedText[textWrappingLength].toLower();
0285         DoTest(fillsLineAndEndsOnSpace.repeated(2).toUtf8().constData(), QStringLiteral("vgjgu").toUtf8().constData(), expectedText.toUtf8().constData());
0286     }
0287 
0288     {
0289         // Ensure gk works in Visual mode.
0290         Q_ASSERT(fillsLineAndEndsOnSpace.toLower() != fillsLineAndEndsOnSpace);
0291         DoTest(fillsLineAndEndsOnSpace.repeated(2).toUtf8().constData(),
0292                QStringLiteral("$vgkgu").toUtf8().constData(),
0293                QString(fillsLineAndEndsOnSpace + fillsLineAndEndsOnSpace.toLower()).toUtf8().constData());
0294     }
0295 
0296     {
0297         // Some tests for how well we handle things with real tabs.
0298         QString beginsWithTabFillsLineEndsOnSpace = QStringLiteral("\t");
0299         while (beginsWithTabFillsLineEndsOnSpace.length() + (tabWidth - 1) < textWrappingLength - 1) {
0300             beginsWithTabFillsLineEndsOnSpace += QLatin1Char('X');
0301         }
0302         beginsWithTabFillsLineEndsOnSpace += QLatin1Char(' ');
0303         const QString unindentedFirstLine = QStringLiteral("stockyhelper\n");
0304         const int posOnThirdLineToChange = 3;
0305         QString expectedThirdLine = fillsLineAndEndsOnSpace;
0306         expectedThirdLine[posOnThirdLineToChange] = QLatin1Char('.');
0307         DoTest((unindentedFirstLine + beginsWithTabFillsLineEndsOnSpace + fillsLineAndEndsOnSpace).toUtf8().constData(),
0308                QString(QStringLiteral("l").repeated(tabWidth + posOnThirdLineToChange) + QStringLiteral("gjgjr.")).toUtf8().constData(),
0309                (unindentedFirstLine + beginsWithTabFillsLineEndsOnSpace + expectedThirdLine).toUtf8().constData());
0310 
0311         // As above, but go down twice and return to the middle line.
0312         const int posOnSecondLineToChange = 2;
0313         QString expectedSecondLine = beginsWithTabFillsLineEndsOnSpace;
0314         expectedSecondLine[posOnSecondLineToChange + 1 /* "+1" as we're not counting the leading tab as a pos */] = QLatin1Char('.');
0315         DoTest((unindentedFirstLine + beginsWithTabFillsLineEndsOnSpace + fillsLineAndEndsOnSpace).toUtf8().constData(),
0316                QString(QStringLiteral("l").repeated(tabWidth + posOnSecondLineToChange) + QStringLiteral("gjgjgkr.")).toUtf8().constData(),
0317                QString(unindentedFirstLine + expectedSecondLine + fillsLineAndEndsOnSpace).toUtf8().constData());
0318     }
0319 
0320     // Restore back to how we were before.
0321     kate_view->rendererConfig()->setFont(oldFont);
0322     KateViewConfig::global()->setDynWordWrap(oldDynWordWrap);
0323     kate_document->config()->setReplaceTabsDyn(oldReplaceTabsDyn);
0324     kate_document->config()->setTabWidth(oldTabWidth);
0325 }
0326 
0327 void ViewTest::ScrollViewTests()
0328 {
0329     QSKIP("This is too unstable in Jenkins", SkipAll);
0330 
0331     // First of all, we have to initialize some sizes and fonts.
0332     ensureKateViewVisible();
0333 
0334     const QFont oldFont = kate_view->rendererConfig()->baseFont();
0335     QFont fixedWidthFont(QStringLiteral("Monospace"));
0336 
0337     fixedWidthFont.setStyleHint(QFont::TypeWriter);
0338     fixedWidthFont.setPixelSize(14);
0339     Q_ASSERT_X(QFontInfo(fixedWidthFont).fixedPitch(), "setting up ScrollViewTests", "Need a fixed pitch font!");
0340     kate_view->rendererConfig()->setFont(fixedWidthFont);
0341 
0342     // Generating our text here.
0343     QString text;
0344     for (int i = 0; i < 20; i++) {
0345         text += QLatin1String("    aaaaaaaaaaaaaaaa\n");
0346     }
0347 
0348     // TODO: fix the visibleRange's tests.
0349 
0350     // zz
0351     BeginTest(text);
0352     TestPressKey(QStringLiteral("10l9jzz"));
0353     QCOMPARE(kate_view->cursorPosition().line(), 9);
0354     QCOMPARE(kate_view->cursorPosition().column(), 10);
0355     QCOMPARE(kate_view->visibleRange(), Range(4, 0, 13, 20));
0356     FinishTest(text.toUtf8().constData());
0357 
0358     // z.
0359     BeginTest(text);
0360     TestPressKey(QStringLiteral("10l9jz."));
0361     QCOMPARE(kate_view->cursorPosition().line(), 9);
0362     QCOMPARE(kate_view->cursorPosition().column(), 4);
0363     QCOMPARE(kate_view->visibleRange(), Range(4, 0, 13, 20));
0364     FinishTest(text.toUtf8().constData());
0365 
0366     // zt
0367     BeginTest(text);
0368     TestPressKey(QStringLiteral("10l9jzt"));
0369     QCOMPARE(kate_view->cursorPosition().line(), 9);
0370     QCOMPARE(kate_view->cursorPosition().column(), 10);
0371     QCOMPARE(kate_view->visibleRange(), Range(9, 0, 18, 20));
0372     FinishTest(text.toUtf8().constData());
0373 
0374     // z<cr>
0375     BeginTest(text);
0376     TestPressKey(QStringLiteral("10l9jz\\return"));
0377     QCOMPARE(kate_view->cursorPosition().line(), 9);
0378     QCOMPARE(kate_view->cursorPosition().column(), 4);
0379     QCOMPARE(kate_view->visibleRange(), Range(9, 0, 18, 20));
0380     FinishTest(text.toUtf8().constData());
0381 
0382     // zb
0383     BeginTest(text);
0384     TestPressKey(QStringLiteral("10l9jzb"));
0385     QCOMPARE(kate_view->cursorPosition().line(), 9);
0386     QCOMPARE(kate_view->cursorPosition().column(), 10);
0387     QCOMPARE(kate_view->visibleRange(), Range(0, 0, 9, 20));
0388     FinishTest(text.toUtf8().constData());
0389 
0390     // z-
0391     BeginTest(text);
0392     TestPressKey(QStringLiteral("10l9jz-"));
0393     QCOMPARE(kate_view->cursorPosition().line(), 9);
0394     QCOMPARE(kate_view->cursorPosition().column(), 4);
0395     QCOMPARE(kate_view->visibleRange(), Range(0, 0, 9, 20));
0396     FinishTest(text.toUtf8().constData());
0397 
0398     // Restore back to how we were before.
0399     kate_view->rendererConfig()->setFont(oldFont);
0400 }
0401 
0402 void ViewTest::clipboardTests_data()
0403 {
0404     QTest::addColumn<QString>("text");
0405     QTest::addColumn<QString>("commands");
0406     QTest::addColumn<QString>("clipboard");
0407 
0408     QTest::newRow("yank") << "yyfoo\nbar"
0409                           << "yy"
0410                           << "yyfoo\n";
0411     QTest::newRow("delete") << "ddfoo\nbar"
0412                             << "dd"
0413                             << "ddfoo\n";
0414     QTest::newRow("yank empty line") << "\nbar"
0415                                      << "yy" << QString();
0416     QTest::newRow("delete word") << "word foo"
0417                                  << "dw"
0418                                  << "word ";
0419     QTest::newRow("delete onechar word") << "w foo"
0420                                          << "dw"
0421                                          << "w ";
0422     QTest::newRow("delete onechar") << "word foo"
0423                                     << "dc" << QString();
0424     QTest::newRow("delete empty lines") << " \t\n\n  \nfoo"
0425                                         << "d3d" << QString();
0426 }
0427 
0428 void ViewTest::clipboardTests()
0429 {
0430     QFETCH(QString, text);
0431     QFETCH(QString, commands);
0432     QFETCH(QString, clipboard);
0433 
0434     QApplication::clipboard()->clear();
0435     BeginTest(text);
0436     TestPressKey(commands);
0437     QCOMPARE(QApplication::clipboard()->text(), clipboard);
0438 }
0439 
0440 QList<Kate::TextRange *> ViewTest::rangesOnFirstLine()
0441 {
0442     return kate_document->buffer().rangesForLine(0, kate_view, true);
0443 }
0444 
0445 #include "moc_view.cpp"