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-01-18 13:12:13 +01:00
|
|
|
#include "languageclientutils.h"
|
2019-03-27 14:05:30 +01:00
|
|
|
#include "languageclientplugin.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>
|
|
|
|
|
#include <projectexplorer/session.h>
|
|
|
|
|
#include <projectexplorer/project.h>
|
|
|
|
|
#include <texteditor/texteditor.h>
|
|
|
|
|
#include <texteditor/textmark.h>
|
|
|
|
|
#include <texteditor/textdocument.h>
|
|
|
|
|
#include <utils/mimetypes/mimedatabase.h>
|
|
|
|
|
#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 {
|
|
|
|
|
|
|
|
|
|
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;
|
2018-09-12 10:19:32 +02:00
|
|
|
JsonRpcMessageHandler::registerMessageProvider<PublishDiagnosticsNotification>();
|
2019-01-25 09:48:44 +01:00
|
|
|
JsonRpcMessageHandler::registerMessageProvider<ApplyWorkspaceEditRequest>();
|
2018-09-12 10:19:32 +02:00
|
|
|
JsonRpcMessageHandler::registerMessageProvider<LogMessageNotification>();
|
2018-09-12 10:24:02 +02:00
|
|
|
JsonRpcMessageHandler::registerMessageProvider<ShowMessageRequest>();
|
|
|
|
|
JsonRpcMessageHandler::registerMessageProvider<ShowMessageNotification>();
|
2019-03-15 10:09:17 +01:00
|
|
|
JsonRpcMessageHandler::registerMessageProvider<WorkSpaceFolderRequest>();
|
2019-03-27 14:05:30 +01:00
|
|
|
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,
|
|
|
|
|
this, &LanguageClientManager::projectAdded);
|
|
|
|
|
connect(SessionManager::instance(), &SessionManager::projectRemoved,
|
|
|
|
|
this, &LanguageClientManager::projectRemoved);
|
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
|
|
|
}
|
|
|
|
|
|
2019-01-31 12:15:43 +01:00
|
|
|
void LanguageClientManager::startClient(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);
|
2018-09-13 15:31:00 +02:00
|
|
|
if (managerInstance->m_shuttingDown) {
|
|
|
|
|
managerInstance->clientFinished(client);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-09-13 13:39:01 +02:00
|
|
|
if (!managerInstance->m_clients.contains(client))
|
|
|
|
|
managerInstance->m_clients.append(client);
|
2019-01-31 12:15:43 +01:00
|
|
|
connect(client, &Client::finished, managerInstance, [client](){
|
2018-07-13 12:33:46 +02:00
|
|
|
managerInstance->clientFinished(client);
|
|
|
|
|
});
|
|
|
|
|
if (client->start())
|
|
|
|
|
client->initialize();
|
|
|
|
|
else
|
|
|
|
|
managerInstance->clientFinished(client);
|
2019-04-05 10:05:25 +02:00
|
|
|
|
|
|
|
|
connect(client,
|
|
|
|
|
&Client::initialized,
|
|
|
|
|
&managerInstance->m_currentDocumentLocatorFilter,
|
|
|
|
|
&DocumentLocatorFilter::updateCurrentClient);
|
2018-07-13 12:33:46 +02:00
|
|
|
}
|
|
|
|
|
|
2019-03-15 11:25:48 +01:00
|
|
|
void LanguageClientManager::startClient(BaseSettings *setting, ProjectExplorer::Project *project)
|
2019-03-28 11:59:32 +01:00
|
|
|
{
|
|
|
|
|
QTC_ASSERT(managerInstance, return);
|
|
|
|
|
QTC_ASSERT(setting, return);
|
|
|
|
|
QTC_ASSERT(setting->isValid(), return);
|
|
|
|
|
Client *client = setting->createClient();
|
|
|
|
|
QTC_ASSERT(client, return);
|
2019-03-15 11:25:48 +01:00
|
|
|
client->setCurrentProject(project);
|
2019-03-28 11:59:32 +01:00
|
|
|
startClient(client);
|
2019-03-15 11:25:48 +01:00
|
|
|
managerInstance->m_clientsForSetting[setting->m_id].append(QPointer<Client>(client));
|
2019-03-28 11:59:32 +01:00
|
|
|
}
|
|
|
|
|
|
2019-01-31 12:15:43 +01:00
|
|
|
QVector<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);
|
2019-01-31 12:15:43 +01:00
|
|
|
for (Client *client : managerInstance->m_exclusiveRequests[id]) {
|
2018-07-13 12:33:46 +02:00
|
|
|
if (client != byClient)
|
|
|
|
|
client->cancelRequest(id);
|
|
|
|
|
}
|
|
|
|
|
managerInstance->m_exclusiveRequests.remove(id);
|
|
|
|
|
}
|
|
|
|
|
|
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);
|
2018-12-05 07:49:05 +01:00
|
|
|
client->disconnect();
|
2018-09-13 13:39:01 +02:00
|
|
|
managerInstance->m_clients.removeAll(client);
|
2019-03-05 12:56:53 +01:00
|
|
|
if (managerInstance->m_shuttingDown)
|
|
|
|
|
delete client;
|
|
|
|
|
else
|
|
|
|
|
client->deleteLater();
|
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;
|
|
|
|
|
managerInstance->m_shuttingDown = true;
|
2018-10-15 08:36:54 +02:00
|
|
|
for (auto interface : managerInstance->m_clients) {
|
|
|
|
|
if (interface->reachable())
|
|
|
|
|
interface->shutdown();
|
|
|
|
|
else
|
|
|
|
|
deleteClient(interface);
|
|
|
|
|
}
|
2018-10-24 08:55:27 +02:00
|
|
|
QTimer::singleShot(3000, managerInstance, [](){
|
|
|
|
|
for (auto interface : managerInstance->m_clients)
|
|
|
|
|
deleteClient(interface);
|
|
|
|
|
emit managerInstance->shutdownFinished();
|
|
|
|
|
});
|
2018-09-13 15:31:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LanguageClientManager *LanguageClientManager::instance()
|
|
|
|
|
{
|
|
|
|
|
return managerInstance;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-31 12:15:43 +01:00
|
|
|
QList<Client *> LanguageClientManager::clientsSupportingDocument(
|
2018-11-23 09:45:51 +01:00
|
|
|
const TextEditor::TextDocument *doc)
|
|
|
|
|
{
|
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);
|
|
|
|
|
}).toList();
|
|
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
|
managerInstance->m_currentSettings = Utils::transform(LanguageClientSettings::currentPageSettings(),
|
|
|
|
|
[](BaseSettings *settings) {
|
|
|
|
|
return settings->copy();
|
|
|
|
|
});
|
|
|
|
|
LanguageClientSettings::toSettings(Core::ICore::settings(), managerInstance->m_currentSettings);
|
2019-03-27 15:09:08 +01:00
|
|
|
|
2019-03-28 11:59:32 +01:00
|
|
|
const QList<BaseSettings *> restarts = Utils::filtered(managerInstance->m_currentSettings,
|
|
|
|
|
[](BaseSettings *settings) {
|
|
|
|
|
return settings->needsRestart();
|
|
|
|
|
});
|
2019-03-27 15:09:08 +01:00
|
|
|
|
|
|
|
|
for (BaseSettings *setting : restarts) {
|
2019-03-15 11:25:48 +01:00
|
|
|
for (const QPointer<Client> &client : clientForSetting(setting)) {
|
2019-03-27 15:09:08 +01:00
|
|
|
if (client->reachable())
|
|
|
|
|
client->shutdown();
|
|
|
|
|
else
|
|
|
|
|
deleteClient(client);
|
|
|
|
|
}
|
2019-03-15 11:25:48 +01:00
|
|
|
if (!setting->isValid() || !setting->m_enabled)
|
|
|
|
|
continue;
|
|
|
|
|
switch (setting->m_startBehavior) {
|
|
|
|
|
case BaseSettings::AlwaysOn:
|
2019-03-28 11:59:32 +01:00
|
|
|
startClient(setting);
|
2019-03-15 11:25:48 +01:00
|
|
|
break;
|
|
|
|
|
case BaseSettings::RequiresFile:
|
|
|
|
|
if (Utils::anyOf(Core::DocumentModel::openedDocuments(),
|
|
|
|
|
[filter = setting->m_languageFilter](Core::IDocument *doc) {
|
|
|
|
|
return filter.isSupported(doc);
|
|
|
|
|
})) {
|
|
|
|
|
startClient(setting);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case BaseSettings::RequiresProject: {
|
|
|
|
|
for (Core::IDocument *doc : Core::DocumentModel::openedDocuments()) {
|
|
|
|
|
if (setting->m_languageFilter.isSupported(doc)) {
|
|
|
|
|
const Utils::FileName filePath = doc->filePath();
|
|
|
|
|
for (ProjectExplorer::Project *project :
|
|
|
|
|
ProjectExplorer::SessionManager::projects()) {
|
|
|
|
|
if (project->isKnownFile(filePath))
|
|
|
|
|
startClient(setting, project);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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-03-15 11:25:48 +01:00
|
|
|
QVector<QPointer<Client>> LanguageClientManager::clientForSetting(const BaseSettings *setting)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(managerInstance, return {});
|
|
|
|
|
return managerInstance->m_clientsForSetting.value(setting->m_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const BaseSettings *LanguageClientManager::settingForClient(Client *client)
|
2019-03-28 11:59:32 +01:00
|
|
|
{
|
|
|
|
|
QTC_ASSERT(managerInstance, return nullptr);
|
2019-03-15 11:25:48 +01:00
|
|
|
for (const QString &id : managerInstance->m_clientsForSetting.keys()) {
|
|
|
|
|
for (const QPointer<Client> &settingClient : managerInstance->m_clientsForSetting[id]) {
|
|
|
|
|
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-04-05 10:05:25 +02:00
|
|
|
Client *LanguageClientManager::clientForEditor(Core::IEditor *iEditor)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(managerInstance, return nullptr);
|
|
|
|
|
|
|
|
|
|
auto editor = qobject_cast<TextEditor::BaseTextEditor *>(iEditor);
|
|
|
|
|
if (!editor)
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
return Utils::findOrDefault(managerInstance->reachableClients(),
|
|
|
|
|
[doc = editor->textDocument()](Client *client) {
|
|
|
|
|
return client->documentOpen(doc);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-31 12:15:43 +01:00
|
|
|
QVector<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-01-31 12:15:43 +01:00
|
|
|
static void sendToInterfaces(const IContent &content, const QVector<Client *> &interfaces)
|
2018-07-13 12:33:46 +02:00
|
|
|
{
|
2019-01-31 12:15:43 +01:00
|
|
|
for (Client *interface : interfaces)
|
2018-07-13 12:33:46 +02:00
|
|
|
interface->sendContent(content);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LanguageClientManager::sendToAllReachableServers(const IContent &content)
|
|
|
|
|
{
|
|
|
|
|
sendToInterfaces(content, reachableClients());
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-31 12:15:43 +01:00
|
|
|
void LanguageClientManager::clientFinished(Client *client)
|
2018-07-13 12:33:46 +02:00
|
|
|
{
|
|
|
|
|
constexpr int restartTimeoutS = 5;
|
2019-01-31 12:15:43 +01:00
|
|
|
const bool unexpectedFinish = client->state() != Client::Shutdown
|
|
|
|
|
&& client->state() != Client::ShutdownRequested;
|
2018-09-14 10:00:29 +02:00
|
|
|
if (unexpectedFinish && !m_shuttingDown && client->reset()) {
|
2018-09-13 15:31:00 +02:00
|
|
|
client->disconnect(this);
|
2018-07-13 12:33:46 +02:00
|
|
|
client->log(tr("Unexpectedly finished. Restarting in %1 seconds.").arg(restartTimeoutS),
|
|
|
|
|
Core::MessageManager::Flash);
|
2019-03-15 11:25:48 +01:00
|
|
|
QTimer::singleShot(restartTimeoutS * 1000, client, [client]() { startClient(client); });
|
2018-07-13 12:33:46 +02:00
|
|
|
} else {
|
2018-10-24 08:59:30 +02:00
|
|
|
if (unexpectedFinish && !m_shuttingDown)
|
|
|
|
|
client->log(tr("Unexpectedly finished."), Core::MessageManager::Flash);
|
2018-09-13 13:39:01 +02:00
|
|
|
deleteClient(client);
|
2018-09-13 15:31:00 +02:00
|
|
|
if (m_shuttingDown && m_clients.isEmpty())
|
|
|
|
|
emit shutdownFinished();
|
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,
|
|
|
|
|
[this, filePath = editor->document()->filePath()]
|
2019-03-26 13:48:06 +01:00
|
|
|
(const QTextCursor &cursor, Utils::ProcessLinkCallback &callback) {
|
2019-03-12 12:53:13 +01:00
|
|
|
findLinkAt(filePath, cursor, callback);
|
|
|
|
|
});
|
|
|
|
|
connect(widget, &TextEditorWidget::requestUsages, this,
|
|
|
|
|
[this, filePath = editor->document()->filePath()]
|
|
|
|
|
(const QTextCursor &cursor){
|
|
|
|
|
findUsages(filePath, cursor);
|
|
|
|
|
});
|
2019-03-12 13:46:31 +01:00
|
|
|
connect(widget, &TextEditorWidget::cursorPositionChanged, this, [this, widget](){
|
|
|
|
|
// TODO This would better be a compressing timer
|
|
|
|
|
QTimer::singleShot(50, this,
|
|
|
|
|
[this, widget = QPointer<TextEditorWidget>(widget)]() {
|
|
|
|
|
if (widget) {
|
|
|
|
|
for (Client *client : this->reachableClients()) {
|
|
|
|
|
if (client->isSupportedDocument(widget->textDocument()))
|
|
|
|
|
client->cursorPositionChanged(widget);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
2019-03-26 13:48:06 +01:00
|
|
|
updateEditorToolBar(editor);
|
2018-07-13 12:33:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-12 12:53:13 +01:00
|
|
|
void LanguageClientManager::documentOpened(Core::IDocument *document)
|
|
|
|
|
{
|
2019-03-15 11:25:48 +01:00
|
|
|
// check whether we have to start servers for this document
|
2019-03-27 15:09:08 +01:00
|
|
|
for (BaseSettings *setting : LanguageClientSettings::currentPageSettings()) {
|
2019-03-15 11:25:48 +01:00
|
|
|
const QVector<QPointer<Client>> clients = clientForSetting(setting);
|
|
|
|
|
if (setting->isValid() && setting->m_enabled
|
|
|
|
|
&& setting->m_languageFilter.isSupported(document)) {
|
|
|
|
|
if (setting->m_startBehavior == BaseSettings::RequiresProject) {
|
|
|
|
|
const Utils::FileName filePath = document->filePath();
|
|
|
|
|
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
|
|
|
|
|
if (Utils::findOrDefault(clients,
|
|
|
|
|
[project](QPointer<Client> client) {
|
|
|
|
|
return client->project() == project;
|
|
|
|
|
})) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
startClient(setting, project);
|
|
|
|
|
}
|
|
|
|
|
} else if (setting->m_startBehavior == BaseSettings::RequiresFile && clients.isEmpty()) {
|
|
|
|
|
startClient(setting);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-12 11:01:25 +01:00
|
|
|
}
|
2019-03-12 12:53:13 +01:00
|
|
|
for (Client *interface : reachableClients())
|
|
|
|
|
interface->openDocument(document);
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-12 12:46:38 +01:00
|
|
|
void LanguageClientManager::documentClosed(Core::IDocument *document)
|
2018-07-13 12:33:46 +02:00
|
|
|
{
|
2019-03-12 12:46:38 +01:00
|
|
|
const DidCloseTextDocumentParams params(
|
|
|
|
|
TextDocumentIdentifier(DocumentUri::fromFileName(document->filePath())));
|
|
|
|
|
for (Client *interface : reachableClients())
|
|
|
|
|
interface->closeDocument(params);
|
2018-07-13 12:33:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LanguageClientManager::documentContentsSaved(Core::IDocument *document)
|
|
|
|
|
{
|
2019-01-31 12:15:43 +01:00
|
|
|
for (Client *interface : reachableClients())
|
2018-07-13 12:33:46 +02:00
|
|
|
interface->documentContentsSaved(document);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LanguageClientManager::documentWillSave(Core::IDocument *document)
|
|
|
|
|
{
|
2019-01-31 12:15:43 +01:00
|
|
|
for (Client *interface : reachableClients())
|
2018-07-13 12:33:46 +02:00
|
|
|
interface->documentContentsSaved(document);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LanguageClientManager::findLinkAt(const Utils::FileName &filePath,
|
|
|
|
|
const QTextCursor &cursor,
|
|
|
|
|
Utils::ProcessLinkCallback callback)
|
|
|
|
|
{
|
|
|
|
|
const DocumentUri uri = DocumentUri::fromFileName(filePath);
|
|
|
|
|
const TextDocumentIdentifier document(uri);
|
|
|
|
|
const Position pos(cursor);
|
|
|
|
|
TextDocumentPositionParams params(document, pos);
|
|
|
|
|
GotoDefinitionRequest request(params);
|
2018-11-20 07:45:22 +01:00
|
|
|
request.setResponseCallback([callback](const GotoDefinitionRequest::Response &response){
|
2018-07-13 12:33:46 +02:00
|
|
|
if (Utils::optional<GotoResult> _result = response.result()) {
|
|
|
|
|
const GotoResult result = _result.value();
|
|
|
|
|
if (Utils::holds_alternative<std::nullptr_t>(result))
|
|
|
|
|
return;
|
|
|
|
|
if (auto ploc = Utils::get_if<Location>(&result)) {
|
|
|
|
|
callback(ploc->toLink());
|
|
|
|
|
} else if (auto plloc = Utils::get_if<QList<Location>>(&result)) {
|
|
|
|
|
if (!plloc->isEmpty())
|
|
|
|
|
callback(plloc->value(0).toLink());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
2019-01-31 12:15:43 +01:00
|
|
|
for (Client *interface : reachableClients()) {
|
2018-07-13 12:33:46 +02:00
|
|
|
if (interface->findLinkAt(request))
|
|
|
|
|
m_exclusiveRequests[request.id()] << interface;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-28 08:16:19 +01:00
|
|
|
QList<Core::SearchResultItem> generateSearchResultItems(const LanguageClientArray<Location> &locations)
|
|
|
|
|
{
|
|
|
|
|
auto convertPosition = [](const Position &pos){
|
|
|
|
|
return Core::Search::TextPosition(pos.line() + 1, pos.character());
|
|
|
|
|
};
|
|
|
|
|
auto convertRange = [convertPosition](const Range &range){
|
|
|
|
|
return Core::Search::TextRange(convertPosition(range.start()), convertPosition(range.end()));
|
|
|
|
|
};
|
|
|
|
|
QList<Core::SearchResultItem> result;
|
|
|
|
|
if (locations.isNull())
|
|
|
|
|
return result;
|
|
|
|
|
QMap<QString, QList<Core::Search::TextRange>> rangesInDocument;
|
|
|
|
|
for (const Location &location : locations.toList())
|
|
|
|
|
rangesInDocument[location.uri().toFileName().toString()] << convertRange(location.range());
|
|
|
|
|
for (auto it = rangesInDocument.begin(); it != rangesInDocument.end(); ++it) {
|
|
|
|
|
const QString &fileName = it.key();
|
|
|
|
|
QFile file(fileName);
|
|
|
|
|
file.open(QFile::ReadOnly);
|
|
|
|
|
|
|
|
|
|
Core::SearchResultItem item;
|
|
|
|
|
item.path = QStringList() << fileName;
|
|
|
|
|
item.useTextEditorFont = true;
|
|
|
|
|
|
|
|
|
|
QStringList lines = QString::fromLocal8Bit(file.readAll()).split(QChar::LineFeed);
|
|
|
|
|
for (const Core::Search::TextRange &range : it.value()) {
|
|
|
|
|
item.mainRange = range;
|
|
|
|
|
if (file.isOpen() && range.begin.line > 0 && range.begin.line <= lines.size())
|
|
|
|
|
item.text = lines[range.begin.line - 1];
|
|
|
|
|
else
|
|
|
|
|
item.text.clear();
|
|
|
|
|
result << item;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LanguageClientManager::findUsages(const Utils::FileName &filePath, const QTextCursor &cursor)
|
|
|
|
|
{
|
|
|
|
|
const DocumentUri uri = DocumentUri::fromFileName(filePath);
|
|
|
|
|
const TextDocumentIdentifier document(uri);
|
|
|
|
|
const Position pos(cursor);
|
|
|
|
|
QTextCursor termCursor(cursor);
|
|
|
|
|
termCursor.select(QTextCursor::WordUnderCursor);
|
|
|
|
|
ReferenceParams params(TextDocumentPositionParams(document, pos));
|
|
|
|
|
params.setContext(ReferenceParams::ReferenceContext(true));
|
|
|
|
|
FindReferencesRequest request(params);
|
|
|
|
|
auto callback = [wordUnderCursor = termCursor.selectedText()]
|
|
|
|
|
(const QString &clientName, const FindReferencesRequest::Response &response){
|
|
|
|
|
if (auto result = response.result()) {
|
|
|
|
|
Core::SearchResult *search = Core::SearchResultWindow::instance()->startNewSearch(
|
|
|
|
|
tr("Find References with %1 for:").arg(clientName), "", wordUnderCursor);
|
|
|
|
|
search->addResults(generateSearchResultItems(result.value()), Core::SearchResult::AddOrdered);
|
|
|
|
|
QObject::connect(search, &Core::SearchResult::activated,
|
|
|
|
|
[](const Core::SearchResultItem& item) {
|
|
|
|
|
Core::EditorManager::openEditorAtSearchResult(item);
|
|
|
|
|
});
|
|
|
|
|
search->finishSearch(false);
|
|
|
|
|
search->popup();
|
|
|
|
|
}
|
|
|
|
|
};
|
2019-01-31 12:15:43 +01:00
|
|
|
for (Client *client : reachableClients()) {
|
2018-11-28 08:16:19 +01:00
|
|
|
request.setResponseCallback([callback, clientName = client->name()]
|
|
|
|
|
(const FindReferencesRequest::Response &response){
|
|
|
|
|
callback(clientName, response);
|
|
|
|
|
});
|
|
|
|
|
if (client->findUsages(request))
|
|
|
|
|
m_exclusiveRequests[request.id()] << client;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-13 12:33:46 +02:00
|
|
|
void LanguageClientManager::projectAdded(ProjectExplorer::Project *project)
|
|
|
|
|
{
|
2019-03-15 11:25:48 +01:00
|
|
|
for (BaseSettings *setting : m_currentSettings) {
|
|
|
|
|
if (setting->isValid()
|
|
|
|
|
&& setting->m_enabled
|
|
|
|
|
&& setting->m_startBehavior == BaseSettings::RequiresProject) {
|
|
|
|
|
if (Utils::findOrDefault(clientForSetting(setting),
|
|
|
|
|
[project](QPointer<Client> client) {
|
|
|
|
|
return client->project() == project;
|
|
|
|
|
})
|
|
|
|
|
.isNull()) {
|
|
|
|
|
for (Core::IDocument *doc : Core::DocumentModel::openedDocuments()) {
|
|
|
|
|
if (setting->m_languageFilter.isSupported(doc)) {
|
|
|
|
|
if (project->isKnownFile(doc->filePath()))
|
|
|
|
|
startClient(setting, project);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-01-31 12:15:43 +01:00
|
|
|
for (Client *interface : reachableClients())
|
2018-07-13 12:33:46 +02:00
|
|
|
interface->projectOpened(project);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LanguageClientManager::projectRemoved(ProjectExplorer::Project *project)
|
|
|
|
|
{
|
2019-03-15 11:25:48 +01:00
|
|
|
for (Client *interface : m_clients)
|
2018-07-13 12:33:46 +02:00
|
|
|
interface->projectClosed(project);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace LanguageClient
|