File indexing completed on 2024-05-05 04:40:49
0001 /* 0002 SPDX-FileCopyrightText: 2012 Aleix Pol <aleixpol@kde.org> 0003 SPDX-FileCopyrightText: 2012 Milian Wolff <mail@milianw.de> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "qmljsparsejob.h" 0009 0010 #include <language/backgroundparser/urlparselock.h> 0011 #include <custom-definesandincludes/idefinesandincludesmanager.h> 0012 0013 #include <language/duchain/duchainlock.h> 0014 #include <language/duchain/duchainutils.h> 0015 #include <language/duchain/duchain.h> 0016 #include <language/duchain/parsingenvironment.h> 0017 #include <language/interfaces/ilanguagesupport.h> 0018 #include <interfaces/icore.h> 0019 #include <interfaces/iprojectcontroller.h> 0020 #include <interfaces/iproject.h> 0021 #include <project/projectmodel.h> 0022 0023 #include "duchain/cache.h" 0024 #include "duchain/declarationbuilder.h" 0025 #include "duchain/parsesession.h" 0026 #include "duchain/usebuilder.h" 0027 0028 #include "debug.h" 0029 0030 #include <QReadLocker> 0031 0032 using namespace KDevelop; 0033 0034 /* 0035 * This function has been copied from kdev-clang 0036 * 0037 * SPDX-FileCopyrightText: 2013 Olivier de Gaalon <olivier.jg@gmail.com> and Milian Wolff <mail@milianw.de> 0038 * Licensed under the GPL v2+ 0039 */ 0040 ProjectFileItem* findProjectFileItem(const IndexedString& url) 0041 { 0042 ProjectFileItem* file = nullptr; 0043 0044 const auto& projects = ICore::self()->projectController()->projects(); 0045 for (auto project: projects) { 0046 const auto files = project->filesForPath(url); 0047 if (files.isEmpty()) { 0048 continue; 0049 } 0050 0051 file = files.last(); 0052 0053 // A file might be defined in different targets. 0054 // Prefer file items defined inside a target with non-empty includes. 0055 for (auto f: files) { 0056 if (!dynamic_cast<ProjectTargetItem*>(f->parent())) { 0057 continue; 0058 } 0059 file = f; 0060 if (!IDefinesAndIncludesManager::manager()->includes(f, IDefinesAndIncludesManager::ProjectSpecific).isEmpty()) { 0061 break; 0062 } 0063 } 0064 } 0065 return file; 0066 } 0067 0068 QmlJsParseJob::QmlJsParseJob(const IndexedString& url, ILanguageSupport* languageSupport) 0069 : ParseJob(url, languageSupport) 0070 { 0071 // Tell the cache that this file has custom include directories 0072 if (auto file = findProjectFileItem(url)) { 0073 QmlJS::Cache::instance().setFileCustomIncludes( 0074 url, 0075 IDefinesAndIncludesManager::manager()->includes(file, 0076 IDefinesAndIncludesManager::Type( 0077 IDefinesAndIncludesManager::ProjectSpecific | IDefinesAndIncludesManager::UserDefined)) 0078 ); 0079 } else { 0080 QmlJS::Cache::instance().setFileCustomIncludes( 0081 url, 0082 IDefinesAndIncludesManager::manager()->includes(url.str(), 0083 IDefinesAndIncludesManager::ProjectSpecific) 0084 ); 0085 } 0086 } 0087 0088 void QmlJsParseJob::run(ThreadWeaver::JobPointer pointer, ThreadWeaver::Thread* thread) 0089 { 0090 Q_UNUSED(pointer) 0091 Q_UNUSED(thread) 0092 0093 UrlParseLock urlLock(document()); 0094 if (abortRequested() || !isUpdateRequired(ParseSession::languageString())) { 0095 return; 0096 } 0097 0098 // Don't parse this file if one of its dependencies is not up to date 0099 const auto& dependencies = QmlJS::Cache::instance().dependencies(document()); 0100 for (auto& dependency : dependencies) { 0101 if (!QmlJS::Cache::instance().isUpToDate(dependency)) { 0102 QmlJS::Cache::instance().setUpToDate(document(), false); 0103 return; 0104 } 0105 } 0106 0107 qCDebug(KDEV_QMLJS) << "parsing" << document().str(); 0108 0109 ProblemPointer p = readContents(); 0110 if (p) { 0111 //TODO: associate problem with topducontext 0112 return; 0113 } 0114 0115 ParseSession session(document(), QString::fromUtf8(contents().contents), priority()); 0116 0117 if (abortRequested()) { 0118 return; 0119 } 0120 0121 ReferencedTopDUContext context; 0122 { 0123 DUChainReadLocker lock; 0124 context = DUChainUtils::standardContextForUrl(document().toUrl()); 0125 } 0126 if (context) { 0127 translateDUChainToRevision(context); 0128 context->setRange(RangeInRevision(0, 0, INT_MAX, INT_MAX)); 0129 } 0130 0131 if (session.ast()) { 0132 QReadLocker parseLock(languageSupport()->parseLock()); 0133 0134 if (abortRequested()) { 0135 abortJob(); 0136 return; 0137 } 0138 0139 DeclarationBuilder builder(&session); 0140 context = builder.build(document(), session.ast(), context); 0141 0142 if (abortRequested()) { 0143 abortJob(); 0144 return; 0145 } 0146 0147 if ( context && (minimumFeatures() & TopDUContext::AllDeclarationsContextsAndUses) ) { 0148 UseBuilder useBuilder(&session); 0149 useBuilder.buildUses(session.ast()); 0150 } 0151 } 0152 0153 if (abortRequested()) { 0154 abortJob(); 0155 return; 0156 } 0157 0158 if (!context) { 0159 DUChainWriteLocker lock; 0160 auto* file = new ParsingEnvironmentFile(document()); 0161 file->setLanguage(ParseSession::languageString()); 0162 context = new TopDUContext(document(), RangeInRevision(0, 0, INT_MAX, INT_MAX), file); 0163 DUChain::self()->addDocumentChain(context); 0164 } 0165 0166 setDuChain(context); 0167 0168 // If the file has become up to date, reparse its importers 0169 bool dependenciesOk = session.allDependenciesSatisfied(); 0170 0171 QmlJS::Cache::instance().setUpToDate(document(), dependenciesOk); 0172 0173 if (dependenciesOk) { 0174 session.reparseImporters(); 0175 } 0176 0177 { 0178 DUChainWriteLocker lock; 0179 context->setProblems(session.problems()); 0180 0181 context->setFeatures(minimumFeatures()); 0182 ParsingEnvironmentFilePointer file = context->parsingEnvironmentFile(); 0183 Q_ASSERT(file); 0184 file->setModificationRevision(contents().modification); 0185 DUChain::self()->updateContextEnvironment( context->topContext(), file.data() ); 0186 } 0187 highlightDUChain(); 0188 0189 DUChain::self()->emitUpdateReady(document(), duChain()); 0190 0191 if (session.isParsedCorrectly()) { 0192 qCDebug(KDEV_QMLJS) << "===Success===" << document().str(); 0193 } else { 0194 qCDebug(KDEV_QMLJS) << "===Failed===" << document().str() << session.problems(); 0195 } 0196 } 0197 0198 #include "moc_qmljsparsejob.cpp"