// 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 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 (std::holds_alternative(token)) return QString::number(std::get(token)); return std::get(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 const QString title = m_titles.value(token, begin.title()); Core::FutureProgress *progress = Core::ProgressManager::addTask( interface->future(), 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); m_progress[token] = {progress, interface}; if (LOGPROGRESS().isDebugEnabled()) m_timer[token].start(); reportProgress(token, begin); } void ProgressManager::reportProgress(const ProgressToken &token, const WorkDoneProgressReport &report) { const LanguageClientProgress &progress = m_progress.value(token); if (progress.progressInterface) { const std::optional &message = report.message(); if (message.has_value()) { progress.progressInterface->setSubtitle(*message); const bool showSubtitle = !message->isEmpty(); progress.progressInterface->setSubtitleVisibleInStatusBar(showSubtitle); } } 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 LanguageClientProgress &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()); auto timer = m_timer.take(token); if (timer.isValid()) { qCDebug(LOGPROGRESS) << QString("%1 took %2") .arg(progress.progressInterface->title()) .arg(QTime::fromMSecsSinceStartOfDay(timer.elapsed()) .toString(Qt::ISODateWithMs)); } } endProgressReport(token); } void ProgressManager::endProgressReport(const ProgressToken &token) { const LanguageClientProgress &progress = m_progress.take(token); if (progress.futureInterface) progress.futureInterface->reportFinished(); delete progress.futureInterface; } } // namespace LanguageClient