diff --git a/src/plugins/qbsprojectmanager/CMakeLists.txt b/src/plugins/qbsprojectmanager/CMakeLists.txt index f039198419e..6102be41254 100644 --- a/src/plugins/qbsprojectmanager/CMakeLists.txt +++ b/src/plugins/qbsprojectmanager/CMakeLists.txt @@ -2,7 +2,7 @@ add_qtc_plugin(QbsProjectManager DEPENDS Qt::Qml Qt::Widgets QmlJS DEFINES IDE_LIBRARY_BASENAME="${IDE_LIBRARY_BASE_PATH}" - PLUGIN_DEPENDS Core ProjectExplorer CppEditor QtSupport QmlJSTools + PLUGIN_DEPENDS Core CppEditor LanguageClient ProjectExplorer QmlJSTools QmlJSEditor QtSupport SOURCES customqbspropertiesdialog.cpp customqbspropertiesdialog.h defaultpropertyprovider.cpp defaultpropertyprovider.h @@ -10,8 +10,10 @@ add_qtc_plugin(QbsProjectManager qbsbuildconfiguration.cpp qbsbuildconfiguration.h qbsbuildstep.cpp qbsbuildstep.h qbscleanstep.cpp qbscleanstep.h + qbseditor.cpp qbseditor.h qbsinstallstep.cpp qbsinstallstep.h qbskitaspect.cpp qbskitaspect.h + qbslanguageclient.cpp qbslanguageclient.h qbsnodes.cpp qbsnodes.h qbsnodetreebuilder.cpp qbsnodetreebuilder.h qbspmlogging.cpp qbspmlogging.h diff --git a/src/plugins/qbsprojectmanager/qbseditor.cpp b/src/plugins/qbsprojectmanager/qbseditor.cpp new file mode 100644 index 00000000000..7eed6977a2c --- /dev/null +++ b/src/plugins/qbsprojectmanager/qbseditor.cpp @@ -0,0 +1,60 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "qbseditor.h" + +#include "qbslanguageclient.h" +#include "qbsprojectmanagertr.h" + +#include +#include + +#include + +using namespace LanguageClient; +using namespace QmlJSEditor; +using namespace Utils; + +namespace QbsProjectManager::Internal { + +class QbsEditorWidget : public QmlJSEditorWidget +{ +private: + void findLinkAt(const QTextCursor &cursor, + const LinkHandler &processLinkCallback, + bool resolveTarget = true, + bool inNextSplit = false) override; +}; + +QbsEditorFactory::QbsEditorFactory() : QmlJSEditorFactory("QbsEditor.QbsEditor") +{ + setDisplayName(Tr::tr("Qbs Editor")); + setMimeTypes({Utils::Constants::QBS_MIMETYPE}); + setEditorWidgetCreator([] { return new QbsEditorWidget; }); +} + +void QbsEditorWidget::findLinkAt(const QTextCursor &cursor, const LinkHandler &processLinkCallback, + bool resolveTarget, bool inNextSplit) +{ + const LinkHandler extendedCallback = [self = QPointer(this), cursor, processLinkCallback, + resolveTarget](const Link &link) { + if (link.hasValidTarget()) + return processLinkCallback(link); + if (!self) + return; + const auto doc = self->textDocument(); + if (!doc) + return; + const QList &candidates = LanguageClientManager::clientsSupportingDocument(doc); + for (Client * const candidate : candidates) { + const auto qbsClient = qobject_cast(candidate); + if (!qbsClient || !qbsClient->isActive() || !qbsClient->documentOpen(doc)) + continue; + qbsClient->findLinkAt(doc, cursor, processLinkCallback, resolveTarget, + LinkTarget::SymbolDef); + } + }; + QmlJSEditorWidget::findLinkAt(cursor, extendedCallback, resolveTarget, inNextSplit); +} + +} // namespace QbsProjectManager::Internal diff --git a/src/plugins/qbsprojectmanager/qbseditor.h b/src/plugins/qbsprojectmanager/qbseditor.h new file mode 100644 index 00000000000..e1f7805ea02 --- /dev/null +++ b/src/plugins/qbsprojectmanager/qbseditor.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +namespace QbsProjectManager::Internal { + +class QbsEditorFactory : public QmlJSEditor::QmlJSEditorFactory +{ +public: + QbsEditorFactory(); +}; + +} // namespace QbsProjectManager::Internal diff --git a/src/plugins/qbsprojectmanager/qbslanguageclient.cpp b/src/plugins/qbsprojectmanager/qbslanguageclient.cpp new file mode 100644 index 00000000000..e067d4ae37f --- /dev/null +++ b/src/plugins/qbsprojectmanager/qbslanguageclient.cpp @@ -0,0 +1,90 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "qbslanguageclient.h" + +#include "qbsproject.h" +#include "qbssettings.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace Core; +using namespace LanguageClient; +using namespace TextEditor; +using namespace Utils; + +namespace QbsProjectManager::Internal { + +class QbsLanguageClientInterface : public LocalSocketClientInterface +{ +public: + QbsLanguageClientInterface(const QString &serverPath) + : LocalSocketClientInterface(serverPath), + m_qbsExecutable(QbsSettings::qbsExecutableFilePath()) {} + +private: + Utils::FilePath serverDeviceTemplate() const override{ return m_qbsExecutable; }; + + const FilePath m_qbsExecutable; +}; + +class QbsLanguageClient::Private +{ +public: + Private(QbsLanguageClient * q) : q(q) {} + + void checkDocument(IDocument *document); + + QbsLanguageClient * const q; + QPointer buildSystem; +}; + + +QbsLanguageClient::QbsLanguageClient(const QString &serverPath, QbsBuildSystem *buildSystem) + : Client(new QbsLanguageClientInterface(serverPath)), d(new Private(this)) +{ + d->buildSystem = buildSystem; + setName(QString::fromLatin1("qbs@%1").arg(serverPath)); + setCurrentProject(buildSystem->project()); + LanguageFilter langFilter; + langFilter.mimeTypes << Utils::Constants::QBS_MIMETYPE; + setSupportedLanguage(langFilter); + connect(EditorManager::instance(), &EditorManager::documentOpened, + this, [this](IDocument *document) { d->checkDocument(document); }); + const QList &allDocuments = DocumentModel::openedDocuments(); + for (IDocument * const document : allDocuments) + d->checkDocument(document); + start(); +} + +QbsLanguageClient::~QbsLanguageClient() { delete d; } + +bool QbsLanguageClient::isActive() const +{ + if (!d->buildSystem) + return false; + if (!d->buildSystem->target()->activeBuildConfiguration()) + return false; + if (d->buildSystem->target()->activeBuildConfiguration()->buildSystem() != d->buildSystem) + return false; + if (d->buildSystem->project()->activeTarget() != d->buildSystem->target()) + return false; + return true; +} + +void QbsLanguageClient::Private::checkDocument(IDocument *document) +{ + if (const auto doc = qobject_cast(document)) + q->openDocument(doc); +} + +} // namespace QbsProjectManager::Internal diff --git a/src/plugins/qbsprojectmanager/qbslanguageclient.h b/src/plugins/qbsprojectmanager/qbslanguageclient.h new file mode 100644 index 00000000000..8421dbfbfd2 --- /dev/null +++ b/src/plugins/qbsprojectmanager/qbslanguageclient.h @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +namespace QbsProjectManager::Internal { +class QbsBuildSystem; + +class QbsLanguageClient : public LanguageClient::Client +{ + Q_OBJECT +public: + QbsLanguageClient(const QString &serverPath, QbsBuildSystem *buildSystem); + ~QbsLanguageClient() override; + + bool isActive() const; + +private: + class Private; + Private * const d; +}; + +} // namespace QbsProjectManager::Internal diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanager.qbs b/src/plugins/qbsprojectmanager/qbsprojectmanager.qbs index dec42fd8902..709043df235 100644 --- a/src/plugins/qbsprojectmanager/qbsprojectmanager.qbs +++ b/src/plugins/qbsprojectmanager/qbsprojectmanager.qbs @@ -11,11 +11,13 @@ QtcPlugin { Depends { name: "QmlJS" } Depends { name: "Utils" } - Depends { name: "ProjectExplorer" } Depends { name: "Core" } Depends { name: "CppEditor" } - Depends { name: "QtSupport" } + Depends { name: "LanguageClient" } + Depends { name: "ProjectExplorer" } + Depends { name: "QmlJSEditor" } Depends { name: "QmlJSTools" } + Depends { name: "QtSupport" } files: [ "customqbspropertiesdialog.h", @@ -29,10 +31,14 @@ QtcPlugin { "qbsbuildstep.h", "qbscleanstep.cpp", "qbscleanstep.h", + "qbseditor.cpp", + "qbseditor.h", "qbsinstallstep.cpp", "qbsinstallstep.h", "qbskitaspect.cpp", "qbskitaspect.h", + "qbslanguageclient.cpp", + "qbslanguageclient.h", "qbsnodes.cpp", "qbsnodes.h", "qbsnodetreebuilder.cpp", @@ -48,7 +54,8 @@ QtcPlugin { "qbsprojectimporter.cpp", "qbsprojectimporter.h", "qbsprojectmanager.qrc", - "qbsprojectmanager_global.h", "qbsprojectmanagertr.h", + "qbsprojectmanager_global.h", + "qbsprojectmanagertr.h", "qbsprojectmanagerconstants.h", "qbsprojectmanagerplugin.cpp", "qbsprojectmanagerplugin.h", diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp index 546634d7926..ea8c9217ccc 100644 --- a/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp +++ b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp @@ -6,6 +6,7 @@ #include "qbsbuildconfiguration.h" #include "qbsbuildstep.h" #include "qbscleanstep.h" +#include "qbseditor.h" #include "qbsinstallstep.h" #include "qbsnodes.h" #include "qbsprofilessettingspage.h" @@ -68,6 +69,7 @@ public: QbsInstallStepFactory installStepFactory; QbsSettingsPage settingsPage; QbsProfilesSettingsPage profilesSetttingsPage; + QbsEditorFactory editorFactory; }; QbsProjectManagerPlugin::~QbsProjectManagerPlugin() diff --git a/src/plugins/qbsprojectmanager/qbssession.cpp b/src/plugins/qbsprojectmanager/qbssession.cpp index 999e83d2d79..bde6f907f92 100644 --- a/src/plugins/qbsprojectmanager/qbssession.cpp +++ b/src/plugins/qbsprojectmanager/qbssession.cpp @@ -3,7 +3,9 @@ #include "qbssession.h" +#include "qbslanguageclient.h" #include "qbspmlogging.h" +#include "qbsproject.h" #include "qbsprojectmanagerconstants.h" #include "qbsprojectmanagertr.h" #include "qbssettings.h" @@ -130,6 +132,7 @@ class QbsSession::Private { public: Process *qbsProcess = nullptr; + QbsLanguageClient *languageClient = nullptr; PacketReader *packetReader = nullptr; QJsonObject currentRequest; QJsonObject projectData; @@ -140,7 +143,7 @@ public: State state = State::Inactive; }; -QbsSession::QbsSession(QObject *parent) : QObject(parent), d(new Private) +QbsSession::QbsSession(QbsBuildSystem *buildSystem) : QObject(buildSystem), d(new Private) { initialize(); } @@ -449,6 +452,12 @@ void QbsSession::handlePacket(const QJsonObject &packet) setError(Error::VersionMismatch); return; } + if (packet.value("api-level").toInt() > 4) { + const QString lspSocket = packet.value("lsp-socket").toString(); + if (!lspSocket.isEmpty()) + d->languageClient = new QbsLanguageClient(lspSocket, + static_cast(parent())); + } d->state = State::Active; sendQueuedRequest(); } else if (type == "project-resolved") { @@ -567,6 +576,7 @@ void QbsSession::setInactive() if (d->qbsProcess->state() == QProcess::Running) sendQuitPacket(); d->qbsProcess = nullptr; + d->languageClient = nullptr; // Owned by LanguageClientManager } FileChangeResult QbsSession::updateFileList(const char *action, const QStringList &files, diff --git a/src/plugins/qbsprojectmanager/qbssession.h b/src/plugins/qbsprojectmanager/qbssession.h index 1a20213f9d6..670388a5bbb 100644 --- a/src/plugins/qbsprojectmanager/qbssession.h +++ b/src/plugins/qbsprojectmanager/qbssession.h @@ -19,6 +19,7 @@ namespace ProjectExplorer { class Target; } namespace QbsProjectManager { namespace Internal { +class QbsBuildSystem; class ErrorInfoItem { @@ -96,7 +97,7 @@ class QbsSession : public QObject { Q_OBJECT public: - explicit QbsSession(QObject *parent = nullptr); + explicit QbsSession(QbsBuildSystem *buildSystem); ~QbsSession() override; enum class State { Initializing, Active, Inactive }; diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp index a590c0fd834..a8cdb569269 100644 --- a/src/plugins/qmljseditor/qmljseditor.cpp +++ b/src/plugins/qmljseditor/qmljseditor.cpp @@ -1134,7 +1134,6 @@ QmlJSEditorFactory::QmlJSEditorFactory(Utils::Id _id) using namespace Utils::Constants; addMimeType(QML_MIMETYPE); addMimeType(QMLPROJECT_MIMETYPE); - addMimeType(QBS_MIMETYPE); addMimeType(QMLTYPES_MIMETYPE); addMimeType(JS_MIMETYPE);