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>

Reply via email to