1 /* This file is part of the KDE project
2 SPDX-FileCopyrightText: 2021 Méven Car <meven.car@kdemail.net>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7 #include "katestashmanager.h"
8
9 #include "katedebug.h"
10
11 #include "kateapp.h"
12 #include "katedocmanager.h"
13 #include "kateviewmanager.h"
14
15 #include "ksharedconfig.h"
16
17 #include <QDir>
18 #include <QFile>
19 #include <QSaveFile>
20 #include <QTextCodec>
21 #include <QUrl>
22
KateStashManager(QObject * parent)23 KateStashManager::KateStashManager(QObject *parent)
24 : QObject(parent)
25 {
26 }
27
clearStashForSession(const KateSession::Ptr session)28 void KateStashManager::clearStashForSession(const KateSession::Ptr session)
29 {
30 const QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
31 QDir dir(appDataPath);
32 if (dir.exists(QStringLiteral("stash"))) {
33 dir.cd(QStringLiteral("stash"));
34 const QString sessionName = session->name();
35 if (dir.exists(sessionName)) {
36 dir.cd(sessionName);
37 dir.removeRecursively();
38 }
39 }
40 }
41
stashDocuments(KConfig * config,const QList<KTextEditor::Document * > & documents)42 void KateStashManager::stashDocuments(KConfig *config, const QList<KTextEditor::Document *> &documents)
43 {
44 // prepare stash directory
45 const QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
46 QDir dir(appDataPath);
47 dir.mkdir(QStringLiteral("stash"));
48 dir.cd(QStringLiteral("stash"));
49
50 const auto activeSession = KateApp::self()->sessionManager()->activeSession();
51 if (!activeSession || activeSession->isAnonymous() || activeSession->name().isEmpty()) {
52 qDebug(LOG_KATE) << "Could not stash files without a session";
53 return;
54 }
55
56 const QString sessionName = activeSession->name();
57 dir.mkdir(sessionName);
58 dir.cd(sessionName);
59
60 int i = 0;
61 for (KTextEditor::Document *doc : documents) {
62 const QString entryName = QStringLiteral("Document %1").arg(i);
63 KConfigGroup cg(config, entryName);
64
65 // stash the file content
66 if (doc->isModified()) {
67 stashDocument(doc, entryName, cg, dir.path());
68 }
69
70 i++;
71 }
72 }
73
willStashDoc(KTextEditor::Document * doc)74 bool KateStashManager::willStashDoc(KTextEditor::Document *doc)
75 {
76 const auto activeSession = KateApp::self()->sessionManager()->activeSession();
77 if (!activeSession || activeSession->isAnonymous() || activeSession->name().isEmpty()) {
78 return false;
79 }
80 if (doc->text().isEmpty()) {
81 return false;
82 }
83 if (doc->url().isEmpty()) {
84 return m_stashNewUnsavedFiles;
85 }
86 if (doc->url().isLocalFile()) {
87 const QString path = doc->url().toLocalFile();
88 if (path.startsWith(QDir::tempPath())) {
89 return false; // inside tmp resource, do not stash
90 }
91 return m_stashUnsavedChanges;
92 }
93 return false;
94 }
95
stashDocument(KTextEditor::Document * doc,const QString & stashfileName,KConfigGroup & kconfig,const QString & path)96 void KateStashManager::stashDocument(KTextEditor::Document *doc, const QString &stashfileName, KConfigGroup &kconfig, const QString &path)
97 {
98 if (!willStashDoc(doc)) {
99 return;
100 }
101 // Stash changes
102 QString stashedFile = path + QStringLiteral("/") + stashfileName;
103
104 // save the current document changes to stash
105 if (!doc->saveAs(QUrl::fromLocalFile(stashedFile))) {
106 qCWarning(LOG_KATE) << "Could not write to stash file" << stashedFile;
107 return;
108 }
109
110 // write stash metadata to config
111 kconfig.writeEntry("stashedFile", stashedFile);
112 if (doc->url().isValid()) {
113 // save checksum for already-saved documents
114 kconfig.writeEntry("checksum", doc->checksum());
115 }
116
117 kconfig.sync();
118 }
119
popDocument(KTextEditor::Document * doc,const KConfigGroup & kconfig)120 bool KateStashManager::popDocument(KTextEditor::Document *doc, const KConfigGroup &kconfig)
121 {
122 if (!(kconfig.hasKey("stashedFile"))) {
123 return false;
124 }
125 qCDebug(LOG_KATE) << "popping stashed document" << doc->url();
126
127 // read metadata
128 auto stashedFile = kconfig.readEntry("stashedFile");
129 auto url = QUrl(kconfig.readEntry("URL"));
130
131 bool checksumOk = true;
132 if (url.isValid()) {
133 const auto sum = kconfig.readEntry(QStringLiteral("checksum")).toLatin1().constData();
134 checksumOk = sum != doc->checksum();
135 }
136
137 if (checksumOk) {
138 // open file with stashed content
139 QFile file(stashedFile);
140 file.open(QIODevice::ReadOnly);
141 QTextStream out(&file);
142 const auto codec = QTextCodec::codecForName(kconfig.readEntry("Encoding").toLocal8Bit());
143 if (codec != nullptr) {
144 out.setCodec(codec);
145 }
146
147 doc->setText(out.readAll());
148
149 // clean stashed file
150 if (!file.remove()) {
151 qCWarning(LOG_KATE) << "Could not remove stash file" << stashedFile;
152 }
153
154 return true;
155 } else {
156 return false;
157 }
158 }
159