Git commit 096c66d3352112b9dbb17aa3b6d7ea5fce21fea4 by Christoph Cullmann, on behalf of Ben Gooding. Committed on 18/02/2024 at 14:54. Pushed by cullmann into branch 'master'.
Added action to restore previously closed tabs - Added unit tests class for KateDocManager - Added tests for document restoration functionality M +2 -0 apps/lib/autotests/CMakeLists.txt A +160 -0 apps/lib/autotests/kate_doc_manager_tests.cpp * A +27 -0 apps/lib/autotests/kate_doc_manager_tests.h * M +16 -0 apps/lib/katedocmanager.cpp M +9 -1 apps/lib/katedocmanager.h M +7 -0 apps/lib/katemainwindow.cpp M +8 -0 apps/lib/kateviewmanager.cpp M +1 -0 apps/lib/kateviewmanager.h M +4 -0 doc/kate/fundamentals.docbook M +13 -0 doc/kate/menus.docbook The files marked with a * at the end have a non valid license. Please read: https://community.kde.org/Policies/Licensing_Policy and use the headers which are listed at that page. https://invent.kde.org/utilities/kate/-/commit/096c66d3352112b9dbb17aa3b6d7ea5fce21fea4 diff --git a/apps/lib/autotests/CMakeLists.txt b/apps/lib/autotests/CMakeLists.txt index b1e1804d58..9cf0b95ab7 100644 --- a/apps/lib/autotests/CMakeLists.txt +++ b/apps/lib/autotests/CMakeLists.txt @@ -22,6 +22,7 @@ if (WIN32) # sessions_action_test urlinfo_test json_utils_test + kate_doc_manager_tests # location_history_test # kate_view_mgmt_tests bytearraysplitter_tests @@ -33,6 +34,7 @@ else() sessions_action_test urlinfo_test json_utils_test + kate_doc_manager_tests location_history_test kate_view_mgmt_tests bytearraysplitter_tests diff --git a/apps/lib/autotests/kate_doc_manager_tests.cpp b/apps/lib/autotests/kate_doc_manager_tests.cpp new file mode 100644 index 0000000000..a7d37f7eb2 --- /dev/null +++ b/apps/lib/autotests/kate_doc_manager_tests.cpp @@ -0,0 +1,160 @@ + +#include "kate_doc_manager_tests.h" +#include "katedocmanager.h" +#include "katemainwindow.h" + +#include <KLocalizedString> + +#include <QCommandLineParser> +#include <QSignalSpy> +#include <QTest> + +QTEST_MAIN(KateDocManagerTests) + +namespace +{ +QCommandLineParser &GetParser() +{ + static QCommandLineParser parser; + return parser; +} + +KateDocumentInfo CreateMockDocument(QUrl url = {}) +{ + KateDocumentInfo mockDocumentInfo; + mockDocumentInfo.normalizedUrl = url; + return mockDocumentInfo; +} +} + +KateDocManagerTests::KateDocManagerTests(QObject *) +{ + // ensure ui file can be found and the translation domain is set to avoid warnings + qApp->setApplicationName(QStringLiteral("kate")); + KLocalizedString::setApplicationDomain(QByteArrayLiteral("kate")); +} + +void KateDocManagerTests::setUp() +{ + auto tempdir = new QTemporaryDir; + QVERIFY(tempdir->isValid()); + + // ensure we use some dummy config + KConfig::setMainConfigName(tempdir->path() + QStringLiteral("/testconfigfilerc")); + + app = std::make_unique<KateApp>(GetParser(), KateApp::ApplicationKWrite, tempdir->path()); +} + +void KateDocManagerTests::tearDown() +{ + app.reset(nullptr); +} + +void KateDocManagerTests::canCreateDocument() +{ + setUp(); + + auto documentManager = app->documentManager(); + + QSignalSpy documentCreatedSpy(documentManager, &KateDocManager::documentCreated); + const auto document = documentManager->createDoc(CreateMockDocument()); + Q_ASSERT(document != nullptr); + Q_ASSERT(documentCreatedSpy.count() == 1); + + tearDown(); +} + +void KateDocManagerTests::popRecentlyClosedURLsClearsRecentlyClosedURLs() +{ + setUp(); + + constexpr auto FirstTestURL = "Test URL 1"; + constexpr auto SecondTestURL = "Test URL 2"; + + auto documentManager = app->documentManager(); + documentManager->closeAllDocuments(); + + const auto createdDocuments = [&documentManager] { + QList<KTextEditor::Document *> createdDocuments; + createdDocuments.push_back(documentManager->createDoc(CreateMockDocument(QUrl(i18n(FirstTestURL))))); + createdDocuments.push_back(documentManager->createDoc(CreateMockDocument(QUrl(i18n(SecondTestURL))))); + return createdDocuments; + }(); + + documentManager->closeDocuments(createdDocuments, false); + + { + const auto recentlyClosedURLs = documentManager->popRecentlyClosedURLs(); + Q_ASSERT(recentlyClosedURLs.size() == 2); + } + + { + const auto recentlyClosedURLs = documentManager->popRecentlyClosedURLs(); + Q_ASSERT(recentlyClosedURLs.size() == 0); + } + + tearDown(); +} + +void KateDocManagerTests::popRecentlyClosedURLsReturnsNoneIfNoTabsClosedDuringSession() +{ + setUp(); + + auto documentManager = app->documentManager(); + + Q_ASSERT(documentManager->popRecentlyClosedURLs().empty()); + + tearDown(); +} + +void KateDocManagerTests::popRecentlyClosedURLsReturnsURLIfTabClosedDuringSession() +{ + setUp(); + + constexpr auto FirstTestURL = "Test URL 1"; + constexpr auto SecondTestURL = "Test URL 2"; + + auto documentManager = app->documentManager(); + documentManager->closeAllDocuments(); + + const auto createdDocuments = [&documentManager] { + QList<KTextEditor::Document *> createdDocuments; + createdDocuments.push_back(documentManager->createDoc(CreateMockDocument(QUrl(i18n(FirstTestURL))))); + createdDocuments.push_back(documentManager->createDoc(CreateMockDocument(QUrl(i18n(SecondTestURL))))); + return createdDocuments; + }(); + + const bool documentClosed = documentManager->closeDocuments({createdDocuments[0]}, false); + Q_ASSERT(documentClosed); + + const auto recentlyClosedURLs = documentManager->popRecentlyClosedURLs(); + Q_ASSERT(recentlyClosedURLs.contains(QUrl(i18n(FirstTestURL)))); + Q_ASSERT(!recentlyClosedURLs.contains(QUrl(i18n(SecondTestURL)))); + + tearDown(); +} + +void KateDocManagerTests::closeDocumentsWithEmptyURLsAreNotRestorable() +{ + setUp(); + + auto documentManager = app->documentManager(); + documentManager->closeAllDocuments(); + + const auto createdDocuments = [&documentManager] { + QList<KTextEditor::Document *> createdDocuments; + createdDocuments.push_back(documentManager->createDoc(CreateMockDocument())); + createdDocuments.push_back(documentManager->createDoc(CreateMockDocument())); + return createdDocuments; + }(); + + const bool documentsClosed = documentManager->closeDocuments(createdDocuments, false); + Q_ASSERT(documentsClosed); + + const auto recentlyClosedURLs = documentManager->popRecentlyClosedURLs(); + Q_ASSERT(recentlyClosedURLs.isEmpty()); + + tearDown(); +} + +#include "moc_kate_doc_manager_tests.cpp" \ No newline at end of file diff --git a/apps/lib/autotests/kate_doc_manager_tests.h b/apps/lib/autotests/kate_doc_manager_tests.h new file mode 100644 index 0000000000..385d86bc9a --- /dev/null +++ b/apps/lib/autotests/kate_doc_manager_tests.h @@ -0,0 +1,27 @@ + +#pragma once + +#include "kateapp.h" + +#include <QObject> + +class KateDocManagerTests : public QObject +{ + Q_OBJECT +public: + KateDocManagerTests(QObject *parent = nullptr); + +private: + void setUp(); + void tearDown(); + +private Q_SLOTS: + void canCreateDocument(); + void popRecentlyClosedURLsClearsRecentlyClosedURLs(); + void popRecentlyClosedURLsReturnsNoneIfNoTabsClosedDuringSession(); + void popRecentlyClosedURLsReturnsURLIfTabClosedDuringSession(); + void closeDocumentsWithEmptyURLsAreNotRestorable(); + +private: + std::unique_ptr<KateApp> app; +}; \ No newline at end of file diff --git a/apps/lib/katedocmanager.cpp b/apps/lib/katedocmanager.cpp index 2cf4941769..000a8ca4db 100644 --- a/apps/lib/katedocmanager.cpp +++ b/apps/lib/katedocmanager.cpp @@ -171,12 +171,28 @@ KTextEditor::Document *KateDocManager::openUrl(const QUrl &url, const QString &e return doc; } +QList<QUrl> KateDocManager::popRecentlyClosedURLs() +{ + const auto recentlyClosedURLs = m_recentlyClosedURLs; + m_recentlyClosedURLs.clear(); + return recentlyClosedURLs; +} + bool KateDocManager::closeDocuments(const QList<KTextEditor::Document *> documents, bool closeUrl) { if (documents.empty()) { return false; } + m_recentlyClosedURLs.clear(); + for (const auto &document : documents) { + const auto &docInfo = m_docInfos.at(document); + + if (!docInfo.normalizedUrl.isEmpty()) { + m_recentlyClosedURLs.push_back(docInfo.normalizedUrl); + } + } + saveMetaInfos(documents); Q_EMIT aboutToDeleteDocuments(QList<KTextEditor::Document *>{documents.begin(), documents.end()}); diff --git a/apps/lib/katedocmanager.h b/apps/lib/katedocmanager.h index 2981ae635c..56c75a61eb 100644 --- a/apps/lib/katedocmanager.h +++ b/apps/lib/katedocmanager.h @@ -7,6 +7,8 @@ #pragma once +#include "kateprivate_export.h" + #include <ktexteditor/document.h> #include <QList> @@ -37,10 +39,12 @@ public: QUrl normalizedUrl; }; -class KateDocManager : public QObject +class KATE_PRIVATE_EXPORT KateDocManager : public QObject { Q_OBJECT + friend class KateDocManagerTests; + public: explicit KateDocManager(QObject *parent); ~KateDocManager() override; @@ -62,6 +66,8 @@ public: std::vector<KTextEditor::Document *> openUrls(const QList<QUrl> &, const QString &encoding = QString(), const KateDocumentInfo &docInfo = KateDocumentInfo()); + QList<QUrl> popRecentlyClosedURLs(); + bool closeDocument(KTextEditor::Document *, bool closeUrl = true); bool closeDocuments(const QList<KTextEditor::Document *> documents, bool closeUrl = true); bool closeDocumentList(const QList<KTextEditor::Document *> &documents, KateMainWindow *window); @@ -171,6 +177,8 @@ private: bool m_saveMetaInfos; int m_daysMetaInfos; + QList<QUrl> m_recentlyClosedURLs; + private Q_SLOTS: void documentOpened(); }; diff --git a/apps/lib/katemainwindow.cpp b/apps/lib/katemainwindow.cpp index bddc4c13b7..53771fc9cd 100644 --- a/apps/lib/katemainwindow.cpp +++ b/apps/lib/katemainwindow.cpp @@ -417,6 +417,13 @@ void KateMainWindow::setupActions() connect(a, &QAction::triggered, this, &KateMainWindow::slotDocumentCloseAll); a->setWhatsThis(i18n("Close all open documents.")); + a = actionCollection()->addAction(QStringLiteral("reopen_latest_closed_tab")); + a->setIcon(QIcon::fromTheme(QStringLiteral("reopentab"))); + a->setText(i18n("&Reopen latest closed tab.")); + actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_T)); + connect(a, &QAction::triggered, m_viewManager, &KateViewManager::slotRestoreLastClosedDocument); + a->setWhatsThis(i18n("Reopen the tab that was most recently closed")); + a = actionCollection()->addAction(KStandardAction::Quit, QStringLiteral("file_quit")); // Qt::QueuedConnection: delay real shutdown, as we are inside menu action handling (bug #185708) connect(a, &QAction::triggered, this, &KateMainWindow::slotFileQuit, Qt::QueuedConnection); diff --git a/apps/lib/kateviewmanager.cpp b/apps/lib/kateviewmanager.cpp index ff152b3c9b..bb3a3d3b2d 100644 --- a/apps/lib/kateviewmanager.cpp +++ b/apps/lib/kateviewmanager.cpp @@ -400,6 +400,14 @@ void KateViewManager::slotDocumentClose() } } +void KateViewManager::slotRestoreLastClosedDocument() +{ + const auto &documentManager = KateApp::self()->documentManager(); + const auto recentlyClosedUrls = documentManager->popRecentlyClosedURLs(); + + openUrls(recentlyClosedUrls, QString()); +} + KTextEditor::Document * KateViewManager::openUrl(const QUrl &url, const QString &encoding, bool activate, bool ignoreForRecentFiles, const KateDocumentInfo &docInfo) { diff --git a/apps/lib/kateviewmanager.h b/apps/lib/kateviewmanager.h index a9412fdfde..5ef3e14aa8 100644 --- a/apps/lib/kateviewmanager.h +++ b/apps/lib/kateviewmanager.h @@ -95,6 +95,7 @@ public Q_SLOTS: void slotDocumentOpen(); void slotDocumentClose(); void slotDocumentClose(KTextEditor::Document *document); + void slotRestoreLastClosedDocument(); void setActiveSpace(KateViewSpace *vs); diff --git a/doc/kate/fundamentals.docbook b/doc/kate/fundamentals.docbook index 9572239ac9..5b562cfc84 100644 --- a/doc/kate/fundamentals.docbook +++ b/doc/kate/fundamentals.docbook @@ -371,6 +371,10 @@ action="simul">&Ctrl;<keycap>W</keycap></keycombo> / <entry><para><keycombo action="simul">&Alt;&Left;</keycombo></para></entry> <entry><para>Previous Tab</para></entry> </row> +<row> +<entry><para><keycombo action="simul">&Ctrl;&Shift;<keycap>T</keycap></keycombo></para></entry> +<entry><para>Reopen Latest Closed Tab</para></entry> +</row> </tbody> </tgroup> diff --git a/doc/kate/menus.docbook b/doc/kate/menus.docbook index 00a26cfe6e..d810c61fa4 100644 --- a/doc/kate/menus.docbook +++ b/doc/kate/menus.docbook @@ -748,6 +748,19 @@ The new instance will be identical to your previous instance. </listitem> </varlistentry> +<varlistentry id="reopen-latest-closed-tab"> +<term><menuchoice> +<shortcut> +<keycombo action="simul">&Ctrl;&Shift;<keycap>T</keycap></keycombo> +</shortcut> +<guimenu>View</guimenu> +<guimenuitem>Reopen Latest Closed Tab</guimenuitem> +</menuchoice></term> +<listitem> +<para>Reopens the latest closed tab.</para> +</listitem> +</varlistentry> + <varlistentry id="view-quick-open"> <term><menuchoice> <shortcut>