forked from qt-creator/qt-creator
LuaLS: Add sendMessageForDocument
The original "sendMessage" function sends a message to each client. To send a message to a specific Language client the sendMessageForDocument and sendMessageForDocumentWithId functions are added. As a fix the way that settings are copied into the Lua wrapper and the id used to find clients was changed. Change-Id: I577ee1f9da983c80e4ef00b8e86ccb2bfe369314 Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
#include <extensionsystem/pluginmanager.h>
|
#include <extensionsystem/pluginmanager.h>
|
||||||
|
|
||||||
#include <projectexplorer/project.h>
|
#include <projectexplorer/project.h>
|
||||||
|
#include <projectexplorer/projectmanager.h>
|
||||||
|
|
||||||
#include <utils/commandline.h>
|
#include <utils/commandline.h>
|
||||||
#include <utils/layoutbuilder.h>
|
#include <utils/layoutbuilder.h>
|
||||||
@@ -204,6 +205,7 @@ public:
|
|||||||
AspectContainer *m_aspects{nullptr};
|
AspectContainer *m_aspects{nullptr};
|
||||||
QString m_name;
|
QString m_name;
|
||||||
Utils::Id m_settingsTypeId;
|
Utils::Id m_settingsTypeId;
|
||||||
|
QString m_clientSettingsId;
|
||||||
QString m_initializationOptions;
|
QString m_initializationOptions;
|
||||||
CommandLine m_cmdLine;
|
CommandLine m_cmdLine;
|
||||||
QString m_serverName;
|
QString m_serverName;
|
||||||
@@ -364,7 +366,7 @@ public:
|
|||||||
|
|
||||||
void updateMessageCallbacks()
|
void updateMessageCallbacks()
|
||||||
{
|
{
|
||||||
for (Client *c : LanguageClientManager::clientsForSettingId(m_settingsTypeId.toString())) {
|
for (Client *c : LanguageClientManager::clientsForSettingId(m_clientSettingsId)) {
|
||||||
if (!c)
|
if (!c)
|
||||||
continue;
|
continue;
|
||||||
for (const auto &[msg, func] : m_messageCallbacks.asKeyValueRange()) {
|
for (const auto &[msg, func] : m_messageCallbacks.asKeyValueRange()) {
|
||||||
@@ -387,27 +389,70 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendMessage(const sol::table &message, const sol::function &callback)
|
void sendMessage(const sol::table &message)
|
||||||
{
|
{
|
||||||
const QJsonValue messageValue = ::Lua::LuaEngine::toJson(message);
|
const QJsonValue messageValue = ::Lua::LuaEngine::toJson(message);
|
||||||
if (!messageValue.isObject())
|
if (!messageValue.isObject())
|
||||||
throw sol::error("Message is not an object");
|
throw sol::error("Message is not an object");
|
||||||
|
|
||||||
auto make_request = [&]() -> std::unique_ptr<LanguageServerProtocol::JsonRpcMessage> {
|
const LanguageServerProtocol::JsonRpcMessage request(messageValue.toObject());
|
||||||
if (callback.valid()) {
|
for (Client *c : LanguageClientManager::clientsForSettingId(m_clientSettingsId)) {
|
||||||
return std::make_unique<RequestWithResponse>(messageValue.toObject(), callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_unique<LanguageServerProtocol::JsonRpcMessage>(messageValue.toObject());
|
|
||||||
};
|
|
||||||
|
|
||||||
auto const request = make_request();
|
|
||||||
for (Client *c : LanguageClientManager::clientsForSettingId(m_settingsTypeId.toString())) {
|
|
||||||
if (c)
|
if (c)
|
||||||
c->sendMessage(*request);
|
c->sendMessage(request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<Client *> clientsForDocument(Core::IDocument *document)
|
||||||
|
{
|
||||||
|
if (m_startBehavior == BaseSettings::RequiresProject) {
|
||||||
|
Project *project = ProjectManager::projectForFile(document->filePath());
|
||||||
|
const auto clients = LanguageClientManager::clientsForSettingId(m_clientSettingsId);
|
||||||
|
return Utils::filtered(clients, [project](Client *c) {
|
||||||
|
return c && c->project() == project;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return LanguageClientManager::clientsForSettingId(m_clientSettingsId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendMessageForDocument(Core::IDocument *document, const sol::table &message)
|
||||||
|
{
|
||||||
|
const QJsonValue messageValue = ::Lua::LuaEngine::toJson(message);
|
||||||
|
if (!messageValue.isObject())
|
||||||
|
throw sol::error("Message is not an object");
|
||||||
|
|
||||||
|
const LanguageServerProtocol::JsonRpcMessage request(messageValue.toObject());
|
||||||
|
|
||||||
|
auto clients = clientsForDocument(document);
|
||||||
|
QTC_CHECK(clients.size() == 1);
|
||||||
|
|
||||||
|
for (Client *c : clients) {
|
||||||
|
if (c)
|
||||||
|
c->sendMessage(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendMessageWithIdForDocument_cb(
|
||||||
|
TextEditor::TextDocument *document, const sol::table &message, const sol::function callback)
|
||||||
|
{
|
||||||
|
const QJsonValue messageValue = ::Lua::LuaEngine::toJson(message);
|
||||||
|
if (!messageValue.isObject())
|
||||||
|
throw sol::error("Message is not an object");
|
||||||
|
|
||||||
|
QJsonObject obj = messageValue.toObject();
|
||||||
|
obj["id"] = QUuid::createUuid().toString();
|
||||||
|
|
||||||
|
const RequestWithResponse request{obj, callback};
|
||||||
|
|
||||||
|
auto clients = clientsForDocument(document);
|
||||||
|
|
||||||
|
QTC_ASSERT(clients.size() != 0, throw sol::error("No client for document found"));
|
||||||
|
QTC_ASSERT(clients.size() == 1, throw sol::error("Multiple clients for document found"));
|
||||||
|
QTC_ASSERT(clients.front(), throw sol::error("Client is null"));
|
||||||
|
|
||||||
|
clients.front()->sendMessage(request);
|
||||||
|
}
|
||||||
|
|
||||||
void updateOptions()
|
void updateOptions()
|
||||||
{
|
{
|
||||||
if (m_cmdLineCallback) {
|
if (m_cmdLineCallback) {
|
||||||
@@ -500,8 +545,13 @@ bool LuaClientSettings::applyFromSettingsWidget(QWidget *widget)
|
|||||||
{
|
{
|
||||||
BaseSettings::applyFromSettingsWidget(widget);
|
BaseSettings::applyFromSettingsWidget(widget);
|
||||||
|
|
||||||
if (auto w = m_wrapper.lock())
|
if (auto w = m_wrapper.lock()) {
|
||||||
|
w->m_name = m_name;
|
||||||
|
w->m_initializationOptions = m_initializationOptions;
|
||||||
|
w->m_languageFilter = m_languageFilter;
|
||||||
|
w->m_startBehavior = m_startBehavior;
|
||||||
w->applySettings();
|
w->applySettings();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -553,6 +603,9 @@ BaseClientInterface *LuaClientSettings::createInterface(ProjectExplorer::Project
|
|||||||
static void registerLuaApi()
|
static void registerLuaApi()
|
||||||
{
|
{
|
||||||
::Lua::LuaEngine::registerProvider("LSP", [](sol::state_view lua) -> sol::object {
|
::Lua::LuaEngine::registerProvider("LSP", [](sol::state_view lua) -> sol::object {
|
||||||
|
sol::table async = lua.script("return require('async')", "_process_").get<sol::table>();
|
||||||
|
sol::function wrap = async["wrap"];
|
||||||
|
|
||||||
sol::table result = lua.create_table();
|
sol::table result = lua.create_table();
|
||||||
|
|
||||||
auto wrapperClass = result.new_usertype<LuaClientWrapper>(
|
auto wrapperClass = result.new_usertype<LuaClientWrapper>(
|
||||||
@@ -569,30 +622,36 @@ static void registerLuaApi()
|
|||||||
&LuaClientWrapper::registerMessageCallback,
|
&LuaClientWrapper::registerMessageCallback,
|
||||||
"sendMessage",
|
"sendMessage",
|
||||||
&LuaClientWrapper::sendMessage,
|
&LuaClientWrapper::sendMessage,
|
||||||
|
"sendMessageForDocument",
|
||||||
|
&LuaClientWrapper::sendMessageForDocument,
|
||||||
|
"sendMessageWithIdForDocument_cb",
|
||||||
|
&LuaClientWrapper::sendMessageWithIdForDocument_cb,
|
||||||
"create",
|
"create",
|
||||||
[](const sol::table &options) -> std::shared_ptr<LuaClientWrapper> {
|
[](const sol::table &options) -> std::shared_ptr<LuaClientWrapper> {
|
||||||
auto luaClient = std::make_shared<LuaClientWrapper>(options);
|
auto luaClientWrapper = std::make_shared<LuaClientWrapper>(options);
|
||||||
auto client = new LuaClientSettings(luaClient);
|
auto clientSettings = new LuaClientSettings(luaClientWrapper);
|
||||||
|
|
||||||
// The order is important!
|
// The order is important!
|
||||||
// First restore the settings ...
|
// First restore the settings ...
|
||||||
const QList<Utils::Store> savedSettings
|
const QList<Utils::Store> savedSettings
|
||||||
= LanguageClientSettings::storesBySettingsType(luaClient->m_settingsTypeId);
|
= LanguageClientSettings::storesBySettingsType(
|
||||||
|
luaClientWrapper->m_settingsTypeId);
|
||||||
|
|
||||||
if (!savedSettings.isEmpty())
|
if (!savedSettings.isEmpty())
|
||||||
client->fromMap(savedSettings.first());
|
clientSettings->fromMap(savedSettings.first());
|
||||||
|
|
||||||
// ... then register the settings.
|
// ... then register the settings.
|
||||||
LanguageClientManager::registerClientSettings(client);
|
LanguageClientManager::registerClientSettings(clientSettings);
|
||||||
|
luaClientWrapper->m_clientSettingsId = clientSettings->m_id;
|
||||||
|
|
||||||
// and the client type.
|
// and the client type.
|
||||||
ClientType type;
|
ClientType type;
|
||||||
type.id = client->m_settingsTypeId;
|
type.id = clientSettings->m_settingsTypeId;
|
||||||
type.name = luaClient->m_name;
|
type.name = luaClientWrapper->m_name;
|
||||||
type.userAddable = false;
|
type.userAddable = false;
|
||||||
LanguageClientSettings::registerClientType(type);
|
LanguageClientSettings::registerClientType(type);
|
||||||
|
|
||||||
return luaClient;
|
return luaClientWrapper;
|
||||||
},
|
},
|
||||||
"documentVersion",
|
"documentVersion",
|
||||||
[](const Utils::FilePath &path) -> int {
|
[](const Utils::FilePath &path) -> int {
|
||||||
@@ -616,6 +675,10 @@ static void registerLuaApi()
|
|||||||
return client->hostPathToServerUri(path).toString();
|
return client->hostPathToServerUri(path).toString();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
wrapperClass["sendMessageWithIdForDocument"]
|
||||||
|
= wrap(wrapperClass["sendMessageWithIdForDocument_cb"].get<sol::function>())
|
||||||
|
.get<sol::function>();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
---@meta LSP
|
---@meta LSP
|
||||||
|
|
||||||
local lsp = {}
|
local lsp = {}
|
||||||
|
|
||||||
|
---@module "TextEditor"
|
||||||
|
|
||||||
---@class ClientOptions
|
---@class ClientOptions
|
||||||
---@field name string The name under which to register the language server.
|
---@field name string The name under which to register the language server.
|
||||||
---@field cmd function|string[] The command to start the language server, or a function returning a string[].
|
---@field cmd function|string[] The command to start the language server, or a function returning a string[].
|
||||||
@@ -30,14 +31,25 @@ function lsp.Client:registerMessage(msg, callback) end
|
|||||||
|
|
||||||
---@param msg table the message to send.
|
---@param msg table the message to send.
|
||||||
---Sends a message to the language server.
|
---Sends a message to the language server.
|
||||||
function lsp.Client:sendMessage(msg, callback) end
|
function lsp.Client:sendMessage(msg) end
|
||||||
|
|
||||||
|
---Sends a message to the language server for a specific document.
|
||||||
|
---@param document TextDocument The document for which to send the message
|
||||||
|
---@param msg table The message to send.
|
||||||
|
function lsp.Client:sendMessageForDocument(document, msg) end
|
||||||
|
|
||||||
|
---@async
|
||||||
|
---Sends a message with an auto generated unique id to the language server for a specific document. Use a.wait(...) to wait for the response.
|
||||||
|
---@param document TextDocument The document for which to send the message
|
||||||
|
---@param msg table The message to send.
|
||||||
|
function lsp.Client:sendMessageWithIdForDocument(document, msg) end
|
||||||
|
|
||||||
---@param filePath FilePath to get the version of.
|
---@param filePath FilePath to get the version of.
|
||||||
---@return int Returns -1 on error, otherwise current document version.
|
---@return integer Returns -1 on error, otherwise current document version.
|
||||||
function lsp.Client:documentVersion(filePath) end
|
function lsp.Client:documentVersion(filePath) end
|
||||||
---
|
---
|
||||||
---@param filePath table file path to get the uri of.
|
---@param filePath table file path to get the uri of.
|
||||||
---@return QString Returns empty string on error, otherwise the server URI string.
|
---@return string Returns empty string on error, otherwise the server URI string.
|
||||||
function lsp.Client:hostPathToServerUri(filePath) end
|
function lsp.Client:hostPathToServerUri(filePath) end
|
||||||
|
|
||||||
---Creates a new Language Client.
|
---Creates a new Language Client.
|
||||||
|
|||||||
Reference in New Issue
Block a user