8#include <QtQmlCompiler/private/qqmljsutils_p.h>
16Q_STATIC_LOGGING_CATEGORY(lcCodeModelManager,
"qt.languageserver.codemodelmanager")
18using namespace QQmlJS::Dom;
19using namespace Qt::StringLiterals;
21void QQmlCodeModelManager::onCMakeProberFinished(
int exitCode, QProcess::ExitStatus exitStatus)
23 if (m_cmakeStatus == DoesNotHaveCMake)
26 if (exitStatus != QProcess::NormalExit || exitCode != 0) {
30 m_cmakeStatus = HasCMake;
31 for (
const auto &ws : m_workspaces)
32 ws.codeModel->tryEnableCMakeCalls(&m_processScheduler);
36
37
38
39
40
41void QQmlCodeModelManager::tryEnableCMakeCalls()
43 m_cmakeStatus = IsProbingCMake;
45 m_cmakeProber.setProgram(u"cmake"_s);
46 m_cmakeProber.setArguments({ u"--version"_s });
47 QObject::connect(&m_cmakeProber, &QProcess::finished,
this,
48 &QQmlCodeModelManager::onCMakeProberFinished);
49 QObject::connect(&m_cmakeProber, &QProcess::errorOccurred,
this,
50 &QQmlCodeModelManager::disableCMakeCalls);
52 m_cmakeProber.start();
55QQmlCodeModelManager::QQmlCodeModelManager(QObject *parent, QQmlToolingSharedSettings *settings)
58 const QByteArray defaultCodeModel;
59 appendWorkspace(defaultCodeModel, ManagedByServer);
60 connect(&m_processScheduler, &QProcessScheduler::done,
this,
61 &QQmlCodeModelManager::onBuildFinished);
62 connect(&m_processScheduler, &QProcessScheduler::started,
this,
63 &QQmlCodeModelManager::backgroundBuildStarted);
64 connect(&m_processScheduler, &QProcessScheduler::cancelled,
this,
65 &QQmlCodeModelManager::backgroundBuildCancelled);
68void QQmlCodeModelManager::onBuildFinished(
const QByteArray &url)
70 qCInfo(lcCodeModelManager) <<
"Build for workspace" << url <<
"finished.";
71 auto it = findWorkspace(url);
72 if (it == fallbackWorkspace() || it == m_workspaces.end()) {
73 emit backgroundBuildFinished(url);
78 const QStringList buildPaths = it->codeModel->buildPaths();
79 m_buildInformation.loadSettingsFrom(buildPaths, ForceUpdate);
80 setBuildPathsOn(&*it, buildPaths, AppendPathsFromFallback);
81 it->codeModel->reloadAllOpenFiles();
82 emit backgroundBuildFinished(url);
83 emit configurationChanged();
86void QQmlCodeModelManager::prepareForShutdown()
88 for (
auto it = m_workspaces.begin(), end = m_workspaces.end(); it != end; ++it)
89 it->codeModel->prepareForShutdown();
92QQmlCodeModelManager::~QQmlCodeModelManager()
95 m_cmakeProber.waitForFinished();
99QQmlCodeModelManager::WorkspaceIterator
100QQmlCodeModelManager::findWorkspaceForFile(
const QByteArray &url)
102 Q_ASSERT(!m_workspaces.empty());
104 if (
auto it = m_file2CodeModel.find(url); it != m_file2CodeModel.end()) {
105 const auto result = findWorkspace(it->second);
106 Q_ASSERT(result != m_workspaces.end());
110 long longestRootUrl = 0;
111 WorkspaceIterator result = m_workspaces.begin();
112 for (
auto it = m_workspaces.begin(), end = m_workspaces.end(); it != end; ++it) {
115 const QByteArray rootUrl = it->url;
116 if (!url.startsWith(rootUrl))
119 if (rootUrl.size() == url.size())
122 const long rootUrlLength = rootUrl.length();
123 if (rootUrlLength > longestRootUrl) {
124 longestRootUrl = rootUrlLength;
130 if (
const ModuleSetting moduleSetting =
131 m_buildInformation.settingFor(QUrl::fromEncoded(url).toLocalFile());
132 !moduleSetting.importPaths.isEmpty()) {
133 const QByteArray rootUrl = QUrl::fromLocalFile(moduleSetting.sourceFolder).toEncoded();
134 if (longestRootUrl < rootUrl.size()) {
135 appendWorkspace(rootUrl, ManagedByServer);
136 return --m_workspaces.end();
139 Q_ASSERT(result != m_workspaces.end());
143QQmlCodeModel *QQmlCodeModelManager::findCodeModelForFile(
const QByteArray &url)
145 return findWorkspaceForFile(url)->codeModel.get();
148QQmlCodeModelManager::WorkspaceMutableIterator
149QQmlCodeModelManager::findWorkspace(
const QByteArray &url)
151 return std::find_if(m_workspaces.begin(), m_workspaces.end(),
152 [&url](
const QQmlWorkspace &ws) {
return ws.url == url; });
155void QQmlCodeModelManager::setBuildPathsOn(
const QQmlWorkspace *ws,
const QStringList &buildFolder,
156 SetBuildPathOption option)
158 ws->codeModel->setBuildPaths(
160 + (option == DontAppendPathsFromFallback ? QStringList{} : defaultBuildPaths()));
162 const QString file = QUrl::fromEncoded(ws->url).toLocalFile();
163 ws->codeModel->setImportPaths(
164 m_buildInformation.importPathsFor(file)
165 + (option == DontAppendPathsFromFallback ? QStringList{} : defaultImportPaths()));
167 if (
const QStringList resourceFiles = m_buildInformation.resourceFilesFor(file);
168 !resourceFiles.isEmpty()) {
169 ws->codeModel->setResourceFiles(resourceFiles);
173 const QStringList resourceFiles =
174 QQmlJSUtils::resourceFilesFromBuildFolders(ws->codeModel->buildPaths());
175 ws->codeModel->setResourceFiles(
177 + (option == DontAppendPathsFromFallback ? QStringList{} : defaultResourceFiles()));
180void QQmlCodeModelManager::appendWorkspace(
const QByteArray &url, ManagedBy managedBy)
184 ws.codeModel = std::make_unique<QQmlCodeModel>(url,
this, m_settings);
187 if (!url.isEmpty()) {
188 ws.codeModel->setCMakeJobs(defaultCMakeJobs());
189 ws.codeModel->setDocumentationRootPath(defaultDocumentationRootPath());
191 setBuildPathsOn(&ws, {}, AppendPathsFromFallback);
194 QObject::connect(ws.codeModel.get(), &QQmlCodeModel::updatedSnapshot,
this,
195 &QQmlCodeModelManager::updatedSnapshot);
196 ws.managedByClient = managedBy == ManagedByClient;
198 switch (m_cmakeStatus) {
199 case DoesNotHaveCMake:
200 ws.codeModel->disableCMakeCalls();
203 ws.codeModel->tryEnableCMakeCalls(&m_processScheduler);
209 m_workspaces.emplace_back(std::move(ws));
212QQmlCodeModelManager::WorkspaceIterator
213QQmlCodeModelManager::workspaceFromBuildFolder(
const QString &fileName,
214 const QStringList &buildFolders)
216 m_buildInformation.loadSettingsFrom(buildFolders);
217 const ModuleSetting setting = m_buildInformation.settingFor(fileName);
218 QByteArray url = QUrl::fromLocalFile(setting.sourceFolder).toEncoded();
219 if (
auto it = findWorkspace(url); it != m_workspaces.end())
221 appendWorkspace(url, ManagedByServer);
222 return --m_workspaces.end();
225void QQmlCodeModelManager::disableCMakeCalls()
227 m_cmakeStatus = DoesNotHaveCMake;
228 for (
const auto &ws : m_workspaces)
229 ws.codeModel->disableCMakeCalls();
232void QQmlCodeModelManager::cancelBackgroundBuild(
const QByteArray &uri)
234 m_processScheduler.cancel(uri);
237OpenDocumentSnapshot QQmlCodeModelManager::snapshotByUrl(
const QByteArray &url)
239 return findCodeModelForFile(url)->snapshotByUrl(url);
242void QQmlCodeModelManager::removeDirectory(
const QByteArray &url)
244 findCodeModelForFile(url)->removeDirectory(url);
247void QQmlCodeModelManager::newOpenFile(
const QByteArray &url,
int version,
const QString &docText)
249 const auto ws = findWorkspaceForFile(url);
250 m_file2CodeModel[url] = ws->url;
251 ws->codeModel->newOpenFile(url, version, docText);
254OpenDocument QQmlCodeModelManager::openDocumentByUrl(
const QByteArray &url)
256 return findCodeModelForFile(url)->openDocumentByUrl(url);
259RegisteredSemanticTokens &QQmlCodeModelManager::registeredTokens(
const QByteArray &url)
261 return findCodeModelForFile(url)->registeredTokens();
264void QQmlCodeModelManager::closeOpenFile(
const QByteArray &url)
266 m_file2CodeModel.erase(url);
267 const auto it = findWorkspaceForFile(url);
268 it->codeModel->closeOpenFile(url);
271 if (it->url.isEmpty())
275 if ((it->managedByClient && it->toBeClosed) || !it->managedByClient) {
276 if (it->codeModel->isEmpty())
277 m_workspaces.erase(it);
281QList<QByteArray> QQmlCodeModelManager::rootUrls()
const
283 QList<QByteArray> result;
284 result.reserve(m_workspaces.size());
285 for (
const QQmlWorkspace &ws : m_workspaces) {
291void QQmlCodeModelManager::addRootUrls(
const QList<QByteArray> &urls)
293 for (
const QByteArray &url : urls) {
294 if (
const auto it = findWorkspace(url); it != m_workspaces.end()) {
295 it->toBeClosed =
false;
299 appendWorkspace(url, ManagedByClient);
303void QQmlCodeModelManager::removeRootUrls(
const QList<QByteArray> &urls)
305 for (
const QByteArray &url : urls) {
306 if (
auto it = findWorkspace(url); it != m_workspaces.end() && it->managedByClient)
307 it->toBeClosed =
true;
311QStringList QQmlCodeModelManager::importPathsForUrl(
const QByteArray &url)
313 return findCodeModelForFile(url)->importPathsForUrl(url);
316QStringList QQmlCodeModelManager::buildPathsForFileUrl(
const QByteArray &url)
318 return findCodeModelForFile(url)->buildPathsForFileUrl(url);
321QStringList QQmlCodeModelManager::resourceFilesForFileUrl(
const QByteArray &url)
323 if (
const QStringList result =
324 m_buildInformation.resourceFilesFor(QUrl::fromEncoded(url).toLocalFile());
330 return findCodeModelForFile(url)->resourceFiles();
333QByteArray QQmlCodeModelManager::shortestRootUrlForFile(
const QByteArray &fileUrl)
const
336 QByteArray candidate;
339 Q_ASSERT(m_workspaces.size() > 0);
340 Q_ASSERT(m_workspaces.front().url.isEmpty());
341 auto it = std::find_if(
342 ++m_workspaces.cbegin(), m_workspaces.cend(),
343 [&fileUrl](
const QQmlWorkspace &ws) {
return fileUrl.startsWith(ws.url); });
345 if (it != m_workspaces.cend())
348 for (; it != m_workspaces.cend(); ++it) {
349 if (it->url.length() < candidate.length() && fileUrl.startsWith(it->url))
355void QQmlCodeModelManager::setDocumentationRootPath(
const QString &path)
360 Q_ASSERT(m_workspaces.size() == 1);
361 for (
const auto &ws : m_workspaces)
362 ws.codeModel->setDocumentationRootPath(path);
365void QQmlCodeModelManager::setVerbose(
bool verbose)
368 for (
const auto &ws : m_workspaces)
369 ws.codeModel->setVerbose(verbose);
372void QQmlCodeModelManager::setCMakeJobs(
int jobs)
374 for (
const auto &ws : m_workspaces)
375 ws.codeModel->setCMakeJobs(jobs);
378void QQmlCodeModelManager::setBuildPathsForRootUrl(
const QByteArray &url,
const QStringList &paths)
380 m_buildInformation.loadSettingsFrom(paths, ForceUpdate);
384 setBuildPathsOn(&*fallbackWorkspace(), paths, DontAppendPathsFromFallback);
386 for (
auto it = beginNonFallbackWorkspace(), end = endNonFallbackWorkspace(); it != end;
388 setBuildPathsOn(&*it, {}, AppendPathsFromFallback);
390 emit configurationChanged();
394 auto ws = findWorkspaceForFile(url);
395 setBuildPathsOn(&*ws, paths, AppendPathsFromFallback);
396 emit configurationChanged();
399void QQmlCodeModelManager::addOpenToUpdate(
const QByteArray &url)
401 findCodeModelForFile(url)->addOpenToUpdate(url, NormalUpdate);
404void QQmlCodeModelManager::setImportPaths(
const QStringList &paths)
409 Q_ASSERT(m_workspaces.size() == 1);
410 for (
const auto &ws : m_workspaces)
411 ws.codeModel->setImportPaths(paths);
414HelpManager *QQmlCodeModelManager::helpManagerForUrl(
const QByteArray &url)
416 return findCodeModelForFile(url)->helpManager();
#define QmlLSPluginInterface_iid