Revision control

++ // Copyright (C) 2021 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 "qlspcustomtypes_p.h"
++ #include "qworkspace_p.h"
++ #include "qqmllanguageserver_p.h"
++ #include "qqmllsutils_p.h"
++
++ #include <QtLanguageServer/private/qlanguageserverspectypes_p.h>
++ #include <QtLanguageServer/private/qlspnotifysignals_p.h>
++
++ #include <QtCore/qfile.h>
++ #include <variant>
++
++ QT_BEGIN_NAMESPACE
++ using namespace Qt::StringLiterals;
++ using namespace QLspSpecification;
++
++ void WorkspaceHandlers::registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol)
++ {
++ QObject::connect(server->notifySignals(),
++ &QLspNotifySignals::receivedDidChangeWorkspaceFoldersNotification, this,
++ [this](const DidChangeWorkspaceFoldersParams &params) {
++ const WorkspaceFoldersChangeEvent &event = params.event;
++
++ const QList<WorkspaceFolder> &removed = event.removed;
++ QList<QByteArray> toRemove;
++ for (const WorkspaceFolder &folder : removed) {
++ toRemove.append(QQmlLSUtils::lspUriToQmlUrl(folder.uri));
++ m_codeModelManager->removeDirectory(
++ QQmlLSUtils::lspUriToQmlUrl(folder.uri));
++ }
++ m_codeModelManager->removeRootUrls(toRemove);
++ const QList<WorkspaceFolder> &added = event.added;
++ QList<QByteArray> toAdd;
++ for (const WorkspaceFolder &folder : added) {
++ toAdd.append(QQmlLSUtils::lspUriToQmlUrl(folder.uri));
++ }
++ m_codeModelManager->addRootUrls(toAdd);
++ });
++
++ QObject::connect(server, &QLanguageServer::clientInitialized, this,
++ &WorkspaceHandlers::clientInitialized, Qt::SingleShotConnection);
++
++ protocol->typedRpc()->registerNotificationHandler<Notifications::AddBuildDirsParams>(
++ QByteArray(Notifications::AddBuildDirsMethod),
++ [this](const QByteArray &, const Notifications::AddBuildDirsParams &params) {
++ for (const auto &buildDirs : params.buildDirsToSet) {
++ QStringList dirPaths;
++ dirPaths.resize(buildDirs.buildDirs.size());
++ std::transform(buildDirs.buildDirs.begin(), buildDirs.buildDirs.end(),
++ dirPaths.begin(), [](const QByteArray &utf8Str) {
++ return QString::fromUtf8(utf8Str);
++ });
++ m_codeModelManager->setBuildPathsForRootUrl(buildDirs.baseUri, dirPaths);
++ }
++ });
++ }
++
++ void WorkspaceHandlers::setupCapabilities(QLspSpecification::ServerCapabilities &caps)
++ {
++ WorkspaceFoldersServerCapabilities folders;
++ folders.supported = true;
++ folders.changeNotifications = true;
++ if (!caps.workspace)
++ caps.workspace.emplace();
++ caps.workspace->workspaceFolders = folders;
++
++ QJsonObject expCap;
++ if (caps.experimental.has_value() && caps.experimental->isObject())
++ expCap = caps.experimental->toObject();
++ expCap.insert(u"addBuildDirs"_s, QJsonObject({ { u"supported"_s, true } }));
++ caps.experimental = expCap;
++ }
++
++ void WorkspaceHandlers::openInitialWorkspace(const InitializeParams &clientInfo)
++ {
++ if (clientInfo.workspaceFolders) {
++ std::visit(qOverloadedVisitor{
++ [](std::nullptr_t) {},
++ [this](const QList<WorkspaceFolder> &folders) {
++ QList<QByteArray> rootPaths;
++ for (const auto &folder : folders) {
++ rootPaths.append(QQmlLSUtils::lspUriToQmlUrl(folder.uri));
++ }
++ m_codeModelManager->addRootUrls(rootPaths);
++ },
++ },
++ *clientInfo.workspaceFolders);
++ return;
++ }
++
++ // note: rootUri is deprecated in the LSP protocol
++ std::visit(qOverloadedVisitor{
++ [](std::nullptr_t) {},
++ [this](const QByteArray &rootUri) {
++ m_codeModelManager->addRootUrls(
++ { QQmlLSUtils::lspUriToQmlUrl(rootUri) });
++ },
++
++ },
++ clientInfo.rootUri);
++
++ // note: rootPath is also deprecated in the LSP protocol. It was deprecated even before rootUri
++ // was deprecated.
++ if (clientInfo.rootPath) {
++ std::visit(qOverloadedVisitor{
++ [](std::nullptr_t) {},
++ [this](const QByteArray &rootPath) {
++ m_codeModelManager->addRootUrls({
++ QUrl::fromLocalFile(QString::fromUtf8(rootPath)).toEncoded(),
++ });
++ },
++
++ },
++ *clientInfo.rootPath);
++ }
++ }
++
++ void WorkspaceHandlers::clientInitialized(QLanguageServer *server)
++ {
++ const auto &clientInfo = server->clientInfo();
++
++ if (clientInfo.capabilities.workspace
++ && clientInfo.capabilities.workspace->workspaceFolders.value_or(false)) {
++ openInitialWorkspace(clientInfo);
++ }
++ }
++
++ QT_END_NAMESPACE