File indexing completed on 2024-04-28 04:37:03

0001 /*
0002     SPDX-FileCopyrightText: 2009 Radu Benea <radub82@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "filemanagerlistjob.h"
0008 
0009 #include <interfaces/iproject.h>
0010 #include <project/projectmodel.h>
0011 
0012 #include "path.h"
0013 #include "debug.h"
0014 
0015 #include <KIO/ListJob>
0016 
0017 #include <QtConcurrentRun>
0018 #include <QDir>
0019 
0020 using namespace KDevelop;
0021 
0022 namespace {
0023 bool isChildItem(ProjectBaseItem* parent, ProjectBaseItem* child)
0024 {
0025     do {
0026         if (child == parent) {
0027             return true;
0028         }
0029         child = child->parent();
0030     } while(child);
0031     return false;
0032 }
0033 }
0034 
0035 FileManagerListJob::FileManagerListJob(ProjectFolderItem* item)
0036     : m_item(item)
0037 {
0038     setCapabilities(Killable);
0039 
0040     qRegisterMetaType<KIO::UDSEntryList>("KIO::UDSEntryList");
0041     qRegisterMetaType<KJob*>();
0042 
0043     /* the following line is not an error in judgment, apparently starting a
0044      * listJob while the previous one hasn't self-destructed takes a lot of time,
0045      * so we give the job a chance to selfdestruct first */
0046     connect( this, &FileManagerListJob::nextJob, this, &FileManagerListJob::startNextJob, Qt::QueuedConnection );
0047 
0048     addSubDir(item);
0049 
0050 #ifdef TIME_IMPORT_JOB
0051     m_timer.start();
0052 #endif
0053 }
0054 
0055 FileManagerListJob::~FileManagerListJob()
0056 {
0057     doKill();
0058     m_localFolderFuture.waitForFinished();
0059 }
0060 
0061 void FileManagerListJob::addSubDir( ProjectFolderItem* item )
0062 {
0063     Q_ASSERT(!m_listQueue.contains(item));
0064     Q_ASSERT(!m_item || m_item == item || m_item->path().isDirectParentOf(item->path()));
0065 
0066     m_listQueue.enqueue(item);
0067 }
0068 
0069 void FileManagerListJob::handleRemovedItem(ProjectBaseItem* item)
0070 {
0071     // NOTE: the item could be (partially) destroyed already, thus it's not save
0072     // to call e.g. item->folder to cast the base item to a folder item...
0073     auto *folder = reinterpret_cast<ProjectFolderItem*>(item);
0074     m_listQueue.removeAll(folder);
0075 
0076     if (isChildItem(item, m_item)) {
0077         kill();
0078     }
0079 }
0080 
0081 void FileManagerListJob::remoteFolderSubjobEntriesFound(KJob* job, const KIO::UDSEntryList& foundEntries)
0082 {
0083     Q_UNUSED(job);
0084     entryList.append(foundEntries);
0085 }
0086 
0087 void FileManagerListJob::startNextJob()
0088 {
0089     if (m_listQueue.empty() || isCanceled()) {
0090         return;
0091     }
0092 
0093 #ifdef TIME_IMPORT_JOB
0094     m_subTimer.start();
0095 #endif
0096 
0097     m_item = m_listQueue.dequeue();
0098     if (m_item->path().isLocalFile()) {
0099         // optimized version for local projects using QDir directly
0100         m_localFolderFuture = QtConcurrent::run([this] (const Path& path) {
0101             if (isCanceled()) {
0102                 return;
0103             }
0104             QDir dir(path.toLocalFile());
0105             const auto entries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden);
0106             if (isCanceled()) {
0107                 return;
0108             }
0109             KIO::UDSEntryList results;
0110             std::transform(entries.begin(), entries.end(), std::back_inserter(results), [] (const QFileInfo& info) -> KIO::UDSEntry {
0111                 KIO::UDSEntry entry;
0112                 entry.fastInsert(KIO::UDSEntry::UDS_NAME, info.fileName());
0113                 if (info.isDir()) {
0114                     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, QT_STAT_DIR);
0115                 }
0116                 if (info.isSymLink()) {
0117                     entry.fastInsert(KIO::UDSEntry::UDS_LINK_DEST, info.symLinkTarget());
0118                 }
0119                 return entry;
0120             });
0121             QMetaObject::invokeMethod(this, "handleResults", Q_ARG(KIO::UDSEntryList, results));
0122         }, m_item->path());
0123     } else {
0124         KIO::ListJob* job = KIO::listDir( m_item->path().toUrl(), KIO::HideProgressInfo );
0125         job->addMetaData(QStringLiteral("details"), QStringLiteral("0"));
0126         connect(job, &KIO::ListJob::entries, this, &FileManagerListJob::remoteFolderSubjobEntriesFound);
0127         connect(job, &KJob::finished, this, &FileManagerListJob::remoteFolderSubjobFinished);
0128 
0129         m_remoteFolderSubjob = job;
0130     }
0131 }
0132 
0133 void FileManagerListJob::remoteFolderSubjobFinished(KJob* job)
0134 {
0135     if( job && job->error() ) {
0136         qCDebug(FILEMANAGER) << "error in list job:" << job->error() << job->errorString();
0137     }
0138 
0139     Q_ASSERT(m_remoteFolderSubjob == job);
0140     m_remoteFolderSubjob = nullptr;
0141 
0142     handleResults(entryList);
0143     entryList.clear();
0144 }
0145 
0146 void FileManagerListJob::handleResults(const KIO::UDSEntryList& entriesIn)
0147 {
0148     if (isCanceled()) {
0149         return;
0150     }
0151 
0152 #ifdef TIME_IMPORT_JOB
0153     {
0154         auto waited = m_subTimer.elapsed();
0155         m_subWaited += waited;
0156         qCDebug(PROJECT) << "TIME FOR SUB JOB:" << waited << m_subWaited;
0157     }
0158 #endif
0159 
0160     emit entries(this, m_item, entriesIn);
0161 
0162     if( m_listQueue.isEmpty() ) {
0163         emitResult();
0164 
0165 #ifdef TIME_IMPORT_JOB
0166         qCDebug(PROJECT) << "TIME FOR LISTJOB:" << m_timer.elapsed();
0167 #endif
0168     } else {
0169         emit nextJob();
0170     }
0171 }
0172 
0173 void FileManagerListJob::start()
0174 {
0175     startNextJob();
0176 }
0177 
0178 bool FileManagerListJob::doKill()
0179 {
0180     m_canceled.store(true, std::memory_order_relaxed);
0181     if (m_remoteFolderSubjob) {
0182         m_remoteFolderSubjob->kill();
0183     }
0184     return true;
0185 }
0186 
0187 bool FileManagerListJob::isCanceled() const
0188 {
0189     return m_canceled.load(std::memory_order_relaxed);
0190 }
0191 
0192 #include "moc_filemanagerlistjob.cpp"