2018-07-13 12:33:46 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** Copyright (C) 2018 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
|
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator.
|
|
|
|
|
**
|
|
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
|
|
|
**
|
|
|
|
|
** GNU General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
|
|
|
**
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "languageclientmanager.h"
|
|
|
|
|
|
2019-03-27 14:05:30 +01:00
|
|
|
#include "languageclientplugin.h"
|
2021-01-25 12:50:27 +01:00
|
|
|
#include "languageclientutils.h"
|
2019-01-18 13:12:13 +01:00
|
|
|
|
2018-07-13 12:33:46 +02:00
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
|
|
|
|
#include <coreplugin/editormanager/ieditor.h>
|
2018-11-28 08:16:19 +01:00
|
|
|
#include <coreplugin/find/searchresultwindow.h>
|
2019-03-27 15:09:08 +01:00
|
|
|
#include <coreplugin/icore.h>
|
2018-07-13 12:33:46 +02:00
|
|
|
#include <languageserverprotocol/messages.h>
|
2021-02-02 09:50:56 +01:00
|
|
|
#include <languageserverprotocol/progresssupport.h>
|
2018-07-13 12:33:46 +02:00
|
|
|
#include <projectexplorer/project.h>
|
2020-05-11 15:00:07 +02:00
|
|
|
#include <projectexplorer/projectexplorer.h>
|
2021-01-25 12:50:27 +01:00
|
|
|
#include <projectexplorer/session.h>
|
|
|
|
|
#include <texteditor/textdocument.h>
|
2018-07-13 12:33:46 +02:00
|
|
|
#include <texteditor/texteditor.h>
|
|
|
|
|
#include <texteditor/textmark.h>
|
2021-02-23 13:51:41 +01:00
|
|
|
#include <utils/algorithm.h>
|
2019-07-24 13:40:12 +02:00
|
|
|
#include <utils/executeondestruction.h>
|
2018-07-13 12:33:46 +02:00
|
|
|
#include <utils/theme/theme.h>
|
|
|
|
|
#include <utils/utilsicons.h>
|
|
|
|
|
|
2019-01-25 09:48:44 +01:00
|
|
|
#include <QTextBlock>
|
2018-07-13 12:33:46 +02:00
|
|
|
#include <QTimer>
|
|
|
|
|
|
|
|
|
|
using namespace LanguageServerProtocol;
|
|
|
|
|
|
|
|
|
|
namespace LanguageClient {
|
|
|
|
|
|
2021-11-09 15:12:04 +01:00
|
|
|
static Q_LOGGING_CATEGORY(Log, "qtc.languageclient.manager", QtWarningMsg)
|
|
|
|
|
|
2018-07-13 12:33:46 +02:00
|
|
|
static LanguageClientManager *managerInstance = nullptr;
|
|
|
|
|
|
2019-03-27 14:05:30 +01:00
|
|
|
LanguageClientManager::LanguageClientManager(QObject *parent)
|
|
|
|
|
: QObject (parent)
|
2018-09-12 10:19:32 +02:00
|
|
|
{
|
2019-03-27 14:05:30 +01:00
|
|
|
using namespace Core;
|
|
|
|
|
using namespace ProjectExplorer;
|
|
|
|
|
connect(EditorManager::instance(), &EditorManager::editorOpened,
|
|
|
|
|
this, &LanguageClientManager::editorOpened);
|
|
|
|
|
connect(EditorManager::instance(), &EditorManager::documentOpened,
|
|
|
|
|
this, &LanguageClientManager::documentOpened);
|
|
|
|
|
connect(EditorManager::instance(), &EditorManager::documentClosed,
|
|
|
|
|
this, &LanguageClientManager::documentClosed);
|
|
|
|
|
connect(EditorManager::instance(), &EditorManager::saved,
|
|
|
|
|
this, &LanguageClientManager::documentContentsSaved);
|
|
|
|
|
connect(EditorManager::instance(), &EditorManager::aboutToSave,
|
|
|
|
|
this, &LanguageClientManager::documentWillSave);
|
|
|
|
|
connect(SessionManager::instance(), &SessionManager::projectAdded,
|
2020-05-22 12:06:30 +02:00
|
|
|
this, &LanguageClientManager::projectAdded);
|
2019-03-27 14:05:30 +01:00
|
|
|
connect(SessionManager::instance(), &SessionManager::projectRemoved,
|
2021-11-11 14:31:22 +01:00
|
|
|
this, [&](Project *project) { project->disconnect(this); });
|
2018-07-13 12:33:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LanguageClientManager::~LanguageClientManager()
|
|
|
|
|
{
|
2018-09-13 15:31:00 +02:00
|
|
|
QTC_ASSERT(m_clients.isEmpty(), qDeleteAll(m_clients));
|
2019-03-27 15:09:08 +01:00
|
|
|
qDeleteAll(m_currentSettings);
|
2019-03-27 14:05:30 +01:00
|
|
|
managerInstance = nullptr;
|
2018-07-13 12:33:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LanguageClientManager::init()
|
|
|
|
|
{
|
2019-03-27 14:05:30 +01:00
|
|
|
if (managerInstance)
|
|
|
|
|
return;
|
|
|
|
|
QTC_ASSERT(LanguageClientPlugin::instance(), return);
|
|
|
|
|
managerInstance = new LanguageClientManager(LanguageClientPlugin::instance());
|
2018-07-13 12:33:46 +02:00
|
|
|
}
|
|
|
|
|
|
2021-08-25 13:55:45 +02:00
|
|
|
void LanguageClient::LanguageClientManager::addClient(Client *client)
|
2018-07-13 12:33:46 +02:00
|
|
|
{
|
2019-03-27 14:05:30 +01:00
|
|
|
QTC_ASSERT(managerInstance, return);
|
2018-09-18 10:43:17 +02:00
|
|
|
QTC_ASSERT(client, return);
|
2021-08-25 13:55:45 +02:00
|
|
|
|
|
|
|
|
if (managerInstance->m_clients.contains(client))
|
2018-09-13 15:31:00 +02:00
|
|
|
return;
|
2021-08-13 14:40:41 +02:00
|
|
|
|
2021-11-09 15:12:04 +01:00
|
|
|
qCDebug(Log) << "add client: " << client->name() << client;
|
2021-08-25 13:55:45 +02:00
|
|
|
managerInstance->m_clients << client;
|
2021-08-13 14:40:41 +02:00
|
|
|
connect(client, &Client::finished, managerInstance, [client]() { clientFinished(client); });
|
|
|
|
|
connect(client,
|
|
|
|
|
&Client::initialized,
|
|
|
|
|
managerInstance,
|
|
|
|
|
[client](const LanguageServerProtocol::ServerCapabilities &capabilities) {
|
|
|
|
|
managerInstance->m_currentDocumentLocatorFilter.updateCurrentClient();
|
|
|
|
|
managerInstance->m_inspector.clientInitialized(client->name(), capabilities);
|
|
|
|
|
});
|
|
|
|
|
connect(client,
|
|
|
|
|
&Client::capabilitiesChanged,
|
|
|
|
|
managerInstance,
|
|
|
|
|
[client](const DynamicCapabilities &capabilities) {
|
|
|
|
|
managerInstance->m_inspector.updateCapabilities(client->name(), capabilities);
|
|
|
|
|
});
|
2021-08-25 13:55:45 +02:00
|
|
|
}
|
2021-02-12 14:01:24 +01:00
|
|
|
|
2021-08-25 13:55:45 +02:00
|
|
|
void LanguageClientManager::clientStarted(Client *client)
|
|
|
|
|
{
|
2021-11-09 15:12:04 +01:00
|
|
|
qCDebug(Log) << "client started: " << client->name() << client;
|
2021-08-25 13:55:45 +02:00
|
|
|
QTC_ASSERT(managerInstance, return);
|
|
|
|
|
QTC_ASSERT(client, return);
|
2021-11-16 15:28:03 +01:00
|
|
|
if (managerInstance->m_shuttingDown) {
|
2021-08-25 13:55:45 +02:00
|
|
|
clientFinished(client);
|
2021-11-16 15:28:03 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
client->initialize();
|
|
|
|
|
const QList<TextEditor::TextDocument *> &clientDocs
|
|
|
|
|
= managerInstance->m_clientForDocument.keys(client);
|
|
|
|
|
for (TextEditor::TextDocument *document : clientDocs)
|
|
|
|
|
client->openDocument(document);
|
2021-02-12 14:01:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LanguageClientManager::clientFinished(Client *client)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(managerInstance, return);
|
|
|
|
|
constexpr int restartTimeoutS = 5;
|
|
|
|
|
const bool unexpectedFinish = client->state() != Client::Shutdown
|
|
|
|
|
&& client->state() != Client::ShutdownRequested;
|
2021-07-14 11:23:29 +02:00
|
|
|
|
|
|
|
|
if (unexpectedFinish) {
|
|
|
|
|
if (!managerInstance->m_shuttingDown) {
|
|
|
|
|
const QList<TextEditor::TextDocument *> &clientDocs
|
|
|
|
|
= managerInstance->m_clientForDocument.keys(client);
|
|
|
|
|
if (client->reset()) {
|
2021-11-09 15:12:04 +01:00
|
|
|
qCDebug(Log) << "restart unexpectedly finished client: " << client->name() << client;
|
2021-07-14 11:23:29 +02:00
|
|
|
client->log(
|
|
|
|
|
tr("Unexpectedly finished. Restarting in %1 seconds.").arg(restartTimeoutS));
|
|
|
|
|
QTimer::singleShot(restartTimeoutS * 1000, client, [client]() { client->start(); });
|
|
|
|
|
for (TextEditor::TextDocument *document : clientDocs)
|
|
|
|
|
client->deactivateDocument(document);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-11-09 15:12:04 +01:00
|
|
|
qCDebug(Log) << "client finished unexpectedly: " << client->name() << client;
|
2021-02-12 14:01:24 +01:00
|
|
|
client->log(tr("Unexpectedly finished."));
|
2021-07-14 11:23:29 +02:00
|
|
|
for (TextEditor::TextDocument *document : clientDocs)
|
|
|
|
|
managerInstance->m_clientForDocument.remove(document);
|
|
|
|
|
}
|
2021-02-12 14:01:24 +01:00
|
|
|
}
|
2021-07-14 11:23:29 +02:00
|
|
|
deleteClient(client);
|
|
|
|
|
if (managerInstance->m_shuttingDown && managerInstance->m_clients.isEmpty())
|
|
|
|
|
emit managerInstance->shutdownFinished();
|
2018-07-13 12:33:46 +02:00
|
|
|
}
|
|
|
|
|
|
2022-03-14 09:34:12 +01:00
|
|
|
Client *LanguageClientManager::startClient(const BaseSettings *setting,
|
|
|
|
|
ProjectExplorer::Project *project)
|
2019-03-28 11:59:32 +01:00
|
|
|
{
|
2019-07-24 13:40:12 +02:00
|
|
|
QTC_ASSERT(managerInstance, return nullptr);
|
|
|
|
|
QTC_ASSERT(setting, return nullptr);
|
|
|
|
|
QTC_ASSERT(setting->isValid(), return nullptr);
|
2021-08-25 12:48:39 +02:00
|
|
|
Client *client = setting->createClient(project);
|
2021-11-09 15:12:04 +01:00
|
|
|
qCDebug(Log) << "start client: " << client->name() << client;
|
2019-07-24 13:40:12 +02:00
|
|
|
QTC_ASSERT(client, return nullptr);
|
2021-02-12 14:01:24 +01:00
|
|
|
client->start();
|
2019-05-09 09:13:54 +02:00
|
|
|
managerInstance->m_clientsForSetting[setting->m_id].append(client);
|
2019-07-24 13:40:12 +02:00
|
|
|
return client;
|
2019-03-28 11:59:32 +01:00
|
|
|
}
|
|
|
|
|
|
2022-03-14 08:36:37 +01:00
|
|
|
QList<Client *> LanguageClientManager::clients()
|
2018-07-13 12:33:46 +02:00
|
|
|
{
|
2019-03-27 14:05:30 +01:00
|
|
|
QTC_ASSERT(managerInstance, return {});
|
2018-07-13 12:33:46 +02:00
|
|
|
return managerInstance->m_clients;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-31 12:15:43 +01:00
|
|
|
void LanguageClientManager::addExclusiveRequest(const MessageId &id, Client *client)
|
2018-07-13 12:33:46 +02:00
|
|
|
{
|
2019-03-27 14:05:30 +01:00
|
|
|
QTC_ASSERT(managerInstance, return);
|
2018-07-13 12:33:46 +02:00
|
|
|
managerInstance->m_exclusiveRequests[id] << client;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-31 12:15:43 +01:00
|
|
|
void LanguageClientManager::reportFinished(const MessageId &id, Client *byClient)
|
2018-07-13 12:33:46 +02:00
|
|
|
{
|
2019-03-27 14:05:30 +01:00
|
|
|
QTC_ASSERT(managerInstance, return);
|
2021-01-25 12:50:27 +01:00
|
|
|
for (Client *client : qAsConst(managerInstance->m_exclusiveRequests[id])) {
|
2018-07-13 12:33:46 +02:00
|
|
|
if (client != byClient)
|
|
|
|
|
client->cancelRequest(id);
|
|
|
|
|
}
|
|
|
|
|
managerInstance->m_exclusiveRequests.remove(id);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-09 09:13:54 +02:00
|
|
|
void LanguageClientManager::shutdownClient(Client *client)
|
|
|
|
|
{
|
|
|
|
|
if (!client)
|
|
|
|
|
return;
|
2021-11-09 15:12:04 +01:00
|
|
|
qCDebug(Log) << "request client shutdown: " << client->name() << client;
|
2021-07-14 11:23:29 +02:00
|
|
|
// reset the documents for that client already when requesting the shutdown so they can get
|
|
|
|
|
// reassigned to another server right after this request to another server
|
|
|
|
|
for (TextEditor::TextDocument *document : managerInstance->m_clientForDocument.keys(client))
|
|
|
|
|
managerInstance->m_clientForDocument.remove(document);
|
2019-05-09 09:13:54 +02:00
|
|
|
if (client->reachable())
|
|
|
|
|
client->shutdown();
|
|
|
|
|
else if (client->state() != Client::Shutdown && client->state() != Client::ShutdownRequested)
|
|
|
|
|
deleteClient(client);
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-31 12:15:43 +01:00
|
|
|
void LanguageClientManager::deleteClient(Client *client)
|
2018-09-13 13:39:01 +02:00
|
|
|
{
|
2019-03-27 14:05:30 +01:00
|
|
|
QTC_ASSERT(managerInstance, return);
|
2018-09-13 13:39:01 +02:00
|
|
|
QTC_ASSERT(client, return);
|
2021-11-09 15:12:04 +01:00
|
|
|
qCDebug(Log) << "delete client: " << client->name() << client;
|
2022-02-18 13:53:50 +01:00
|
|
|
client->disconnect(managerInstance);
|
2018-09-13 13:39:01 +02:00
|
|
|
managerInstance->m_clients.removeAll(client);
|
2022-03-14 08:36:37 +01:00
|
|
|
for (QList<Client *> &clients : managerInstance->m_clientsForSetting)
|
2019-05-09 09:13:54 +02:00
|
|
|
clients.removeAll(client);
|
2022-04-04 17:27:49 +02:00
|
|
|
client->deleteLater();
|
|
|
|
|
if (!managerInstance->m_shuttingDown)
|
2021-06-18 16:30:03 +02:00
|
|
|
emit instance()->clientRemoved(client);
|
2018-09-13 13:39:01 +02:00
|
|
|
}
|
|
|
|
|
|
2018-09-13 15:31:00 +02:00
|
|
|
void LanguageClientManager::shutdown()
|
|
|
|
|
{
|
2019-03-27 14:05:30 +01:00
|
|
|
QTC_ASSERT(managerInstance, return);
|
2018-09-13 15:31:00 +02:00
|
|
|
if (managerInstance->m_shuttingDown)
|
|
|
|
|
return;
|
2021-11-09 15:12:04 +01:00
|
|
|
qCDebug(Log) << "shutdown manager";
|
2018-09-13 15:31:00 +02:00
|
|
|
managerInstance->m_shuttingDown = true;
|
2022-01-17 10:36:59 +01:00
|
|
|
const auto clients = managerInstance->clients();
|
|
|
|
|
for (Client *client : clients)
|
2019-05-09 09:13:54 +02:00
|
|
|
shutdownClient(client);
|
2022-01-17 10:36:59 +01:00
|
|
|
QTimer::singleShot(3000, managerInstance, [] {
|
|
|
|
|
const auto clients = managerInstance->clients();
|
|
|
|
|
for (Client *client : clients)
|
2021-01-25 12:50:27 +01:00
|
|
|
deleteClient(client);
|
2018-10-24 08:55:27 +02:00
|
|
|
emit managerInstance->shutdownFinished();
|
|
|
|
|
});
|
2018-09-13 15:31:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LanguageClientManager *LanguageClientManager::instance()
|
|
|
|
|
{
|
|
|
|
|
return managerInstance;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-11 11:15:39 +02:00
|
|
|
QList<Client *> LanguageClientManager::clientsSupportingDocument(const TextEditor::TextDocument *doc)
|
2018-11-23 09:45:51 +01:00
|
|
|
{
|
2019-03-27 14:05:30 +01:00
|
|
|
QTC_ASSERT(managerInstance, return {});
|
2018-11-23 09:45:51 +01:00
|
|
|
QTC_ASSERT(doc, return {};);
|
2019-01-31 12:15:43 +01:00
|
|
|
return Utils::filtered(managerInstance->reachableClients(), [doc](Client *client) {
|
2018-11-23 09:45:51 +01:00
|
|
|
return client->isSupportedDocument(doc);
|
2022-03-14 08:36:37 +01:00
|
|
|
});
|
2018-11-23 09:45:51 +01:00
|
|
|
}
|
|
|
|
|
|
2019-03-27 15:09:08 +01:00
|
|
|
void LanguageClientManager::applySettings()
|
|
|
|
|
{
|
2019-03-28 11:59:32 +01:00
|
|
|
QTC_ASSERT(managerInstance, return);
|
|
|
|
|
qDeleteAll(managerInstance->m_currentSettings);
|
2019-07-24 13:40:12 +02:00
|
|
|
managerInstance->m_currentSettings
|
2021-01-22 13:57:15 +01:00
|
|
|
= Utils::transform(LanguageClientSettings::pageSettings(), &BaseSettings::copy);
|
|
|
|
|
const QList<BaseSettings *> restarts = LanguageClientSettings::changedSettings();
|
2019-03-28 11:59:32 +01:00
|
|
|
LanguageClientSettings::toSettings(Core::ICore::settings(), managerInstance->m_currentSettings);
|
2019-03-27 15:09:08 +01:00
|
|
|
|
|
|
|
|
for (BaseSettings *setting : restarts) {
|
2019-09-11 11:15:39 +02:00
|
|
|
QList<TextEditor::TextDocument *> documents;
|
2022-03-14 08:56:32 +01:00
|
|
|
const QList<Client *> currentClients = clientsForSetting(setting);
|
2021-01-25 12:50:27 +01:00
|
|
|
for (Client *client : currentClients) {
|
2019-07-24 13:40:12 +02:00
|
|
|
documents << managerInstance->m_clientForDocument.keys(client);
|
2019-05-09 09:13:54 +02:00
|
|
|
shutdownClient(client);
|
2019-07-24 13:40:12 +02:00
|
|
|
}
|
2021-01-25 12:50:27 +01:00
|
|
|
for (auto document : qAsConst(documents))
|
2019-09-20 07:41:21 +02:00
|
|
|
managerInstance->m_clientForDocument.remove(document);
|
2019-03-15 11:25:48 +01:00
|
|
|
if (!setting->isValid() || !setting->m_enabled)
|
|
|
|
|
continue;
|
|
|
|
|
switch (setting->m_startBehavior) {
|
2019-07-24 13:40:12 +02:00
|
|
|
case BaseSettings::AlwaysOn: {
|
|
|
|
|
Client *client = startClient(setting);
|
2021-01-25 12:50:27 +01:00
|
|
|
for (TextEditor::TextDocument *document : qAsConst(documents))
|
2019-07-24 13:40:12 +02:00
|
|
|
managerInstance->m_clientForDocument[document] = client;
|
2019-03-15 11:25:48 +01:00
|
|
|
break;
|
2019-07-24 13:40:12 +02:00
|
|
|
}
|
|
|
|
|
case BaseSettings::RequiresFile: {
|
2021-01-25 12:50:27 +01:00
|
|
|
const QList<Core::IDocument *> &openedDocuments = Core::DocumentModel::openedDocuments();
|
|
|
|
|
for (Core::IDocument *document : openedDocuments) {
|
2019-09-20 07:41:21 +02:00
|
|
|
if (auto textDocument = qobject_cast<TextEditor::TextDocument *>(document)) {
|
|
|
|
|
if (setting->m_languageFilter.isSupported(document))
|
|
|
|
|
documents << textDocument;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!documents.isEmpty()) {
|
2019-07-24 13:40:12 +02:00
|
|
|
Client *client = startClient(setting);
|
2021-01-25 12:50:27 +01:00
|
|
|
for (TextEditor::TextDocument *document : qAsConst(documents))
|
2019-09-20 07:41:21 +02:00
|
|
|
client->openDocument(document);
|
2019-03-15 11:25:48 +01:00
|
|
|
}
|
|
|
|
|
break;
|
2019-07-24 13:40:12 +02:00
|
|
|
}
|
2019-03-15 11:25:48 +01:00
|
|
|
case BaseSettings::RequiresProject: {
|
2021-01-25 12:39:58 +01:00
|
|
|
const QList<Core::IDocument *> &openedDocuments = Core::DocumentModel::openedDocuments();
|
|
|
|
|
QHash<ProjectExplorer::Project *, Client *> clientForProject;
|
|
|
|
|
for (Core::IDocument *document : openedDocuments) {
|
|
|
|
|
auto textDocument = qobject_cast<TextEditor::TextDocument *>(document);
|
|
|
|
|
if (!textDocument || !setting->m_languageFilter.isSupported(textDocument))
|
|
|
|
|
continue;
|
|
|
|
|
const Utils::FilePath filePath = textDocument->filePath();
|
|
|
|
|
for (ProjectExplorer::Project *project :
|
|
|
|
|
ProjectExplorer::SessionManager::projects()) {
|
|
|
|
|
if (project->isKnownFile(filePath)) {
|
|
|
|
|
Client *client = clientForProject[project];
|
|
|
|
|
if (!client) {
|
|
|
|
|
client = startClient(setting, project);
|
|
|
|
|
if (!client)
|
|
|
|
|
continue;
|
|
|
|
|
clientForProject[project] = client;
|
|
|
|
|
}
|
|
|
|
|
client->openDocument(textDocument);
|
2019-03-15 11:25:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-03-27 15:09:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QList<BaseSettings *> LanguageClientManager::currentSettings()
|
|
|
|
|
{
|
2019-03-28 11:59:32 +01:00
|
|
|
QTC_ASSERT(managerInstance, return {});
|
|
|
|
|
return managerInstance->m_currentSettings;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-18 14:50:42 +02:00
|
|
|
void LanguageClientManager::registerClientSettings(BaseSettings *settings)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(managerInstance, return);
|
|
|
|
|
LanguageClientSettings::addSettings(settings);
|
|
|
|
|
managerInstance->applySettings();
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-17 10:03:57 +02:00
|
|
|
void LanguageClientManager::enableClientSettings(const QString &settingsId)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(managerInstance, return);
|
|
|
|
|
LanguageClientSettings::enableSettings(settingsId);
|
|
|
|
|
managerInstance->applySettings();
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-14 08:56:32 +01:00
|
|
|
QList<Client *> LanguageClientManager::clientsForSetting(const BaseSettings *setting)
|
2019-03-15 11:25:48 +01:00
|
|
|
{
|
|
|
|
|
QTC_ASSERT(managerInstance, return {});
|
2019-05-09 09:13:54 +02:00
|
|
|
auto instance = managerInstance;
|
|
|
|
|
return instance->m_clientsForSetting.value(setting->m_id);
|
2019-03-15 11:25:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const BaseSettings *LanguageClientManager::settingForClient(Client *client)
|
2019-03-28 11:59:32 +01:00
|
|
|
{
|
|
|
|
|
QTC_ASSERT(managerInstance, return nullptr);
|
2020-12-08 15:41:46 +01:00
|
|
|
for (auto it = managerInstance->m_clientsForSetting.cbegin();
|
|
|
|
|
it != managerInstance->m_clientsForSetting.cend(); ++it) {
|
|
|
|
|
const QString &id = it.key();
|
|
|
|
|
for (const Client *settingClient : it.value()) {
|
2019-03-15 11:25:48 +01:00
|
|
|
if (settingClient == client) {
|
|
|
|
|
return Utils::findOrDefault(managerInstance->m_currentSettings,
|
|
|
|
|
[id](BaseSettings *setting) {
|
|
|
|
|
return setting->m_id == id;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
2019-03-27 15:09:08 +01:00
|
|
|
}
|
|
|
|
|
|
2019-09-11 11:15:39 +02:00
|
|
|
Client *LanguageClientManager::clientForDocument(TextEditor::TextDocument *document)
|
2019-04-05 10:05:25 +02:00
|
|
|
{
|
|
|
|
|
QTC_ASSERT(managerInstance, return nullptr);
|
2019-09-20 07:41:21 +02:00
|
|
|
return document == nullptr ? nullptr
|
|
|
|
|
: managerInstance->m_clientForDocument.value(document).data();
|
2019-07-24 13:40:12 +02:00
|
|
|
}
|
2019-04-05 10:05:25 +02:00
|
|
|
|
2019-09-10 08:03:58 +02:00
|
|
|
Client *LanguageClientManager::clientForFilePath(const Utils::FilePath &filePath)
|
2019-07-24 13:40:12 +02:00
|
|
|
{
|
2019-10-01 09:11:02 +02:00
|
|
|
return clientForDocument(TextEditor::TextDocument::textDocumentForFilePath(filePath));
|
2019-09-10 08:03:58 +02:00
|
|
|
}
|
2019-04-05 10:05:25 +02:00
|
|
|
|
2019-09-10 08:03:58 +02:00
|
|
|
Client *LanguageClientManager::clientForUri(const DocumentUri &uri)
|
|
|
|
|
{
|
|
|
|
|
return clientForFilePath(uri.toFilePath());
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-23 13:51:41 +01:00
|
|
|
const QList<Client *> LanguageClientManager::clientsForProject(
|
|
|
|
|
const ProjectExplorer::Project *project)
|
|
|
|
|
{
|
|
|
|
|
return Utils::filtered(managerInstance->m_clients, [project](const Client *c) {
|
|
|
|
|
return c->project() == project;
|
2022-03-14 08:36:37 +01:00
|
|
|
});
|
2021-02-23 13:51:41 +01:00
|
|
|
}
|
|
|
|
|
|
2020-05-12 09:20:01 +02:00
|
|
|
void LanguageClientManager::openDocumentWithClient(TextEditor::TextDocument *document, Client *client)
|
2019-09-10 08:03:58 +02:00
|
|
|
{
|
2021-11-19 10:31:19 +01:00
|
|
|
if (!document)
|
|
|
|
|
return;
|
2020-05-12 09:20:01 +02:00
|
|
|
Client *currentClient = clientForDocument(document);
|
|
|
|
|
if (client == currentClient)
|
|
|
|
|
return;
|
|
|
|
|
if (currentClient)
|
2019-09-10 08:03:58 +02:00
|
|
|
currentClient->deactivateDocument(document);
|
|
|
|
|
managerInstance->m_clientForDocument[document] = client;
|
2020-05-12 09:20:01 +02:00
|
|
|
if (client) {
|
2021-11-09 15:12:04 +01:00
|
|
|
qCDebug(Log) << "open" << document->filePath() << "with" << client->name() << client;
|
2020-05-12 09:20:01 +02:00
|
|
|
if (!client->documentOpen(document))
|
|
|
|
|
client->openDocument(document);
|
|
|
|
|
else
|
|
|
|
|
client->activateDocument(document);
|
|
|
|
|
}
|
|
|
|
|
TextEditor::IOutlineWidgetFactory::updateOutline();
|
2019-04-05 10:05:25 +02:00
|
|
|
}
|
|
|
|
|
|
2020-02-20 10:13:14 +01:00
|
|
|
void LanguageClientManager::logBaseMessage(const LspLogMessage::MessageSender sender,
|
|
|
|
|
const QString &clientName,
|
|
|
|
|
const BaseMessage &message)
|
|
|
|
|
{
|
2021-02-10 14:03:45 +01:00
|
|
|
instance()->m_inspector.log(sender, clientName, message);
|
2020-02-20 10:13:14 +01:00
|
|
|
}
|
|
|
|
|
|
2021-02-10 14:03:45 +01:00
|
|
|
void LanguageClientManager::showInspector()
|
2020-02-20 10:13:14 +01:00
|
|
|
{
|
2021-02-11 09:54:09 +01:00
|
|
|
QString clientName;
|
|
|
|
|
if (Client *client = clientForDocument(TextEditor::TextDocument::currentTextDocument()))
|
|
|
|
|
clientName = client->name();
|
|
|
|
|
QWidget *inspectorWidget = instance()->m_inspector.createWidget(clientName);
|
2021-02-10 14:03:45 +01:00
|
|
|
inspectorWidget->setAttribute(Qt::WA_DeleteOnClose);
|
2022-02-07 09:53:19 +01:00
|
|
|
Core::ICore::registerWindow(inspectorWidget, Core::Context("LanguageClient.Inspector"));
|
2021-02-10 14:03:45 +01:00
|
|
|
inspectorWidget->show();
|
2020-02-20 10:13:14 +01:00
|
|
|
}
|
|
|
|
|
|
2022-03-14 08:36:37 +01:00
|
|
|
QList<Client *> LanguageClientManager::reachableClients()
|
2018-07-13 12:33:46 +02:00
|
|
|
{
|
2019-01-31 12:15:43 +01:00
|
|
|
return Utils::filtered(m_clients, &Client::reachable);
|
2018-07-13 12:33:46 +02:00
|
|
|
}
|
|
|
|
|
|
2019-03-12 12:53:13 +01:00
|
|
|
void LanguageClientManager::editorOpened(Core::IEditor *editor)
|
2018-07-13 12:33:46 +02:00
|
|
|
{
|
|
|
|
|
using namespace TextEditor;
|
2019-03-12 12:53:13 +01:00
|
|
|
if (auto *textEditor = qobject_cast<BaseTextEditor *>(editor)) {
|
|
|
|
|
if (TextEditorWidget *widget = textEditor->editorWidget()) {
|
|
|
|
|
connect(widget, &TextEditorWidget::requestLinkAt, this,
|
2020-05-06 10:26:31 +02:00
|
|
|
[document = textEditor->textDocument()]
|
2019-01-21 14:28:13 +01:00
|
|
|
(const QTextCursor &cursor, Utils::ProcessLinkCallback &callback, bool resolveTarget) {
|
2020-05-06 10:26:31 +02:00
|
|
|
if (auto client = clientForDocument(document))
|
|
|
|
|
client->symbolSupport().findLinkAt(document, cursor, callback, resolveTarget);
|
2019-03-12 12:53:13 +01:00
|
|
|
});
|
|
|
|
|
connect(widget, &TextEditorWidget::requestUsages, this,
|
2020-05-06 10:26:31 +02:00
|
|
|
[document = textEditor->textDocument()](const QTextCursor &cursor) {
|
|
|
|
|
if (auto client = clientForDocument(document))
|
|
|
|
|
client->symbolSupport().findUsages(document, cursor);
|
2019-03-12 13:46:31 +01:00
|
|
|
});
|
2020-05-11 08:41:57 +02:00
|
|
|
connect(widget, &TextEditorWidget::requestRename, this,
|
|
|
|
|
[document = textEditor->textDocument()](const QTextCursor &cursor) {
|
|
|
|
|
if (auto client = clientForDocument(document))
|
|
|
|
|
client->symbolSupport().renameSymbol(document, cursor);
|
|
|
|
|
});
|
2021-03-12 13:49:13 +01:00
|
|
|
connect(widget, &TextEditorWidget::cursorPositionChanged, this, [widget]() {
|
|
|
|
|
if (Client *client = clientForDocument(widget->textDocument()))
|
|
|
|
|
if (client->reachable())
|
|
|
|
|
client->cursorPositionChanged(widget);
|
2019-09-10 08:03:58 +02:00
|
|
|
});
|
2019-03-26 13:48:06 +01:00
|
|
|
updateEditorToolBar(editor);
|
2019-09-11 11:15:39 +02:00
|
|
|
if (TextEditor::TextDocument *document = textEditor->textDocument()) {
|
2019-07-24 13:40:12 +02:00
|
|
|
if (Client *client = m_clientForDocument[document])
|
|
|
|
|
widget->addHoverHandler(client->hoverHandler());
|
|
|
|
|
}
|
2018-07-13 12:33:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-12 12:53:13 +01:00
|
|
|
void LanguageClientManager::documentOpened(Core::IDocument *document)
|
|
|
|
|
{
|
2019-09-11 11:15:39 +02:00
|
|
|
auto textDocument = qobject_cast<TextEditor::TextDocument *>(document);
|
|
|
|
|
if (!textDocument)
|
|
|
|
|
return;
|
|
|
|
|
|
2019-03-15 11:25:48 +01:00
|
|
|
// check whether we have to start servers for this document
|
2021-01-29 06:52:22 +01:00
|
|
|
const QList<BaseSettings *> settings = currentSettings();
|
2021-01-22 13:57:15 +01:00
|
|
|
for (BaseSettings *setting : settings) {
|
2019-03-15 11:25:48 +01:00
|
|
|
if (setting->isValid() && setting->m_enabled
|
|
|
|
|
&& setting->m_languageFilter.isSupported(document)) {
|
2022-03-14 08:56:32 +01:00
|
|
|
QList<Client *> clients = clientsForSetting(setting);
|
2019-03-15 11:25:48 +01:00
|
|
|
if (setting->m_startBehavior == BaseSettings::RequiresProject) {
|
2021-01-25 12:50:27 +01:00
|
|
|
const Utils::FilePath &filePath = document->filePath();
|
2019-03-15 11:25:48 +01:00
|
|
|
for (ProjectExplorer::Project *project :
|
|
|
|
|
ProjectExplorer::SessionManager::projects()) {
|
|
|
|
|
// check whether file is part of this project
|
|
|
|
|
if (!project->isKnownFile(filePath))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// check whether we already have a client running for this project
|
2021-02-23 08:38:39 +01:00
|
|
|
Client *clientForProject = Utils::findOrDefault(clients,
|
|
|
|
|
[project](Client *client) {
|
|
|
|
|
return client->project()
|
|
|
|
|
== project;
|
|
|
|
|
});
|
2021-02-25 12:54:14 +01:00
|
|
|
if (!clientForProject)
|
2021-02-23 08:38:39 +01:00
|
|
|
clientForProject = startClient(setting, project);
|
2021-02-25 12:54:14 +01:00
|
|
|
|
2021-02-23 08:38:39 +01:00
|
|
|
QTC_ASSERT(clientForProject, continue);
|
|
|
|
|
openDocumentWithClient(textDocument, clientForProject);
|
2021-02-25 12:54:14 +01:00
|
|
|
// Since we already opened the document in this client we remove the client
|
|
|
|
|
// from the list of clients that receive the openDocument call
|
|
|
|
|
clients.removeAll(clientForProject);
|
2019-03-15 11:25:48 +01:00
|
|
|
}
|
|
|
|
|
} else if (setting->m_startBehavior == BaseSettings::RequiresFile && clients.isEmpty()) {
|
2019-07-24 13:40:12 +02:00
|
|
|
clients << startClient(setting);
|
|
|
|
|
}
|
2021-01-25 12:50:27 +01:00
|
|
|
for (auto client : qAsConst(clients))
|
2020-05-12 09:20:01 +02:00
|
|
|
client->openDocument(textDocument);
|
2019-03-15 11:25:48 +01:00
|
|
|
}
|
2019-03-12 11:01:25 +01:00
|
|
|
}
|
2019-07-24 13:40:12 +02:00
|
|
|
}
|
|
|
|
|
|
2019-03-12 12:46:38 +01:00
|
|
|
void LanguageClientManager::documentClosed(Core::IDocument *document)
|
2018-07-13 12:33:46 +02:00
|
|
|
{
|
2022-02-11 14:50:57 +01:00
|
|
|
if (auto textDocument = qobject_cast<TextEditor::TextDocument *>(document))
|
2019-09-11 11:15:39 +02:00
|
|
|
m_clientForDocument.remove(textDocument);
|
2018-07-13 12:33:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LanguageClientManager::documentContentsSaved(Core::IDocument *document)
|
|
|
|
|
{
|
2019-09-11 11:15:39 +02:00
|
|
|
if (auto textDocument = qobject_cast<TextEditor::TextDocument *>(document)) {
|
2022-03-14 08:36:37 +01:00
|
|
|
const QList<Client *> &clients = reachableClients();
|
2021-01-25 12:50:27 +01:00
|
|
|
for (Client *client : clients)
|
|
|
|
|
client->documentContentsSaved(textDocument);
|
2019-09-11 11:15:39 +02:00
|
|
|
}
|
2018-07-13 12:33:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LanguageClientManager::documentWillSave(Core::IDocument *document)
|
|
|
|
|
{
|
2019-09-11 11:15:39 +02:00
|
|
|
if (auto textDocument = qobject_cast<TextEditor::TextDocument *>(document)) {
|
2022-03-14 08:36:37 +01:00
|
|
|
const QList<Client *> &clients = reachableClients();
|
2021-01-25 12:50:27 +01:00
|
|
|
for (Client *client : clients)
|
|
|
|
|
client->documentWillSave(textDocument);
|
2019-09-11 11:15:39 +02:00
|
|
|
}
|
2018-07-13 12:33:46 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-11 15:00:07 +02:00
|
|
|
void LanguageClientManager::updateProject(ProjectExplorer::Project *project)
|
2018-07-13 12:33:46 +02:00
|
|
|
{
|
2021-01-25 12:50:27 +01:00
|
|
|
for (BaseSettings *setting : qAsConst(m_currentSettings)) {
|
2019-03-15 11:25:48 +01:00
|
|
|
if (setting->isValid()
|
|
|
|
|
&& setting->m_enabled
|
|
|
|
|
&& setting->m_startBehavior == BaseSettings::RequiresProject) {
|
2022-03-14 08:56:32 +01:00
|
|
|
if (Utils::findOrDefault(clientsForSetting(setting),
|
2021-01-25 12:50:27 +01:00
|
|
|
[project](const QPointer<Client> &client) {
|
2019-03-15 11:25:48 +01:00
|
|
|
return client->project() == project;
|
|
|
|
|
})
|
2020-05-11 15:00:07 +02:00
|
|
|
== nullptr) {
|
|
|
|
|
Client *newClient = nullptr;
|
2021-01-25 12:50:27 +01:00
|
|
|
const QList<Core::IDocument *> &openedDocuments = Core::DocumentModel::openedDocuments();
|
|
|
|
|
for (Core::IDocument *doc : openedDocuments) {
|
2020-05-11 15:00:07 +02:00
|
|
|
if (setting->m_languageFilter.isSupported(doc)
|
|
|
|
|
&& project->isKnownFile(doc->filePath())) {
|
|
|
|
|
if (auto textDoc = qobject_cast<TextEditor::TextDocument *>(doc)) {
|
|
|
|
|
if (!newClient)
|
|
|
|
|
newClient = startClient(setting, project);
|
|
|
|
|
if (!newClient)
|
|
|
|
|
break;
|
2020-05-12 09:20:01 +02:00
|
|
|
newClient->openDocument(textDoc);
|
2020-05-11 15:00:07 +02:00
|
|
|
}
|
2019-03-15 11:25:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-22 12:06:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LanguageClientManager::projectAdded(ProjectExplorer::Project *project)
|
|
|
|
|
{
|
2020-05-11 15:00:07 +02:00
|
|
|
connect(project, &ProjectExplorer::Project::fileListChanged, this, [this, project]() {
|
|
|
|
|
updateProject(project);
|
|
|
|
|
});
|
2022-03-14 08:36:37 +01:00
|
|
|
const QList<Client *> &clients = reachableClients();
|
2021-11-30 14:28:54 +01:00
|
|
|
for (Client *client : clients)
|
|
|
|
|
client->projectOpened(project);
|
2018-07-13 12:33:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace LanguageClient
|