void KateFoldingTest::testFolding_collapse_expand_local() { KTemporaryFile file; file.setSuffix(".c"); file.open(); QTextStream stream(&file); stream << "if () {\n" << " if () {\n" << " if () {\n" << " if () {\n" << " }\n" << " }\n" << " }\n" << " if () {\n" << " foo()\n" << " }\n" << " }\n"; stream << flush; file.close(); KateDocument doc(false, false, false); QVERIFY(doc.openUrl(KUrl(file.fileName()))); KateView* view = new KateView(&doc, 0); // is set to allow kate's hl to be called view->config()->setDynWordWrap(true); QCOMPARE(doc.visibleLines(), 12u); view->setCursorPosition(KTextEditor::Cursor(2,12)); QAction* action = view->action("folding_collapselocal"); QVERIFY(action); action->trigger(); QCOMPARE(doc.visibleLines(), 9u); view->setCursorPosition(KTextEditor::Cursor(2,11)); action = view->action("folding_collapselocal"); QVERIFY(action); action->trigger(); QCOMPARE(doc.visibleLines(), 7u); view->setCursorPosition(KTextEditor::Cursor(1,9)); action = view->action("folding_expandlocal"); QVERIFY(action); action->trigger(); QCOMPARE(doc.visibleLines(), 9u); }
// This test makes sure that, // - if you have selected text // - that spans a folded range, // - and the cursor is at the end of the text selection, // - and you type a char, e.g. 'x', // then the resulting text is correct, and changing region // visibility does not mess around with the text cursor. // // See https://bugs.kde.org/show_bug.cgi?id=295632 void KateFoldingTest::testBug295632() { KateDocument doc(false, false, false); QString text = "oooossssssss\n" "{\n" "\n" "}\n" "ssssss----------"; doc.setText(text); // view must be visible... KateView* view = static_cast<KateView*>(doc.createView(0)); view->show(); view->resize(400, 300); qint64 foldId = view->textFolding().newFoldingRange (KTextEditor::Range(1, 0, 3, 1)); view->textFolding().foldRange(foldId); QVERIFY(view->textFolding().isLineVisible(0)); QVERIFY(view->textFolding().isLineVisible(1)); QVERIFY(!view->textFolding().isLineVisible(2)); QVERIFY(!view->textFolding().isLineVisible(3)); QVERIFY(view->textFolding().isLineVisible(4)); view->setSelection(Range(Cursor(0,4), Cursor(4, 6))); view->setCursorPosition(Cursor(4, 6)); QTest::qWait(100); doc.typeChars(view, "x"); QTest::qWait(100); QString line = doc.line(0); QCOMPARE(line, QString("oooox----------")); }
void UndoManagerTest::testSelectionUndo() { TestDocument doc; KateView *view = static_cast<KateView*>(doc.createView(0)); doc.setText("aaaa bbbb cccc\n" "dddd eeee ffff"); view->setCursorPosition(KTextEditor::Cursor(1, 9)); KTextEditor::Range selectionRange(KTextEditor::Cursor(0, 5), KTextEditor::Cursor(1, 9)); view->setSelection(selectionRange); doc.typeChars(view, "eeee"); // cursor position: "aaaa eeee| ffff", no selection anymore QCOMPARE(view->cursorPosition(), KTextEditor::Cursor(0, 9)); QCOMPARE(view->selection(), false); // undo to remove "eeee" and add selection and text again doc.undo(); QCOMPARE(view->cursorPosition(), KTextEditor::Cursor(1, 9)); QCOMPARE(view->selection(), true); QCOMPARE(view->selectionRange(), selectionRange); // redo to insert "eeee" again and remove selection // cursor position: "aaaa eeee| ffff", no selection anymore doc.redo(); QCOMPARE(view->cursorPosition(), KTextEditor::Cursor(0, 9)); QCOMPARE(view->selection(), false); delete view; }
// // when text is folded, and you set the text selection from top to bottom and // type a character, the resulting text is borked. // // See https://bugs.kde.org/show_bug.cgi?id=295632 // void KateFoldedSelectionTest::foldedSelectionTest() { KateDocument doc(false, false, false); QString text = "oooossssssss\n" "{\n" "\n" "}\n" "ssssss----------"; doc.setText(text); doc.setHighlightingMode("C++"); doc.buffer().ensureHighlighted (doc.lines()); // view must be visible... KateView* view = static_cast<KateView*>(doc.createView(0)); view->show(); view->resize(400, 300); QTest::qWait(500); doc.foldingTree()->collapseOne(1, 1); QTest::qWait(500); view->setSelection(Range(Cursor(0,4), Cursor(4, 6))); view->setCursorPosition(Cursor(4, 6)); QTest::qWait(500); doc.typeChars(view, "x"); QTest::qWait(500); QString line = doc.line(0); QCOMPARE(line, QString("oooox----------")); }
// tests: // - RangeFeedback::mouseEnteredRange // - RangeFeedback::mouseExitedRange void MovingRangeTest::testFeedbackMouse() { KateDocument doc (false, false, false); // the range created below will span the 'x' characters QString text("..xxxx\n" "xxxx.."); doc.setText(text); KateView* view = static_cast<KateView*>(doc.createView(0)); view->setCursorPosition(Cursor(1, 6)); view->show(); view->resize(200, 100); // create range feedback RangeFeedback rf; QVERIFY(!rf.mouseEnteredRangeCalled()); QVERIFY(!rf.mouseExitedRangeCalled()); // allow empty MovingRange* range = doc.newMovingRange(Range(Cursor(0, 2), Cursor(1, 4)), KTextEditor::MovingRange::ExpandLeft | KTextEditor::MovingRange::ExpandRight, KTextEditor::MovingRange::InvalidateIfEmpty); range->setFeedback(&rf); rf.verifyReset(); // left (nothing) QTest::mouseMove(view, view->cursorToCoordinate(Cursor(0, 0)) + QPoint(0, 5)); QTest::qWait(200); // process mouse events. do not move mouse manually QVERIFY(!rf.mouseEnteredRangeCalled()); QVERIFY(!rf.mouseExitedRangeCalled()); // middle (enter) rf.reset(); QTest::mouseMove(view, view->cursorToCoordinate(Cursor(0, 3)) + QPoint(0, 5)); QTest::qWait(200); // process mouse events. do not move mouse manually QVERIFY(rf.mouseEnteredRangeCalled()); QVERIFY(!rf.mouseExitedRangeCalled()); // right (exit) rf.reset(); QTest::mouseMove(view, view->cursorToCoordinate(Cursor(1, 6)) + QPoint(10, 5)); QTest::qWait(200); // process mouse events. do not move mouse manually QVERIFY(!rf.mouseEnteredRangeCalled()); QVERIFY(rf.mouseExitedRangeCalled()); }
// This is a unit test for bug 311866 (http://bugs.kde.org/show_bug.cgi?id=311866) // It loads 5 lines of C++ code, places the cursor in line 4, and then folds // the code. // Expected behavior: the cursor should be moved so it stays visible // Buggy behavior: the cursor is hidden, and moving the hidden cursor crashes kate void KateFoldingTest::testCrash311866() { KateDocument doc(false, false, false); QString url = KDESRCDIR + QString("data/bug311866.cpp"); doc.openUrl(url); doc.setHighlightingMode("C++"); doc.buffer().ensureHighlighted (6); KateView* view = static_cast<KateView*>(doc.createView(0)); view->show(); view->resize(400, 300); view->setCursorPosition(Cursor(3, 0)); QTest::qWait(100); view->slotFoldToplevelNodes(); doc.buffer().ensureHighlighted (6); qDebug() << "!!! Does the next line crash?"; view->up(); }
void UndoManagerTest::testCursorPosition() { TestDocument doc; KateView *view = static_cast<KateView*>(doc.createView(0)); doc.setText("aaaa bbbb cccc\n" "dddd ffff"); view->setCursorPosition(KTextEditor::Cursor(1, 5)); doc.typeChars(view, "eeee"); // cursor position: "dddd eeee| ffff" QCOMPARE(view->cursorPosition(), KTextEditor::Cursor(1, 9)); // undo once to remove "eeee", cursor position: "dddd | ffff" doc.undo(); QCOMPARE(view->cursorPosition(), KTextEditor::Cursor(1, 5)); // redo once to insert "eeee" again. cursor position: "dddd eeee| ffff" doc.redo(); QCOMPARE(view->cursorPosition(), KTextEditor::Cursor(1, 9)); delete view; }
// tests: // - RangeFeedback::caretEnteredRange // - RangeFeedback::caretExitedRange void MovingRangeTest::testFeedbackCaret() { KateDocument doc (false, false, false); // the range created below will span the 'x' characters QString text("..xxxx\n" "xxxx.."); doc.setText(text); KateView* view = static_cast<KateView*>(doc.createView(0)); // create range feedback RangeFeedback rf; // first test: with ExpandLeft | ExpandRight { view->setCursorPosition(Cursor(1, 6)); MovingRange* range = doc.newMovingRange(Range(Cursor(0, 2), Cursor(1, 4)), KTextEditor::MovingRange::ExpandLeft | KTextEditor::MovingRange::ExpandRight, KTextEditor::MovingRange::InvalidateIfEmpty); rf.reset(); range->setFeedback(&rf); rf.verifyReset(); // left view->cursorLeft(); QCOMPARE(view->cursorPosition(), Cursor(1, 5)); QVERIFY(!rf.caretEnteredRangeCalled()); QVERIFY(!rf.caretExitedRangeCalled()); view->cursorLeft(); QCOMPARE(view->cursorPosition(), Cursor(1, 4)); QVERIFY(rf.caretEnteredRangeCalled()); // ExpandRight: include cursor already now QVERIFY(!rf.caretExitedRangeCalled()); rf.reset(); view->cursorLeft(); QCOMPARE(view->cursorPosition(), Cursor(1, 3)); QVERIFY(!rf.caretEnteredRangeCalled()); QVERIFY(!rf.caretExitedRangeCalled()); rf.reset(); view->up(); QCOMPARE(view->cursorPosition(), Cursor(0, 3)); QVERIFY(!rf.caretEnteredRangeCalled()); QVERIFY(!rf.caretExitedRangeCalled()); rf.reset(); view->cursorLeft(); QCOMPARE(view->cursorPosition(), Cursor(0, 2)); QVERIFY(!rf.caretEnteredRangeCalled()); QVERIFY(!rf.caretExitedRangeCalled()); rf.reset(); view->cursorLeft(); QCOMPARE(view->cursorPosition(), Cursor(0, 1)); // ExpandLeft: now we left it, not before QVERIFY(!rf.caretEnteredRangeCalled()); QVERIFY(rf.caretExitedRangeCalled()); delete range; } // second test: with DoNotExpand { view->setCursorPosition(Cursor(1, 6)); MovingRange* range = doc.newMovingRange(Range(Cursor(0, 2), Cursor(1, 4)), KTextEditor::MovingRange::DoNotExpand, KTextEditor::MovingRange::InvalidateIfEmpty); rf.reset(); range->setFeedback(&rf); rf.verifyReset(); // left view->cursorLeft(); QCOMPARE(view->cursorPosition(), Cursor(1, 5)); QVERIFY(!rf.caretEnteredRangeCalled()); QVERIFY(!rf.caretExitedRangeCalled()); view->cursorLeft(); QCOMPARE(view->cursorPosition(), Cursor(1, 4)); QVERIFY(!rf.caretEnteredRangeCalled()); // DoNotExpand: does not include cursor QVERIFY(!rf.caretExitedRangeCalled()); rf.reset(); view->cursorLeft(); QCOMPARE(view->cursorPosition(), Cursor(1, 3)); QVERIFY(rf.caretEnteredRangeCalled()); QVERIFY(!rf.caretExitedRangeCalled()); rf.reset(); view->up(); QCOMPARE(view->cursorPosition(), Cursor(0, 3)); QVERIFY(!rf.caretEnteredRangeCalled()); QVERIFY(!rf.caretExitedRangeCalled()); rf.reset(); view->cursorLeft(); QCOMPARE(view->cursorPosition(), Cursor(0, 2)); QVERIFY(!rf.caretEnteredRangeCalled()); QVERIFY(rf.caretExitedRangeCalled()); // DoNotExpand: that's why we leave already now rf.reset(); view->cursorLeft(); QCOMPARE(view->cursorPosition(), Cursor(0, 1)); QVERIFY(!rf.caretEnteredRangeCalled()); QVERIFY(!rf.caretExitedRangeCalled()); delete range; } }
bool KateCommands::CoreCommands::exec(KTextEditor::View *view, const QString &_cmd, QString &errorMsg, const KTextEditor::Range& range) { #define KCC_ERR(s) { errorMsg=s; return false; } // cast it hardcore, we know that it is really a kateview :) KateView *v = static_cast<KateView*>(view); if ( ! v ) KCC_ERR( i18n("Could not access view") ); //create a list of args QStringList args(_cmd.split( QRegExp("\\s+"), QString::SkipEmptyParts)) ; QString cmd ( args.takeFirst() ); // ALL commands that takes no arguments. if ( cmd == "indent" ) { if ( range.isValid() ) { v->doc()->editStart(); for ( int line = range.start().line(); line <= range.end().line(); line++ ) { v->doc()->indent( KTextEditor::Range(line, 0, line, 0), 1 ); } v->doc()->editEnd(); } else { v->indent(); } return true; } else if ( cmd == "unindent" ) { if ( range.isValid() ) { v->doc()->editStart(); for ( int line = range.start().line(); line <= range.end().line(); line++ ) { v->doc()->indent( KTextEditor::Range(line, 0, line, 0), -1 ); } v->doc()->editEnd(); } else { v->unIndent(); } return true; } else if ( cmd == "cleanindent" ) { if ( range.isValid() ) { v->doc()->editStart(); for ( int line = range.start().line(); line <= range.end().line(); line++ ) { v->doc()->indent( KTextEditor::Range(line, 0, line, 0), 0 ); } v->doc()->editEnd(); } else { v->cleanIndent(); } return true; } else if ( cmd == "comment" ) { if ( range.isValid() ) { v->doc()->editStart(); for ( int line = range.start().line(); line <= range.end().line(); line++ ) { v->doc()->comment( v, line, 0, 1 ); } v->doc()->editEnd(); } else { v->comment(); } return true; } else if ( cmd == "uncomment" ) { if ( range.isValid() ) { v->doc()->editStart(); for ( int line = range.start().line(); line <= range.end().line(); line++ ) { v->doc()->comment( v, line, 0, -1 ); } v->doc()->editEnd(); } else { v->uncomment(); } return true; } else if ( cmd == "kill-line" ) { if ( range.isValid() ) { v->doc()->editStart(); for ( int line = range.start().line(); line <= range.end().line(); line++ ) { v->doc()->removeLine( range.start().line() ); } v->doc()->editEnd(); } else { v->killLine(); } return true; } else if ( cmd == "print" ) { v->doc()->printDialog(); return true; } // ALL commands that take a string argument else if ( cmd == "set-indent-mode" || cmd == "set-highlight" || cmd == "set-mode" ) { // need at least one item, otherwise args.first() crashes if ( ! args.count() ) KCC_ERR( i18n("Missing argument. Usage: %1 <value>", cmd ) ); if ( cmd == "set-indent-mode" ) { v->doc()->config()->setIndentationMode( args.join(" ") ); v->doc()->rememberUserDidSetIndentationMode (); return true; } else if ( cmd == "set-highlight" ) { if ( v->doc()->setHighlightingMode( args.first()) ) { static_cast<KateDocument*>(v->doc())->setDontChangeHlOnSave (); return true; } KCC_ERR( i18n("No such highlighting '%1'", args.first() ) ); } else if ( cmd == "set-mode" ) { if ( v->doc()->setMode( args.first()) ) return true; KCC_ERR( i18n("No such mode '%1'", args.first() ) ); } } // ALL commands that takes exactly one integer argument. else if ( cmd == "set-tab-width" || cmd == "set-indent-width" || cmd == "set-word-wrap-column" || cmd == "goto" ) { // find a integer value > 0 if ( ! args.count() ) KCC_ERR( i18n("Missing argument. Usage: %1 <value>", cmd ) ); bool ok; int val ( args.first().toInt( &ok, 10 ) ); // use base 10 even if the string starts with '0' if ( !ok ) KCC_ERR( i18n("Failed to convert argument '%1' to integer.", args.first() ) ); if ( cmd == "set-tab-width" ) { if ( val < 1 ) KCC_ERR( i18n("Width must be at least 1.") ); v->doc()->config()->setTabWidth( val ); } else if ( cmd == "set-indent-width" ) { if ( val < 1 ) KCC_ERR( i18n("Width must be at least 1.") ); v->doc()->config()->setIndentationWidth( val ); } else if ( cmd == "set-word-wrap-column" ) { if ( val < 2 ) KCC_ERR( i18n("Column must be at least 1.") ); v->doc()->setWordWrapAt( val ); } else if ( cmd == "goto" ) { if ( args.first().at(0) == '-' || args.first().at(0) == '+' ) { // if the number starts with a minus or plus sign, add/subract the number val = v->cursorPosition().line() + val; } else { val--; // convert given line number to the internal representation of line numbers } // constrain cursor to the range [0, number of lines] if ( val < 0 ) { val = 0; } else if ( val > v->doc()->lines()-1 ) { val = v->doc()->lines()-1; } v->setCursorPosition( KTextEditor::Cursor( val, 0 ) ); return true; } return true; } // ALL commands that takes 1 boolean argument. else if ( cmd == "set-icon-border" || cmd == "set-folding-markers" || cmd == "set-line-numbers" || cmd == "set-replace-tabs" || cmd == "set-show-tabs" || cmd == "set-word-wrap" || cmd == "set-wrap-cursor" || cmd == "set-replace-tabs-save" || cmd == "set-show-indent" ) { if ( ! args.count() ) KCC_ERR( i18n("Usage: %1 on|off|1|0|true|false", cmd ) ); bool enable = false; KateDocumentConfig * const config = v->doc()->config(); if ( getBoolArg( args.first(), &enable ) ) { if ( cmd == "set-icon-border" ) v->setIconBorder( enable ); else if (cmd == "set-folding-markers") v->setFoldingMarkersOn( enable ); else if ( cmd == "set-line-numbers" ) v->setLineNumbersOn( enable ); else if ( cmd == "set-show-indent" ) v->renderer()->setShowIndentLines( enable ); else if ( cmd == "set-replace-tabs" ) config->setReplaceTabsDyn( enable ); else if ( cmd == "set-show-tabs" ) config->setShowTabs( enable ); else if ( cmd == "set-show-trailing-spaces" ) config->setShowSpaces( enable ); else if ( cmd == "set-word-wrap" ) v->doc()->setWordWrap( enable ); return true; } else KCC_ERR( i18n("Bad argument '%1'. Usage: %2 on|off|1|0|true|false", args.first() , cmd ) ); } else if ( cmd == "set-remove-trailing-spaces" ) { // need at least one item, otherwise args.first() crashes if ( args.count() != 1 ) KCC_ERR( i18n("Usage: set-remove-trailing-spaces 0|-|none or 1|+|mod|modified or 2|*|all") ); QString tmp = args.first().toLower().trimmed(); if (tmp == "1" || tmp == "modified" || tmp == "mod" || tmp == "+") { v->doc()->config()->setRemoveSpaces(1); } else if (tmp == "2" || tmp == "all" || tmp == "*") { v->doc()->config()->setRemoveSpaces(2); } else { v->doc()->config()->setRemoveSpaces(0); } } // unlikely.. KCC_ERR( i18n("Unknown command '%1'", cmd) ); }