diff --git a/src/ExtendedScintilla.cpp b/src/ExtendedScintilla.cpp index 882b1590a..dfec42390 100644 --- a/src/ExtendedScintilla.cpp +++ b/src/ExtendedScintilla.cpp @@ -78,6 +78,17 @@ ExtendedScintilla::ExtendedScintilla(QWidget* parent) : QShortcut* shortcutPrint = new QShortcut(QKeySequence(tr("Ctrl+P")), this, nullptr, nullptr, Qt::WidgetShortcut); connect(shortcutPrint, &QShortcut::activated, this, &ExtendedScintilla::openPrintDialog); + // QScintilla only binds Ctrl++ and Ctrl+- to zoom. On keyboards where the plus sign needs the Shift key (Ctrl+Shift+= on + // a US layout, Ctrl+Shift+; on a Japanese one) the Shift modifier is sent too, so that binding never matches and there + // was no way to zoom in. Add the usual alternatives as well as a way back to the normal size. Ctrl+0 would be the + // obvious key for the latter but it is already used by the main window to reset the window layout. + QShortcut* shortcutZoomInShift = new QShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Plus), this, nullptr, nullptr, Qt::WidgetShortcut); + connect(shortcutZoomInShift, &QShortcut::activated, this, [this]() { zoomIn(); }); + QShortcut* shortcutZoomInEqual = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Equal), this, nullptr, nullptr, Qt::WidgetShortcut); + connect(shortcutZoomInEqual, &QShortcut::activated, this, [this]() { zoomIn(); }); + QShortcut* shortcutZoomReset = new QShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_0), this, nullptr, nullptr, Qt::WidgetShortcut); + connect(shortcutZoomReset, &QShortcut::activated, this, [this]() { zoomTo(0); }); + // Prepare for adding the find/replace option to the QScintilla context menu setContextMenuPolicy(Qt::CustomContextMenu); connect(this, &ExtendedScintilla::customContextMenuRequested, this, &ExtendedScintilla::showContextMenu); diff --git a/src/PreferencesDialog.cpp b/src/PreferencesDialog.cpp index a488e5600..afafed7b6 100644 --- a/src/PreferencesDialog.cpp +++ b/src/PreferencesDialog.cpp @@ -167,6 +167,7 @@ void PreferencesDialog::loadSettings() ui->checkErrorIndicators->setChecked(Settings::getValue("editor", "error_indicators").toBool()); ui->checkHorizontalTiling->setChecked(Settings::getValue("editor", "horizontal_tiling").toBool()); ui->checkCloseButtonOnTabs->setChecked(Settings::getValue("editor", "close_button_on_tabs").toBool()); + ui->checkToggleCommentOnMarginClick->setChecked(Settings::getValue("editor", "toggle_comment_on_margin_click").toBool()); ui->listExtensions->addItems(Settings::getValue("extensions", "list").toStringList()); for (int i=0;ilistBuiltinExtensions->count();++i) @@ -244,6 +245,7 @@ void PreferencesDialog::saveSettings(bool accept) Settings::setValue("editor", "error_indicators", ui->checkErrorIndicators->isChecked()); Settings::setValue("editor", "horizontal_tiling", ui->checkHorizontalTiling->isChecked()); Settings::setValue("editor", "close_button_on_tabs", ui->checkCloseButtonOnTabs->isChecked()); + Settings::setValue("editor", "toggle_comment_on_margin_click", ui->checkToggleCommentOnMarginClick->isChecked()); QStringList extList; for(int i=0;ilistExtensions->count();++i) diff --git a/src/PreferencesDialog.ui b/src/PreferencesDialog.ui index 38743fb03..904f3b61d 100644 --- a/src/PreferencesDialog.ui +++ b/src/PreferencesDialog.ui @@ -1880,6 +1880,26 @@ Can be set to 0 for disabling completion. + + + + Toggle comment on line number click + + + checkToggleCommentOnMarginClick + + + + + + + If enabled, clicking a line number in the SQL editors comments or uncomments that line instead of selecting it. The Un/comment block command and its keyboard shortcut can always be used. + + + enabled + + + @@ -2086,6 +2106,7 @@ Can be set to 0 for disabling completion. checkErrorIndicators checkHorizontalTiling checkCloseButtonOnTabs + checkToggleCommentOnMarginClick listExtensions buttonAddExtension buttonRemoveExtension diff --git a/src/Settings.cpp b/src/Settings.cpp index 0cf6aa52e..9aa9c9778 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -410,6 +410,10 @@ QVariant Settings::getDefaultValue(const std::string& group, const std::string& if(group == "editor" && name == "horizontal_tiling") return false; + // editor/toggle_comment_on_margin_click? + if(group == "editor" && name == "toggle_comment_on_margin_click") + return false; + // editor/splitter1_sizes? if(group == "editor" && name == "splitter1_sizes") return QVariant(); diff --git a/src/docktextedit.cpp b/src/docktextedit.cpp index 8c4ade71a..089b6acaf 100644 --- a/src/docktextedit.cpp +++ b/src/docktextedit.cpp @@ -18,6 +18,9 @@ DockTextEdit::DockTextEdit(QWidget* parent) : if(xmlLexer == nullptr) xmlLexer = new QsciLexerXML(this); + // The content of a cell is not SQL code, so don't comment lines by clicking on their number + commentOnMarginClickApplies = false; + // Set plain text as default setLanguage(PlainText); diff --git a/src/sqltextedit.cpp b/src/sqltextedit.cpp index 93f8efd2c..075f78c73 100644 --- a/src/sqltextedit.cpp +++ b/src/sqltextedit.cpp @@ -12,7 +12,8 @@ SqlUiLexer* SqlTextEdit::sqlLexer = nullptr; SqlTextEdit::SqlTextEdit(QWidget* parent) : - ExtendedScintilla(parent) + ExtendedScintilla(parent), + commentOnMarginClickApplies(true) { // Create lexer object if not done yet if(sqlLexer == nullptr) @@ -53,6 +54,12 @@ SqlTextEdit::SqlTextEdit(QWidget* parent) : QShortcut* shortcutToggleComment = new QShortcut(QKeySequence(tr("Ctrl+/")), this, nullptr, nullptr, Qt::WidgetShortcut); connect(shortcutToggleComment, &QShortcut::activated, this, &SqlTextEdit::toggleBlockComment); + // Margin 0 is the one with the line numbers. It only emits this signal when it is sensitive to clicks, see updateMarginSensitivity. + connect(this, &SqlTextEdit::marginClicked, this, [this](int margin, int line, Qt::KeyboardModifiers) { + if(margin == 0) + toggleLineComments(line, line); + }); + // Do rest of initialisation reloadSettings(); } @@ -71,6 +78,7 @@ void SqlTextEdit::reloadSettings() } // Set wrap lines setWrapMode(static_cast(Settings::getValue("editor", "wrap_lines").toInt())); + updateMarginSensitivity(); ExtendedScintilla::reloadSettings(); @@ -100,6 +108,18 @@ void SqlTextEdit::reloadSettings() setupSyntaxHighlightingFormat(sqlLexer, "identifier", QsciLexerSQL::QuotedIdentifier); } +void SqlTextEdit::setReadOnly(bool ro) +{ + ExtendedScintilla::setReadOnly(ro); + updateMarginSensitivity(); +} + +void SqlTextEdit::updateMarginSensitivity() +{ + // A click on a line number selects that line. When asked for, comment or uncomment the line instead. A sensitive margin + // takes over all the clicks on it, so only do this if there is something to comment. It is off by default. + setMarginSensitivity(0, commentOnMarginClickApplies && !isReadOnly() && Settings::getValue("editor", "toggle_comment_on_margin_click").toBool()); +} void SqlTextEdit::toggleBlockComment() { @@ -121,13 +141,23 @@ void SqlTextEdit::toggleBlockComment() getSelection(&lineFrom, &indexFrom, &lineTo, &indexTo); - bool uncomment = text(lineFrom).contains(QRegularExpression("^[ \t]*--")); - // If the selection ends before the first character of a line, don't // take this line into account for un/commenting. if (indexTo==0) lineTo--; + toggleLineComments(lineFrom, lineTo); +} + +void SqlTextEdit::toggleLineComments(int lineFrom, int lineTo) +{ + lineFrom = qMax(lineFrom, 0); + lineTo = qMin(lineTo, lines() - 1); + if (lineFrom > lineTo) + return; + + bool uncomment = text(lineFrom).contains(QRegularExpression("^[ \t]*--")); + beginUndoAction(); // Iterate over the selected lines, get line text, make @@ -141,7 +171,7 @@ void SqlTextEdit::toggleBlockComment() else lineText.replace(QRegularExpression("^"), "-- "); - indexTo = text(line).length(); + int indexTo = text(line).length(); if (lineText.endsWith("\r\n")) indexTo--; diff --git a/src/sqltextedit.h b/src/sqltextedit.h index ffcd6cf88..12d711a18 100644 --- a/src/sqltextedit.h +++ b/src/sqltextedit.h @@ -18,10 +18,22 @@ class SqlTextEdit : public ExtendedScintilla static SqlUiLexer* sqlLexer; + // Override parent setReadOnly + void setReadOnly(bool ro) override; + public slots: void reloadSettings(); void toggleBlockComment(); + // Comment or uncomment the lines from lineFrom to lineTo (both included) as a single undo action. + // The first of them decides whether all of them get commented or uncommented. + void toggleLineComments(int lineFrom, int lineTo); + +protected: + // Whether the setting to comment a line by clicking its number applies to this editor. Subclasses which don't hold SQL code turn it off. + bool commentOnMarginClickApplies; +private: + void updateMarginSensitivity(); }; #endif diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 75ce74283..b98d5ec0c 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -123,3 +123,63 @@ set(TESTCACHE_HDR add_executable(test-cache ${TESTCACHE_HDR} ${TESTCACHE_SRC}) target_link_libraries(test-cache ${QT_MAJOR}::Test) add_test(NAME test-cache COMMAND $) + +# test sql editor + +set(TESTSQLEDITOR_SRC + TestSqlEditor.cpp + SqlEditorTestSupport.cpp + ../sqltextedit.cpp + ../docktextedit.cpp + ../ExtendedScintilla.cpp + ../FindReplaceDialog.cpp + ../SqlUiLexer.cpp + ../Settings.cpp + ../sql/sqlitetypes.cpp + ../sql/ObjectIdentifier.cpp + ../sql/Query.cpp + ../sql/parser/ParserDriver.cpp + ../sql/parser/sqlite3_lexer.cpp + ../sql/parser/sqlite3_parser.cpp +) + +set(TESTSQLEDITOR_HDR + TestSqlEditor.h + ../sqltextedit.h + ../docktextedit.h + ../ExtendedScintilla.h + ../FindReplaceDialog.h + ../SqlUiLexer.h + ../Settings.h + ../sql/sqlitetypes.h + ../sql/Query.h + ../sql/ObjectIdentifier.h + ../sql/parser/ParserDriver.h + ../sql/parser/sqlite3_lexer.h + ../sql/parser/sqlite3_location.h + ../sql/parser/sqlite3_parser.hpp +) + +add_executable(test-sql-editor ${TESTSQLEDITOR_HDR} ${TESTSQLEDITOR_SRC} ../FindReplaceDialog.ui) +if(QT_MAJOR STREQUAL "Qt6") + set_target_properties(test-sql-editor PROPERTIES AUTOUIC_OPTIONS "--connections=string") +endif() +target_link_libraries(test-sql-editor ${QT_MAJOR}::Test ${QT_MAJOR}::Widgets ${QT_MAJOR}::PrintSupport QScintilla::QScintilla ${LIBSQLITE_NAME} ${QT5_COMPAT}) +add_test(NAME test-sql-editor COMMAND $) +if(WIN32) + set(_se_extra_paths "") + foreach(_p IN LISTS CMAKE_PREFIX_PATH) + list(APPEND _se_extra_paths "${_p}" "${_p}/bin") + # OpenSSL 3 Universal installer layout: bin/x86, bin/x64 + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + list(APPEND _se_extra_paths "${_p}/bin/x64") + else() + list(APPEND _se_extra_paths "${_p}/bin/x86") + endif() + endforeach() + # SQLite and SQLCipher are built as DLLs in the CMake prefix by the Windows CI jobs. + string(REPLACE ";" "\\;" TESTSQLEDITOR_PATH "${_se_extra_paths};$ENV{PATH}") + set_tests_properties(test-sql-editor PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen;PATH=${TESTSQLEDITOR_PATH}") +else() + set_tests_properties(test-sql-editor PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen") +endif() diff --git a/src/tests/SqlEditorTestSupport.cpp b/src/tests/SqlEditorTestSupport.cpp new file mode 100644 index 000000000..a06860b60 --- /dev/null +++ b/src/tests/SqlEditorTestSupport.cpp @@ -0,0 +1,12 @@ +#include "../sqlitedb.h" + +namespace sqlb +{ + +// The same wrapper sqlitedb.cpp provides. It is repeated here so the editor tests do not have to link the whole database code. +QString escapeIdentifier(const QString& id) +{ + return QString::fromStdString(escapeIdentifier(id.toStdString())); +} + +} diff --git a/src/tests/TestSqlEditor.cpp b/src/tests/TestSqlEditor.cpp new file mode 100644 index 000000000..3c2509541 --- /dev/null +++ b/src/tests/TestSqlEditor.cpp @@ -0,0 +1,312 @@ +#include "TestSqlEditor.h" + +#include "../Settings.h" +#include "../docktextedit.h" +#include "../sqltextedit.h" + +#include +#include +#include + +QTEST_MAIN(TestSqlEditor) + +namespace +{ + +// Zoom level of the editor in points relative to its normal font size +int zoomLevel(SqlTextEdit& edit) +{ + return static_cast(edit.SendScintilla(QsciScintillaBase::SCI_GETZOOM)); +} + +// Shortcuts constrained to the widget only fire when the editor has the focus in the active window +bool showWithFocus(SqlTextEdit& edit) +{ + edit.resize(400, 300); + edit.show(); + if(!QTest::qWaitForWindowActive(&edit)) + return false; + edit.setFocus(); + return edit.hasFocus(); +} + +// A point in the line number margin on the given line +QPoint marginPoint(SqlTextEdit& edit, int line) +{ + const long lineStart = edit.SendScintilla(QsciScintillaBase::SCI_POSITIONFROMLINE, static_cast(line)); + const int top = static_cast(edit.SendScintilla(QsciScintillaBase::SCI_POINTYFROMPOSITION, 0UL, lineStart)); + const int height = static_cast(edit.SendScintilla(QsciScintillaBase::SCI_TEXTHEIGHT, static_cast(line))); + return QPoint(edit.marginWidth(0) / 2, top + height / 2); +} + +void enableMarginClick(SqlTextEdit& edit, bool enable) +{ + Settings::setValue("editor", "toggle_comment_on_margin_click", enable, /* save_to_disk */ false); + edit.reloadSettings(); +} + +} + +void TestSqlEditor::initTestCase() +{ + // Never read or write the settings of the real user + QVERIFY(settingsDir.isValid()); + Settings::setUserSettingsFile(settingsDir.filePath("settings.ini")); + + lexerOwner = new SqlTextEdit(); +} + +void TestSqlEditor::cleanup() +{ + Settings::setValue("editor", "toggle_comment_on_margin_click", false, /* save_to_disk */ false); +} + +void TestSqlEditor::cleanupTestCase() +{ + delete lexerOwner; + lexerOwner = nullptr; +} + +void TestSqlEditor::zoomKeys_data() +{ + QTest::addColumn("key"); + QTest::addColumn("modifiers"); + QTest::addColumn("startZoom"); + QTest::addColumn("expectedZoom"); + + const int ctrl = Qt::ControlModifier; + const int shift = Qt::ShiftModifier; + const int keypad = Qt::KeypadModifier; + + // The bindings QScintilla provides + QTest::newRow("Ctrl++ zooms in") << int(Qt::Key_Plus) << ctrl << 0 << 1; + QTest::newRow("Ctrl+- zooms out") << int(Qt::Key_Minus) << ctrl << 0 << -1; + QTest::newRow("Ctrl+keypad+ zooms in") << int(Qt::Key_Plus) << (ctrl | keypad) << 0 << 1; + QTest::newRow("Ctrl+keypad- zooms out") << int(Qt::Key_Minus) << (ctrl | keypad) << 0 << -1; + + // On keyboards where + needs Shift (US: Shift+=, Japanese: Shift+;) the plain Ctrl++ never arrives + QTest::newRow("Ctrl+Shift++ zooms in") << int(Qt::Key_Plus) << (ctrl | shift) << 0 << 1; + QTest::newRow("Ctrl+= zooms in") << int(Qt::Key_Equal) << ctrl << 0 << 1; + + // Back to the normal size + QTest::newRow("Ctrl+Shift+0 resets from bigger") << int(Qt::Key_0) << (ctrl | shift) << 4 << 0; + QTest::newRow("Ctrl+Shift+0 resets from smaller") << int(Qt::Key_0) << (ctrl | shift) << -3 << 0; + QTest::newRow("Ctrl+Shift+0 keeps the normal size") << int(Qt::Key_0) << (ctrl | shift) << 0 << 0; +} + +void TestSqlEditor::zoomKeys() +{ + QFETCH(int, key); + QFETCH(int, modifiers); + QFETCH(int, startZoom); + QFETCH(int, expectedZoom); + + SqlTextEdit edit; + QVERIFY(showWithFocus(edit)); + edit.zoomTo(startZoom); + QCOMPARE(zoomLevel(edit), startZoom); + + QTest::keyClick(&edit, static_cast(key), static_cast(modifiers)); + + QCOMPARE(zoomLevel(edit), expectedZoom); +} + +void TestSqlEditor::ctrlZeroStaysWithTheApplication() +{ + // Ctrl+0 is the shortcut of View > Window Layout > Reset Window Layout in the main window. + // The editor must not claim it, otherwise that shortcut would stop working while typing SQL. + SqlTextEdit edit; + QVERIFY(showWithFocus(edit)); + + QKeyEvent override(QEvent::ShortcutOverride, Qt::Key_0, Qt::ControlModifier); + override.ignore(); + QApplication::sendEvent(&edit, &override); + QVERIFY(!override.isAccepted()); + + edit.zoomTo(3); + QTest::keyClick(&edit, Qt::Key_0, Qt::ControlModifier); + QCOMPARE(zoomLevel(edit), 3); +} + +void TestSqlEditor::marginClickIsOffByDefault() +{ + // This must run before any other test changes the setting + QVERIFY(!Settings::getValue("editor", "toggle_comment_on_margin_click").toBool()); +} + +void TestSqlEditor::toggleBlockCommentUsesSelection_data() +{ + QTest::addColumn("text"); + QTest::addColumn("lineFrom"); + QTest::addColumn("indexFrom"); + QTest::addColumn("lineTo"); + QTest::addColumn("indexTo"); + QTest::addColumn("expected"); + + // No selection: the line of the cursor (this is the position the cursor is moved to) + QTest::newRow("cursor line") << "a\nb\nc" << 1 << 1 << 1 << 1 << "a\n-- b\nc"; + // A selection ending at the start of a line doesn't include that line + QTest::newRow("selection up to the start of a line") << "a\nb\nc\nd" << 0 << 0 << 2 << 0 << "-- a\n-- b\nc\nd"; + QTest::newRow("selection inside lines") << "a\nb\nc\nd" << 1 << 1 << 2 << 1 << "a\n-- b\n-- c\nd"; +} + +void TestSqlEditor::toggleBlockCommentUsesSelection() +{ + QFETCH(QString, text); + QFETCH(int, lineFrom); + QFETCH(int, indexFrom); + QFETCH(int, lineTo); + QFETCH(int, indexTo); + QFETCH(QString, expected); + + SqlTextEdit edit; + edit.setText(text); + if(lineFrom == lineTo && indexFrom == indexTo) + edit.setCursorPosition(lineFrom, indexFrom); + else + edit.setSelection(lineFrom, indexFrom, lineTo, indexTo); + + edit.toggleBlockComment(); + + QCOMPARE(edit.text(), expected); +} + +void TestSqlEditor::toggleLineComments_data() +{ + QTest::addColumn("text"); + QTest::addColumn("lineFrom"); + QTest::addColumn("lineTo"); + QTest::addColumn("expected"); + + QTest::newRow("comment one line") << "a\nb\nc" << 1 << 1 << "a\n-- b\nc"; + QTest::newRow("uncomment one line") << "a\n-- b\nc" << 1 << 1 << "a\nb\nc"; + QTest::newRow("uncomment without space") << "a\n--b\nc" << 1 << 1 << "a\nb\nc"; + QTest::newRow("uncomment keeps indentation") << "a\n -- b\nc" << 1 << 1 << "a\n b\nc"; + QTest::newRow("comment a range") << "a\nb\nc\nd" << 1 << 2 << "a\n-- b\n-- c\nd"; + QTest::newRow("uncomment a range") << "-- a\n-- b\n-- c" << 0 << 1 << "a\nb\n-- c"; + QTest::newRow("first line decides: comment") << "a\n-- b" << 0 << 1 << "-- a\n-- -- b"; + QTest::newRow("first line decides: uncomment") << "-- a\nb\n-- c" << 0 << 2 << "a\nb\nc"; + QTest::newRow("last line without line break") << "a\nb" << 1 << 1 << "a\n-- b"; + QTest::newRow("Windows line breaks") << "a\r\nb\r\nc" << 0 << 1 << "-- a\r\n-- b\r\nc"; + QTest::newRow("whole text") << "a\nb" << 0 << 1 << "-- a\n-- b"; + QTest::newRow("last line past the end") << "a\nb" << 1 << 7 << "a\n-- b"; + QTest::newRow("first line before the start") << "a\nb" << -3 << 0 << "-- a\nb"; + QTest::newRow("empty range") << "a\nb" << 1 << 0 << "a\nb"; + QTest::newRow("range outside the text") << "a\nb" << 5 << 6 << "a\nb"; +} + +void TestSqlEditor::toggleLineComments() +{ + QFETCH(QString, text); + QFETCH(int, lineFrom); + QFETCH(int, lineTo); + QFETCH(QString, expected); + + SqlTextEdit edit; + edit.setText(text); + + edit.toggleLineComments(lineFrom, lineTo); + + QCOMPARE(edit.text(), expected); +} + +void TestSqlEditor::toggleLineCommentsIsOneUndoStep() +{ + SqlTextEdit edit; + edit.setText("a\nb\nc"); + + edit.toggleLineComments(0, 2); + QCOMPARE(edit.text(), QString("-- a\n-- b\n-- c")); + + edit.undo(); + QCOMPARE(edit.text(), QString("a\nb\nc")); +} + +void TestSqlEditor::marginClickSelectsLineByDefault() +{ + // What a click on a line number does today: it selects that line and doesn't touch the text + SqlTextEdit edit; + edit.setText("a\nb\nc"); + QVERIFY(showWithFocus(edit)); + + QTest::mouseClick(edit.viewport(), Qt::LeftButton, Qt::NoModifier, marginPoint(edit, 1)); + + QCOMPARE(edit.text(), QString("a\nb\nc")); + QVERIFY(edit.hasSelectedText()); + QVERIFY(edit.selectedText().startsWith("b")); +} + +void TestSqlEditor::marginClickTogglesComment() +{ + SqlTextEdit edit; + edit.setText("a\nb\nc"); + QVERIFY(showWithFocus(edit)); + enableMarginClick(edit, true); + + QTest::mouseClick(edit.viewport(), Qt::LeftButton, Qt::NoModifier, marginPoint(edit, 1)); + QCOMPARE(edit.text(), QString("a\n-- b\nc")); + + QTest::mouseClick(edit.viewport(), Qt::LeftButton, Qt::NoModifier, marginPoint(edit, 1)); + QCOMPARE(edit.text(), QString("a\nb\nc")); + + // Other lines are not affected + QTest::mouseClick(edit.viewport(), Qt::LeftButton, Qt::NoModifier, marginPoint(edit, 2)); + QCOMPARE(edit.text(), QString("a\nb\n-- c")); + + // Turning it off again brings the old behaviour back + enableMarginClick(edit, false); + QTest::mouseClick(edit.viewport(), Qt::LeftButton, Qt::NoModifier, marginPoint(edit, 0)); + QCOMPARE(edit.text(), QString("a\nb\n-- c")); + QVERIFY(edit.hasSelectedText()); +} + +void TestSqlEditor::marginClickOutsideMarginDoesNotToggle() +{ + SqlTextEdit edit; + edit.setText("a\nb\nc"); + QVERIFY(showWithFocus(edit)); + enableMarginClick(edit, true); + + QPoint inText = marginPoint(edit, 1); + inText.setX(edit.marginWidth(0) + edit.marginWidth(1) + edit.marginWidth(2) + 40); + QTest::mouseClick(edit.viewport(), Qt::LeftButton, Qt::NoModifier, inText); + + QCOMPARE(edit.text(), QString("a\nb\nc")); +} + +void TestSqlEditor::marginClickSelectsLineInReadOnlyEditor() +{ + // Nothing to comment in an editor that can't be changed, so clicking the line numbers must keep selecting the line there + SqlTextEdit edit; + edit.setText("a\nb\nc"); + edit.setReadOnly(true); + QVERIFY(showWithFocus(edit)); + enableMarginClick(edit, true); + + QTest::mouseClick(edit.viewport(), Qt::LeftButton, Qt::NoModifier, marginPoint(edit, 1)); + + QCOMPARE(edit.text(), QString("a\nb\nc")); + QVERIFY(edit.hasSelectedText()); + QVERIFY(edit.selectedText().startsWith("b")); + + // ... but works as soon as the editor can be changed + edit.setReadOnly(false); + QTest::mouseClick(edit.viewport(), Qt::LeftButton, Qt::NoModifier, marginPoint(edit, 1)); + + QCOMPARE(edit.text(), QString("a\n-- b\nc")); +} + +void TestSqlEditor::marginClickSelectsLineInCellEditor() +{ + // The editor for the cell content is based on SqlTextEdit but doesn't hold SQL code + DockTextEdit edit; + edit.setText("a\nb\nc"); + QVERIFY(showWithFocus(edit)); + enableMarginClick(edit, true); + + QTest::mouseClick(edit.viewport(), Qt::LeftButton, Qt::NoModifier, marginPoint(edit, 1)); + + QCOMPARE(edit.text(), QString("a\nb\nc")); + QVERIFY(edit.hasSelectedText()); + QVERIFY(edit.selectedText().startsWith("b")); +} diff --git a/src/tests/TestSqlEditor.h b/src/tests/TestSqlEditor.h new file mode 100644 index 000000000..c2c7dabe6 --- /dev/null +++ b/src/tests/TestSqlEditor.h @@ -0,0 +1,41 @@ +#ifndef TESTSQLEDITOR_H +#define TESTSQLEDITOR_H + +#include +#include + +class SqlTextEdit; + +class TestSqlEditor : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void cleanup(); + + void zoomKeys_data(); + void zoomKeys(); + void ctrlZeroStaysWithTheApplication(); + + void marginClickIsOffByDefault(); + void toggleBlockCommentUsesSelection_data(); + void toggleBlockCommentUsesSelection(); + void toggleLineComments_data(); + void toggleLineComments(); + void toggleLineCommentsIsOneUndoStep(); + void marginClickSelectsLineByDefault(); + void marginClickTogglesComment(); + void marginClickOutsideMarginDoesNotToggle(); + void marginClickSelectsLineInReadOnlyEditor(); + void marginClickSelectsLineInCellEditor(); + +private: + QTemporaryDir settingsDir; + + // The static SQL lexer is owned by the first SqlTextEdit that is created, so this one must outlive all the others + SqlTextEdit* lexerOwner; +}; + +#endif // TESTSQLEDITOR_H