Beautifier for CMake files

Fixes: QTCREATORBUG-25773
Change-Id: I30df110512553b28894427e4d473814400153923
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Cristian Adam <cristian.adam@qt.io>
This commit is contained in:
Xavier BESSON
2022-11-21 00:10:25 +01:00
parent bcfaf00d00
commit ac2ca7244a
10 changed files with 525 additions and 1 deletions

View File

@@ -13,6 +13,9 @@ add_qtc_plugin(CMakeProjectManager
cmakeconfigitem.cpp cmakeconfigitem.h cmakeconfigitem.cpp cmakeconfigitem.h
cmakeeditor.cpp cmakeeditor.h cmakeeditor.cpp cmakeeditor.h
cmakefilecompletionassist.cpp cmakefilecompletionassist.h cmakefilecompletionassist.cpp cmakefilecompletionassist.h
cmakeformatter.cpp cmakeformatter.h
cmakeformatteroptionspage.cpp cmakeformatteroptionspage.h
cmakeformattersettings.cpp cmakeformattersettings.h
cmakeindenter.cpp cmakeindenter.h cmakeindenter.cpp cmakeindenter.h
cmakekitinformation.cpp cmakekitinformation.h cmakekitinformation.cpp cmakekitinformation.h
cmakelocatorfilter.cpp cmakelocatorfilter.h cmakelocatorfilter.cpp cmakelocatorfilter.h

View File

@@ -0,0 +1,70 @@
// Copyright (C) 2016 Lorenz Haas
// Copyright (C) 2022 Xavier BESSON
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "cmakeformatter.h"
#include "cmakeformattersettings.h"
#include "cmakeprojectconstants.h"
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/idocument.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/projecttree.h>
#include <texteditor/formattexteditor.h>
#include <texteditor/texteditor.h>
#include <utils/fileutils.h>
#include <QAction>
#include <QMenu>
#include <QVersionNumber>
using namespace TextEditor;
namespace CMakeProjectManager {
namespace Internal {
void CMakeFormatter::updateActions(Core::IEditor *editor)
{
const bool enabled = editor && CMakeFormatterSettings::instance()->isApplicable(editor->document());
m_formatFile->setEnabled(enabled);
}
void CMakeFormatter::formatFile()
{
formatCurrentFile(command());
}
Command CMakeFormatter::command() const
{
Command command;
command.setExecutable(CMakeFormatterSettings::instance()->command().toString());
command.setProcessing(Command::PipeProcessing);
command.addOption("%file");
return command;
}
bool CMakeFormatter::isApplicable(const Core::IDocument *document) const
{
return CMakeFormatterSettings::instance()->isApplicable(document);
}
void CMakeFormatter::initialize()
{
m_formatFile = new QAction(tr("Format &Current File"), this);
Core::Command *cmd = Core::ActionManager::registerAction(m_formatFile, Constants::CMAKEFORMATTER_ACTION_ID);
connect(m_formatFile, &QAction::triggered, this, &CMakeFormatter::formatFile);
Core::ActionManager::actionContainer(Constants::CMAKEFORMATTER_MENU_ID)->addAction(cmd);
connect(CMakeFormatterSettings::instance(), &CMakeFormatterSettings::supportedMimeTypesChanged,
[this] { updateActions(Core::EditorManager::currentEditor()); });
}
} // namespace Internal
} // namespace CMakeProjectManager

View File

@@ -0,0 +1,38 @@
// Copyright (C) 2016 Lorenz Haas
// Copyright (C) 2022 Xavier BESSON
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#pragma once
#include <texteditor/command.h>
#include "cmakeformatteroptionspage.h"
namespace Core {
class IDocument;
class IEditor;
}
namespace CMakeProjectManager {
namespace Internal {
class CMakeFormatter : public QObject
{
Q_OBJECT
public:
void updateActions(Core::IEditor *editor);
TextEditor::Command command() const;
bool isApplicable(const Core::IDocument *document) const;
void initialize();
private:
void formatFile();
QAction *m_formatFile = nullptr;
CMakeFormatterOptionsPage m_page;
};
} // namespace Internal
} // namespace CMakeProjectManager

View File

@@ -0,0 +1,102 @@
// Copyright (C) 2016 Lorenz Haas
// Copyright (C) 2022 Xavier BESSON
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "cmakeprojectconstants.h"
#include "cmakeprojectmanagertr.h"
#include "cmakeformatteroptionspage.h"
#include "cmakeformattersettings.h"
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
#include <QApplication>
#include <QCheckBox>
#include <QComboBox>
#include <QLabel>
#include <QLineEdit>
namespace CMakeProjectManager::Internal {
class CMakeFormatterOptionsPageWidget : public Core::IOptionsPageWidget
{
Q_DECLARE_TR_FUNCTIONS(CMakeFormat::Internal::GeneralOptionsPageWidget)
public:
explicit CMakeFormatterOptionsPageWidget();
private:
void apply() final;
Utils::PathChooser *m_command;
QCheckBox *m_autoFormat;
QLineEdit *m_autoFormatMime;
QCheckBox *m_autoFormatOnlyCurrentProject;
};
CMakeFormatterOptionsPageWidget::CMakeFormatterOptionsPageWidget()
{
resize(817, 631);
auto settings = CMakeFormatterSettings::instance();
m_autoFormat = new QCheckBox(tr("Enable auto format on file save"));
m_autoFormat->setChecked(settings->autoFormatOnSave());
auto mimeLabel = new QLabel(tr("Restrict to MIME types:"));
mimeLabel->setEnabled(false);
m_autoFormatMime = new QLineEdit(settings->autoFormatMimeAsString());
m_autoFormatMime->setEnabled(m_autoFormat->isChecked());
m_autoFormatOnlyCurrentProject =
new QCheckBox(tr("Restrict to files contained in the current project"));
m_autoFormatOnlyCurrentProject->setEnabled(m_autoFormat->isChecked());
m_autoFormatOnlyCurrentProject->setChecked(settings->autoFormatOnlyCurrentProject());
m_command = new Utils::PathChooser;
m_command->setExpectedKind(Utils::PathChooser::ExistingCommand);
m_command->setCommandVersionArguments({"--version"});
m_command->setPromptDialogTitle(tr("%1 Command").arg(Tr::Tr::tr("Formatter")));
m_command->setFilePath(settings->command());
using namespace Utils::Layouting;
Column {
Group {
title(tr("Automatic Formatting on File Save")),
Form {
tr("CMakeFormat command:"), m_command, br,
Span(2, m_autoFormat), br,
mimeLabel, m_autoFormatMime, br,
Span(2, m_autoFormatOnlyCurrentProject)
}
},
st
}.attachTo(this);
connect(m_autoFormat, &QCheckBox::toggled, m_autoFormatMime, &QLineEdit::setEnabled);
connect(m_autoFormat, &QCheckBox::toggled, mimeLabel, &QLabel::setEnabled);
connect(m_autoFormat, &QCheckBox::toggled, m_autoFormatOnlyCurrentProject, &QCheckBox::setEnabled);
}
void CMakeFormatterOptionsPageWidget::apply()
{
auto settings = CMakeFormatterSettings::instance();
settings->setCommand(m_command->filePath().toString());
settings->setAutoFormatOnSave(m_autoFormat->isChecked());
settings->setAutoFormatMime(m_autoFormatMime->text());
settings->setAutoFormatOnlyCurrentProject(m_autoFormatOnlyCurrentProject->isChecked());
settings->save();
}
CMakeFormatterOptionsPage::CMakeFormatterOptionsPage()
{
setId(Constants::Settings::FORMATTER_ID);
setDisplayName(Tr::Tr::tr("Formatter"));
setDisplayCategory("CMake");
setCategory(Constants::Settings::CATEGORY);
setWidgetCreator([] { return new CMakeFormatterOptionsPageWidget; });
}
} // CMakeProjectManager::Internal

View File

@@ -0,0 +1,17 @@
// Copyright (C) 2016 Lorenz Haas
// Copyright (C) 2022 Xavier BESSON
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#pragma once
#include <coreplugin/dialogs/ioptionspage.h>
namespace CMakeProjectManager::Internal {
class CMakeFormatterOptionsPage final : public Core::IOptionsPage
{
public:
explicit CMakeFormatterOptionsPage();
};
} // CMakeProjectManager::Internal

View File

@@ -0,0 +1,144 @@
// Copyright (C) 2016 Lorenz Haas
// Copyright (C) 2022 Xavier BESSON
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "cmakeformattersettings.h"
#include "cmakeprojectconstants.h"
#include <coreplugin/icore.h>
#include <coreplugin/idocument.h>
#include <utils/algorithm.h>
#include <utils/genericconstants.h>
#include <utils/mimeutils.h>
#include <utils/qtcprocess.h>
namespace CMakeProjectManager {
namespace Internal {
namespace {
const char AUTO_FORMAT_COMMAND[] = "autoFormatCommand";
const char AUTO_FORMAT_MIME[] = "autoFormatMime";
const char AUTO_FORMAT_ONLY_CURRENT_PROJECT[] = "autoFormatOnlyCurrentProject";
const char AUTO_FORMAT_ON_SAVE[] = "autoFormatOnSave";
}
CMakeFormatterSettings::CMakeFormatterSettings(QObject* parent)
: QObject(parent)
{
read();
}
CMakeFormatterSettings *CMakeFormatterSettings::instance()
{
static CMakeFormatterSettings m_instance;
return &m_instance;
}
void CMakeFormatterSettings::read()
{
QSettings *s = Core::ICore::settings();
s->beginGroup(Constants::CMAKEFORMATTER_SETTINGS_GROUP);
s->beginGroup(Constants::CMAKEFORMATTER_GENERAL_GROUP);
setCommand(s->value(AUTO_FORMAT_COMMAND, QString("cmake-format")).toString());
m_autoFormatOnSave = s->value(AUTO_FORMAT_ON_SAVE, false).toBool();
setAutoFormatMime(s->value(AUTO_FORMAT_MIME, QString("text/x-cmake")).toString());
m_autoFormatOnlyCurrentProject = s->value(AUTO_FORMAT_ONLY_CURRENT_PROJECT, true).toBool();
s->endGroup();
s->endGroup();
}
void CMakeFormatterSettings::save()
{
QSettings *s = Core::ICore::settings();
s->beginGroup(Constants::CMAKEFORMATTER_SETTINGS_GROUP);
s->beginGroup(Constants::CMAKEFORMATTER_GENERAL_GROUP);
Utils::QtcSettings::setValueWithDefault(s, AUTO_FORMAT_COMMAND, m_command, QString("cmake-format"));
Utils::QtcSettings::setValueWithDefault(s, AUTO_FORMAT_ON_SAVE, m_autoFormatOnSave, false);
Utils::QtcSettings::setValueWithDefault(s, AUTO_FORMAT_MIME, autoFormatMimeAsString(), QString("text/x-cmake"));
Utils::QtcSettings::setValueWithDefault(s, AUTO_FORMAT_ONLY_CURRENT_PROJECT, m_autoFormatOnlyCurrentProject, true);
s->endGroup();
s->endGroup();
}
Utils::FilePath CMakeFormatterSettings::command() const
{
return Utils::FilePath::fromString(m_command);
}
void CMakeFormatterSettings::setCommand(const QString &cmd)
{
if (cmd == m_command)
return;
m_command = cmd;
}
bool CMakeFormatterSettings::autoFormatOnSave() const
{
return m_autoFormatOnSave;
}
void CMakeFormatterSettings::setAutoFormatOnSave(bool autoFormatOnSave)
{
m_autoFormatOnSave = autoFormatOnSave;
}
QList<Utils::MimeType> CMakeFormatterSettings::autoFormatMime() const
{
return m_autoFormatMime;
}
QString CMakeFormatterSettings::autoFormatMimeAsString() const
{
return Utils::transform(m_autoFormatMime, &Utils::MimeType::name).join("; ");
}
void CMakeFormatterSettings::setAutoFormatMime(const QList<Utils::MimeType> &autoFormatMime)
{
if (m_autoFormatMime == autoFormatMime)
return;
m_autoFormatMime = autoFormatMime;
emit supportedMimeTypesChanged();
}
void CMakeFormatterSettings::setAutoFormatMime(const QString &mimeList)
{
const QStringList stringTypes = mimeList.split(';');
QList<Utils::MimeType> types;
types.reserve(stringTypes.count());
for (QString t : stringTypes) {
t = t.trimmed();
const Utils::MimeType mime = Utils::mimeTypeForName(t);
if (mime.isValid())
types << mime;
}
setAutoFormatMime(types);
}
bool CMakeFormatterSettings::autoFormatOnlyCurrentProject() const
{
return m_autoFormatOnlyCurrentProject;
}
void CMakeFormatterSettings::setAutoFormatOnlyCurrentProject(bool autoFormatOnlyCurrentProject)
{
m_autoFormatOnlyCurrentProject = autoFormatOnlyCurrentProject;
}
bool CMakeFormatterSettings::isApplicable(const Core::IDocument *document) const
{
if (!document)
return false;
if (m_autoFormatMime.isEmpty())
return true;
const Utils::MimeType documentMimeType = Utils::mimeTypeForName(document->mimeType());
return Utils::anyOf(m_autoFormatMime, [&documentMimeType](const Utils::MimeType &mime) {
return documentMimeType.inherits(mime.name());
});
}
} // namespace Internal
} // namespace CMakeProjectManager

View File

@@ -0,0 +1,58 @@
// Copyright (C) 2016 Lorenz Haas
// Copyright (C) 2022 Xavier BESSON
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#pragma once
#include <utils/mimeutils.h>
#include <QList>
#include <QVersionNumber>
namespace Core { class IDocument; }
namespace CMakeProjectManager {
namespace Internal {
class VersionUpdater;
class CMakeFormatterSettings : public QObject
{
Q_OBJECT
public:
explicit CMakeFormatterSettings(QObject* parent = nullptr);
static CMakeFormatterSettings *instance();
void read();
void save();
Utils::FilePath command() const;
void setCommand(const QString &cmd);
bool autoFormatOnSave() const;
void setAutoFormatOnSave(bool autoFormatOnSave);
QList<Utils::MimeType> autoFormatMime() const;
QString autoFormatMimeAsString() const;
void setAutoFormatMime(const QList<Utils::MimeType> &autoFormatMime);
void setAutoFormatMime(const QString &mimeList);
bool autoFormatOnlyCurrentProject() const;
void setAutoFormatOnlyCurrentProject(bool autoFormatOnlyCurrentProject);
bool isApplicable(const Core::IDocument *document) const;
signals:
void supportedMimeTypesChanged();
private:
QString m_command;
bool m_autoFormatOnSave = false;
bool m_autoFormatOnlyCurrentProject = true;
QString m_autoFormatTool;
QList<Utils::MimeType> m_autoFormatMime;
};
} // namespace Internal
} // namespace CMakeProjectManager

View File

@@ -18,6 +18,11 @@ const char BUILD_FILE[] = "CMakeProject.BuildFile";
const char CMAKE_HOME_DIR[] = "CMakeProject.HomeDirectory"; const char CMAKE_HOME_DIR[] = "CMakeProject.HomeDirectory";
const char QML_DEBUG_SETTING[] = "CMakeProject.EnableQmlDebugging"; const char QML_DEBUG_SETTING[] = "CMakeProject.EnableQmlDebugging";
const char CMAKEFORMATTER_SETTINGS_GROUP[] = "CMakeFormatter";
const char CMAKEFORMATTER_GENERAL_GROUP[] = "General";
const char CMAKEFORMATTER_ACTION_ID[] = "CMakeFormatter.Action";
const char CMAKEFORMATTER_MENU_ID[] = "CMakeFormatter.Menu";
// Project // Project
const char CMAKE_PROJECT_ID[] = "CMakeProjectManager.CMakeProject"; const char CMAKE_PROJECT_ID[] = "CMakeProjectManager.CMakeProject";
@@ -29,6 +34,7 @@ const char M_CONTEXT[] = "CMakeEditor.ContextMenu";
namespace Settings { namespace Settings {
const char GENERAL_ID[] = "CMakeSpecifcSettings"; const char GENERAL_ID[] = "CMakeSpecifcSettings";
const char TOOLS_ID[] = "K.CMake.Tools"; const char TOOLS_ID[] = "K.CMake.Tools";
const char FORMATTER_ID[] = "K.CMake.Formatter";
const char CATEGORY[] = "K.CMake"; const char CATEGORY[] = "K.CMake";
} // namespace Settings } // namespace Settings

View File

@@ -31,6 +31,12 @@ QtcPlugin {
"cmakeeditor.h", "cmakeeditor.h",
"cmakefilecompletionassist.cpp", "cmakefilecompletionassist.cpp",
"cmakefilecompletionassist.h", "cmakefilecompletionassist.h",
"cmakeformat.cpp",
"cmakeformat.h",
"cmakeformatteroptionspage.cpp",
"cmakeformatteroptionspage.h",
"cmakeformattersettings.cpp",
"cmakeformattersettings.h",
"cmakekitinformation.h", "cmakekitinformation.h",
"cmakekitinformation.cpp", "cmakekitinformation.cpp",
"cmakelocatorfilter.cpp", "cmakelocatorfilter.cpp",

View File

@@ -7,6 +7,8 @@
#include "cmakebuildstep.h" #include "cmakebuildstep.h"
#include "cmakebuildsystem.h" #include "cmakebuildsystem.h"
#include "cmakeeditor.h" #include "cmakeeditor.h"
#include "cmakeformatter.h"
#include "cmakeformattersettings.h"
#include "cmakekitinformation.h" #include "cmakekitinformation.h"
#include "cmakelocatorfilter.h" #include "cmakelocatorfilter.h"
#include "cmakeproject.h" #include "cmakeproject.h"
@@ -26,12 +28,14 @@
#include <projectexplorer/projectmanager.h> #include <projectexplorer/projectmanager.h>
#include <projectexplorer/projecttree.h> #include <projectexplorer/projecttree.h>
#include <texteditor/formattexteditor.h>
#include <texteditor/snippets/snippetprovider.h> #include <texteditor/snippets/snippetprovider.h>
#include <utils/fsengine/fileiconprovider.h> #include <utils/fsengine/fileiconprovider.h>
#include <utils/parameteraction.h> #include <utils/parameteraction.h>
#include <QTimer> #include <QTimer>
#include <QMenu>
using namespace Core; using namespace Core;
using namespace ProjectExplorer; using namespace ProjectExplorer;
@@ -39,9 +43,28 @@ using namespace Utils;
namespace CMakeProjectManager::Internal { namespace CMakeProjectManager::Internal {
class CMakeProjectPluginPrivate bool isAutoFormatApplicable(const Core::IDocument *document,
const QList<Utils::MimeType> &allowedMimeTypes)
{
if (!document)
return false;
if (allowedMimeTypes.isEmpty())
return true;
const Utils::MimeType documentMimeType = Utils::mimeTypeForName(document->mimeType());
return Utils::anyOf(allowedMimeTypes, [&documentMimeType](const Utils::MimeType &mime) {
return documentMimeType.inherits(mime.name());
});
}
class CMakeProjectPluginPrivate : public QObject
{ {
public: public:
CMakeProjectPluginPrivate();
void updateActions(Core::IEditor *editor = nullptr);
void autoFormatOnSave(Core::IDocument *document);
CMakeToolManager cmakeToolManager; // have that before the first CMakeKitAspect CMakeToolManager cmakeToolManager; // have that before the first CMakeKitAspect
ParameterAction buildTargetContextAction{ ParameterAction buildTargetContextAction{
@@ -63,8 +86,57 @@ public:
CMakeKitAspect cmakeKitAspect; CMakeKitAspect cmakeKitAspect;
CMakeGeneratorKitAspect cmakeGeneratorKitAspect; CMakeGeneratorKitAspect cmakeGeneratorKitAspect;
CMakeConfigurationKitAspect cmakeConfigurationKitAspect; CMakeConfigurationKitAspect cmakeConfigurationKitAspect;
CMakeFormatter cmakeFormatter;
}; };
CMakeProjectPluginPrivate::CMakeProjectPluginPrivate()
{
const Core::EditorManager *editorManager = Core::EditorManager::instance();
QObject::connect(editorManager, &Core::EditorManager::currentEditorChanged,
this, &CMakeProjectPluginPrivate::updateActions);
QObject::connect(editorManager, &Core::EditorManager::aboutToSave,
this, &CMakeProjectPluginPrivate::autoFormatOnSave);
}
void CMakeProjectPluginPrivate::updateActions(Core::IEditor *editor)
{
cmakeFormatter.updateActions(editor);
}
void CMakeProjectPluginPrivate::autoFormatOnSave(Core::IDocument *document)
{
if (!CMakeFormatterSettings::instance()->autoFormatOnSave())
return;
if (!isAutoFormatApplicable(document, CMakeFormatterSettings::instance()->autoFormatMime()))
return;
// Check if file is contained in the current project (if wished)
if (CMakeFormatterSettings::instance()->autoFormatOnlyCurrentProject()) {
const ProjectExplorer::Project *pro = ProjectExplorer::ProjectTree::currentProject();
if (!pro || pro->files([document](const ProjectExplorer::Node *n) {
return ProjectExplorer::Project::SourceFiles(n)
&& n->filePath() == document->filePath();
}).isEmpty()) {
return;
}
}
if (!cmakeFormatter.isApplicable(document))
return;
const TextEditor::Command command = cmakeFormatter.command();
if (!command.isValid())
return;
const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForDocument(document);
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);
}
CMakeSpecificSettings *CMakeProjectPlugin::projectTypeSpecificSettings() CMakeSpecificSettings *CMakeProjectPlugin::projectTypeSpecificSettings()
{ {
static CMakeSpecificSettings theSettings; static CMakeSpecificSettings theSettings;
@@ -115,6 +187,14 @@ bool CMakeProjectPlugin::initialize(const QStringList & /*arguments*/, QString *
} }
}); });
Core::ActionContainer *menu = Core::ActionManager::createMenu(Constants::CMAKEFORMATTER_MENU_ID);
menu->menu()->setTitle(QCoreApplication::translate("CMakeFormatter", "CMakeFormatter"));
menu->setOnAllDisabledBehavior(Core::ActionContainer::Show);
Core::ActionManager::actionContainer(Core::Constants::M_TOOLS)->addMenu(menu);
d->cmakeFormatter.initialize();
d->updateActions();
return true; return true;
} }