diff --git a/src/plugins/qmljseditor/CMakeLists.txt b/src/plugins/qmljseditor/CMakeLists.txt index fcba4765f3b..06e848b8b83 100644 --- a/src/plugins/qmljseditor/CMakeLists.txt +++ b/src/plugins/qmljseditor/CMakeLists.txt @@ -4,6 +4,7 @@ add_qtc_plugin(QmlJSEditor SOURCES qmlexpressionundercursor.cpp qmlexpressionundercursor.h qmllsclient.h qmllsclient.cpp + qmllsclientsettings.h qmllsclientsettings.cpp qmljsautocompleter.cpp qmljsautocompleter.h qmljscompletionassist.cpp qmljscompletionassist.h qmljscomponentfromobjectdef.cpp qmljscomponentfromobjectdef.h diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp index a079fdf3b75..b09a9f57e06 100644 --- a/src/plugins/qmljseditor/qmljseditor.cpp +++ b/src/plugins/qmljseditor/qmljseditor.cpp @@ -5,15 +5,16 @@ #include "qmljsautocompleter.h" #include "qmljscompletionassist.h" -#include "qmljseditorsettings.h" #include "qmljseditorconstants.h" #include "qmljseditordocument.h" #include "qmljseditorplugin.h" +#include "qmljseditorsettings.h" #include "qmljseditortr.h" #include "qmljsfindreferences.h" #include "qmljshighlighter.h" #include "qmljshoverhandler.h" #include "qmljsquickfixassist.h" +#include "qmllsclientsettings.h" #include "qmloutlinemodel.h" #include "quicktoolbar.h" @@ -98,15 +99,13 @@ namespace QmlJSEditor { static LanguageClient::Client *getQmllsClient(const Utils::FilePath &fileName) { - // the value in disableBuiltinCodemodel is only valid when useQmlls is enabled - if (settings().useQmlls() && !settings().disableBuiltinCodemodel()) + if (qmllsSettings()->useQmllsWithBuiltinCodemodelOnProject(fileName)) return nullptr; auto client = LanguageClient::LanguageClientManager::clientForFilePath(fileName); return client; } - // // QmlJSEditorWidget // diff --git a/src/plugins/qmljseditor/qmljseditorconstants.h b/src/plugins/qmljseditor/qmljseditorconstants.h index 5ffbbc98c3b..75500d36f56 100644 --- a/src/plugins/qmljseditor/qmljseditorconstants.h +++ b/src/plugins/qmljseditor/qmljseditorconstants.h @@ -25,5 +25,7 @@ const char QML_SNIPPETS_GROUP_ID[] = "QML"; const char QMLLINT_BUILD_TARGET[] = "all_qmllint"; +const char QMLLS_CLIENT_SETTINGS_ID[] = "LanguageClient::QmllsClientSettingsID"; + } // namespace Constants } // namespace QmlJSEditor diff --git a/src/plugins/qmljseditor/qmljseditordocument.cpp b/src/plugins/qmljseditor/qmljseditordocument.cpp index 1bec169fe1e..b9460ad3c21 100644 --- a/src/plugins/qmljseditor/qmljseditordocument.cpp +++ b/src/plugins/qmljseditor/qmljseditordocument.cpp @@ -6,13 +6,12 @@ #include "qmljseditordocument_p.h" #include "qmljseditorplugin.h" #include "qmljseditortr.h" -#include "qmljseditorsettings.h" #include "qmljshighlighter.h" #include "qmljsquickfixassist.h" #include "qmljssemantichighlighter.h" #include "qmljssemanticinfoupdater.h" #include "qmljstextmark.h" -#include "qmllsclient.h" +#include "qmllsclientsettings.h" #include "qmloutlinemodel.h" #include @@ -43,8 +42,6 @@ using namespace Utils; namespace { -Q_LOGGING_CATEGORY(qmllsLog, "qtc.qmlls.editor", QtWarningMsg); - enum { UPDATE_DOCUMENT_DEFAULT_INTERVAL = 100, UPDATE_OUTLINE_INTERVAL = 500 @@ -480,13 +477,6 @@ QmlJSEditorDocumentPrivate::QmlJSEditorDocumentPrivate(QmlJSEditorDocument *pare this, &QmlJSEditorDocumentPrivate::reparseDocument); connect(modelManager, &ModelManagerInterface::documentUpdated, this, &QmlJSEditorDocumentPrivate::onDocumentUpdated); - connect(QmllsSettingsManager::instance(), &QmllsSettingsManager::settingsChanged, - this, &QmlJSEditorDocumentPrivate::settingsChanged); - connect( - modelManager, - &ModelManagerInterface::projectInfoUpdated, - this, - &QmlJSEditorDocumentPrivate::settingsChanged); // semantic info m_semanticInfoUpdater = new SemanticInfoUpdater(); @@ -734,104 +724,12 @@ void QmlJSEditorDocumentPrivate::setSourcesWithCapabilities( setSemanticWarningSource(QmllsStatus::Source::Qmlls); else setSemanticWarningSource(QmllsStatus::Source::EmbeddedCodeModel); - if (cap.semanticTokensProvider() && settings().enableQmllsSemanticHighlighting()) + if (cap.semanticTokensProvider() && qmllsSettings()->m_useQmllsSemanticHighlighting) setSemanticHighlightSource(QmllsStatus::Source::Qmlls); else setSemanticHighlightSource(QmllsStatus::Source::EmbeddedCodeModel); } -static FilePath qmllsForFile(const FilePath &file, QmlJS::ModelManagerInterface *modelManager) -{ - QmllsSettingsManager *settingsManager = QmllsSettingsManager::instance(); - ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::projectForFile(file); - const bool enabled = settingsManager->useQmlls(project); - if (!enabled) - return {}; - if (settingsManager->useLatestQmlls()) - return settingsManager->latestQmlls(); - QmlJS::ModelManagerInterface::ProjectInfo pInfo = modelManager->projectInfoForPath(file); - - if (!settings().ignoreMinimumQmllsVersion() - && QVersionNumber::fromString(pInfo.qtVersionString) - < QmlJsEditingSettings::mininumQmllsVersion) { - return {}; - } - return pInfo.qmllsPath.exists() ? pInfo.qmllsPath : Utils::FilePath(); -} - -void QmlJSEditorDocumentPrivate::settingsChanged() -{ - if (q->isTemporary()) - return; - - FilePath newQmlls = qmllsForFile(q->filePath(), ModelManagerInterface::instance()); - if (m_qmllsStatus.qmllsPath == newQmlls) { - if (QmllsClient *client = QmllsClient::clientForQmlls(newQmlls)) { - client->updateQmllsSemanticHighlightingCapability(); - setSourcesWithCapabilities(client->capabilities()); - client->activateDocument(q); - } - return; - } - - using namespace LanguageClient; - m_qmllsStatus.qmllsPath = newQmlls; - if (newQmlls.isEmpty()) { - qCDebug(qmllsLog) << "disabling qmlls for" << q->filePath(); - if (LanguageClientManager::clientForDocument(q) != nullptr) { - qCDebug(qmllsLog) << "deactivating " << q->filePath() << "in qmlls" << newQmlls; - LanguageClientManager::openDocumentWithClient(q, nullptr); - } else - qCWarning(qmllsLog) << "Could not find client to disable for document " << q->filePath() - << " in LanguageClient::LanguageClientManager"; - setCompletionSource(QmllsStatus::Source::EmbeddedCodeModel); - setSemanticWarningSource(QmllsStatus::Source::EmbeddedCodeModel); - setSemanticHighlightSource(QmllsStatus::Source::EmbeddedCodeModel); - } else if (QmllsClient *client = QmllsClient::clientForQmlls(newQmlls)) { - bool shouldActivate = false; - if (auto oldClient = LanguageClientManager::clientForDocument(q)) { - // check if it was disabled - if (client == oldClient) - shouldActivate = true; - } - client->updateQmllsSemanticHighlightingCapability(); - switch (client->state()) { - case Client::State::Uninitialized: - case Client::State::InitializeRequested: - connect(client, - &Client::initialized, - this, - &QmlJSEditorDocumentPrivate::setSourcesWithCapabilities); - break; - case Client::State::Initialized: - setSourcesWithCapabilities(client->capabilities()); - break; - case Client::State::FailedToInitialize: - case Client::State::Error: - qCWarning(qmllsLog) << "qmlls" << newQmlls << "requested for document" << q->filePath() - << "had errors, skipping setSourcesWithCababilities"; - break; - case Client::State::Shutdown: - qCWarning(qmllsLog) << "qmlls" << newQmlls << "requested for document" << q->filePath() - << "did stop, skipping setSourcesWithCababilities"; - break; - case Client::State::ShutdownRequested: - qCWarning(qmllsLog) << "qmlls" << newQmlls << "requested for document" << q->filePath() - << "is stopping, skipping setSourcesWithCababilities"; - break; - } - if (shouldActivate) { - qCDebug(qmllsLog) << "reactivating " << q->filePath() << "in qmlls" << newQmlls; - client->activateDocument(q); - } else { - qCDebug(qmllsLog) << "opening " << q->filePath() << "in qmlls" << newQmlls; - LanguageClientManager::openDocumentWithClient(q, client); - } - } else { - qCWarning(qmllsLog) << "could not start qmlls " << newQmlls << "for" << q->filePath(); - } -} - } // Internal QmlJSEditorDocument::QmlJSEditorDocument(Utils::Id id) @@ -840,8 +738,6 @@ QmlJSEditorDocument::QmlJSEditorDocument(Utils::Id id) setId(id); connect(this, &TextEditor::TextDocument::tabSettingsChanged, d, &Internal::QmlJSEditorDocumentPrivate::invalidateFormatterCache); - connect(this, &TextEditor::TextDocument::openFinishedSuccessfully, - d, &Internal::QmlJSEditorDocumentPrivate::settingsChanged); resetSyntaxHighlighter([] { return new QmlJSHighlighter(); }); setCodec(QTextCodec::codecForName("UTF-8")); // qml files are defined to be utf-8 setIndenter(createQmlJsIndenter(document())); diff --git a/src/plugins/qmljseditor/qmljseditordocument_p.h b/src/plugins/qmljseditor/qmljseditordocument_p.h index 6c4d5bb78c2..fcddf1d5ac9 100644 --- a/src/plugins/qmljseditor/qmljseditordocument_p.h +++ b/src/plugins/qmljseditor/qmljseditordocument_p.h @@ -62,7 +62,6 @@ public: void setCompletionSource(QmllsStatus::Source newSource); public slots: void setSourcesWithCapabilities(const LanguageServerProtocol::ServerCapabilities &); - void settingsChanged(); public: QmlJSEditorDocument *q = nullptr; diff --git a/src/plugins/qmljseditor/qmljseditorplugin.cpp b/src/plugins/qmljseditor/qmljseditorplugin.cpp index 3839765008d..b95c19f0689 100644 --- a/src/plugins/qmljseditor/qmljseditorplugin.cpp +++ b/src/plugins/qmljseditor/qmljseditorplugin.cpp @@ -9,6 +9,7 @@ #include "qmljseditortr.h" #include "qmljsoutline.h" #include "qmljsquickfixassist.h" +#include "qmllsclientsettings.h" #include "qmltaskmanager.h" #include @@ -89,7 +90,7 @@ static QmlJSEditorPluginPrivate *dd = nullptr; QmlJSEditorPluginPrivate::QmlJSEditorPluginPrivate() { QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance(); - QmllsSettingsManager::instance(); + setupQmllsClientSettings(); // QML task updating manager connect(modelManager, &QmlJS::ModelManagerInterface::documentChangedOnDisk, @@ -346,7 +347,6 @@ class QmlJSEditorPlugin final : public ExtensionSystem::IPlugin Tr::tr("QML Analysis"), Tr::tr("Issues that the QML static analyzer found."), false}); - QmllsSettingsManager::instance()->setupAutoupdate(); } }; diff --git a/src/plugins/qmljseditor/qmljseditorsettings.cpp b/src/plugins/qmljseditor/qmljseditorsettings.cpp index f92a062ff98..7d34f296df5 100644 --- a/src/plugins/qmljseditor/qmljseditorsettings.cpp +++ b/src/plugins/qmljseditor/qmljseditorsettings.cpp @@ -4,10 +4,17 @@ #include "qmljseditorsettings.h" #include "qmljseditorconstants.h" #include "qmljseditortr.h" +#include "qmllsclient.h" +#include "qmllsclientsettings.h" #include #include +#include +#include +#include + +#include #include #include #include @@ -49,8 +56,6 @@ using namespace ProjectExplorer; namespace QmlJSEditor::Internal { -static Q_LOGGING_CATEGORY(qmllsLog, "qtc.qmlls.settings", QtWarningMsg) - const char SETTINGS_KEY_MAIN[] = "QmlJSEditor"; const char AUTO_FORMAT_ON_SAVE[] = "QmlJSEditor.AutoFormatOnSave"; const char AUTO_FORMAT_ONLY_CURRENT_PROJECT[] = "QmlJSEditor.AutoFormatOnlyCurrentProject"; @@ -59,11 +64,6 @@ const char QML_CONTEXTPANEPIN_KEY[] = "QmlJSEditor.ContextPanePinned"; const char FOLD_AUX_DATA[] = "QmlJSEditor.FoldAuxData"; const char USE_GLOBAL_SETTINGS[] = "QmlJSEditor.UseGlobalSettings"; const char USE_QMLLS[] = "QmlJSEditor.UseQmlls"; -const char USE_LATEST_QMLLS[] = "QmlJSEditor.UseLatestQmlls"; -const char IGNORE_MINIMUM_QMLLS_VERSION[] = "QmlJSEditor.IgnoreMinimumQmllsVersion"; -const char USE_QMLLS_SEMANTIC_HIGHLIGHTING[] = "QmlJSEditor.EnableQmllsSemanticHighlighting"; -const char DISABLE_BUILTIN_CODEMODEL[] = "QmlJSEditor.DisableBuiltinCodemodel"; -const char GENERATE_QMLLS_INI_FILES[] = "QmlJSEditor.GenerateQmllsIniFiles"; const char UIQML_OPEN_MODE[] = "QmlJSEditor.openUiQmlMode"; const char FORMAT_COMMAND[] = "QmlJSEditor.formatCommand"; const char FORMAT_COMMAND_OPTIONS[] = "QmlJSEditor.formatCommandOptions"; @@ -81,110 +81,7 @@ QmlJsEditingSettings &settings() return settings; } -static FilePath evaluateLatestQmlls() -{ - // find latest qmlls, i.e. vals - if (!QtVersionManager::isLoaded()) - return {}; - const QtVersions versions = QtVersionManager::versions(); - FilePath latestQmlls; - QVersionNumber latestVersion; - FilePath latestQmakeFilePath; - int latestUniqueId = std::numeric_limits::min(); - for (QtVersion *v : versions) { - // check if we find qmlls - QVersionNumber vNow = v->qtVersion(); - FilePath qmllsNow = QmlJS::ModelManagerInterface::qmllsForBinPath(v->hostBinPath(), vNow); - if (!qmllsNow.isExecutableFile()) - continue; - if (latestVersion > vNow) - continue; - FilePath qmakeNow = v->qmakeFilePath(); - int uniqueIdNow = v->uniqueId(); - if (latestVersion == vNow) { - if (latestQmakeFilePath > qmakeNow) - continue; - if (latestQmakeFilePath == qmakeNow && latestUniqueId >= v->uniqueId()) - continue; - } - latestVersion = vNow; - latestQmlls = qmllsNow; - latestQmakeFilePath = qmakeNow; - latestUniqueId = uniqueIdNow; - } - return latestQmlls; -} - -QmllsSettingsManager *QmllsSettingsManager::instance() -{ - static QmllsSettingsManager *manager = new QmllsSettingsManager; - return manager; -} - -FilePath QmllsSettingsManager::latestQmlls() -{ - QMutexLocker l(&m_mutex); - return m_latestQmlls; -} - -void QmllsSettingsManager::setupAutoupdate() -{ - QObject::connect(QtVersionManager::instance(), - &QtVersionManager::qtVersionsChanged, - this, - &QmllsSettingsManager::checkForChanges); - if (QtVersionManager::isLoaded()) - checkForChanges(); - else - QObject::connect(QtVersionManager::instance(), - &QtVersionManager::qtVersionsLoaded, - this, - &QmllsSettingsManager::checkForChanges); -} - -void QmllsSettingsManager::checkForChanges() -{ - const QmlJsEditingSettings &newSettings = settings(); - FilePath newLatest = newSettings.useLatestQmlls() && newSettings.useQmlls() - ? evaluateLatestQmlls() : m_latestQmlls; - if (m_useQmlls == newSettings.useQmlls() - && m_useLatestQmlls == newSettings.useLatestQmlls() - && m_disableBuiltinCodemodel == newSettings.disableBuiltinCodemodel() - && m_generateQmllsIniFiles == newSettings.generateQmllsIniFiles() - && m_enableQmllsSemanticHighlighting == newSettings.enableQmllsSemanticHighlighting() - && newLatest == m_latestQmlls) - return; - qCDebug(qmllsLog) << "qmlls settings changed:" << newSettings.useQmlls() - << newSettings.useLatestQmlls() << newLatest; - { - QMutexLocker l(&m_mutex); - m_latestQmlls = newLatest; - m_useQmlls = newSettings.useQmlls(); - m_useLatestQmlls = newSettings.useLatestQmlls(); - m_disableBuiltinCodemodel = newSettings.disableBuiltinCodemodel(); - m_enableQmllsSemanticHighlighting = newSettings.enableQmllsSemanticHighlighting(); - m_generateQmllsIniFiles = newSettings.generateQmllsIniFiles(); - } - emit settingsChanged(); -} - -bool QmllsSettingsManager::useLatestQmlls() const -{ - return m_useLatestQmlls; -} - -bool QmllsSettingsManager::useQmlls(Project* onProject) const -{ - if (!onProject) - return m_useQmlls; - // check if disabled via project specific settings - ProjectSettings projectSettings{onProject}; - - const bool result = projectSettings.useGlobalSettings() ? m_useQmlls - : projectSettings.useQmlls(); - - return result; -} +using namespace LanguageClient; static QList defaultDisabledMessages() { @@ -220,10 +117,6 @@ QmlJsEditingSettings::QmlJsEditingSettings() { const Key group = QmlJSEditor::Constants::SETTINGS_CATEGORY_QML; - useQmlls.setSettingsKey(group, USE_QMLLS); - useQmlls.setDefaultValue(true); - useQmlls.setLabelText(Tr::tr("Turn on")); - enableContextPane.setSettingsKey(group, QML_CONTEXTPANE_KEY); enableContextPane.setLabelText(Tr::tr("Always show Qt Quick Toolbar")); @@ -249,27 +142,6 @@ QmlJsEditingSettings::QmlJsEditingSettings() uiQmlOpenMode.addOption({Tr::tr("Qt Design Studio"), {}, Core::Constants::MODE_DESIGN}); uiQmlOpenMode.addOption({Tr::tr("Qt Creator"), {}, Core::Constants::MODE_EDIT}); - useLatestQmlls.setSettingsKey(group, USE_LATEST_QMLLS); - useLatestQmlls.setLabelText(Tr::tr("Use from latest Qt version")); - - disableBuiltinCodemodel.setSettingsKey(group, DISABLE_BUILTIN_CODEMODEL); - disableBuiltinCodemodel.setLabelText( - Tr::tr("Use advanced features (renaming, find usages, and so on) " - "(experimental)")); - - generateQmllsIniFiles.setSettingsKey(group, GENERATE_QMLLS_INI_FILES); - generateQmllsIniFiles.setLabelText( - Tr::tr("Create .qmlls.ini files for new projects")); - - ignoreMinimumQmllsVersion.setSettingsKey(group, IGNORE_MINIMUM_QMLLS_VERSION); - ignoreMinimumQmllsVersion.setLabelText( - Tr::tr("Allow versions below Qt %1") - .arg(QmlJsEditingSettings::mininumQmllsVersion.toString())); - - enableQmllsSemanticHighlighting.setSettingsKey(group, USE_QMLLS_SEMANTIC_HIGHLIGHTING); - enableQmllsSemanticHighlighting.setLabelText( - Tr::tr("Enable semantic highlighting (experimental)")); - useCustomFormatCommand.setSettingsKey(group, CUSTOM_COMMAND); useCustomFormatCommand.setLabelText( Tr::tr("Use custom command instead of built-in formatter")); @@ -299,11 +171,6 @@ QmlJsEditingSettings::QmlJsEditingSettings() readSettings(); autoFormatOnlyCurrentProject.setEnabler(&autoFormatOnSave); - useLatestQmlls.setEnabler(&useQmlls); - disableBuiltinCodemodel.setEnabler(&useQmlls); - generateQmllsIniFiles.setEnabler(&useQmlls); - ignoreMinimumQmllsVersion.setEnabler(&useQmlls); - enableQmllsSemanticHighlighting.setEnabler(&useQmlls); formatCommand.setEnabler(&useCustomFormatCommand); formatCommandOptions.setEnabler(&useCustomFormatCommand); } @@ -426,14 +293,7 @@ public: }, Group{ title(Tr::tr("QML Language Server")), - Column { - s.useQmlls, - s.ignoreMinimumQmllsVersion, - s.disableBuiltinCodemodel, - s.enableQmllsSemanticHighlighting, - s.useLatestQmlls, - s.generateQmllsIniFiles - }, + // TODO: link to new settings }, Group { title(Tr::tr("Static Analyzer")), @@ -469,7 +329,6 @@ public: s.disabledMessages.setValue(disabled); s.disabledMessagesForNonQuickUi.setValue(disabledForNonQuickUi); s.writeSettings(); - QmllsSettingsManager::instance()->checkForChanges(); } private: @@ -544,7 +403,11 @@ void ProjectSettings::save(Project *project) toMap(map); project->setNamedSettings(SETTINGS_KEY_MAIN, variantFromStore(map)); - emit QmllsSettingsManager::instance()->settingsChanged(); + // TODO: this does not do anything for now. Either force re-apply when the functionality + // is available in LanguageClient (tracked in QTCREATORBUG-32015) or remove ProjectSettings + // class completely in favor of the LanguageClient project specific settings implementation + // (tracked in QTCREATORBUG-31987). + LanguageClientManager::applySettings(); } class QmlJsEditingProjectSettingsWidget final : public ProjectSettingsWidget diff --git a/src/plugins/qmljseditor/qmljseditorsettings.h b/src/plugins/qmljseditor/qmljseditorsettings.h index 4aa1608d7c3..f2bf64d1a9d 100644 --- a/src/plugins/qmljseditor/qmljseditorsettings.h +++ b/src/plugins/qmljseditor/qmljseditorsettings.h @@ -18,8 +18,6 @@ namespace QmlJSEditor::Internal { class QmlJsEditingSettings final : public Utils::AspectContainer { public: - static const inline QVersionNumber mininumQmllsVersion = QVersionNumber(6, 8); - QmlJsEditingSettings(); QString defaultFormatCommand() const; @@ -31,12 +29,6 @@ public: Utils::BoolAspect foldAuxData{this}; Utils::BoolAspect useCustomFormatCommand{this}; Utils::BoolAspect useCustomAnalyzer{this}; - Utils::BoolAspect useQmlls{this}; - Utils::BoolAspect useLatestQmlls{this}; - Utils::BoolAspect ignoreMinimumQmllsVersion{this}; - Utils::BoolAspect enableQmllsSemanticHighlighting{this}; - Utils::BoolAspect disableBuiltinCodemodel{this}; - Utils::BoolAspect generateQmllsIniFiles{this}; Utils::SelectionAspect uiQmlOpenMode{this}; Utils::StringAspect formatCommand{this}; Utils::StringAspect formatCommandOptions{this}; @@ -44,34 +36,6 @@ public: Utils::IntegersAspect disabledMessagesForNonQuickUi{this}; }; -class QmllsSettingsManager : public QObject -{ - Q_OBJECT - -public: - static QmllsSettingsManager *instance(); - - Utils::FilePath latestQmlls(); - void setupAutoupdate(); - - bool useQmlls(ProjectExplorer::Project* project) const; - bool useLatestQmlls() const; - -public slots: - void checkForChanges(); -signals: - void settingsChanged(); - -private: - QMutex m_mutex; - bool m_useQmlls = true; - bool m_useLatestQmlls = false; - bool m_disableBuiltinCodemodel = false; - bool m_generateQmllsIniFiles = false; - bool m_enableQmllsSemanticHighlighting = false; - Utils::FilePath m_latestQmlls; -}; - QmlJsEditingSettings &settings(); class QmlJsEditingSettingsPage : public Core::IOptionsPage diff --git a/src/plugins/qmljseditor/qmljshoverhandler.cpp b/src/plugins/qmljseditor/qmljshoverhandler.cpp index f17ec289254..cf61970974e 100644 --- a/src/plugins/qmljseditor/qmljshoverhandler.cpp +++ b/src/plugins/qmljseditor/qmljshoverhandler.cpp @@ -5,8 +5,8 @@ #include "qmljseditor.h" #include "qmljseditordocument.h" -#include "qmljseditorsettings.h" #include "qmljseditortr.h" +#include "qmllsclientsettings.h" #include #include @@ -382,7 +382,8 @@ void QmlJSHoverHandler::reset() void QmlJSHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const QPoint &point) { // disable hoverhandling in case qmlls is enabled - if (settings().useQmlls()) { + if (editorWidget->textDocument() + && qmllsSettings()->isEnabledOnProjectFile(editorWidget->textDocument()->filePath())) { BaseHoverHandler::operateTooltip(editorWidget, point); return; } diff --git a/src/plugins/qmljseditor/qmllsclient.cpp b/src/plugins/qmljseditor/qmllsclient.cpp index 1fdc30a124a..5976ce7880f 100644 --- a/src/plugins/qmljseditor/qmllsclient.cpp +++ b/src/plugins/qmljseditor/qmllsclient.cpp @@ -4,9 +4,8 @@ #include "qmllsclient.h" #include "qmljseditorconstants.h" -#include "qmljseditortr.h" -#include "qmljseditorsettings.h" #include "qmljsquickfix.h" +#include "qmllsclientsettings.h" #include #include @@ -43,37 +42,6 @@ static QHash &qmllsClients() return clients; } -QmllsClient *QmllsClient::clientForQmlls(const FilePath &qmlls) -{ - if (auto client = qmllsClients()[qmlls]) { - switch (client->state()) { - case Client::State::Uninitialized: - case Client::State::InitializeRequested: - case Client::State::Initialized: - return client; - case Client::State::FailedToInitialize: - case Client::State::ShutdownRequested: - case Client::State::Shutdown: - case Client::State::Error: - qCDebug(qmllsLog) << "client was stopping or failed, restarting"; - break; - } - } - auto interface = new StdIOClientInterface; - interface->setCommandLine(CommandLine(qmlls)); - auto client = new QmllsClient(interface); - client->setName(Tr::tr("Qmlls (%1)").arg(qmlls.toUserOutput())); - client->setActivateDocumentAutomatically(true); - LanguageFilter filter; - using namespace Utils::Constants; - filter.mimeTypes = {QML_MIMETYPE, QMLUI_MIMETYPE, QBS_MIMETYPE, QMLPROJECT_MIMETYPE, - QMLTYPES_MIMETYPE, JS_MIMETYPE, JSON_MIMETYPE}; - client->setSupportedLanguage(filter); - client->start(); - qmllsClients()[qmlls] = client; - return client; -} - QMap QmllsClient::semanticTokenTypesMap() { QMap result; @@ -90,7 +58,7 @@ QMap QmllsClient::semanticTokenTypesMap() void QmllsClient::updateQmllsSemanticHighlightingCapability() { const QString methodName = QStringLiteral("textDocument/semanticTokens"); - if (!QmlJSEditor::Internal::settings().enableQmllsSemanticHighlighting()) { + if (!qmllsSettings()->m_useQmllsSemanticHighlighting) { LanguageServerProtocol::Unregistration unregister; unregister.setMethod(methodName); unregister.setId({}); diff --git a/src/plugins/qmljseditor/qmllsclient.h b/src/plugins/qmljseditor/qmllsclient.h index a174d0f0e2d..8ea9dfbeec7 100644 --- a/src/plugins/qmljseditor/qmllsclient.h +++ b/src/plugins/qmljseditor/qmllsclient.h @@ -54,7 +54,6 @@ public: ~QmllsClient(); void startImpl() override; - static QmllsClient *clientForQmlls(const Utils::FilePath &qmlls); void updateQmllsSemanticHighlightingCapability(); private: static QMap semanticTokenTypesMap(); diff --git a/src/plugins/qmljseditor/qmllsclientsettings.cpp b/src/plugins/qmljseditor/qmllsclientsettings.cpp new file mode 100644 index 00000000000..663ed0ed91c --- /dev/null +++ b/src/plugins/qmljseditor/qmllsclientsettings.cpp @@ -0,0 +1,332 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "qmllsclientsettings.h" +#include "qmljseditorconstants.h" +#include "qmljseditortr.h" +#include "qmllsclient.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +using namespace LanguageClient; +using namespace QtSupport; +using namespace Utils; +using namespace ProjectExplorer; + +namespace QmlJSEditor { + +constexpr char useLatestQmllsKey[] = "useLatestQmlls"; +constexpr char disableBuiltinCodemodelKey[] = "disableBuiltinCodemodel"; +constexpr char generateQmllsIniFilesKey[] = "generateQmllsIniFiles"; +constexpr char ignoreMinimumQmllsVersionKey[] = "ignoreMinimumQmllsVersion"; +constexpr char useQmllsSemanticHighlightingKey[] = "enableQmllsSemanticHighlighting"; + +QmllsClientSettings *qmllsSettings() +{ + BaseSettings *qmllsSettings + = Utils::findOrDefault(LanguageClientManager::currentSettings(), [](BaseSettings *setting) { + return setting->m_settingsTypeId == Constants::QMLLS_CLIENT_SETTINGS_ID; + }); + return static_cast(qmllsSettings); +} + +QmllsClientSettings::QmllsClientSettings() +{ + m_name = "QML Language Server"; + + using namespace Utils::Constants; + m_languageFilter.mimeTypes + = {QML_MIMETYPE, + QMLUI_MIMETYPE, + QBS_MIMETYPE, + QMLPROJECT_MIMETYPE, + QMLTYPES_MIMETYPE, + JS_MIMETYPE, + JSON_MIMETYPE}; + + m_settingsTypeId = Constants::QMLLS_CLIENT_SETTINGS_ID; + m_startBehavior = RequiresProject; +} + +static QtVersion *qtVersionFromProject(const Project *project) +{ + if (!project) + return {}; + auto *target = project->activeTarget(); + if (!target) + return {}; + auto *kit = target->kit(); + if (!kit) + return {}; + auto qtVersion = QtKitAspect::qtVersion(kit); + return qtVersion; +} + +static std::pair evaluateLatestQmlls() +{ + // find latest qmlls, i.e. vals + if (!QtVersionManager::isLoaded()) + return {}; + const QtVersions versions = QtVersionManager::versions(); + FilePath latestQmlls; + QVersionNumber latestVersion; + FilePath latestQmakeFilePath; + int latestUniqueId = std::numeric_limits::min(); + for (QtVersion *v : versions) { + // check if we find qmlls + QVersionNumber vNow = v->qtVersion(); + FilePath qmllsNow = QmlJS::ModelManagerInterface::qmllsForBinPath(v->hostBinPath(), vNow); + if (!qmllsNow.isExecutableFile()) + continue; + if (latestVersion > vNow) + continue; + FilePath qmakeNow = v->qmakeFilePath(); + int uniqueIdNow = v->uniqueId(); + if (latestVersion == vNow) { + if (latestQmakeFilePath > qmakeNow) + continue; + if (latestQmakeFilePath == qmakeNow && latestUniqueId >= v->uniqueId()) + continue; + } + latestVersion = vNow; + latestQmlls = qmllsNow; + latestQmakeFilePath = qmakeNow; + latestUniqueId = uniqueIdNow; + } + return std::make_pair(latestQmlls, latestVersion); +} + +static CommandLine commandLineForQmlls(const Project *project) +{ + const auto *qtVersion = qtVersionFromProject(project); + if (!qtVersion) + return {}; + + auto [executable, version] + = qmllsSettings()->m_useLatestQmlls + ? evaluateLatestQmlls() + : std::make_pair(qtVersion->binPath() / "qmlls", qtVersion->qtVersion()); + + CommandLine result{executable, {}}; + + if (auto *configuration = project->activeTarget()->activeBuildConfiguration()) + result.addArgs({"-b", configuration->buildDirectory().path()}); + + // qmlls 6.8 and later require the import path + if (version >= QVersionNumber(6, 8, 0)) + result.addArgs({"-I", qtVersion->qmlPath().path()}); + + // qmlls 6.8.1 and later require the documentation path + if (version >= QVersionNumber(6, 8, 1)) + result.addArgs({"-d", qtVersion->docsPath().path()}); + + return result; +} + +BaseClientInterface *QmllsClientSettings::createInterface(Project *project) const +{ + auto interface = new StdIOClientInterface; + interface->setCommandLine(commandLineForQmlls(project)); + return interface; +} + +Client *QmllsClientSettings::createClient(BaseClientInterface *interface) const +{ + return new QmllsClient(static_cast(interface)); +} + +class QmllsClientSettingsWidget : public QWidget +{ + Q_OBJECT +public: + explicit QmllsClientSettingsWidget( + const QmllsClientSettings *settings, QWidget *parent = nullptr); + + bool useLatestQmlls() const; + bool disableBuiltinCodemodel() const; + bool generateQmllsIniFiles() const; + bool ignoreMinimumQmllsVersion() const; + bool useQmllsSemanticHighlighting() const; + +private: + QCheckBox *m_useLatestQmlls; + QCheckBox *m_disableBuiltinCodemodel; + QCheckBox *m_generateQmllsIniFiles; + QCheckBox *m_ignoreMinimumQmllsVersion; + QCheckBox *m_useQmllsSemanticHighlighting; +}; + +QWidget *QmllsClientSettings::createSettingsWidget(QWidget *parent) const +{ + return new QmllsClientSettingsWidget(this, parent); +} + +bool QmllsClientSettings::applyFromSettingsWidget(QWidget *widget) +{ + bool changed = BaseSettings::applyFromSettingsWidget(widget); + + QmllsClientSettingsWidget *qmllsWidget = qobject_cast(widget); + if (!qmllsWidget) + return changed; + + if (m_useLatestQmlls != qmllsWidget->useLatestQmlls()) { + m_useLatestQmlls = qmllsWidget->useLatestQmlls(); + changed = true; + } + + if (m_disableBuiltinCodemodel != qmllsWidget->disableBuiltinCodemodel()) { + m_disableBuiltinCodemodel = qmllsWidget->disableBuiltinCodemodel(); + changed = true; + } + + if (m_generateQmllsIniFiles != qmllsWidget->generateQmllsIniFiles()) { + m_generateQmllsIniFiles = qmllsWidget->generateQmllsIniFiles(); + changed = true; + } + + if (m_ignoreMinimumQmllsVersion != qmllsWidget->ignoreMinimumQmllsVersion()) { + m_ignoreMinimumQmllsVersion = qmllsWidget->ignoreMinimumQmllsVersion(); + changed = true; + } + + if (m_useQmllsSemanticHighlighting != qmllsWidget->useQmllsSemanticHighlighting()) { + m_useQmllsSemanticHighlighting = qmllsWidget->useQmllsSemanticHighlighting(); + changed = true; + } + + return changed; +} + +void QmllsClientSettings::toMap(Store &map) const +{ + BaseSettings::toMap(map); + + map.insert(useLatestQmllsKey, m_useLatestQmlls); + map.insert(disableBuiltinCodemodelKey, m_disableBuiltinCodemodel); + map.insert(generateQmllsIniFilesKey, m_generateQmllsIniFiles); + map.insert(ignoreMinimumQmllsVersionKey, m_ignoreMinimumQmllsVersion); + map.insert(useQmllsSemanticHighlightingKey, m_useQmllsSemanticHighlighting); +} + +void QmllsClientSettings::fromMap(const Store &map) +{ + BaseSettings::fromMap(map); + + m_useLatestQmlls = map[useLatestQmllsKey].toBool(); + m_disableBuiltinCodemodel = map[disableBuiltinCodemodelKey].toBool(); + m_generateQmllsIniFiles = map[generateQmllsIniFilesKey].toBool(); + m_ignoreMinimumQmllsVersion = map[ignoreMinimumQmllsVersionKey].toBool(); + m_useQmllsSemanticHighlighting = map[useQmllsSemanticHighlightingKey].toBool(); + return; +} + +bool QmllsClientSettings::isEnabledOnProjectFile(const Utils::FilePath &file) const +{ + Project *project = ProjectManager::projectForFile(file); + return isEnabledOnProject(project); +} + +bool QmllsClientSettings::useQmllsWithBuiltinCodemodelOnProject(const Utils::FilePath &file) const +{ + if (m_disableBuiltinCodemodel) + return false; + + // disableBuitinCodemodel only makes sense when qmlls is enabled + Project *project = ProjectManager::projectForFile(file); + return isEnabledOnProject(project); +} + +void setupQmllsClientSettings() +{ + using namespace LanguageClient; + QmllsClientSettings *clientSettings = new QmllsClientSettings(); + + const ClientType type{ + Constants::QMLLS_CLIENT_SETTINGS_ID, + clientSettings->m_name, + []() { return new QmllsClientSettings; }, + false}; + + const QList savedSettings = LanguageClientSettings::storesBySettingsType(type.id); + + if (!savedSettings.isEmpty()) + clientSettings->fromMap(savedSettings.first()); + + LanguageClientManager::registerClientSettings(clientSettings); + LanguageClientSettings::registerClientType(type); +} + +QmllsClientSettingsWidget::QmllsClientSettingsWidget( + const QmllsClientSettings *settings, QWidget *parent) + : QWidget(parent) + , m_useLatestQmlls(new QCheckBox(Tr::tr("Use from latest Qt version"), this)) + , m_disableBuiltinCodemodel(new QCheckBox( + Tr::tr("Use advanced features (renaming, find usages, and so on) (experimental)"), this)) + , m_generateQmllsIniFiles( + new QCheckBox(Tr::tr("Create .qmlls.ini files for new projects"), this)) + , m_ignoreMinimumQmllsVersion(new QCheckBox( + Tr::tr("Allow versions below Qt %1") + .arg(QmllsClientSettings::mininumQmllsVersion.toString()), + this)) + , m_useQmllsSemanticHighlighting( + new QCheckBox(Tr::tr("Enable semantic highlighting (experimental)"), this)) +{ + m_useLatestQmlls->setChecked(settings->m_useLatestQmlls); + m_disableBuiltinCodemodel->setChecked(settings->m_disableBuiltinCodemodel); + m_generateQmllsIniFiles->setChecked(settings->m_generateQmllsIniFiles); + m_ignoreMinimumQmllsVersion->setChecked(settings->m_ignoreMinimumQmllsVersion); + m_useQmllsSemanticHighlighting->setChecked(settings->m_useQmllsSemanticHighlighting); + + using namespace Layouting; + // clang-format off + auto form = Form { + m_ignoreMinimumQmllsVersion, br, + m_disableBuiltinCodemodel, br, + m_useQmllsSemanticHighlighting, br, + m_useLatestQmlls, br, + m_generateQmllsIniFiles, br, + }; + // clang-format on + + form.attachTo(this); +} +bool QmllsClientSettingsWidget::useLatestQmlls() const +{ + return m_useLatestQmlls->isChecked(); +} +bool QmllsClientSettingsWidget::disableBuiltinCodemodel() const +{ + return m_disableBuiltinCodemodel->isChecked(); +} +bool QmllsClientSettingsWidget::generateQmllsIniFiles() const +{ + return m_generateQmllsIniFiles->isChecked(); +} +bool QmllsClientSettingsWidget::ignoreMinimumQmllsVersion() const +{ + return m_ignoreMinimumQmllsVersion->isChecked(); +} +bool QmllsClientSettingsWidget::useQmllsSemanticHighlighting() const +{ + return m_useQmllsSemanticHighlighting->isChecked(); +} + +} // namespace QmlJSEditor + +#include "qmllsclientsettings.moc" diff --git a/src/plugins/qmljseditor/qmllsclientsettings.h b/src/plugins/qmljseditor/qmllsclientsettings.h new file mode 100644 index 00000000000..d27f0ad350b --- /dev/null +++ b/src/plugins/qmljseditor/qmllsclientsettings.h @@ -0,0 +1,46 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include + +#include + +namespace QmlJSEditor { + +class QmllsClientSettings : public LanguageClient::BaseSettings +{ +public: + static const inline QVersionNumber mininumQmllsVersion = QVersionNumber(6, 8); + + QmllsClientSettings(); + BaseSettings *copy() const override { return new QmllsClientSettings(*this); } + + QWidget *createSettingsWidget(QWidget *parent = nullptr) const override; + bool applyFromSettingsWidget(QWidget *widget) override; + + void toMap(Utils::Store &map) const override; + void fromMap(const Utils::Store &map) override; + + // helpers: + bool isEnabledOnProjectFile(const Utils::FilePath &file) const; + bool useQmllsWithBuiltinCodemodelOnProject(const Utils::FilePath &file) const; + + bool m_useLatestQmlls = false; + bool m_ignoreMinimumQmllsVersion = false; + bool m_useQmllsSemanticHighlighting = false; + bool m_disableBuiltinCodemodel = false; + bool m_generateQmllsIniFiles = false; + +protected: + LanguageClient::BaseClientInterface *createInterface(ProjectExplorer::Project *) const override; + LanguageClient::Client *createClient( + LanguageClient::BaseClientInterface *interface) const override; +}; + +QmllsClientSettings *qmllsSettings(); +void setupQmllsClientSettings(); + +} // namespace QmlJSEditor diff --git a/src/plugins/qmljseditor/qmltaskmanager.cpp b/src/plugins/qmljseditor/qmltaskmanager.cpp index 43c908a6445..9f7423b794c 100644 --- a/src/plugins/qmljseditor/qmltaskmanager.cpp +++ b/src/plugins/qmljseditor/qmltaskmanager.cpp @@ -3,7 +3,7 @@ #include "qmltaskmanager.h" #include "qmljseditorconstants.h" -#include "qmljseditorsettings.h" +#include "qmllsclientsettings.h" #include #include @@ -122,7 +122,7 @@ void QmlTaskManager::updateSemanticMessagesNow() const bool isCMake = buildSystem->name() == "cmake"; // heuristic: qmllint will output meaningful warnings if qmlls is enabled - if (isCMake && QmllsSettingsManager::instance()->useQmlls(buildSystem->project())) { + if (isCMake && qmllsSettings()->isEnabledOnProject(buildSystem->project())) { // abort any update that's going on already, and remove old codemodel warnings m_messageCollector.cancel(); removeAllTasks(true);