Files
qt-creator/src/plugins/clangformat/clangformatglobalconfigwidget.cpp
Artem Sokolovskii 6db8067821 ClangFormat: Fix warning readability message on dark theme
Fixes: QTCREATORBUG-30339
Change-Id: I57291907656b379468a066a09e767b886f17d7a0
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: David Schulz <david.schulz@qt.io>
2024-02-12 08:46:26 +00:00

384 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 <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;
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);
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());
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