forked from qt-creator/qt-creator
This puts the implementation introduced in acf1ecb47f into use, after
significant simplifications in the class hierarchy. CRTP is not used
anymore, and the new tag based dispatch is also used for Layout::addItem,
effectively reducing the number of different code paths.
The Lua based settings access is disabled for now.
Change-Id: Idb6d1a25675378757c5267bdb630bcd4c1f52d34
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
388 lines
15 KiB
C++
388 lines
15 KiB
C++
// Copyright (C) 2022 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
#include "clangformatglobalconfigwidget.h"
|
|
|
|
#include "clangformatconfigwidget.h"
|
|
#include "clangformatconstants.h"
|
|
#include "clangformatindenter.h"
|
|
#include "clangformatsettings.h"
|
|
#include "clangformattr.h"
|
|
#include "clangformatutils.h"
|
|
|
|
#include <cppeditor/cppcodestylepreferencesfactory.h>
|
|
#include <cppeditor/cppcodestylesettingspage.h>
|
|
#include <cppeditor/cppeditorconstants.h>
|
|
|
|
#include <projectexplorer/project.h>
|
|
#include <projectexplorer/projecttree.h>
|
|
|
|
#include <texteditor/icodestylepreferences.h>
|
|
#include <texteditor/texteditorsettings.h>
|
|
|
|
#include <utils/guard.h>
|
|
#include <utils/infolabel.h>
|
|
#include <utils/layoutbuilder.h>
|
|
|
|
#include <QCheckBox>
|
|
#include <QComboBox>
|
|
#include <QGroupBox>
|
|
#include <QLabel>
|
|
#include <QSpinBox>
|
|
|
|
using namespace CppEditor;
|
|
using namespace ProjectExplorer;
|
|
using namespace TextEditor;
|
|
using namespace Utils;
|
|
|
|
namespace ClangFormat {
|
|
|
|
class ClangFormatGlobalConfigWidget final : public CodeStyleEditorWidget
|
|
{
|
|
public:
|
|
ClangFormatGlobalConfigWidget(ICodeStylePreferences *codeStyle,
|
|
Project *project,
|
|
QWidget *parent);
|
|
void apply() final;
|
|
void finish() final;
|
|
|
|
private:
|
|
void initCheckBoxes();
|
|
void initIndentationOrFormattingCombobox();
|
|
void initCustomSettingsCheckBox();
|
|
void initUseGlobalSettingsCheckBox();
|
|
void initFileSizeThresholdSpinBox();
|
|
void initCurrentProjectLabel();
|
|
|
|
bool projectClangFormatFileExists();
|
|
|
|
Project *m_project;
|
|
ICodeStylePreferences *m_codeStyle;
|
|
Guard m_ignoreChanges;
|
|
bool m_useCustomSettings;
|
|
|
|
QLabel *m_projectHasClangFormat;
|
|
QLabel *m_formattingModeLabel;
|
|
QLabel *m_fileSizeThresholdLabel;
|
|
QSpinBox *m_fileSizeThresholdSpinBox;
|
|
QComboBox *m_indentingOrFormatting;
|
|
QCheckBox *m_formatWhileTyping;
|
|
QCheckBox *m_formatOnSave;
|
|
QCheckBox *m_useCustomSettingsCheckBox;
|
|
QCheckBox *m_useGlobalSettings;
|
|
InfoLabel *m_currentProjectLabel;
|
|
};
|
|
|
|
ClangFormatGlobalConfigWidget::ClangFormatGlobalConfigWidget(ICodeStylePreferences *codeStyle,
|
|
Project *project, QWidget *parent)
|
|
: TextEditor::CodeStyleEditorWidget(parent)
|
|
, m_project(project)
|
|
, m_codeStyle(codeStyle)
|
|
{
|
|
const QString sizeThresholdToolTip = Tr::tr(
|
|
"Files greater than this will not be indented by ClangFormat.\n"
|
|
"The built-in code indenter will handle indentation.");
|
|
|
|
m_projectHasClangFormat = new QLabel(this);
|
|
m_formattingModeLabel = new QLabel(Tr::tr("Formatting mode:"));
|
|
m_fileSizeThresholdLabel = new QLabel(Tr::tr("Ignore files greater than:"));
|
|
m_fileSizeThresholdLabel->setToolTip(sizeThresholdToolTip);
|
|
m_fileSizeThresholdSpinBox = new QSpinBox(this);
|
|
m_fileSizeThresholdSpinBox->setToolTip(sizeThresholdToolTip);
|
|
m_indentingOrFormatting = new QComboBox(this);
|
|
m_formatWhileTyping = new QCheckBox(Tr::tr("Format while typing"));
|
|
m_formatOnSave = new QCheckBox(Tr::tr("Format edited code on file save"));
|
|
m_useCustomSettingsCheckBox = new QCheckBox(Tr::tr("Use custom settings"));
|
|
m_useGlobalSettings = new QCheckBox(Tr::tr("Use global settings"));
|
|
m_useGlobalSettings->hide();
|
|
m_useCustomSettings = ClangFormatSettings::instance().useCustomSettings();
|
|
|
|
m_currentProjectLabel = new Utils::InfoLabel(
|
|
Tr::tr("Please note that the current project includes a .clang-format file, which will be "
|
|
"used for code indenting and formatting."),
|
|
Utils::InfoLabel::Warning);
|
|
m_currentProjectLabel->setWordWrap(true);
|
|
|
|
using namespace Layouting;
|
|
|
|
QWidget *globalSettingsGroupBoxWidget = nullptr;
|
|
|
|
// clang-format off
|
|
Group globalSettingsGroupBox {
|
|
bindTo(&globalSettingsGroupBoxWidget),
|
|
title(Tr::tr("ClangFormat Settings:")),
|
|
Column {
|
|
m_useGlobalSettings,
|
|
Form {
|
|
m_formattingModeLabel, m_indentingOrFormatting, st, br,
|
|
m_fileSizeThresholdLabel, m_fileSizeThresholdSpinBox, st, br
|
|
},
|
|
m_formatWhileTyping,
|
|
m_formatOnSave,
|
|
m_projectHasClangFormat,
|
|
m_useCustomSettingsCheckBox,
|
|
m_currentProjectLabel
|
|
}
|
|
};
|
|
|
|
Column {
|
|
globalSettingsGroupBox,
|
|
noMargin
|
|
}.attachTo(this);
|
|
// clang-format on
|
|
|
|
initCheckBoxes();
|
|
initIndentationOrFormattingCombobox();
|
|
initCustomSettingsCheckBox();
|
|
initUseGlobalSettingsCheckBox();
|
|
initFileSizeThresholdSpinBox();
|
|
initCurrentProjectLabel();
|
|
|
|
if (project) {
|
|
m_formatOnSave->hide();
|
|
m_formatWhileTyping->hide();
|
|
|
|
m_useGlobalSettings->show();
|
|
return;
|
|
}
|
|
|
|
globalSettingsGroupBoxWidget->show();
|
|
}
|
|
|
|
void ClangFormatGlobalConfigWidget::initCheckBoxes()
|
|
{
|
|
auto setEnableCheckBoxes = [this](int index) {
|
|
bool isFormatting = index == static_cast<int>(ClangFormatSettings::Mode::Formatting);
|
|
|
|
m_formatOnSave->setEnabled(isFormatting);
|
|
m_formatWhileTyping->setEnabled(isFormatting);
|
|
};
|
|
setEnableCheckBoxes(m_indentingOrFormatting->currentIndex());
|
|
connect(m_indentingOrFormatting, &QComboBox::currentIndexChanged,
|
|
this, setEnableCheckBoxes);
|
|
|
|
m_formatOnSave->setChecked(ClangFormatSettings::instance().formatOnSave());
|
|
m_formatWhileTyping->setChecked(ClangFormatSettings::instance().formatWhileTyping());
|
|
}
|
|
|
|
void ClangFormatGlobalConfigWidget::initIndentationOrFormattingCombobox()
|
|
{
|
|
m_indentingOrFormatting->insertItem(static_cast<int>(ClangFormatSettings::Mode::Indenting),
|
|
Tr::tr("Indenting only"));
|
|
m_indentingOrFormatting->insertItem(static_cast<int>(ClangFormatSettings::Mode::Formatting),
|
|
Tr::tr("Full formatting"));
|
|
m_indentingOrFormatting->insertItem(static_cast<int>(ClangFormatSettings::Mode::Disable),
|
|
Tr::tr("Use built-in indenter"));
|
|
|
|
m_indentingOrFormatting->setCurrentIndex(
|
|
static_cast<int>(getProjectIndentationOrFormattingSettings(m_project)));
|
|
|
|
connect(m_indentingOrFormatting, &QComboBox::currentIndexChanged, this, [this](int index) {
|
|
if (m_project)
|
|
m_project->setNamedSettings(Constants::MODE_ID, index);
|
|
});
|
|
}
|
|
|
|
void ClangFormatGlobalConfigWidget::initUseGlobalSettingsCheckBox()
|
|
{
|
|
if (!m_project)
|
|
return;
|
|
|
|
const auto enableProjectSettings = [this] {
|
|
const bool isDisabled = m_project && m_useGlobalSettings->isChecked();
|
|
m_indentingOrFormatting->setDisabled(isDisabled);
|
|
m_formattingModeLabel->setDisabled(isDisabled);
|
|
m_projectHasClangFormat->setDisabled(
|
|
isDisabled
|
|
|| (m_indentingOrFormatting->currentIndex()
|
|
== static_cast<int>(ClangFormatSettings::Mode::Disable)));
|
|
m_useCustomSettingsCheckBox->setDisabled(isDisabled
|
|
|| (m_indentingOrFormatting->currentIndex()
|
|
== static_cast<int>(ClangFormatSettings::Mode::Disable)));
|
|
};
|
|
|
|
m_useGlobalSettings->setChecked(getProjectUseGlobalSettings(m_project));
|
|
enableProjectSettings();
|
|
|
|
connect(m_useGlobalSettings, &QCheckBox::toggled,
|
|
this, [this, enableProjectSettings] (bool checked) {
|
|
m_project->setNamedSettings(Constants::USE_GLOBAL_SETTINGS, checked);
|
|
enableProjectSettings();
|
|
});
|
|
}
|
|
|
|
void ClangFormatGlobalConfigWidget::initFileSizeThresholdSpinBox()
|
|
{
|
|
m_fileSizeThresholdSpinBox->setMinimum(1);
|
|
m_fileSizeThresholdSpinBox->setMaximum(std::numeric_limits<int>::max());
|
|
m_fileSizeThresholdSpinBox->setSuffix(" KB");
|
|
m_fileSizeThresholdSpinBox->setValue(ClangFormatSettings::instance().fileSizeThreshold());
|
|
if (m_project) {
|
|
m_fileSizeThresholdSpinBox->hide();
|
|
m_fileSizeThresholdLabel->hide();
|
|
}
|
|
|
|
connect(m_indentingOrFormatting, &QComboBox::currentIndexChanged, this, [this](int index) {
|
|
m_fileSizeThresholdLabel->setEnabled(
|
|
index != static_cast<int>(ClangFormatSettings::Mode::Disable));
|
|
m_fileSizeThresholdSpinBox->setEnabled(
|
|
index != static_cast<int>(ClangFormatSettings::Mode::Disable));
|
|
});
|
|
}
|
|
|
|
void ClangFormatGlobalConfigWidget::initCurrentProjectLabel()
|
|
{
|
|
auto setCurrentProjectLabelVisible = [this]() {
|
|
ProjectExplorer::Project *currentProject
|
|
= m_project ? m_project : ProjectExplorer::ProjectTree::currentProject();
|
|
|
|
if (currentProject) {
|
|
Utils::FilePath settingsFilePath = currentProject->projectDirectory()
|
|
/ Constants::SETTINGS_FILE_NAME;
|
|
Utils::FilePath settingsAltFilePath = currentProject->projectDirectory()
|
|
/ Constants::SETTINGS_FILE_ALT_NAME;
|
|
|
|
if ((settingsFilePath.exists() || settingsAltFilePath.exists())
|
|
&& m_useCustomSettingsCheckBox->checkState() == Qt::CheckState::Unchecked) {
|
|
m_currentProjectLabel->show();
|
|
return;
|
|
}
|
|
}
|
|
m_currentProjectLabel->hide();
|
|
};
|
|
setCurrentProjectLabelVisible();
|
|
connect(m_useCustomSettingsCheckBox, &QCheckBox::toggled, this, setCurrentProjectLabelVisible);
|
|
}
|
|
|
|
bool ClangFormatGlobalConfigWidget::projectClangFormatFileExists()
|
|
{
|
|
llvm::Expected<clang::format::FormatStyle> styleFromProjectFolder
|
|
= clang::format::getStyle("file", m_project->projectFilePath().path().toStdString(), "none");
|
|
|
|
return styleFromProjectFolder && !(*styleFromProjectFolder == clang::format::getNoStyle());
|
|
}
|
|
|
|
void ClangFormatGlobalConfigWidget::initCustomSettingsCheckBox()
|
|
{
|
|
if (!m_project || !projectClangFormatFileExists()) {
|
|
m_projectHasClangFormat->hide();
|
|
} else {
|
|
m_projectHasClangFormat->show();
|
|
m_projectHasClangFormat->setText(Tr::tr("The current project has its own .clang-format file which "
|
|
"can be overridden by the settings below."));
|
|
}
|
|
|
|
auto setTemporarilyReadOnly = [this]() {
|
|
if (m_ignoreChanges.isLocked())
|
|
return;
|
|
Utils::GuardLocker locker(m_ignoreChanges);
|
|
m_codeStyle->currentPreferences()->setTemporarilyReadOnly(
|
|
!m_useCustomSettingsCheckBox->isChecked());
|
|
m_codeStyle->currentPreferences()->setIsAdditionalTabVisible(
|
|
m_useCustomSettingsCheckBox->isEnabled());
|
|
m_codeStyle->currentPreferences()->setAdditionalTabExist(true);
|
|
ClangFormatSettings::instance().write();
|
|
emit m_codeStyle->currentPreferencesChanged(m_codeStyle->currentPreferences());
|
|
};
|
|
|
|
auto setEnableCustomSettingsCheckBox = [this, setTemporarilyReadOnly](int index) {
|
|
bool isDisable = index == static_cast<int>(ClangFormatSettings::Mode::Disable);
|
|
m_useCustomSettingsCheckBox->setDisabled(isDisable);
|
|
m_projectHasClangFormat->setDisabled(isDisable);
|
|
setTemporarilyReadOnly();
|
|
};
|
|
|
|
setEnableCustomSettingsCheckBox(m_indentingOrFormatting->currentIndex());
|
|
connect(m_indentingOrFormatting, &QComboBox::currentIndexChanged,
|
|
this, setEnableCustomSettingsCheckBox);
|
|
|
|
m_useCustomSettingsCheckBox->setToolTip("<html>"
|
|
+ Tr::tr("When this option is enabled, ClangFormat will use a "
|
|
"user-specified configuration from the widget below, "
|
|
"instead of the project .clang-format file. You can "
|
|
"customize the formatting options for your code by "
|
|
"adjusting the settings in the widget. Note that any "
|
|
"changes made there will only affect the current "
|
|
"configuration, and will not modify the project "
|
|
".clang-format file."));
|
|
|
|
m_useCustomSettingsCheckBox->setChecked(getProjectCustomSettings(m_project));
|
|
setTemporarilyReadOnly();
|
|
|
|
connect(m_useCustomSettingsCheckBox,
|
|
&QCheckBox::toggled,
|
|
this,
|
|
[this, setTemporarilyReadOnly](bool checked) {
|
|
if (m_project) {
|
|
m_project->setNamedSettings(Constants::USE_CUSTOM_SETTINGS_ID, checked);
|
|
} else {
|
|
ClangFormatSettings::instance().setUseCustomSettings(checked);
|
|
setTemporarilyReadOnly();
|
|
}
|
|
});
|
|
|
|
connect(m_codeStyle,
|
|
&TextEditor::ICodeStylePreferences::currentPreferencesChanged,
|
|
this,
|
|
setTemporarilyReadOnly);
|
|
}
|
|
|
|
|
|
void ClangFormatGlobalConfigWidget::apply()
|
|
{
|
|
ClangFormatSettings &settings = ClangFormatSettings::instance();
|
|
settings.setFormatOnSave(m_formatOnSave->isChecked());
|
|
settings.setFormatWhileTyping(m_formatWhileTyping->isChecked());
|
|
if (!m_project) {
|
|
settings.setMode(
|
|
static_cast<ClangFormatSettings::Mode>(m_indentingOrFormatting->currentIndex()));
|
|
settings.setUseCustomSettings(m_useCustomSettingsCheckBox->isChecked());
|
|
settings.setFileSizeThreshold(m_fileSizeThresholdSpinBox->value());
|
|
m_useCustomSettings = m_useCustomSettingsCheckBox->isChecked();
|
|
}
|
|
settings.write();
|
|
}
|
|
|
|
void ClangFormatGlobalConfigWidget::finish()
|
|
{
|
|
ClangFormatSettings::instance().setUseCustomSettings(m_useCustomSettings);
|
|
m_codeStyle->currentPreferences()->setTemporarilyReadOnly(
|
|
!ClangFormatSettings::instance().useCustomSettings());
|
|
}
|
|
|
|
class ClangFormatStyleFactory final : public CppCodeStylePreferencesFactory
|
|
{
|
|
public:
|
|
Indenter *createIndenter(QTextDocument *doc) const final
|
|
{
|
|
return new ClangFormatForwardingIndenter(doc);
|
|
}
|
|
|
|
std::pair<TextEditor::CodeStyleEditorWidget *, QString> additionalTab(
|
|
ICodeStylePreferences *codeStyle, Project *project, QWidget *parent) const final
|
|
{
|
|
return {createClangFormatConfigWidget(codeStyle, project, parent), Tr::tr("ClangFormat")};
|
|
}
|
|
|
|
CodeStyleEditorWidget *createAdditionalGlobalSettings(
|
|
ICodeStylePreferences *codeStyle, Project *project, QWidget *parent) final
|
|
{
|
|
return new ClangFormatGlobalConfigWidget(codeStyle, project, parent);
|
|
}
|
|
};
|
|
|
|
void setupClangFormatStyleFactory(QObject *guard)
|
|
{
|
|
static ClangFormatStyleFactory theClangFormatStyleFactory;
|
|
|
|
// Replace the default one.
|
|
TextEditorSettings::unregisterCodeStyleFactory(CppEditor::Constants::CPP_SETTINGS_ID);
|
|
TextEditorSettings::registerCodeStyleFactory(&theClangFormatStyleFactory);
|
|
|
|
QObject::connect(guard, &QObject::destroyed, [] {
|
|
TextEditorSettings::unregisterCodeStyleFactory(CppEditor::Constants::CPP_SETTINGS_ID);
|
|
});
|
|
}
|
|
|
|
} // namespace ClangFormat
|