From 3694d00dce77576ff60fe8ce31d8f7fd6e546c5d Mon Sep 17 00:00:00 2001 From: Xavier BESSON Date: Thu, 3 Nov 2022 15:20:13 +0300 Subject: [PATCH] Optionally use qmlformat for Qt Quick auto format on save Task-number: QTCREATORBUG-28192 Task-number: QTCREATORBUG-26602 Change-Id: I22a3917e8a4de4aaf297e9d9d622bdede782c6a6 Reviewed-by: Xavier BESSON Reviewed-by: Ulf Hermann Reviewed-by: Reviewed-by: Eike Ziller --- .../qmljseditor/qmljseditingsettingspage.cpp | 124 ++++++++++++++++-- .../qmljseditor/qmljseditingsettingspage.h | 15 ++- src/plugins/qmljseditor/qmljseditorplugin.cpp | 32 +++++ src/plugins/texteditor/command.cpp | 5 + src/plugins/texteditor/command.h | 1 + 5 files changed, 165 insertions(+), 12 deletions(-) diff --git a/src/plugins/qmljseditor/qmljseditingsettingspage.cpp b/src/plugins/qmljseditor/qmljseditingsettingspage.cpp index 58ddc91d5eb..84d05d01a0e 100644 --- a/src/plugins/qmljseditor/qmljseditingsettingspage.cpp +++ b/src/plugins/qmljseditor/qmljseditingsettingspage.cpp @@ -7,11 +7,16 @@ #include #include -#include #include +#include +#include +#include +#include #include #include +#include +#include #include #include @@ -23,6 +28,10 @@ const char FOLD_AUX_DATA[] = "QmlJSEditor.FoldAuxData"; const char USE_QMLLS[] = "QmlJSEditor.UseQmlls"; const char USE_LATEST_QMLLS[] = "QmlJSEditor.UseLatestQmlls"; 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 DEFAULT_CUSTOM_FORMAT_COMMAND[] = "%{CurrentDocument:Project:QT_HOST_BINS}/qmlformat"; using namespace QmlJSEditor; using namespace QmlJSEditor::Internal; @@ -45,6 +54,9 @@ void QmlJsEditingSettings::fromSettings(QSettings *settings) m_uiQmlOpenMode = settings->value(UIQML_OPEN_MODE, "").toString(); m_qmllsSettings.useQmlls = settings->value(USE_QMLLS, QVariant(false)).toBool(); m_qmllsSettings.useLatestQmlls = settings->value(USE_LATEST_QMLLS, QVariant(false)).toBool(); + m_formatCommand = settings->value(FORMAT_COMMAND, {}).toString(); + m_formatCommandOptions = settings->value(FORMAT_COMMAND_OPTIONS, {}).toString(); + m_useCustomFormatCommand = settings->value(CUSTOM_COMMAND, QVariant(false)).toBool(); settings->endGroup(); } @@ -59,6 +71,15 @@ void QmlJsEditingSettings::toSettings(QSettings *settings) const settings->setValue(UIQML_OPEN_MODE, m_uiQmlOpenMode); settings->setValue(USE_QMLLS, m_qmllsSettings.useQmlls); settings->setValue(USE_LATEST_QMLLS, m_qmllsSettings.useLatestQmlls); + Utils::QtcSettings::setValueWithDefault(settings, FORMAT_COMMAND, m_formatCommand, {}); + Utils::QtcSettings::setValueWithDefault(settings, + FORMAT_COMMAND_OPTIONS, + m_formatCommandOptions, + {}); + Utils::QtcSettings::setValueWithDefault(settings, + CUSTOM_COMMAND, + m_useCustomFormatCommand, + false); settings->endGroup(); QmllsSettingsManager::instance()->checkForChanges(); } @@ -69,9 +90,10 @@ bool QmlJsEditingSettings::equals(const QmlJsEditingSettings &other) const && m_pinContextPane == other.m_pinContextPane && m_autoFormatOnSave == other.m_autoFormatOnSave && m_autoFormatOnlyCurrentProject == other.m_autoFormatOnlyCurrentProject - && m_foldAuxData == other.m_foldAuxData - && m_qmllsSettings == other.m_qmllsSettings - && m_uiQmlOpenMode == other.m_uiQmlOpenMode; + && m_foldAuxData == other.m_foldAuxData && m_qmllsSettings == other.m_qmllsSettings + && m_uiQmlOpenMode == other.m_uiQmlOpenMode && m_formatCommand == other.m_formatCommand + && m_formatCommandOptions == other.m_formatCommandOptions + && m_useCustomFormatCommand == other.m_useCustomFormatCommand; } bool QmlJsEditingSettings::enableContextPane() const @@ -124,6 +146,41 @@ void QmlJsEditingSettings::setFoldAuxData(const bool foldAuxData) m_foldAuxData = foldAuxData; } +QString QmlJsEditingSettings::defaultFormatCommand() const +{ + return DEFAULT_CUSTOM_FORMAT_COMMAND; +} + +QString QmlJsEditingSettings::formatCommand() const +{ + return m_formatCommand; +} + +void QmlJsEditingSettings::setFormatCommand(const QString &formatCommand) +{ + m_formatCommand = formatCommand; +} + +QString QmlJsEditingSettings::formatCommandOptions() const +{ + return m_formatCommandOptions; +} + +void QmlJsEditingSettings::setFormatCommandOptions(const QString &formatCommandOptions) +{ + m_formatCommandOptions = formatCommandOptions; +} + +bool QmlJsEditingSettings::useCustomFormatCommand() const +{ + return m_useCustomFormatCommand; +} + +void QmlJsEditingSettings::setUseCustomFormatCommand(bool customCommand) +{ + m_useCustomFormatCommand = customCommand; +} + QmllsSettings &QmlJsEditingSettings::qmllsSettigs() { return m_qmllsSettings; @@ -156,6 +213,16 @@ public: new QCheckBox(Tr::tr("Restrict to files contained in the current project")); autoFormatOnlyCurrentProject->setChecked(s.autoFormatOnlyCurrentProject()); autoFormatOnlyCurrentProject->setEnabled(autoFormatOnSave->isChecked()); + useCustomFormatCommand = new QCheckBox( + Tr::tr("Use custom command instead of built-in formatter")); + useCustomFormatCommand->setChecked(s.useCustomFormatCommand()); + auto formatCommandLabel = new QLabel(Tr::tr("Command:")); + formatCommand = new QLineEdit(); + formatCommand->setText(s.formatCommand()); + formatCommand->setPlaceholderText(s.defaultFormatCommand()); + auto formatCommandOptionsLabel = new QLabel(Tr::tr("Arguments:")); + formatCommandOptions = new QLineEdit(); + formatCommandOptions->setText(s.formatCommandOptions()); pinContextPane = new QCheckBox(Tr::tr("Pin Qt Quick Toolbar")); pinContextPane->setChecked(s.pinContextPane()); enableContextPane = new QCheckBox(Tr::tr("Always show Qt Quick Toolbar")); @@ -180,11 +247,23 @@ public: useLatestQmlls->setEnabled(checked != Qt::Unchecked); }); using namespace Utils::Layouting; - Column { + // clang-format off + const auto formattingGroup = Group { title(Tr::tr("Automatic Formatting on File Save")), - Column { autoFormatOnSave, autoFormatOnlyCurrentProject }, - }, + Column { + autoFormatOnSave, + autoFormatOnlyCurrentProject, + useCustomFormatCommand, + Form { + formatCommandLabel, formatCommand, br, + formatCommandOptionsLabel, formatCommandOptions + } + }, + }; + + Column { + formattingGroup, Group { title(Tr::tr("Qt Quick Toolbars")), Column { pinContextPane, enableContextPane }, @@ -201,11 +280,28 @@ public: Column{useQmlls, useLatestQmlls}, }, st, - } - .attachTo(this); + }.attachTo(this); + // clang-format on - connect(autoFormatOnSave, &QCheckBox::toggled, - autoFormatOnlyCurrentProject, &QWidget::setEnabled); + Utils::VariableChooser::addSupportForChildWidgets(formattingGroup.widget, + Utils::globalMacroExpander()); + + const auto updateFormatCommandState = [&, formatCommandLabel, formatCommandOptionsLabel] { + const bool enabled = useCustomFormatCommand->isChecked() + && autoFormatOnSave->isChecked(); + formatCommandLabel->setEnabled(enabled); + formatCommand->setEnabled(enabled); + formatCommandOptionsLabel->setEnabled(enabled); + formatCommandOptions->setEnabled(enabled); + }; + updateFormatCommandState(); + + connect(autoFormatOnSave, &QCheckBox::toggled, this, [&, updateFormatCommandState]() { + autoFormatOnlyCurrentProject->setEnabled(autoFormatOnSave->isChecked()); + useCustomFormatCommand->setEnabled(autoFormatOnSave->isChecked()); + updateFormatCommandState(); + }); + connect(useCustomFormatCommand, &QCheckBox::toggled, this, updateFormatCommandState); } void apply() final @@ -215,6 +311,9 @@ public: s.setPinContextPane(pinContextPane->isChecked()); s.setAutoFormatOnSave(autoFormatOnSave->isChecked()); s.setAutoFormatOnlyCurrentProject(autoFormatOnlyCurrentProject->isChecked()); + s.setUseCustomFormatCommand(useCustomFormatCommand->isChecked()); + s.setFormatCommand(formatCommand->text()); + s.setFormatCommandOptions(formatCommandOptions->text()); s.setFoldAuxData(foldAuxData->isChecked()); s.setUiQmlOpenMode(uiQmlOpenComboBox->currentData().toString()); s.qmllsSettigs().useQmlls = useQmlls->isChecked(); @@ -225,6 +324,9 @@ public: private: QCheckBox *autoFormatOnSave; QCheckBox *autoFormatOnlyCurrentProject; + QCheckBox *useCustomFormatCommand; + QLineEdit *formatCommand; + QLineEdit *formatCommandOptions; QCheckBox *pinContextPane; QCheckBox *enableContextPane; QCheckBox *foldAuxData; diff --git a/src/plugins/qmljseditor/qmljseditingsettingspage.h b/src/plugins/qmljseditor/qmljseditingsettingspage.h index bf2dc08d4a2..9f34f2b8827 100644 --- a/src/plugins/qmljseditor/qmljseditingsettingspage.h +++ b/src/plugins/qmljseditor/qmljseditingsettingspage.h @@ -42,6 +42,16 @@ public: bool foldAuxData() const; void setFoldAuxData(const bool foldAuxData); + QString defaultFormatCommand() const; + QString formatCommand() const; + void setFormatCommand(const QString &formatCommand); + + QString formatCommandOptions() const; + void setFormatCommandOptions(const QString &formatCommandOptions); + + bool useCustomFormatCommand() const; + void setUseCustomFormatCommand(bool customCommand); + QmllsSettings &qmllsSettigs(); const QmllsSettings &qmllsSettigs() const; @@ -59,8 +69,11 @@ private: bool m_autoFormatOnSave = false; bool m_autoFormatOnlyCurrentProject = false; bool m_foldAuxData = true; + bool m_useCustomFormatCommand = false; QmllsSettings m_qmllsSettings; - QString m_uiQmlOpenMode = {}; + QString m_uiQmlOpenMode; + QString m_formatCommand; + QString m_formatCommandOptions; }; namespace Internal { diff --git a/src/plugins/qmljseditor/qmljseditorplugin.cpp b/src/plugins/qmljseditor/qmljseditorplugin.cpp index 109b45a45a9..62f08568b36 100644 --- a/src/plugins/qmljseditor/qmljseditorplugin.cpp +++ b/src/plugins/qmljseditor/qmljseditorplugin.cpp @@ -30,12 +30,15 @@ #include #include #include +#include #include #include #include +#include #include #include #include +#include #include #include @@ -237,6 +240,35 @@ void QmlJSEditorPluginPrivate::renameUsages() void QmlJSEditorPluginPrivate::reformatFile() { if (m_currentDocument) { + if (QmlJsEditingSettings::get().useCustomFormatCommand()) { + QString formatCommand = QmlJsEditingSettings::get().formatCommand(); + if (formatCommand.isEmpty()) + formatCommand = QmlJsEditingSettings::get().defaultFormatCommand(); + const auto exe = FilePath::fromUserInput(globalMacroExpander()->expand(formatCommand)); + const QString args = globalMacroExpander()->expand( + QmlJsEditingSettings::get().formatCommandOptions()); + const CommandLine commandLine(exe, args, CommandLine::Raw); + TextEditor::Command command; + command.setExecutable(commandLine.executable().toString()); + command.setProcessing(TextEditor::Command::FileProcessing); + command.addOptions(commandLine.splitArguments()); + command.addOption("--inplace"); + command.addOption("%file"); + + if (!command.isValid()) + return; + + const QList editors = Core::DocumentModel::editorsForDocument(m_currentDocument); + if (editors.isEmpty()) + return; + IEditor *currentEditor = EditorManager::currentEditor(); + IEditor *editor = editors.contains(currentEditor) ? currentEditor : editors.first(); + if (auto widget = TextEditor::TextEditorWidget::fromEditor(editor)) + TextEditor::formatEditor(widget, command); + + return; + } + QmlJS::Document::Ptr document = m_currentDocument->semanticInfo().document; QmlJS::Snapshot snapshot = QmlJS::ModelManagerInterface::instance()->snapshot(); diff --git a/src/plugins/texteditor/command.cpp b/src/plugins/texteditor/command.cpp index b9a359ce3a1..3d635bfcfb1 100644 --- a/src/plugins/texteditor/command.cpp +++ b/src/plugins/texteditor/command.cpp @@ -30,6 +30,11 @@ void Command::addOption(const QString &option) m_options << option; } +void Command::addOptions(const QStringList &options) +{ + m_options += options; +} + Command::Processing Command::processing() const { return m_processing; diff --git a/src/plugins/texteditor/command.h b/src/plugins/texteditor/command.h index d237daca4d5..1d1b54e8ae1 100644 --- a/src/plugins/texteditor/command.h +++ b/src/plugins/texteditor/command.h @@ -25,6 +25,7 @@ public: QStringList options() const; void addOption(const QString &option); + void addOptions(const QStringList &options); Processing processing() const; void setProcessing(const Processing &processing);