File indexing completed on 2024-05-12 04:37:43
0001 /* 0002 SPDX-FileCopyrightText: 2009 David Nolden <david.nolden.kdevelop@art-master.de> 0003 0004 SPDX-License-Identifier: LGPL-2.0-only 0005 */ 0006 0007 #include "parseprojectjob.h" 0008 0009 #include <debug.h> 0010 0011 #include <interfaces/icore.h> 0012 #include <interfaces/ilanguagecontroller.h> 0013 #include <interfaces/idocumentcontroller.h> 0014 #include <interfaces/iproject.h> 0015 #include <interfaces/icompletionsettings.h> 0016 0017 #include <language/backgroundparser/backgroundparser.h> 0018 0019 #include <KLocalizedString> 0020 0021 #include <QCoreApplication> 0022 #include <QPointer> 0023 #include <QSet> 0024 #include <QTimer> 0025 0026 using namespace KDevelop; 0027 0028 class KDevelop::ParseProjectJobPrivate 0029 { 0030 public: 0031 explicit ParseProjectJobPrivate(bool forceUpdate, bool parseAllProjectSources) 0032 : forceUpdate(forceUpdate) 0033 , parseAllProjectSources{parseAllProjectSources} 0034 { 0035 } 0036 0037 const bool forceUpdate; 0038 const bool parseAllProjectSources; 0039 int fileCountLeftToParse = 0; 0040 QSet<IndexedString> filesToParse; 0041 }; 0042 0043 bool ParseProjectJob::doKill() 0044 { 0045 qCDebug(LANGUAGE) << "stopping project parse job"; 0046 ICore::self()->languageController()->backgroundParser()->revertAllRequests(this); 0047 return true; 0048 } 0049 0050 ParseProjectJob::~ParseProjectJob() = default; 0051 0052 ParseProjectJob::ParseProjectJob(IProject* project, bool forceUpdate, bool parseAllProjectSources) 0053 : d_ptr{new ParseProjectJobPrivate(forceUpdate, parseAllProjectSources)} 0054 { 0055 Q_D(ParseProjectJob); 0056 0057 if (parseAllProjectSources) { 0058 d->filesToParse = project->fileSet(); 0059 } else { 0060 // In case we don't want to parse the whole project, still add all currently open files that belong to the project to the background-parser 0061 const auto documents = ICore::self()->documentController()->openDocuments(); 0062 const auto projectFiles = project->fileSet(); 0063 for (auto* document : documents) { 0064 const auto path = IndexedString(document->url()); 0065 if (projectFiles.contains(path)) { 0066 d->filesToParse.insert(path); 0067 } 0068 } 0069 } 0070 d->fileCountLeftToParse = d->filesToParse.size(); 0071 0072 setCapabilities(Killable); 0073 0074 setObjectName(i18np("Process 1 file in %2", "Process %1 files in %2", d->filesToParse.size(), project->name())); 0075 } 0076 0077 void ParseProjectJob::updateReady(const IndexedString& url, const ReferencedTopDUContext& topContext) 0078 { 0079 Q_D(ParseProjectJob); 0080 0081 Q_UNUSED(url); 0082 Q_UNUSED(topContext); 0083 --d->fileCountLeftToParse; 0084 Q_ASSERT(d->fileCountLeftToParse >= 0); 0085 if (d->fileCountLeftToParse == 0) { 0086 deleteLater(); 0087 } 0088 } 0089 0090 void ParseProjectJob::start() 0091 { 0092 Q_D(ParseProjectJob); 0093 0094 if (d->filesToParse.isEmpty()) { 0095 deleteLater(); 0096 return; 0097 } 0098 0099 qCDebug(LANGUAGE) << "starting project parse job"; 0100 // Avoid calling QCoreApplication::processEvents() directly in start() to prevent 0101 // a crash in RunController::checkState(). 0102 QTimer::singleShot(0, this, &ParseProjectJob::queueFilesToParse); 0103 } 0104 0105 void ParseProjectJob::queueFilesToParse() 0106 { 0107 Q_D(ParseProjectJob); 0108 0109 const auto isJobKilled = [this] { 0110 if (Q_UNLIKELY(isFinished())) { 0111 qCDebug(LANGUAGE) << "Aborting queuing project files to parse." 0112 " This job has been killed:" << objectName(); 0113 return true; 0114 } 0115 return false; 0116 }; 0117 0118 if (isJobKilled()) { 0119 return; 0120 } 0121 0122 TopDUContext::Features processingLevel = d->filesToParse.size() < 0123 ICore::self()->languageController()->completionSettings()-> 0124 minFilesForSimplifiedParsing() ? 0125 TopDUContext::VisibleDeclarationsAndContexts : TopDUContext:: 0126 SimplifiedVisibleDeclarationsAndContexts; 0127 TopDUContext::Features openDocumentProcessingLevel{TopDUContext::AllDeclarationsContextsAndUses}; 0128 0129 if (d->forceUpdate) { 0130 if (processingLevel & TopDUContext::VisibleDeclarationsAndContexts) { 0131 processingLevel = TopDUContext::AllDeclarationsContextsAndUses; 0132 } 0133 processingLevel |= TopDUContext::ForceUpdate; 0134 openDocumentProcessingLevel |= TopDUContext::ForceUpdate; 0135 } 0136 0137 if (auto currentDocument = ICore::self()->documentController()->activeDocument()) { 0138 const auto path = IndexedString(currentDocument->url()); 0139 const auto fileIt = d->filesToParse.constFind(path); 0140 if (fileIt != d->filesToParse.cend()) { 0141 ICore::self()->languageController()->backgroundParser()->addDocument(path, 0142 openDocumentProcessingLevel, BackgroundParser::BestPriority, this); 0143 d->filesToParse.erase(fileIt); 0144 } 0145 } 0146 0147 int priority{BackgroundParser::InitialParsePriority}; 0148 const int openDocumentPriority{10}; 0149 if (d->parseAllProjectSources) { 0150 // Add all currently open files that belong to the project to the 0151 // background-parser, so that they'll be parsed first of all. 0152 const auto documents = ICore::self()->documentController()->openDocuments(); 0153 for (auto* document : documents) { 0154 const auto path = IndexedString(document->url()); 0155 const auto fileIt = d->filesToParse.constFind(path); 0156 if (fileIt != d->filesToParse.cend()) { 0157 ICore::self()->languageController()->backgroundParser()->addDocument(path, 0158 openDocumentProcessingLevel, openDocumentPriority, this); 0159 d->filesToParse.erase(fileIt); 0160 } 0161 } 0162 } else { 0163 // In this case the constructor inserts only open documents into d->filesToParse. 0164 processingLevel = openDocumentProcessingLevel; 0165 priority = openDocumentPriority; 0166 } 0167 0168 // prevent UI-lockup by processing events after some files 0169 // esp. noticeable when dealing with huge projects 0170 const int processAfter = 1000; 0171 int processed = 0; 0172 // guard against reentrancy issues, see also bug 345480 0173 auto crashGuard = QPointer<ParseProjectJob> {this}; 0174 for (const IndexedString& url : qAsConst(d->filesToParse)) { 0175 ICore::self()->languageController()->backgroundParser()->addDocument(url, processingLevel, 0176 priority, 0177 this); 0178 ++processed; 0179 if (processed == processAfter) { 0180 QCoreApplication::processEvents(); 0181 if (Q_UNLIKELY(!crashGuard)) { 0182 qCDebug(LANGUAGE) << "Aborting queuing project files to parse." 0183 " This job has been destroyed."; 0184 return; 0185 } 0186 if (isJobKilled()) { 0187 return; 0188 } 0189 processed = 0; 0190 } 0191 } 0192 0193 d->filesToParse = {}; // free memory or prevent detaching 0194 } 0195 0196 #include "moc_parseprojectjob.cpp"