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"