diff options
| -rw-r--r-- | src/qmlls/qlanguageserver.cpp | 31 | ||||
| -rw-r--r-- | src/qmlls/qlanguageserver_p.h | 1 | ||||
| -rw-r--r-- | src/qmlls/qlanguageserver_p_p.h | 1 | ||||
| -rw-r--r-- | tests/auto/qmlls/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | tests/auto/qmlls/qqmllanguageserver/CMakeLists.txt | 18 | ||||
| -rw-r--r-- | tests/auto/qmlls/qqmllanguageserver/tst_qqmllanguageserver.cpp | 67 | ||||
| -rw-r--r-- | tests/auto/qmlls/qqmllanguageserver/tst_qqmllanguageserver.h | 20 |
7 files changed, 110 insertions, 29 deletions
diff --git a/src/qmlls/qlanguageserver.cpp b/src/qmlls/qlanguageserver.cpp index e6812e6f3b..6525044b80 100644 --- a/src/qmlls/qlanguageserver.cpp +++ b/src/qmlls/qlanguageserver.cpp @@ -109,13 +109,9 @@ void QLanguageServer::registerMethods(QJsonRpc::TypedRpc &typedRpc) } } } - if (!sendErrorResponse) { - if (id.isString() || id.isDouble()) { - QMutexLocker l(&d->mutex); - d->requestsInProgress.insert(id, QRequestInProgress {}); - } + if (!sendErrorResponse) return QJsonRpcProtocol::Processing::Continue; - } + if (rState == RunStatus::NotInitialized) responder(QJsonRpcProtocol::MessageHandler::error( int(QLspSpecification::ErrorCodes::ServerNotInitialized), @@ -129,21 +125,6 @@ void QLanguageServer::registerMethods(QJsonRpc::TypedRpc &typedRpc) int(rState)))); return QJsonRpcProtocol::Processing::Stop; }); - typedRpc.installOnCloseAction([this](QJsonRpc::TypedResponse::Status, - const QJsonRpc::IdType &id, QJsonRpc::TypedRpc &) { - Q_D(QLanguageServer); - QJsonValue idValue = QTypedJson::toJsonValue(id); - bool lastReq; - { - QMutexLocker l(&d->mutex); - d->requestsInProgress.remove(idValue); - lastReq = d->runStatus == RunStatus::WaitPending && d->requestsInProgress.size() <= 1; - if (lastReq) - d->runStatus = RunStatus::Stopping; - } - if (lastReq) - executeShutdown(); - }); } const QLspSpecification::InitializeParams &QLanguageServer::clientInfo() const @@ -207,12 +188,8 @@ void QLanguageServer::registerHandlers(QLanguageServerProtocol *protocol) rStatus = d->runStatus; if (rStatus == RunStatus::Initialized) { d->shutdownResponse = std::move(response); - if (d->requestsInProgress.size() <= 1) { - d->runStatus = RunStatus::Stopping; - shouldExecuteShutdown = true; - } else { - d->runStatus = RunStatus::WaitPending; - } + d->runStatus = RunStatus::Stopping; + shouldExecuteShutdown = true; } } if (rStatus != RunStatus::Initialized) diff --git a/src/qmlls/qlanguageserver_p.h b/src/qmlls/qlanguageserver_p.h index 2e50506927..48a48f61e6 100644 --- a/src/qmlls/qlanguageserver_p.h +++ b/src/qmlls/qlanguageserver_p.h @@ -44,7 +44,6 @@ public: enum class RunStatus { NotInitialized, Initialized, // normal state of execution - WaitPending, Stopping, WaitingForExit, Stopped diff --git a/src/qmlls/qlanguageserver_p_p.h b/src/qmlls/qlanguageserver_p_p.h index 55a312ecf0..55c61e8889 100644 --- a/src/qmlls/qlanguageserver_p_p.h +++ b/src/qmlls/qlanguageserver_p_p.h @@ -47,7 +47,6 @@ public: mutable QMutex mutex; // mutex gated, monotonically increasing QLanguageServer::RunStatus runStatus = QLanguageServer::RunStatus::NotInitialized; - QHash<QJsonValue, QRequestInProgress> requestsInProgress; // mutex gated QLspSpecification::InitializeParams clientInfo; // immutable after runStatus > Initialized QLspSpecification::InitializeResult serverInfo; // immutable after runStatus > Initialized QLspSpecification::Responses::ShutdownResponseType shutdownResponse; diff --git a/tests/auto/qmlls/CMakeLists.txt b/tests/auto/qmlls/CMakeLists.txt index daf7d64cd4..92e70aae0c 100644 --- a/tests/auto/qmlls/CMakeLists.txt +++ b/tests/auto/qmlls/CMakeLists.txt @@ -8,5 +8,6 @@ if (TARGET Qt::QmlLSPrivate) add_subdirectory(qmlls) add_subdirectory(modules) add_subdirectory(cli) + add_subdirectory(qqmllanguageserver) endif() diff --git a/tests/auto/qmlls/qqmllanguageserver/CMakeLists.txt b/tests/auto/qmlls/qqmllanguageserver/CMakeLists.txt new file mode 100644 index 0000000000..7aecc9005c --- /dev/null +++ b/tests/auto/qmlls/qqmllanguageserver/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (C) 2026 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qqmllanguageserver LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qqmllanguageserver + SOURCES + tst_qqmllanguageserver.cpp tst_qqmllanguageserver.h + LIBRARIES + Qt::CorePrivate + Qt::LanguageServerPrivate + Qt::Test + Qt::QmlLSPrivate +) diff --git a/tests/auto/qmlls/qqmllanguageserver/tst_qqmllanguageserver.cpp b/tests/auto/qmlls/qqmllanguageserver/tst_qqmllanguageserver.cpp new file mode 100644 index 0000000000..0847a57ddb --- /dev/null +++ b/tests/auto/qmlls/qqmllanguageserver/tst_qqmllanguageserver.cpp @@ -0,0 +1,67 @@ +// Copyright (C) 2026 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <private/qlanguageserver_p.h> +#include <private/qqmllanguageserver_p.h> +#include <QtTest/qtest.h> + +#include "tst_qqmllanguageserver.h" + +using namespace Qt::StringLiterals; +using namespace QLspSpecification; +using namespace QmlLsp; + +tst_qqmllanguageserver::tst_qqmllanguageserver() { } + +struct ClientAndServer +{ + std::unique_ptr<QLanguageServerProtocol> client; + std::unique_ptr<QQmlLanguageServer> server; + + ClientAndServer() + { + client = std::make_unique<QLanguageServerProtocol>( + [this](const QByteArray &data) { server->receiveData(data, true); }); + server = std::make_unique<QQmlLanguageServer>( + [this](const QByteArray &data) { client->receiveData(data); }); + } + + static ClientAndServer createAndInitialize() + { + ClientAndServer result; + + bool initializedOk = false; + InitializeParams initializeParams; + initializeParams.capabilities.window.emplace().workDoneProgress = true; + result.client->requestInitialize( + initializeParams, + [&initializedOk](const InitializeResult &) { initializedOk = true; }); + [&initializedOk] { QTRY_VERIFY_WITH_TIMEOUT(initializedOk, 3000); }(); + result.client->notifyInitialized({ }); + + return result; + } +}; + +void tst_qqmllanguageserver::noFreezeAfterRequestToClient() +{ + auto [client, server] = ClientAndServer::createAndInitialize(); + + client->registerWorkspaceSemanticTokensRefreshRequestHandler( + [](const QByteArray, const std::nullptr_t &, auto &&response) { + response.sendResponse({ }); + }); + + bool ok = false; + server->protocol()->requestWorkspaceSemanticTokensRefresh({ }, [&ok]() { ok = true; }); + QTRY_VERIFY_WITH_TIMEOUT(ok, 5000); + + ok = false; + client->requestShutdown({ }, [&ok, &c = client]() { + c->notifyExit({ }); + ok = true; + }); + QTRY_VERIFY_WITH_TIMEOUT(ok, 5000); +} + +QTEST_MAIN(tst_qqmllanguageserver) diff --git a/tests/auto/qmlls/qqmllanguageserver/tst_qqmllanguageserver.h b/tests/auto/qmlls/qqmllanguageserver/tst_qqmllanguageserver.h new file mode 100644 index 0000000000..b555b3d77c --- /dev/null +++ b/tests/auto/qmlls/qqmllanguageserver/tst_qqmllanguageserver.h @@ -0,0 +1,20 @@ +// Copyright (C) 2026 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef TST_QQMLLANGUAGESERVER_H +#define TST_QQMLLANGUAGESERVER_H + +#include <QtCore/qobject.h> +#include <QtTest/qtest.h> + +class tst_qqmllanguageserver : public QObject +{ + Q_OBJECT +public: + tst_qqmllanguageserver(); + +private slots: + void noFreezeAfterRequestToClient(); +}; + +#endif // TST_QQMLLANGUAGESERVER_H |
