// Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "progressmanager.h" #include #include #include #include #include using namespace LanguageServerProtocol; namespace LanguageClient { static Q_LOGGING_CATEGORY(LOGPROGRESS, "qtc.languageclient.progress", QtWarningMsg); ProgressManager::ProgressManager() {} ProgressManager::~ProgressManager() { reset(); } void ProgressManager::handleProgress(const LanguageServerProtocol::ProgressParams ¶ms) { const ProgressToken &token = params.token(); ProgressParams::ProgressType value = params.value(); if (auto begin = std::get_if(&value)) beginProgress(token, *begin); else if (auto report = std::get_if(&value)) reportProgress(token, *report); else if (auto end = std::get_if(&value)) endProgress(token, *end); } void ProgressManager::setTitleForToken(const LanguageServerProtocol::ProgressToken &token, const QString &message) { m_titles.insert(token, message); } void ProgressManager::setClickHandlerForToken(const LanguageServerProtocol::ProgressToken &token, const std::function &handler) { m_clickHandlers.insert(token, handler); } void ProgressManager::setCancelHandlerForToken(const LanguageServerProtocol::ProgressToken &token, const std::function &handler) { m_cancelHandlers.insert(token, handler); } void ProgressManager::reset() { const QList &tokens = m_progress.keys(); for (const ProgressToken &token : tokens) endProgressReport(token); } bool ProgressManager::isProgressEndMessage(const LanguageServerProtocol::ProgressParams ¶ms) { return std::holds_alternative(params.value()); } Utils::Id languageClientProgressId(const ProgressToken &token) { constexpr char k_LanguageClientProgressId[] = "LanguageClient.ProgressId."; auto toString = [](const ProgressToken &token) { if (const auto i = std::get_if(&token)) return QString::number(*i); return *std::get_if(&token); }; return Utils::Id(k_LanguageClientProgressId).withSuffix(toString(token)); } void ProgressManager::beginProgress(const ProgressToken &token, const WorkDoneProgressBegin &begin) { auto interface = new QFutureInterface(); interface->reportStarted(); interface->setProgressRange(0, 100); // LSP always reports percentage of the task ProgressItem progressItem; progressItem.futureInterface = interface; progressItem.title = m_titles.value(token, begin.title()); if (LOGPROGRESS().isDebugEnabled()) progressItem.timer.start(); progressItem.showBarTimer = new QTimer(); progressItem.showBarTimer->setSingleShot(true); progressItem.showBarTimer->setInterval(750); progressItem.showBarTimer->callOnTimeout([this, token]() { spawnProgressBar(token); }); progressItem.showBarTimer->start(); m_progress[token] = progressItem; reportProgress(token, begin); } void ProgressManager::spawnProgressBar(const LanguageServerProtocol::ProgressToken &token) { ProgressItem &progressItem = m_progress[token]; QTC_ASSERT(progressItem.futureInterface, return); Core::FutureProgress *progress = Core::ProgressManager::addTask(progressItem.futureInterface->future(), progressItem.title, languageClientProgressId(token)); const std::function clickHandler = m_clickHandlers.value(token); if (clickHandler) QObject::connect(progress, &Core::FutureProgress::clicked, clickHandler); const std::function cancelHandler = m_cancelHandlers.value(token); if (cancelHandler) QObject::connect(progress, &Core::FutureProgress::canceled, cancelHandler); else progress->setCancelEnabled(false); if (!progressItem.message.isEmpty()) { progress->setSubtitle(progressItem.message); progress->setSubtitleVisibleInStatusBar(true); } progressItem.progressInterface = progress; } void ProgressManager::reportProgress(const ProgressToken &token, const WorkDoneProgressReport &report) { ProgressItem &progress = m_progress[token]; const std::optional &message = report.message(); if (progress.progressInterface) { if (message.has_value()) { progress.progressInterface->setSubtitle(*message); const bool showSubtitle = !message->isEmpty(); progress.progressInterface->setSubtitleVisibleInStatusBar(showSubtitle); } } else if (message.has_value()) { progress.message = *message; } if (progress.futureInterface) { if (const std::optional &percentage = report.percentage(); percentage.has_value()) progress.futureInterface->setProgressValue(*percentage); } } void ProgressManager::endProgress(const ProgressToken &token, const WorkDoneProgressEnd &end) { const ProgressItem &progress = m_progress.value(token); const QString &message = end.message().value_or(QString()); if (progress.progressInterface) { if (!message.isEmpty()) { progress.progressInterface->setKeepOnFinish( Core::FutureProgress::KeepOnFinishTillUserInteraction); } progress.progressInterface->setSubtitle(message); progress.progressInterface->setSubtitleVisibleInStatusBar(!message.isEmpty()); if (progress.timer.isValid()) { qCDebug(LOGPROGRESS) << QString("%1 took %2") .arg(progress.progressInterface->title()) .arg(QTime::fromMSecsSinceStartOfDay( progress.timer.elapsed()) .toString(Qt::ISODateWithMs)); } } endProgressReport(token); } void ProgressManager::endProgressReport(const ProgressToken &token) { ProgressItem progress = m_progress.take(token); delete progress.showBarTimer; if (progress.futureInterface) progress.futureInterface->reportFinished(); delete progress.futureInterface; } } // namespace LanguageClient