From ef6ba0771b51d4dd649fc75c021e149a11251264 Mon Sep 17 00:00:00 2001 From: Stephen Brennan Date: Thu, 4 Sep 2025 08:38:17 -0700 Subject: [PATCH 1/2] Extract docsets into the docset directory The use of QFile::move() to move the docset into its final location only works if the temporary directory and the docset directory are on the same filesystem. Rather than recursively copying the docset directory, just use a temporary directory within the docset directory. This should make it nearly certain that they reside on the same filesystem. --- src/plugin.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugin.cpp b/src/plugin.cpp index 286d087..dfaf66d 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -229,10 +229,11 @@ void Plugin::downloadDocset(uint index) connect(download_, &QNetworkReply::finished, this, [this, &ds] { + auto docsetDir = QDir(docsetsLocation()); if (download_) // else aborted { debug(tr("Download finished.")); - if (auto tmp_dir = QTemporaryDir(); + if (auto tmp_dir = QTemporaryDir(docsetDir.filePath(u"extractXXXXXX"_s)); tmp_dir.isValid()) { // write downloaded data to file @@ -253,8 +254,7 @@ void Plugin::downloadDocset(uint index) it.hasNext()) { auto src = it.next(); - auto dst = QDir(docsetsLocation()) - .filePath(u"%1.docset"_s.arg(ds.name)); + auto dst = docsetDir.filePath(u"%1.docset"_s.arg(ds.name)); debug(tr("Renaming '%1' to '%2'").arg(src, dst)); if (QFile::rename(src, dst)) { From 872a50075cdfe1ac787c852ace43feee9a44d5f9 Mon Sep 17 00:00:00 2001 From: Stephen Brennan Date: Thu, 4 Sep 2025 10:11:51 -0700 Subject: [PATCH 2/2] Allow custom docsets Zeal has an extensive library of docsets, but it's possible to manually create docsets too. Allow the user to place docsets into a separate directory (~/.local/share/albert/docs/custom_docsets) with an appropriate icon file. Search this directory for valid docsets and index them just like the ones downloaded from Zeal. --- src/plugin.cpp | 16 ++++++++++++++++ src/plugin.h | 1 + 2 files changed, 17 insertions(+) diff --git a/src/plugin.cpp b/src/plugin.cpp index dfaf66d..a434d92 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -98,6 +98,7 @@ Plugin::Plugin() throw "QSQLITE driver unavailable"; tryCreateDirectory(docsetsLocation()); + tryCreateDirectory(customDocsetsLocation()); tryCreateDirectory(iconsLocation()); connect(this, &Plugin::docsetsChanged, this, &Plugin::updateIndexItems); @@ -167,6 +168,19 @@ void Plugin::updateDocsetList() docsets_.clear(); + for (const auto &entry: filesystem::directory_iterator(customDocsetsLocation())) + { + const auto path = entry.path(); + if (entry.is_directory() && path.extension() == ".docset" && filesystem::exists(path / "icon.png")) + { + const auto dirname = path.filename().string(); + const auto name = QString::fromStdString(dirname.substr(0, dirname.length() - sizeof(".docset") + 1)); + const auto icon_path = QString::fromStdString((path / "icon.png").string()); + docsets_.emplace_back(name, name, u"custom"_s, icon_path); + docsets_.back().path = QString::fromStdString(path.string()); + } + } + QJsonParseError parse_error; const QJsonDocument json_document = QJsonDocument::fromJson(replyData, &parse_error); if (parse_error.error == QJsonParseError::NoError) @@ -350,4 +364,6 @@ void Plugin::error(const QString &msg, QWidget * modal_parent) filesystem::path Plugin::docsetsLocation() const { return dataLocation() / "docsets"; } +filesystem::path Plugin::customDocsetsLocation() const { return dataLocation() / "custom_docsets"; } + filesystem::path Plugin::iconsLocation() const { return dataLocation() / "icons"; } diff --git a/src/plugin.h b/src/plugin.h index 4dc5f78..ee9d35f 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -36,6 +36,7 @@ class Plugin : public albert::util::ExtensionPlugin, void debug(const QString &); void error(const QString &, QWidget *modal_parent = nullptr); std::filesystem::path docsetsLocation() const; + std::filesystem::path customDocsetsLocation() const; std::filesystem::path iconsLocation() const; std::vector docsets_;