diff --git a/src/plugins/qmljseditor/qmljseditordocument.cpp b/src/plugins/qmljseditor/qmljseditordocument.cpp index f70e4e971fb..5b78ae916f5 100644 --- a/src/plugins/qmljseditor/qmljseditordocument.cpp +++ b/src/plugins/qmljseditor/qmljseditordocument.cpp @@ -19,11 +19,12 @@ #include +#include #include #include #include -#include +#include #include #include @@ -466,6 +467,7 @@ QmlJSEditorDocumentPrivate::QmlJSEditorDocumentPrivate(QmlJSEditorDocument *pare : q(parent) , m_semanticHighlighter(new SemanticHighlighter(parent)) , m_outlineModel(new QmlOutlineModel(parent)) + , m_tabSettings(parent->TextDocument::tabSettings()) { ModelManagerInterface *modelManager = ModelManagerInterface::instance(); @@ -838,5 +840,17 @@ void QmlJSEditorDocument::setSourcesWithCapabilities( d->setSourcesWithCapabilities(cap); } +TextEditor::TabSettings QmlJSEditorDocument::tabSettings() const +{ + return d->m_tabSettings; +} + +void QmlJSEditorDocument::setTabSettings(const TextEditor::TabSettings &tabSettings) +{ + if (tabSettings != d->m_tabSettings) { + d->m_tabSettings = tabSettings; + emit tabSettingsChanged(); + } +} } // QmlJSEditor diff --git a/src/plugins/qmljseditor/qmljseditordocument.h b/src/plugins/qmljseditor/qmljseditordocument.h index a1bef52e341..d0b64408423 100644 --- a/src/plugins/qmljseditor/qmljseditordocument.h +++ b/src/plugins/qmljseditor/qmljseditordocument.h @@ -40,6 +40,8 @@ public: void setSourcesWithCapabilities(const LanguageServerProtocol::ServerCapabilities &cap); + virtual TextEditor::TabSettings tabSettings() const override; + void setTabSettings(const TextEditor::TabSettings &tabSettings); signals: void updateCodeWarnings(QmlJS::Document::Ptr doc); void semanticInfoUpdated(const QmlJSTools::SemanticInfo &semanticInfo); diff --git a/src/plugins/qmljseditor/qmljseditordocument_p.h b/src/plugins/qmljseditor/qmljseditordocument_p.h index fcddf1d5ac9..2628771acf4 100644 --- a/src/plugins/qmljseditor/qmljseditordocument_p.h +++ b/src/plugins/qmljseditor/qmljseditordocument_p.h @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include @@ -83,6 +85,7 @@ public: QmllsStatus::Source::EmbeddedCodeModel, QmllsStatus::Source::EmbeddedCodeModel, {}}; + TextEditor::TabSettings m_tabSettings; }; } // Internal diff --git a/src/plugins/qmljseditor/qmljseditorplugin.cpp b/src/plugins/qmljseditor/qmljseditorplugin.cpp index e2df914adda..8467422fb25 100644 --- a/src/plugins/qmljseditor/qmljseditorplugin.cpp +++ b/src/plugins/qmljseditor/qmljseditorplugin.cpp @@ -16,6 +16,8 @@ #include #include +#include +#include #include #include @@ -25,6 +27,7 @@ #include #include #include +#include #include @@ -42,6 +45,7 @@ #include #include +#include #include #include #include @@ -54,6 +58,7 @@ using namespace ProjectExplorer; using namespace Core; using namespace Utils; +using namespace QmlJSTools; namespace QmlJSEditor::Internal { @@ -70,7 +75,7 @@ public: Command *addToolAction(QAction *a, Context &context, Id id, ActionContainer *c1, const QString &keySequence); - void reformatFile(); + FormatResult reformatFile(); QmlJS::JsonSchemaManager *jsonManager() { return &m_jsonManager;} QmlJSQuickFixAssistProvider *quickFixAssistProvider() { return &m_quickFixAssistProvider; } @@ -140,11 +145,15 @@ QmlJSEditorPluginPrivate::QmlJSEditorPluginPrivate() connect(semanticScan, &QAction::triggered, this, &QmlJSEditorPluginPrivate::runSemanticScan); qmlToolsMenu->addAction(cmd); - m_reformatFileAction = new QAction(Tr::tr("Reformat File"), this); - cmd = ActionManager::registerAction(m_reformatFileAction, - Id("QmlJSEditor.ReformatFile"), - context); - connect(m_reformatFileAction, &QAction::triggered, this, &QmlJSEditorPluginPrivate::reformatFile); + + m_reformatFileAction = ActionBuilder(this, TextEditor::Constants::REFORMAT_FILE) + .setContext(context) + .addOnTriggered([this] { reformatFile(); }) + .setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Shift+;"))) + .setText(Tr::tr("Reformat Document")) + .addToContainer(Core::Constants::M_EDIT_ADVANCED, Core::Constants::G_EDIT_FORMAT) + .contextAction(); + cmd = ActionManager::command(TextEditor::Constants::REFORMAT_FILE); qmlToolsMenu->addAction(cmd); QAction *inspectElementAction = new QAction(Tr::tr("Inspect API for Element Under Cursor"), this); @@ -171,6 +180,9 @@ QmlJSEditorPluginPrivate::QmlJSEditorPluginPrivate() cmd = ActionManager::command(TextEditor::Constants::AUTO_INDENT_SELECTION); contextMenu->addAction(cmd); + cmd = ActionManager::command(TextEditor::Constants::REFORMAT_FILE); + contextMenu->addAction(cmd); + cmd = ActionManager::command(TextEditor::Constants::UN_COMMENT_SELECTION); contextMenu->addAction(cmd); @@ -188,35 +200,56 @@ QmlJS::JsonSchemaManager *jsonManager() return dd->jsonManager(); } -static void reformatByQmlFormat(QPointer document) +static void overrideTabSettings(QPointer document) { - QString formatCommand = settings().formatCommand(); - if (formatCommand.isEmpty()) - formatCommand = settings().defaultFormatCommand(); - const auto exe = FilePath::fromUserInput(globalMacroExpander()->expand(formatCommand)); - const QString args = globalMacroExpander()->expand( - settings().formatCommandOptions()); - const CommandLine commandLine(exe, args, CommandLine::Raw); + // Search .qmlformat.ini and read the tab settings from it + if (!document) + return; + + TextEditor::TabSettings tabSettings = document->tabSettings(); + QSettings settings( + QmlJSTools::QmlFormatSettings::currentQmlFormatIniFile(document->filePath()).toUrlishString(), + QSettings::IniFormat); + + if (settings.contains("IndentWidth")) + tabSettings.m_indentSize = settings.value("IndentWidth").toInt(); + if (settings.contains("UseTabs")) + tabSettings.m_tabPolicy = settings.value("UseTabs").toBool() + ? TextEditor::TabSettings::TabPolicy::TabsOnlyTabPolicy + : TextEditor::TabSettings::TabPolicy::SpacesOnlyTabPolicy; + document->setTabSettings(tabSettings); +} + +static FormatResult reformatByQmlFormat(QPointer document) +{ + const FilePath &qmlformatPath = QmlFormatSettings::instance().latestQmlFormatPath(); + if (!qmlformatPath.isExecutableFile()) { + Core::MessageManager::writeSilently( + Tr::tr("QmlFormat not found.")); + return FormatResult::Failed; + } + const CommandLine commandLine(qmlformatPath, {}); TextEditor::Command command; command.setExecutable(commandLine.executable()); command.setProcessing(TextEditor::Command::FileProcessing); command.addOptions(commandLine.splitArguments()); command.addOption("--inplace"); command.addOption("%file"); - if (!command.isValid()) - return; - + return FormatResult::Failed; const QList editors = Core::DocumentModel::editorsForDocument(document); if (editors.isEmpty()) - return; + return FormatResult::Failed; IEditor *currentEditor = EditorManager::currentEditor(); IEditor *editor = editors.contains(currentEditor) ? currentEditor : editors.first(); - if (auto widget = TextEditor::TextEditorWidget::fromEditor(editor)) + if (auto widget = TextEditor::TextEditorWidget::fromEditor(editor)) { TextEditor::formatEditor(widget, command); + return FormatResult::Success; + } + return FormatResult::Failed; } -static void reformatByBuiltInFormatter(QPointer document) +static FormatResult reformatByBuiltInFormatter(QPointer document) { QmlJS::Document::Ptr documentPtr = document->semanticInfo().document; QmlJS::Snapshot snapshot = QmlJS::ModelManagerInterface::instance()->snapshot(); @@ -224,7 +257,7 @@ static void reformatByBuiltInFormatter(QPointer document) if (document->isSemanticInfoOutdated()) { QmlJS::Document::MutablePtr latestDocument; - const Utils::FilePath fileName = document->filePath(); + const FilePath fileName = document->filePath(); latestDocument = snapshot.documentFromSource( QString::fromUtf8(document->contents()), fileName, @@ -235,14 +268,16 @@ static void reformatByBuiltInFormatter(QPointer document) } if (!documentPtr->isParsedCorrectly()) - return; + return FormatResult::Failed; - TextEditor::TabSettings tabSettings = document->tabSettings(); + QmlJSTools::QmlJSCodeStylePreferences *codeStyle + = QmlJSTools::QmlJSToolsSettings::globalCodeStyle(); + TextEditor::TabSettings tabSettings = codeStyle->currentTabSettings(); const QString newText = QmlJS::reformat( documentPtr, tabSettings.m_indentSize, tabSettings.m_tabSize, - QmlJSTools::QmlJSToolsSettings::globalCodeStyle()->currentCodeStyleSettings().lineLength); + codeStyle->currentCodeStyleSettings().lineLength); auto ed = qobject_cast(EditorManager::currentEditor()); if (ed) { TextEditor::updateEditorText(ed->editorWidget(), newText); @@ -252,45 +287,98 @@ static void reformatByBuiltInFormatter(QPointer document) tc.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); tc.insertText(newText); } + return FormatResult::Success; } -static bool reformatUsingLanguageServer(QPointer document) +static FormatResult reformatUsingLanguageServer(QPointer document) { if (!document) - return false; + return FormatResult::Failed; if (!document->formatter()) - return false; + return FormatResult::Failed; TextEditor::BaseTextEditor *editor = qobject_cast( EditorManager::currentEditor()); if (!editor) - return false; + return FormatResult::Failed; TextEditor::TextEditorWidget *editorWidget = editor->editorWidget(); if (!editorWidget) - return false; + return FormatResult::Failed; + overrideTabSettings(document); document->setFormatterMode(TextEditor::Formatter::FormatMode::FullDocument); editorWidget->autoFormat(); - - return true; + return FormatResult::Success; } -void QmlJSEditorPluginPrivate::reformatFile() +static FormatResult reformatByCustomFormatter( + QPointer document, const QmlJSTools::QmlJSCodeStyleSettings &settings) { - if (!m_currentDocument) - return; + const FilePath &formatter = settings.customFormatterPath; + const QStringList &args = settings.customFormatterArguments.split(" ", Qt::SkipEmptyParts); + if (!formatter.isExecutableFile()) { + MessageManager::writeSilently( + Tr::tr("Custom Formatter path not found.")); + return FormatResult::Failed; + } + const CommandLine commandLine(formatter, args); + TextEditor::Command command; + command.setExecutable(commandLine.executable()); + command.setProcessing(TextEditor::Command::FileProcessing); + command.addOptions(commandLine.splitArguments()); + command.addOption("--inplace"); + command.addOption("%file"); + if (!command.isValid()) + return FormatResult::Failed; + const QList editors = Core::DocumentModel::editorsForDocument(document); + if (editors.isEmpty()) + return FormatResult::Failed; + IEditor *currentEditor = EditorManager::currentEditor(); + IEditor *editor = editors.contains(currentEditor) ? currentEditor : editors.first(); + if (auto widget = TextEditor::TextEditorWidget::fromEditor(editor)) { + TextEditor::formatEditor(widget, command); + return FormatResult::Success; + } + return FormatResult::Failed; +} - if (reformatUsingLanguageServer(m_currentDocument)) - return; - - if (settings().useCustomFormatCommand()) { - reformatByQmlFormat(m_currentDocument); - return; +FormatResult QmlJSEditorPluginPrivate::reformatFile() +{ + if (!m_currentDocument) { + MessageManager::writeSilently(Tr::tr("Error: No current document to format.")); + return FormatResult::Failed; } - reformatByBuiltInFormatter(m_currentDocument); + const QmlJSCodeStyleSettings settings + = QmlJSToolsSettings::globalCodeStyle()->currentCodeStyleSettings(); + + const auto tryReformat = [this](auto formatterFunction) { + const FormatResult result = formatterFunction(m_currentDocument); + if (result != FormatResult::Success) { + MessageManager::writeSilently( + Tr::tr("Error: Formatting failed with the selected formatter.")); + } + return result; + }; + + switch (settings.formatter) { + case QmlJSCodeStyleSettings::Formatter::QmlFormat: + return tryReformat([](auto doc) { + return reformatUsingLanguageServer(doc) == FormatResult::Success + ? FormatResult::Success + : reformatByQmlFormat(doc); + }); + + case QmlJSCodeStyleSettings::Formatter::Custom: + return tryReformat( + [&settings](auto doc) { return reformatByCustomFormatter(doc, settings); }); + + case QmlJSCodeStyleSettings::Formatter::Builtin: + default: + return tryReformat([](auto doc) { return reformatByBuiltInFormatter(doc); }); + } } Command *QmlJSEditorPluginPrivate::addToolAction(QAction *a, diff --git a/src/plugins/qmljseditor/qmljseditorplugin.h b/src/plugins/qmljseditor/qmljseditorplugin.h index 483e51babe1..f8303624a28 100644 --- a/src/plugins/qmljseditor/qmljseditorplugin.h +++ b/src/plugins/qmljseditor/qmljseditorplugin.h @@ -16,4 +16,9 @@ void setupQmlJSEditor(); void inspectElement(); void showContextPane(); +enum class FormatResult { + Success, + Failed +}; + } // QmlJSEditor::Internal diff --git a/src/plugins/qmljseditor/qmljseditorsettings.cpp b/src/plugins/qmljseditor/qmljseditorsettings.cpp index 41917bafaad..e95b3e47017 100644 --- a/src/plugins/qmljseditor/qmljseditorsettings.cpp +++ b/src/plugins/qmljseditor/qmljseditorsettings.cpp @@ -63,15 +63,10 @@ const char QML_CONTEXTPANE_KEY[] = "QmlJSEditor.ContextPaneEnabled"; const char QML_CONTEXTPANEPIN_KEY[] = "QmlJSEditor.ContextPanePinned"; const char FOLD_AUX_DATA[] = "QmlJSEditor.FoldAuxData"; const char UIQML_OPEN_MODE[] = "QmlJSEditor.openUiQmlMode"; -const char FORMAT_COMMAND[] = "QmlJSEditor.formatCommand"; -const char FORMAT_COMMAND_OPTIONS[] = "QmlJSEditor.formatCommandOptions"; -const char CUSTOM_COMMAND[] = "QmlJSEditor.useCustomFormatCommand"; const char CUSTOM_ANALYZER[] = "QmlJSEditor.useCustomAnalyzer"; const char DISABLED_MESSAGES[] = "QmlJSEditor.disabledMessages"; const char DISABLED_MESSAGES_NONQUICKUI[] = "QmlJSEditor.disabledMessagesNonQuickUI"; const char QDS_COMMAND[] = "QmlJSEditor.qdsCommand"; -const char DEFAULT_CUSTOM_FORMAT_COMMAND[] - = "%{CurrentDocument:Project:QT_HOST_BINS}/qmlformat%{HostOs:ExecutableSuffix}"; const char SETTINGS_PAGE[] = "C.QmlJsEditing"; QmlJsEditingSettings &settings() @@ -142,19 +137,6 @@ QmlJsEditingSettings::QmlJsEditingSettings() uiQmlOpenMode.addOption({Tr::tr("Qt Design Studio"), {}, Core::Constants::MODE_DESIGN}); uiQmlOpenMode.addOption({Tr::tr("Qt Creator"), {}, Core::Constants::MODE_EDIT}); - useCustomFormatCommand.setSettingsKey(group, CUSTOM_COMMAND); - useCustomFormatCommand.setLabelText( - Tr::tr("Use custom command instead of built-in formatter")); - - formatCommand.setSettingsKey(group, FORMAT_COMMAND); - formatCommand.setDisplayStyle(StringAspect::LineEditDisplay); - formatCommand.setPlaceHolderText(defaultFormatCommand()); - formatCommand.setLabelText(Tr::tr("Command:")); - - formatCommandOptions.setSettingsKey(group, FORMAT_COMMAND_OPTIONS); - formatCommandOptions.setDisplayStyle(StringAspect::LineEditDisplay); - formatCommandOptions.setLabelText(Tr::tr("Arguments:")); - useCustomAnalyzer.setSettingsKey(group, CUSTOM_ANALYZER); useCustomAnalyzer.setLabelText(Tr::tr("Use customized static analyzer")); @@ -174,15 +156,6 @@ QmlJsEditingSettings::QmlJsEditingSettings() qdsCommand.setVisible(false); readSettings(); - - autoFormatOnlyCurrentProject.setEnabler(&autoFormatOnSave); - formatCommand.setEnabler(&useCustomFormatCommand); - formatCommandOptions.setEnabler(&useCustomFormatCommand); -} - -QString QmlJsEditingSettings::defaultFormatCommand() const -{ - return DEFAULT_CUSTOM_FORMAT_COMMAND; } FilePath QmlJsEditingSettings::defaultQdsCommand() const @@ -285,15 +258,10 @@ public: Column { Group { bindTo(&formattingGroup), - title(Tr::tr("Automatic Formatting on File Save")), + title(Tr::tr("Formatting")), Column { s.autoFormatOnSave, s.autoFormatOnlyCurrentProject, - s.useCustomFormatCommand, - Form { - s.formatCommand, br, - s.formatCommandOptions - } }, }, Group { diff --git a/src/plugins/qmljseditor/qmljseditorsettings.h b/src/plugins/qmljseditor/qmljseditorsettings.h index 666ae1743e1..29f3c42418c 100644 --- a/src/plugins/qmljseditor/qmljseditorsettings.h +++ b/src/plugins/qmljseditor/qmljseditorsettings.h @@ -20,7 +20,6 @@ class QmlJsEditingSettings final : public Utils::AspectContainer public: QmlJsEditingSettings(); - QString defaultFormatCommand() const; Utils::FilePath defaultQdsCommand() const; Utils::BoolAspect enableContextPane{this}; @@ -28,11 +27,8 @@ public: Utils::BoolAspect autoFormatOnSave{this}; Utils::BoolAspect autoFormatOnlyCurrentProject{this}; Utils::BoolAspect foldAuxData{this}; - Utils::BoolAspect useCustomFormatCommand{this}; Utils::BoolAspect useCustomAnalyzer{this}; Utils::SelectionAspect uiQmlOpenMode{this}; - Utils::StringAspect formatCommand{this}; - Utils::StringAspect formatCommandOptions{this}; Utils::IntegersAspect disabledMessages{this}; Utils::IntegersAspect disabledMessagesForNonQuickUi{this}; Utils::FilePathAspect qdsCommand{this}; diff --git a/src/plugins/qmljstools/CMakeLists.txt b/src/plugins/qmljstools/CMakeLists.txt index f4b52d7ddd4..2b864887520 100644 --- a/src/plugins/qmljstools/CMakeLists.txt +++ b/src/plugins/qmljstools/CMakeLists.txt @@ -7,8 +7,10 @@ add_qtc_plugin(QmlJSTools qmljsbundleprovider.cpp qmljsbundleprovider.h qmljscodestylepreferenceswidget.cpp qmljscodestylepreferenceswidget.h qmljscodestylesettings.cpp qmljscodestylesettings.h - qmljscodestylesettingswidget.cpp qmljscodestylesettingswidget.h qmljscodestylesettingspage.cpp qmljscodestylesettingspage.h + qmljscustomformatterwidget.cpp qmljscustomformatterwidget.h + qmlformatsettingswidget.cpp qmlformatsettingswidget.h + qmljsformatterselectionwidget.cpp qmljsformatterselectionwidget.h qmljsfunctionfilter.cpp qmljsfunctionfilter.h qmljsindenter.cpp qmljsindenter.h qmljsmodelmanager.cpp qmljsmodelmanager.h diff --git a/src/plugins/qmljstools/qmlformatsettingswidget.cpp b/src/plugins/qmljstools/qmlformatsettingswidget.cpp new file mode 100644 index 00000000000..e18f75b773c --- /dev/null +++ b/src/plugins/qmljstools/qmlformatsettingswidget.cpp @@ -0,0 +1,98 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "qmlformatsettingswidget.h" +#include "qmljsformatterselectionwidget.h" +#include "qmlformatsettings.h" +#include "qmljstoolstr.h" + +#include +#include + +#include + +namespace QmlJSTools { + +QmlFormatSettingsWidget::QmlFormatSettingsWidget( + QWidget *parent, FormatterSelectionWidget *selection) + : QmlCodeStyleWidgetBase(parent) + , m_qmlformatConfigTextEdit(std::make_unique()) + , m_formatterSelectionWidget(selection) +{ + QSizePolicy sp(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + sp.setHorizontalStretch(1); + m_qmlformatConfigTextEdit->setSizePolicy(sp); + + using namespace Layouting; + // clang-format off + Column { + Group { + title(Tr::tr("Global qmlformat Configuration")), + Column { + m_qmlformatConfigTextEdit.get(), + }, + }, + noMargin + }.attachTo(this); + // clang-format on + + connect( + m_qmlformatConfigTextEdit.get(), + &TextEditor::SnippetEditorWidget::textChanged, + this, + &QmlFormatSettingsWidget::slotSettingsChanged); +} + +void QmlFormatSettingsWidget::setCodeStyleSettings(const QmlJSCodeStyleSettings &s) +{ + QSignalBlocker blocker(this); + if (s.qmlformatIniContent != m_qmlformatConfigTextEdit->toPlainText()) + m_qmlformatConfigTextEdit->setPlainText(s.qmlformatIniContent); +} + +void QmlFormatSettingsWidget::setPreferences(QmlJSCodeStylePreferences *preferences) +{ + if (m_preferences == preferences) + return; // nothing changes + + slotCurrentPreferencesChanged(preferences); + + // cleanup old + if (m_preferences) { + disconnect(m_preferences, &QmlJSCodeStylePreferences::currentValueChanged, this, nullptr); + disconnect(m_preferences, &QmlJSCodeStylePreferences::currentPreferencesChanged, + this, &QmlFormatSettingsWidget::slotCurrentPreferencesChanged); + } + m_preferences = preferences; + // fillup new + if (m_preferences) { + setCodeStyleSettings(m_preferences->currentCodeStyleSettings()); + connect(m_preferences, &QmlJSCodeStylePreferences::currentValueChanged, this, [this] { + this->setCodeStyleSettings(m_preferences->currentCodeStyleSettings()); + }); + connect(m_preferences, &QmlJSCodeStylePreferences::currentPreferencesChanged, + this, &QmlFormatSettingsWidget::slotCurrentPreferencesChanged); + } +} + +void QmlFormatSettingsWidget::slotCurrentPreferencesChanged( + TextEditor::ICodeStylePreferences *preferences) +{ + auto *current = dynamic_cast( + preferences ? preferences->currentPreferences() : nullptr); + const bool enableWidgets = current && !current->isReadOnly() && m_formatterSelectionWidget + && m_formatterSelectionWidget->selection().value() + == QmlCodeStyleWidgetBase::QmlFormat; + setEnabled(enableWidgets); +} + +void QmlFormatSettingsWidget::slotSettingsChanged() +{ + QmlJSCodeStyleSettings settings = m_preferences ? m_preferences->currentCodeStyleSettings() + : QmlJSCodeStyleSettings::currentGlobalCodeStyle(); + settings.qmlformatIniContent = m_qmlformatConfigTextEdit->toPlainText(); + QmlFormatSettings::instance().globalQmlFormatIniFile().writeFileContents(settings.qmlformatIniContent.toUtf8()); + emit settingsChanged(settings); +} + +} // namespace QmlJSTools diff --git a/src/plugins/qmljstools/qmlformatsettingswidget.h b/src/plugins/qmljstools/qmlformatsettingswidget.h new file mode 100644 index 00000000000..d192004377b --- /dev/null +++ b/src/plugins/qmljstools/qmlformatsettingswidget.h @@ -0,0 +1,34 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "qmljsformatterselectionwidget.h" +#include "qmljscodestylesettings.h" + +#include + +#include +#include + +namespace QmlJSTools { +class FormatterSelectionWidget; + +class QmlFormatSettingsWidget : public QmlCodeStyleWidgetBase +{ +public: + explicit QmlFormatSettingsWidget( + QWidget *parent = nullptr, + FormatterSelectionWidget *selection = nullptr); + void setCodeStyleSettings(const QmlJSCodeStyleSettings &s) override; + void setPreferences(QmlJSCodeStylePreferences *preferences) override; + void slotCurrentPreferencesChanged(TextEditor::ICodeStylePreferences* preferences) override; + +private: + void slotSettingsChanged(); + std::unique_ptr m_qmlformatConfigTextEdit; + FormatterSelectionWidget *m_formatterSelectionWidget = nullptr; + QmlJSCodeStylePreferences *m_preferences = nullptr; +}; + +} // namespace QmlJSTools diff --git a/src/plugins/qmljstools/qmljscodestylepreferenceswidget.cpp b/src/plugins/qmljstools/qmljscodestylepreferenceswidget.cpp index c341757b657..319549ce2a4 100644 --- a/src/plugins/qmljstools/qmljscodestylepreferenceswidget.cpp +++ b/src/plugins/qmljstools/qmljscodestylepreferenceswidget.cpp @@ -4,23 +4,58 @@ #include "qmljscodestylepreferenceswidget.h" #include "qmljscodestylesettings.h" -#include "qmljscodestylesettingswidget.h" +#include "qmljsformatterselectionwidget.h" +#include "qmljstoolstr.h" + +#include +#include +#include +#include #include #include +using namespace TextEditor; namespace QmlJSTools { -QmlJSCodeStylePreferencesWidget::QmlJSCodeStylePreferencesWidget(QWidget *parent) : - QWidget(parent) +BuiltinFormatterSettingsWidget::BuiltinFormatterSettingsWidget(QWidget *parent, FormatterSelectionWidget *selection) + : QmlCodeStyleWidgetBase(parent) + , m_tabSettingsWidget(new TextEditor::TabSettingsWidget) + , m_formatterSelectionWidget(selection) { - m_codeStyleSettingsWidget = new QmlJSCodeStyleSettingsWidget(this); - auto layout = new QVBoxLayout(this); - layout->addWidget(m_codeStyleSettingsWidget); - layout->setContentsMargins(QMargins()); + m_lineLength.setRange(0, 999); + m_tabSettingsWidget->setParent(this); + + using namespace Layouting; + Column { + Group { + title(Tr::tr("Builtin Formatter Settings")), + Column { + m_tabSettingsWidget, + Group { + title(Tr::tr("Other Settings")), + Form { + Tr::tr("Line Length:"), m_lineLength, br + } + } + } + } + }.attachTo(this); + + connect( + &m_lineLength, + &Utils::IntegerAspect::changed, + this, + &BuiltinFormatterSettingsWidget::slotSettingsChanged); } -void QmlJSCodeStylePreferencesWidget::setPreferences(QmlJSCodeStylePreferences *preferences) +void BuiltinFormatterSettingsWidget::setCodeStyleSettings(const QmlJSCodeStyleSettings &settings) +{ + QSignalBlocker blocker(this); + m_lineLength.setValue(settings.lineLength); +} + +void BuiltinFormatterSettingsWidget::setPreferences(QmlJSCodeStylePreferences *preferences) { if (m_preferences == preferences) return; // nothing changes @@ -31,41 +66,58 @@ void QmlJSCodeStylePreferencesWidget::setPreferences(QmlJSCodeStylePreferences * if (m_preferences) { disconnect(m_preferences, &QmlJSCodeStylePreferences::currentValueChanged, this, nullptr); disconnect(m_preferences, &QmlJSCodeStylePreferences::currentPreferencesChanged, - this, &QmlJSCodeStylePreferencesWidget::slotCurrentPreferencesChanged); - disconnect(m_codeStyleSettingsWidget, &QmlJSCodeStyleSettingsWidget::settingsChanged, - this, &QmlJSCodeStylePreferencesWidget::slotSettingsChanged); + this, &BuiltinFormatterSettingsWidget::slotCurrentPreferencesChanged); + disconnect(m_preferences, &ICodeStylePreferences::currentTabSettingsChanged, + m_tabSettingsWidget, &TabSettingsWidget::setTabSettings); + disconnect(m_tabSettingsWidget, &TabSettingsWidget::settingsChanged, + this, &BuiltinFormatterSettingsWidget::slotTabSettingsChanged); } m_preferences = preferences; // fillup new if (m_preferences) { - m_codeStyleSettingsWidget->setCodeStyleSettings(m_preferences->currentCodeStyleSettings()); - + setCodeStyleSettings(m_preferences->currentCodeStyleSettings()); connect(m_preferences, &QmlJSCodeStylePreferences::currentValueChanged, this, [this] { - m_codeStyleSettingsWidget->setCodeStyleSettings(m_preferences->currentCodeStyleSettings()); + setCodeStyleSettings(m_preferences->currentCodeStyleSettings()); }); connect(m_preferences, &QmlJSCodeStylePreferences::currentPreferencesChanged, - this, &QmlJSCodeStylePreferencesWidget::slotCurrentPreferencesChanged); - connect(m_codeStyleSettingsWidget, &QmlJSCodeStyleSettingsWidget::settingsChanged, - this, &QmlJSCodeStylePreferencesWidget::slotSettingsChanged); + this, &BuiltinFormatterSettingsWidget::slotCurrentPreferencesChanged); + m_tabSettingsWidget->setTabSettings(m_preferences->currentTabSettings()); + connect(m_preferences, &ICodeStylePreferences::currentTabSettingsChanged, + m_tabSettingsWidget, &TabSettingsWidget::setTabSettings); + connect(m_tabSettingsWidget, &TabSettingsWidget::settingsChanged, + this, &BuiltinFormatterSettingsWidget::slotTabSettingsChanged); } } -void QmlJSCodeStylePreferencesWidget::slotCurrentPreferencesChanged(TextEditor::ICodeStylePreferences *preferences) +void BuiltinFormatterSettingsWidget::slotCurrentPreferencesChanged( + TextEditor::ICodeStylePreferences *preferences) { - m_codeStyleSettingsWidget->setEnabled(preferences && preferences->currentPreferences() && - !preferences->currentPreferences()->isReadOnly()); + QmlJSCodeStylePreferences *current = dynamic_cast( + preferences ? preferences->currentPreferences() : nullptr); + const bool enableWidgets = current && !current->isReadOnly() && m_formatterSelectionWidget + && m_formatterSelectionWidget->selection().value() + == QmlCodeStyleWidgetBase::Builtin; + setEnabled(enableWidgets); } -void QmlJSCodeStylePreferencesWidget::slotSettingsChanged(const QmlJSCodeStyleSettings &settings) +void BuiltinFormatterSettingsWidget::slotSettingsChanged() +{ + QmlJSCodeStyleSettings settings = m_preferences ? m_preferences->currentCodeStyleSettings() + : QmlJSCodeStyleSettings::currentGlobalCodeStyle(); + settings.lineLength = m_lineLength.value(); + emit settingsChanged(settings); +} + +void BuiltinFormatterSettingsWidget::slotTabSettingsChanged(const TextEditor::TabSettings &settings) { if (!m_preferences) return; - QmlJSCodeStylePreferences *current = dynamic_cast(m_preferences->currentPreferences()); + ICodeStylePreferences *current = m_preferences->currentPreferences(); if (!current) return; - current->setCodeStyleSettings(settings); + current->setTabSettings(settings); } } // namespace QmlJSTools diff --git a/src/plugins/qmljstools/qmljscodestylepreferenceswidget.h b/src/plugins/qmljstools/qmljscodestylepreferenceswidget.h index a8a2f770dd4..efba9dd9404 100644 --- a/src/plugins/qmljstools/qmljscodestylepreferenceswidget.h +++ b/src/plugins/qmljstools/qmljscodestylepreferenceswidget.h @@ -3,30 +3,35 @@ #pragma once -#include "qmljstools_global.h" - #include "qmljscodestylesettings.h" +#include "qmljsformatterselectionwidget.h" + +#include +#include #include +QT_BEGIN_NAMESPACE +class QSpinBox; +QT_END_NAMESPACE namespace QmlJSTools { -class QmlJSCodeStyleSettingsWidget; -class QMLJSTOOLS_EXPORT QmlJSCodeStylePreferencesWidget : public QWidget +class BuiltinFormatterSettingsWidget : public QmlCodeStyleWidgetBase { - Q_OBJECT - public: - explicit QmlJSCodeStylePreferencesWidget(QWidget *parent = nullptr); - - void setPreferences(QmlJSCodeStylePreferences *tabPreferences); + explicit BuiltinFormatterSettingsWidget(QWidget *parent, FormatterSelectionWidget *selection); + void setCodeStyleSettings(const QmlJSCodeStyleSettings &settings) override; + void setPreferences(QmlJSCodeStylePreferences *tabPreferences) override; + void slotCurrentPreferencesChanged(TextEditor::ICodeStylePreferences* preferences) override; private: - void slotCurrentPreferencesChanged(TextEditor::ICodeStylePreferences* preferences); - void slotSettingsChanged(const QmlJSCodeStyleSettings &settings); + void slotSettingsChanged(); + void slotTabSettingsChanged(const TextEditor::TabSettings &settings); - QmlJSCodeStyleSettingsWidget *m_codeStyleSettingsWidget; + Utils::IntegerAspect m_lineLength; + TextEditor::TabSettingsWidget *m_tabSettingsWidget; QmlJSCodeStylePreferences *m_preferences = nullptr; + FormatterSelectionWidget *m_formatterSelectionWidget; }; } // namespace QmlJSTools diff --git a/src/plugins/qmljstools/qmljscodestylesettings.cpp b/src/plugins/qmljstools/qmljscodestylesettings.cpp index dacf9fb92e1..073b2e7ef21 100644 --- a/src/plugins/qmljstools/qmljscodestylesettings.cpp +++ b/src/plugins/qmljstools/qmljscodestylesettings.cpp @@ -18,9 +18,12 @@ #include static const char lineLengthKey[] = "LineLength"; +static const char qmlformatIniContentKey[] = "QmlFormatIniContent"; +static const char formatterKey[] = "Formatter"; +static const char customFormatterPathKey[] = "CustomFormatterPath"; +static const char customFormatterArgumentsKey[] = "CustomFormatterArguments"; using namespace Utils; - namespace QmlJSTools { // QmlJSCodeStyleSettings @@ -30,18 +33,28 @@ QmlJSCodeStyleSettings::QmlJSCodeStyleSettings() = default; Store QmlJSCodeStyleSettings::toMap() const { return { - {lineLengthKey, lineLength} + {formatterKey, formatter}, + {lineLengthKey, lineLength}, + {qmlformatIniContentKey, qmlformatIniContent}, + {customFormatterPathKey, customFormatterPath.toUrlishString()}, + {customFormatterArgumentsKey, customFormatterArguments} }; } void QmlJSCodeStyleSettings::fromMap(const Store &map) { lineLength = map.value(lineLengthKey, lineLength).toInt(); + qmlformatIniContent = map.value(qmlformatIniContentKey, qmlformatIniContent).toString(); + formatter = static_cast(map.value(formatterKey, formatter).toInt()); + customFormatterPath = Utils::FilePath::fromString(map.value(customFormatterPathKey).toString()); + customFormatterArguments = map.value(customFormatterArgumentsKey).toString(); } bool QmlJSCodeStyleSettings::equals(const QmlJSCodeStyleSettings &rhs) const { - return lineLength == rhs.lineLength; + return lineLength == rhs.lineLength && qmlformatIniContent == rhs.qmlformatIniContent + && formatter == rhs.formatter && customFormatterPath == rhs.customFormatterPath + && customFormatterArguments == rhs.customFormatterArguments; } QmlJSCodeStyleSettings QmlJSCodeStyleSettings::currentGlobalCodeStyle() diff --git a/src/plugins/qmljstools/qmljscodestylesettings.h b/src/plugins/qmljstools/qmljscodestylesettings.h index fe0483324b9..adc96eb4fd5 100644 --- a/src/plugins/qmljstools/qmljscodestylesettings.h +++ b/src/plugins/qmljstools/qmljscodestylesettings.h @@ -7,6 +7,7 @@ #include +#include #include namespace TextEditor { class TabSettings; } @@ -19,7 +20,17 @@ class QMLJSTOOLS_EXPORT QmlJSCodeStyleSettings public: QmlJSCodeStyleSettings(); + enum Formatter { + Builtin, + QmlFormat, + Custom + }; + int lineLength = 80; + QString qmlformatIniContent; + Formatter formatter = Builtin; + Utils::FilePath customFormatterPath; + QString customFormatterArguments; Utils::Store toMap() const; void fromMap(const Utils::Store &map); diff --git a/src/plugins/qmljstools/qmljscodestylesettingspage.cpp b/src/plugins/qmljstools/qmljscodestylesettingspage.cpp index 62ac8ffab60..bc76c8bdc18 100644 --- a/src/plugins/qmljstools/qmljscodestylesettingspage.cpp +++ b/src/plugins/qmljstools/qmljscodestylesettingspage.cpp @@ -3,64 +3,112 @@ #include "qmljscodestylesettingspage.h" +#include "qmlformatsettings.h" +#include "qmlformatsettingswidget.h" #include "qmljscodestylepreferenceswidget.h" #include "qmljscodestylesettings.h" +#include "qmljscustomformatterwidget.h" +#include "qmljsformatterselectionwidget.h" #include "qmljsqtstylecodeformatter.h" #include "qmljstoolsconstants.h" #include "qmljstoolssettings.h" #include "qmljstoolstr.h" #include +#include #include +#include #include +#include #include #include +#include #include #include #include #include #include #include +#include +#include #include -#include #include #include +#include using namespace TextEditor; - +using namespace QmlJSTools; namespace QmlJSTools::Internal { +constexpr int BuiltinFormatterIndex = QmlCodeStyleWidgetBase::Builtin; +constexpr int QmlFormatIndex = QmlCodeStyleWidgetBase::QmlFormat; +constexpr int CustomFormatterIndex = QmlCodeStyleWidgetBase::Custom; + // QmlJSCodeStylePreferencesWidget QmlJSCodeStylePreferencesWidget::QmlJSCodeStylePreferencesWidget( const QString &previewText, QWidget *parent) : TextEditor::CodeStyleEditorWidget(parent) + , m_formatterSelectionWidget(new FormatterSelectionWidget(this)) + , m_formatterSettingsStack(new QStackedWidget(this)) { - m_tabPreferencesWidget = new SimpleCodeStylePreferencesWidget; - m_codeStylePreferencesWidget = new QmlJSTools::QmlJSCodeStylePreferencesWidget; - m_previewTextEdit = new SnippetEditorWidget; + m_formatterSettingsStack->insertWidget(BuiltinFormatterIndex, new BuiltinFormatterSettingsWidget(this, m_formatterSelectionWidget)); + m_formatterSettingsStack->insertWidget(QmlFormatIndex, new QmlFormatSettingsWidget(this, m_formatterSelectionWidget)); + m_formatterSettingsStack->insertWidget(CustomFormatterIndex, new CustomFormatterWidget(this, m_formatterSelectionWidget)); + + for (const auto &formatterWidget : + m_formatterSettingsStack->findChildren()) { + connect( + formatterWidget, + &QmlCodeStyleWidgetBase::settingsChanged, + this, + &QmlJSCodeStylePreferencesWidget::slotSettingsChanged); + } + + const int index = m_formatterSelectionWidget->selection().value(); + m_formatterSettingsStack->setCurrentIndex(index); + + m_previewTextEdit = new SnippetEditorWidget(this); m_previewTextEdit->setPlainText(previewText); QSizePolicy sp(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); sp.setHorizontalStretch(1); m_previewTextEdit->setSizePolicy(sp); - decorateEditor(TextEditorSettings::fontSettings()); + connect( + TextEditorSettings::instance(), + &TextEditorSettings::fontSettingsChanged, + this, + &QmlJSCodeStylePreferencesWidget::decorateEditor); + + connect( + m_formatterSelectionWidget, + &FormatterSelectionWidget::settingsChanged, + [this](const QmlJSCodeStyleSettings &settings) { + int index = m_formatterSelectionWidget->selection().volatileValue(); + if (index < 0 || index >= static_cast(m_formatterSettingsStack->count())) + return; + + m_formatterSettingsStack->setCurrentIndex(index); + if (auto *current = dynamic_cast( + m_formatterSettingsStack->widget(index))) { + current->slotCurrentPreferencesChanged(m_preferences); + } + slotSettingsChanged(settings); + }); + using namespace Layouting; Row { Column { - m_tabPreferencesWidget, - m_codeStylePreferencesWidget, - st, + m_formatterSelectionWidget, br, + m_formatterSettingsStack, + st }, m_previewTextEdit, noMargin }.attachTo(this); - connect(TextEditorSettings::instance(), &TextEditorSettings::fontSettingsChanged, - this, &QmlJSCodeStylePreferencesWidget::decorateEditor); - setVisualizeWhitespace(true); updatePreview(); @@ -69,14 +117,20 @@ QmlJSCodeStylePreferencesWidget::QmlJSCodeStylePreferencesWidget( void QmlJSCodeStylePreferencesWidget::setPreferences(QmlJSCodeStylePreferences *preferences) { m_preferences = preferences; - m_tabPreferencesWidget->setPreferences(preferences); - m_codeStylePreferencesWidget->setPreferences(preferences); + m_formatterSelectionWidget->setPreferences(preferences); + for (const auto &formatterWidget : + m_formatterSettingsStack->findChildren()) { + formatterWidget->setPreferences(preferences); + } if (m_preferences) { connect(m_preferences, &ICodeStylePreferences::currentTabSettingsChanged, - this, &QmlJSCodeStylePreferencesWidget::slotSettingsChanged); + this, &QmlJSCodeStylePreferencesWidget::updatePreview); connect(m_preferences, &QmlJSCodeStylePreferences::currentValueChanged, - this, &QmlJSCodeStylePreferencesWidget::slotSettingsChanged); + [this]{ + m_formatterSettingsStack->setCurrentIndex(m_formatterSelectionWidget->selection().value()); + updatePreview(); + }); } updatePreview(); } @@ -95,12 +149,36 @@ void QmlJSCodeStylePreferencesWidget::setVisualizeWhitespace(bool on) m_previewTextEdit->setDisplaySettings(displaySettings); } -void QmlJSCodeStylePreferencesWidget::slotSettingsChanged() +void QmlJSCodeStylePreferencesWidget::slotSettingsChanged(const QmlJSCodeStyleSettings &settings) { + if (!m_preferences) + return; + + QmlJSCodeStylePreferences *current = dynamic_cast(m_preferences->currentPreferences()); + if (!current) + return; + + current->setCodeStyleSettings(settings); + updatePreview(); } void QmlJSCodeStylePreferencesWidget::updatePreview() +{ + switch (m_formatterSelectionWidget->selection().value()) { + case QmlCodeStyleWidgetBase::Builtin: + builtInFormatterPreview(); + break; + case QmlCodeStyleWidgetBase::QmlFormat: + qmlformatPreview(); + break; + case QmlCodeStyleWidgetBase::Custom: + customFormatterPreview(); + break; + } +} + +void QmlJSCodeStylePreferencesWidget::builtInFormatterPreview() { QTextDocument *doc = m_previewTextEdit->document(); @@ -121,6 +199,63 @@ void QmlJSCodeStylePreferencesWidget::updatePreview() tc.endEditBlock(); } +void QmlJSCodeStylePreferencesWidget::qmlformatPreview() +{ + using namespace Core; + const Utils::FilePath &qmlFormatPath = QmlFormatSettings::instance().latestQmlFormatPath(); + if (qmlFormatPath.isEmpty()) { + MessageManager::writeSilently("QmlFormat not found."); + return; + } + const Utils::CommandLine commandLine(qmlFormatPath); + TextEditor::Command command; + command.setExecutable(commandLine.executable()); + command.setProcessing(TextEditor::Command::FileProcessing); + command.addOptions(commandLine.splitArguments()); + command.addOption("--inplace"); + command.addOption("%file"); + if (!command.isValid()) + return; + TextEditor::TabSettings tabSettings; + QSettings settings( + QmlJSTools::QmlFormatSettings::globalQmlFormatIniFile().toUrlishString(), + QSettings::IniFormat); + if (settings.contains("IndentWidth")) + tabSettings.m_indentSize = settings.value("IndentWidth").toInt(); + if (settings.contains("UseTabs")) + tabSettings.m_tabPolicy = settings.value("UseTabs").toBool() + ? TextEditor::TabSettings::TabPolicy::TabsOnlyTabPolicy + : TextEditor::TabSettings::TabPolicy::SpacesOnlyTabPolicy; + QString dummyFilePath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/dummy.qml"; + m_previewTextEdit->textDocument()->setFilePath(Utils::FilePath::fromString(dummyFilePath)); + m_previewTextEdit->textDocument()->setTabSettings(tabSettings); + TextEditor::formatEditor(m_previewTextEdit, command); +} + +void QmlJSCodeStylePreferencesWidget::customFormatterPreview() +{ + Utils::FilePath path = m_preferences->currentCodeStyleSettings().customFormatterPath; + QStringList args = m_preferences->currentCodeStyleSettings() + .customFormatterArguments.split(" ", Qt::SkipEmptyParts); + if (path.isEmpty()) { + Core::MessageManager::writeSilently("Custom formatter not found."); + return; + } + const Utils::CommandLine commandLine(path, args); + TextEditor::Command command; + command.setExecutable(commandLine.executable()); + command.setProcessing(TextEditor::Command::FileProcessing); + command.addOptions(commandLine.splitArguments()); + command.addOption("--inplace"); + command.addOption("%file"); + if (!command.isValid()) + return; + + QString dummyFilePath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/dummy.qml"; + m_previewTextEdit->textDocument()->setFilePath(Utils::FilePath::fromString(dummyFilePath)); + TextEditor::formatEditor(m_previewTextEdit, command); +} + // QmlJSCodeStyleSettingsPageWidget class QmlJSCodeStyleSettingsPageWidget : public Core::IOptionsPageWidget diff --git a/src/plugins/qmljstools/qmljscodestylesettingspage.h b/src/plugins/qmljstools/qmljscodestylesettingspage.h index 9631cf0e002..a9990857c3d 100644 --- a/src/plugins/qmljstools/qmljscodestylesettingspage.h +++ b/src/plugins/qmljstools/qmljscodestylesettingspage.h @@ -4,10 +4,13 @@ #pragma once #include "qmljscodestylesettings.h" +#include "qmljsformatterselectionwidget.h" #include #include +#include + QT_BEGIN_NAMESPACE class QString; class QWidget; @@ -15,14 +18,10 @@ QT_END_NAMESPACE namespace TextEditor { class FontSettings; - class SimpleCodeStylePreferencesWidget; - class SnippetEditorWidget; } namespace QmlJSTools { -class QmlJSCodeStylePreferencesWidget; class QmlJSCodeStyleSettings; - namespace Internal { class QmlJSCodeStylePreferencesWidget : public TextEditor::CodeStyleEditorWidget @@ -37,16 +36,19 @@ public: private: void decorateEditor(const TextEditor::FontSettings &fontSettings); void setVisualizeWhitespace(bool on); - void slotSettingsChanged(); + void slotSettingsChanged(const QmlJSCodeStyleSettings &); + void slotCurrentPreferencesChanged(TextEditor::ICodeStylePreferences *preferences); void updatePreview(); + void builtInFormatterPreview(); + void qmlformatPreview(); + void customFormatterPreview(); - QmlJSCodeStylePreferences *m_preferences = nullptr; - TextEditor::SimpleCodeStylePreferencesWidget *m_tabPreferencesWidget; - QmlJSTools::QmlJSCodeStylePreferencesWidget *m_codeStylePreferencesWidget; + FormatterSelectionWidget *m_formatterSelectionWidget; + QStackedWidget *m_formatterSettingsStack; TextEditor::SnippetEditorWidget *m_previewTextEdit; + QmlJSCodeStylePreferences *m_preferences = nullptr; }; - class QmlJSCodeStyleSettingsPage : public Core::IOptionsPage { public: diff --git a/src/plugins/qmljstools/qmljscodestylesettingswidget.cpp b/src/plugins/qmljstools/qmljscodestylesettingswidget.cpp deleted file mode 100644 index 093fc3ff4f4..00000000000 --- a/src/plugins/qmljstools/qmljscodestylesettingswidget.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "qmljscodestylesettingswidget.h" -#include "qmljscodestylesettings.h" -#include "qmljstoolstr.h" - -#include - -#include -#include - -namespace QmlJSTools { - -QmlJSCodeStyleSettingsWidget::QmlJSCodeStyleSettingsWidget(QWidget *parent) - : QWidget(parent) -{ - m_lineLengthSpinBox = new QSpinBox; - m_lineLengthSpinBox->setMinimum(0); - m_lineLengthSpinBox->setMaximum(999); - - using namespace Layouting; - // clang-format off - Column { - Group { - title(Tr::tr("Other")), - Form { - Tr::tr("&Line length:"), m_lineLengthSpinBox, br, - } - }, - noMargin - }.attachTo(this); - // clang-format on - - connect(m_lineLengthSpinBox, &QSpinBox::valueChanged, - this, &QmlJSCodeStyleSettingsWidget::slotSettingsChanged); -} - -void QmlJSCodeStyleSettingsWidget::setCodeStyleSettings(const QmlJSCodeStyleSettings &settings) -{ - QSignalBlocker blocker(this); - m_lineLengthSpinBox->setValue(settings.lineLength); -} - -QmlJSCodeStyleSettings QmlJSCodeStyleSettingsWidget::codeStyleSettings() const -{ - QmlJSCodeStyleSettings set; - - set.lineLength = m_lineLengthSpinBox->value(); - - return set; -} - -void QmlJSCodeStyleSettingsWidget::slotSettingsChanged() -{ - emit settingsChanged(codeStyleSettings()); -} - -} // namespace TextEditor diff --git a/src/plugins/qmljstools/qmljscodestylesettingswidget.h b/src/plugins/qmljstools/qmljscodestylesettingswidget.h deleted file mode 100644 index 3f08a52c402..00000000000 --- a/src/plugins/qmljstools/qmljscodestylesettingswidget.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "qmljstools_global.h" - -#include - -QT_BEGIN_NAMESPACE -class QSpinBox; -QT_END_NAMESPACE - -namespace QmlJSTools { -class QmlJSCodeStyleSettings; - -class QMLJSTOOLS_EXPORT QmlJSCodeStyleSettingsWidget : public QWidget -{ - Q_OBJECT - -public: - enum CodingStyleLink { - CppLink, - QtQuickLink - }; - - explicit QmlJSCodeStyleSettingsWidget(QWidget *parent = nullptr); - - QmlJSCodeStyleSettings codeStyleSettings() const; - - void setCodingStyleWarningVisible(bool visible); - void setCodeStyleSettings(const QmlJSCodeStyleSettings &settings); - -signals: - void settingsChanged(const QmlJSCodeStyleSettings &); - -private: - void slotSettingsChanged(); - void codingStyleLinkActivated(const QString &linkString); - - QSpinBox *m_lineLengthSpinBox; -}; - -} // namespace QmlJSTools diff --git a/src/plugins/qmljstools/qmljscustomformatterwidget.cpp b/src/plugins/qmljstools/qmljscustomformatterwidget.cpp new file mode 100644 index 00000000000..efe658d7061 --- /dev/null +++ b/src/plugins/qmljstools/qmljscustomformatterwidget.cpp @@ -0,0 +1,117 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "qmljscustomformatterwidget.h" +#include "qmlformatsettings.h" +#include "qmljsformatterselectionwidget.h" +#include "qmljstoolstr.h" + +#include + +#include + +namespace QmlJSTools { + +CustomFormatterWidget::CustomFormatterWidget(QWidget *parent, FormatterSelectionWidget *selection) + : QmlCodeStyleWidgetBase(parent) + , m_formatterSelectionWidget(selection) +{ + + m_customFormatterPath.setParent(this); + m_customFormatterArguments.setParent(this); + + m_customFormatterPath.setPlaceHolderText( + QmlFormatSettings::instance().latestQmlFormatPath().toUrlishString()); + m_customFormatterPath.setLabelText(Tr::tr("Command:")); + + m_customFormatterArguments.setLabelText(Tr::tr("Arguments:")); + m_customFormatterArguments.setDisplayStyle(Utils::StringAspect::LineEditDisplay); + + using namespace Layouting; + // clang-format off + Column { + Group { + title(Tr::tr("Custom Formatter Configuration")), + Column { + m_customFormatterPath, br, + m_customFormatterArguments, br, + st + }, + }, + noMargin, + }.attachTo(this); + + // clang-format on + connect( + &m_customFormatterPath, + &Utils::BaseAspect::changed, + this, + &CustomFormatterWidget::slotSettingsChanged); + connect( + &m_customFormatterArguments, + &Utils::BaseAspect::changed, + this, + &CustomFormatterWidget::slotSettingsChanged); +} + +void CustomFormatterWidget::setCodeStyleSettings(const QmlJSCodeStyleSettings& s) +{ + QSignalBlocker blocker(this); + if (s.customFormatterPath != m_customFormatterPath.expandedValue()) { + m_customFormatterPath.setValue(s.customFormatterPath); + } + if (s.customFormatterArguments != m_customFormatterArguments.value()) { + m_customFormatterArguments.setValue(s.customFormatterArguments); + } +} + +void CustomFormatterWidget::setPreferences(QmlJSCodeStylePreferences *preferences) +{ + if (m_preferences == preferences) + return; // nothing changes + + slotCurrentPreferencesChanged(preferences); + + // cleanup old + if (m_preferences) { + disconnect(m_preferences, &QmlJSCodeStylePreferences::currentValueChanged, this, nullptr); + disconnect(m_preferences, &QmlJSCodeStylePreferences::currentPreferencesChanged, + this, &CustomFormatterWidget::slotCurrentPreferencesChanged); + } + m_preferences = preferences; + // fillup new + if (m_preferences) { + setCodeStyleSettings(m_preferences->currentCodeStyleSettings()); + connect(m_preferences, &QmlJSCodeStylePreferences::currentValueChanged, this, [this] { + this->setCodeStyleSettings(m_preferences->currentCodeStyleSettings()); + }); + connect(m_preferences, &QmlJSCodeStylePreferences::currentPreferencesChanged, + this, &CustomFormatterWidget::slotCurrentPreferencesChanged); + } +} + +void CustomFormatterWidget::slotCurrentPreferencesChanged( + TextEditor::ICodeStylePreferences *preferences) +{ + QmlJSCodeStylePreferences *current = dynamic_cast( + preferences ? preferences->currentPreferences() : nullptr); + const bool enableWidgets = current && !current->isReadOnly() && m_formatterSelectionWidget + && m_formatterSelectionWidget->selection().value() + == QmlCodeStyleWidgetBase::Custom; + setEnabled(enableWidgets); +} + +void CustomFormatterWidget::slotSettingsChanged() +{ + QmlJSCodeStyleSettings settings = m_preferences ? m_preferences->currentCodeStyleSettings() + : QmlJSCodeStyleSettings::currentGlobalCodeStyle(); + if (m_customFormatterPath.value().isEmpty()) { + m_customFormatterPath.setValue( + QmlFormatSettings::instance().latestQmlFormatPath().toUrlishString()); + } + settings.customFormatterPath = m_customFormatterPath.expandedValue(); + settings.customFormatterArguments = m_customFormatterArguments.value(); + emit settingsChanged(settings); +} + +} // namespace QmlJSTools diff --git a/src/plugins/qmljstools/qmljscustomformatterwidget.h b/src/plugins/qmljstools/qmljscustomformatterwidget.h new file mode 100644 index 00000000000..9f8e20ad444 --- /dev/null +++ b/src/plugins/qmljstools/qmljscustomformatterwidget.h @@ -0,0 +1,27 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "qmljscodestylesettings.h" +#include "qmljsformatterselectionwidget.h" + +#include +namespace QmlJSTools { +class CustomFormatterWidget : public QmlCodeStyleWidgetBase +{ +public: + explicit CustomFormatterWidget(QWidget *parent, FormatterSelectionWidget *selection = nullptr); + void setCodeStyleSettings(const QmlJSCodeStyleSettings &s) override; + void setPreferences(QmlJSCodeStylePreferences *preferences) override; + void slotCurrentPreferencesChanged(TextEditor::ICodeStylePreferences* preferences) override; + +private: + void slotSettingsChanged(); + Utils::FilePathAspect m_customFormatterPath; + Utils::StringAspect m_customFormatterArguments; + FormatterSelectionWidget *m_formatterSelectionWidget = nullptr; + QmlJSCodeStylePreferences *m_preferences = nullptr; +}; + +} // namespace QmlJSTools diff --git a/src/plugins/qmljstools/qmljsformatterselectionwidget.cpp b/src/plugins/qmljstools/qmljsformatterselectionwidget.cpp new file mode 100644 index 00000000000..42093c19faa --- /dev/null +++ b/src/plugins/qmljstools/qmljsformatterselectionwidget.cpp @@ -0,0 +1,89 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "qmljsformatterselectionwidget.h" +#include "qmljstoolstr.h" +#include + +namespace QmlJSTools { + +QmlCodeStyleWidgetBase::QmlCodeStyleWidgetBase(QWidget *parent) + : QWidget(parent) +{ +} + +FormatterSelectionWidget::FormatterSelectionWidget(QWidget *parent) + : QmlCodeStyleWidgetBase(parent) +{ + using namespace Utils; + m_formatterSelection.setDefaultValue(Builtin); + m_formatterSelection.setDisplayStyle(SelectionAspect::DisplayStyle::RadioButtons); + m_formatterSelection.addOption(Tr::tr("Built-In Formatter [Deprecated]") ); + m_formatterSelection.addOption(Tr::tr("QmlFormat [LSP]")); + m_formatterSelection.addOption(Tr::tr("Custom Formatter [Must be qmlformat compatible]")); + m_formatterSelection.setLabelText(Tr::tr("Formatter")); + + connect(&m_formatterSelection, &SelectionAspect::changed, + this, &FormatterSelectionWidget::slotSettingsChanged); + + using namespace Layouting; + Column { + Group { + title(Tr::tr("Formatter Selection")), + Column { + m_formatterSelection, br + } + } + }.attachTo(this); +} + +void FormatterSelectionWidget::setCodeStyleSettings(const QmlJSCodeStyleSettings &s) +{ + if (s.formatter != m_formatterSelection.value()) + m_formatterSelection.setValue(s.formatter); +} + +void FormatterSelectionWidget::setPreferences(QmlJSCodeStylePreferences *preferences) +{ + if (m_preferences == preferences) + return; // nothing changes + + slotCurrentPreferencesChanged(preferences); + + // cleanup old + if (m_preferences) { + disconnect(m_preferences, &QmlJSCodeStylePreferences::currentValueChanged, this, nullptr); + disconnect(m_preferences, &QmlJSCodeStylePreferences::currentPreferencesChanged, + this, &FormatterSelectionWidget::slotCurrentPreferencesChanged); + } + m_preferences = preferences; + // fillup new + if (m_preferences) { + setCodeStyleSettings(m_preferences->currentCodeStyleSettings()); + connect(m_preferences, &QmlJSCodeStylePreferences::currentValueChanged, this, [this] { + setCodeStyleSettings(m_preferences->currentCodeStyleSettings()); + }); + connect(m_preferences, &QmlJSCodeStylePreferences::currentPreferencesChanged, + this, &FormatterSelectionWidget::slotCurrentPreferencesChanged); + } +} + +void FormatterSelectionWidget::slotCurrentPreferencesChanged( + TextEditor::ICodeStylePreferences *preferences) +{ + QmlJSCodeStylePreferences *current = dynamic_cast( + preferences ? preferences->currentPreferences() : nullptr); + const bool enableWidgets = current && !current->isReadOnly(); + setEnabled(enableWidgets); +} + +void FormatterSelectionWidget::slotSettingsChanged() +{ + QmlJSCodeStyleSettings settings = m_preferences + ? m_preferences->currentCodeStyleSettings() + : QmlJSCodeStyleSettings::currentGlobalCodeStyle(); + settings.formatter = static_cast(m_formatterSelection.value()); + emit settingsChanged(settings); +} + +} // namespace QmlJSTools diff --git a/src/plugins/qmljstools/qmljsformatterselectionwidget.h b/src/plugins/qmljstools/qmljsformatterselectionwidget.h new file mode 100644 index 00000000000..c4069426acd --- /dev/null +++ b/src/plugins/qmljstools/qmljsformatterselectionwidget.h @@ -0,0 +1,51 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "qmljscodestylesettings.h" + +#include + +namespace QmlJSTools { +class QmlJSCodeStyleSettings; + +class QmlCodeStyleWidgetBase : public QWidget +{ + Q_OBJECT +public: + QmlCodeStyleWidgetBase(QWidget *parent = nullptr); + virtual ~QmlCodeStyleWidgetBase() = default; + enum Formatter { + Builtin, + QmlFormat, + Custom, + Count + }; + virtual void setCodeStyleSettings(const QmlJSCodeStyleSettings &s) = 0; + virtual void setPreferences(QmlJSCodeStylePreferences *preferences) = 0; + virtual void slotCurrentPreferencesChanged(TextEditor::ICodeStylePreferences *preferences) = 0; + +signals: + void settingsChanged(const QmlJSCodeStyleSettings &); +}; +class FormatterSelectionWidget : public QmlCodeStyleWidgetBase +{ + Q_OBJECT +public: + explicit FormatterSelectionWidget(QWidget *parent); + + const Utils::SelectionAspect &selection() const { return m_formatterSelection; } + Utils::SelectionAspect &selection() { return m_formatterSelection; } + + void setCodeStyleSettings(const QmlJSCodeStyleSettings &s) override; + void setPreferences(QmlJSCodeStylePreferences *preferences) override; + void slotCurrentPreferencesChanged(TextEditor::ICodeStylePreferences *preferences) override; + +private: + void slotSettingsChanged(); + Utils::SelectionAspect m_formatterSelection; + QmlJSCodeStylePreferences *m_preferences = nullptr; +}; + +} // namespace QmlJSTools diff --git a/src/plugins/qmljstools/qmljstools.qbs b/src/plugins/qmljstools/qmljstools.qbs index 038b131fa22..60f6f421eef 100644 --- a/src/plugins/qmljstools/qmljstools.qbs +++ b/src/plugins/qmljstools/qmljstools.qbs @@ -25,8 +25,6 @@ QtcPlugin { "qmljscodestylesettings.h", "qmljscodestylesettingspage.cpp", "qmljscodestylesettingspage.h", - "qmljscodestylesettingswidget.cpp", - "qmljscodestylesettingswidget.h", "qmljsfunctionfilter.cpp", "qmljsfunctionfilter.h", "qmljsindenter.cpp", diff --git a/src/plugins/qmljstools/qmljstoolssettings.cpp b/src/plugins/qmljstools/qmljstoolssettings.cpp index 9143bb8fbd9..c8320a3222a 100644 --- a/src/plugins/qmljstools/qmljstoolssettings.cpp +++ b/src/plugins/qmljstools/qmljstoolssettings.cpp @@ -8,6 +8,7 @@ #include "qmljsindenter.h" #include "qmljstoolsconstants.h" #include "qmljstoolssettings.h" +#include "qmlformatsettings.h" #include "qmljstoolstr.h" #include @@ -20,6 +21,7 @@ #include #include +#include #include #include #include @@ -166,9 +168,22 @@ QmlJSToolsSettings::QmlJSToolsSettings() qtTabSettings.m_indentSize = 4; qtTabSettings.m_continuationAlignBehavior = TabSettings::ContinuationAlignWithIndent; qtCodeStyle->setTabSettings(qtTabSettings); - QmlJSCodeStyleSettings qtQmlJSSetings; - qtQmlJSSetings.lineLength = 80; - qtCodeStyle->setCodeStyleSettings(qtQmlJSSetings); + + connect(&QmlFormatSettings::instance(), &QmlFormatSettings::qmlformatIniCreated, [](Utils::FilePath qmlformatIniPath) { + QmlJSCodeStyleSettings s; + s.lineLength = 80; + Utils::expected_str fileContents = qmlformatIniPath.fileContents(); + if (fileContents) + s.qmlformatIniContent = QString::fromUtf8(*qmlformatIniPath.fileContents()); + auto builtInCodeStyles = TextEditorSettings::codeStylePool( + QmlJSTools::Constants::QML_JS_SETTINGS_ID) + ->builtInCodeStyles(); + for (auto codeStyle : builtInCodeStyles) { + if (auto qtCodeStyle = dynamic_cast(codeStyle)) + qtCodeStyle->setCodeStyleSettings(s); + } + }); + pool->addCodeStyle(qtCodeStyle); // default delegate for global preferences diff --git a/src/plugins/texteditor/texteditorconstants.h b/src/plugins/texteditor/texteditorconstants.h index c927529759a..b60c448a424 100644 --- a/src/plugins/texteditor/texteditorconstants.h +++ b/src/plugins/texteditor/texteditorconstants.h @@ -138,6 +138,7 @@ const char UNFOLD_RECURSIVELY[] = "TextEditor.UnfoldRecursively"; const char UNFOLD_ALL[] = "TextEditor.UnFoldAll"; const char AUTO_INDENT_SELECTION[] = "TextEditor.AutoIndentSelection"; const char AUTO_FORMAT_SELECTION[] = "TextEditor.AutoFormatSelection"; +const char REFORMAT_FILE[] = "TextEditor.ReformatFile"; const char INCREASE_FONT_SIZE[] = "TextEditor.IncreaseFontSize"; const char DECREASE_FONT_SIZE[] = "TextEditor.DecreaseFontSize"; const char RESET_FONT_SIZE[] = "TextEditor.ResetFontSize";