Revision control
Copy as Markdown
// Copyright (C) 2026 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include <QtCore/qstring.h>
#include "qqmlprogresssupport_p.h"
using namespace Qt::StringLiterals;
QT_BEGIN_NAMESPACE
QQmlProgressSupport::QQmlProgressSupport(QmlLsp::QQmlCodeModelManager *manager)
: m_codeModelManager(manager)
{
}
void QQmlProgressSupport::registerHandlers(QLanguageServer *server,
QLanguageServerProtocol *protocol)
{
m_protocol = protocol;
QObject::connect(server, &QLanguageServer::clientInitialized, this,
&QQmlProgressSupport::clientInitialized);
}
void QQmlProgressSupport::clientInitialized(QLanguageServer *server)
{
if (auto window = server->clientInfo().capabilities.window;
!window || !window->workDoneProgress.value_or(false)) {
return;
}
QObject::connect(m_codeModelManager, &QmlLsp::QQmlCodeModelManager::backgroundBuildStarted,
this, &QQmlProgressSupport::onBackgroundBuildStarted);
QObject::connect(m_codeModelManager, &QmlLsp::QQmlCodeModelManager::backgroundBuildFinished,
this, &QQmlProgressSupport::onBackgroundBuildDone);
QObject::connect(server->notifySignals(),
&QLspNotifySignals::receivedWorkDoneProgressCancelNotification, this,
&QQmlProgressSupport::onBackgroundBuildCancelRequested);
}
int QQmlProgressSupport::Tokens::createUniqueToken(const QByteArray &uri)
{
const int token = m_idForBackgroundBuilds++;
m_tokens.insert(uri, { uri, token, InCreation });
m_uriByToken.insert(token, uri);
return token;
}
QQmlProgressSupport::UriWithToken *QQmlProgressSupport::Tokens::find(const QByteArray &uri)
{
const auto it = m_tokens.find(uri);
return it == m_tokens.end() ? nullptr : &*it;
}
std::optional<QQmlProgressSupport::UriWithToken> QQmlProgressSupport::Tokens::takeToken(int token)
{
const auto it = m_uriByToken.find(token);
if (it == m_uriByToken.end())
return {};
const auto it2 = m_tokens.find(*it);
m_uriByToken.erase(it);
if (it2 == m_tokens.end())
return {};
std::optional<QQmlProgressSupport::UriWithToken> result = std::move(*it2);
m_tokens.erase(it2);
return result;
}
void QQmlProgressSupport::Tokens::removeToken(const QByteArray &uri)
{
const auto it = m_tokens.find(uri);
if (it != m_tokens.end())
return;
if (const auto it2 = m_uriByToken.find(it->token); it2 != m_uriByToken.end())
m_uriByToken.erase(it2);
m_tokens.erase(it);
}
void QQmlProgressSupport::onBackgroundBuildStarted(const QByteArray &uri)
{
QLspSpecification::Requests::WorkDoneProgressCreateParamsType p;
const int token = m_tokens.createUniqueToken(uri);
p.token = token;
m_protocol->requestWorkDoneProgressCreate(p, [this, uri, token]() {
QLspSpecification::ProgressParams beginParams{ token };
QLspSpecification::WorkDoneProgressBegin workDoneProgressBegin{};
workDoneProgressBegin.title = "Qmlls running background build";
workDoneProgressBegin.cancellable = true;
workDoneProgressBegin.message =
"Building \"" + QUrl::fromEncoded(uri).toLocalFile().toUtf8() + "\"";
workDoneProgressBegin.cancellable = true;
beginParams.value = workDoneProgressBegin;
m_protocol->notifyProgress(beginParams);
const auto token = m_tokens.find(uri);
if (!token)
return;
switch (token->status) {
case InCreation:
token->status = Created;
break;
case Created:
Q_ASSERT(false);
case Finished:
onBackgroundBuildDone(token->uri);
break;
}
});
}
void QQmlProgressSupport::onBackgroundBuildDone(const QByteArray &uri)
{
const auto token = m_tokens.find(uri);
if (!token)
return;
switch (token->status) {
case InCreation:
// We can't report progress if the WorkDoneProgressCreate request didn't finish yet. Let the
// WorkDoneProgressCreate callback call this method again after the request was created.
token->status = Finished;
return;
case Finished:
case Created:
QLspSpecification::WorkDoneProgressEnd workDoneProgressEnd;
workDoneProgressEnd.message = "Build terminated";
const QLspSpecification::ProgressParams endParams{ token->token, workDoneProgressEnd };
m_protocol->notifyProgress(endParams);
m_tokens.removeToken(token->uri);
}
}
void QQmlProgressSupport::onBackgroundBuildCancelRequested(
const QLspSpecification::Notifications::WorkDoneProgressCancelParamsType &p)
{
const auto tokenNumber = std::get_if<int>(&p.token);
if (!tokenNumber) {
qCWarning(lspServerLog) << "Ignoring unknown token" << std::get<QByteArray>(p.token)
<< "in cancellation request.";
return;
}
const auto token = m_tokens.takeToken(*tokenNumber);
if (!token) {
qCWarning(lspServerLog) << "Ignoring unknown token" << *tokenNumber
<< "in cancellation request.";
return;
}
m_codeModelManager->cancelBackgroundBuild(token->uri);
}
void QQmlProgressSupport::setupCapabilities(QLspSpecification::ServerCapabilities &) { }
QT_END_NAMESPACE