diff --git a/src/plugins/clangformat/clangformatconfigwidget.cpp b/src/plugins/clangformat/clangformatconfigwidget.cpp index faaf6d937ff..27192a67f64 100644 --- a/src/plugins/clangformat/clangformatconfigwidget.cpp +++ b/src/plugins/clangformat/clangformatconfigwidget.cpp @@ -59,6 +59,24 @@ ClangFormatConfigWidget::ClangFormatConfigWidget(ProjectExplorer::Project *proje { m_ui->setupUi(this); + m_preview = new TextEditor::SnippetEditorWidget(this); + m_ui->horizontalLayout_2->addWidget(m_preview); + if (m_project) { + m_ui->applyButton->show(); + hideGlobalCheckboxes(); + m_ui->overrideDefault->setChecked( + m_project->namedSettings(Constants::OVERRIDE_FILE_ID).toBool()); + } else { + m_ui->applyButton->hide(); + showGlobalCheckboxes(); + m_ui->overrideDefault->setChecked(ClangFormatSettings::instance().overrideDefaultFile()); + } + + connect(m_ui->overrideDefault, &QCheckBox::toggled, this, [this](bool checked) { + if (checked) + createStyleFileIfNeeded(!m_project); + initialize(); + }); initialize(); } @@ -81,15 +99,18 @@ void ClangFormatConfigWidget::showGlobalCheckboxes() m_ui->formatOnSave->show(); } +static bool projectConfigExists() +{ + return Utils::FileName::fromString(Core::ICore::userResourcePath()) + .appendPath(currentProjectUniqueId()) + .appendPath((Constants::SETTINGS_FILE_NAME)) + .exists(); +} + void ClangFormatConfigWidget::initialize() { - m_ui->projectHasClangFormat->show(); - m_ui->clangFormatOptionsTable->show(); - m_ui->applyButton->show(); - hideGlobalCheckboxes(); + m_ui->projectHasClangFormat->hide(); - m_preview = new TextEditor::SnippetEditorWidget(this); - m_ui->horizontalLayout_2->addWidget(m_preview); m_preview->setPlainText(QLatin1String(CppTools::Constants::DEFAULT_CODE_STYLE_SNIPPETS[0])); m_preview->textDocument()->setIndenter(new ClangFormatIndenter(m_preview->document())); m_preview->textDocument()->setFontSettings(TextEditor::TextEditorSettings::fontSettings()); @@ -103,21 +124,15 @@ void ClangFormatConfigWidget::initialize() if (lastItem->spacerItem()) m_ui->verticalLayout->removeItem(lastItem); - if (m_project - && !m_project->projectDirectory().appendPath(Constants::SETTINGS_FILE_NAME).exists()) { - m_ui->projectHasClangFormat->setText(tr("No .clang-format file for the project.")); + if (!m_ui->overrideDefault->isChecked()) { m_ui->clangFormatOptionsTable->hide(); - m_ui->applyButton->hide(); + m_preview->hide(); m_ui->verticalLayout->addStretch(1); - - connect(m_ui->createFileButton, &QPushButton::clicked, this, [this]() { - createStyleFileIfNeeded(false); - initialize(); - }); return; } - m_ui->createFileButton->hide(); + m_ui->clangFormatOptionsTable->show(); + m_preview->show(); Utils::FileName fileName; if (m_project) { @@ -126,19 +141,13 @@ void ClangFormatConfigWidget::initialize() fileName = m_project->projectFilePath().appendPath("snippet.cpp"); } else { const Project *currentProject = SessionManager::startupProject(); - if (!currentProject - || !currentProject->projectDirectory() - .appendPath(Constants::SETTINGS_FILE_NAME) - .exists()) { + if (!currentProject || !projectConfigExists()) { m_ui->projectHasClangFormat->hide(); } else { m_ui->projectHasClangFormat->setText( - tr("Current project has its own .clang-format file " + tr("Current project has its own overridden .clang-format file " "and can be configured in Projects > Code Style > C++.")); } - createStyleFileIfNeeded(true); - showGlobalCheckboxes(); - m_ui->applyButton->hide(); fileName = Utils::FileName::fromString(Core::ICore::userResourcePath()) .appendPath("snippet.cpp"); } @@ -160,7 +169,7 @@ void ClangFormatConfigWidget::fillTable() { clang::format::FormatStyle style = m_project ? currentProjectStyle() : currentGlobalStyle(); - std::string configText = clang::format::configurationAsText(style); + const std::string configText = clang::format::configurationAsText(style); m_ui->clangFormatOptionsTable->setPlainText(QString::fromStdString(configText)); } @@ -168,13 +177,16 @@ ClangFormatConfigWidget::~ClangFormatConfigWidget() = default; void ClangFormatConfigWidget::apply() { + ClangFormatSettings &settings = ClangFormatSettings::instance(); if (!m_project) { - ClangFormatSettings &settings = ClangFormatSettings::instance(); settings.setFormatCodeInsteadOfIndent(m_ui->formatAlways->isChecked()); settings.setFormatWhileTyping(m_ui->formatWhileTyping->isChecked()); settings.setFormatOnSave(m_ui->formatOnSave->isChecked()); - settings.write(); + settings.setOverrideDefaultFile(m_ui->overrideDefault->isChecked()); + } else { + m_project->setNamedSettings(Constants::OVERRIDE_FILE_ID, m_ui->overrideDefault->isChecked()); } + settings.write(); const QString text = m_ui->clangFormatOptionsTable->toPlainText(); clang::format::FormatStyle style; @@ -184,16 +196,18 @@ void ClangFormatConfigWidget::apply() QMessageBox::warning(this, tr("Error in ClangFormat configuration"), QString::fromStdString(error.message())); - fillTable(); - updatePreview(); + if (m_ui->overrideDefault->isChecked()) { + fillTable(); + updatePreview(); + } return; } - QString filePath; + QString filePath = Core::ICore::userResourcePath(); if (m_project) - filePath = m_project->projectDirectory().appendPath(Constants::SETTINGS_FILE_NAME).toString(); - else - filePath = Core::ICore::userResourcePath() + "/" + Constants::SETTINGS_FILE_NAME; + filePath += "/" + currentProjectUniqueId(); + filePath += "/" + QLatin1String(Constants::SETTINGS_FILE_NAME); + QFile file(filePath); if (!file.open(QFile::WriteOnly)) return; @@ -201,7 +215,8 @@ void ClangFormatConfigWidget::apply() file.write(text.toUtf8()); file.close(); - updatePreview(); + if (m_ui->overrideDefault->isChecked()) + updatePreview(); } } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatconfigwidget.ui b/src/plugins/clangformat/clangformatconfigwidget.ui index 41e40ef1fdc..c8225981f59 100644 --- a/src/plugins/clangformat/clangformatconfigwidget.ui +++ b/src/plugins/clangformat/clangformatconfigwidget.ui @@ -54,6 +54,13 @@ + + + + Override Clang Format configuration file + + + @@ -63,13 +70,6 @@ - - - - Create Clang Format Configuration File - - - diff --git a/src/plugins/clangformat/clangformatconstants.h b/src/plugins/clangformat/clangformatconstants.h index 8b7cbcf9bd2..65ea9b6e81f 100644 --- a/src/plugins/clangformat/clangformatconstants.h +++ b/src/plugins/clangformat/clangformatconstants.h @@ -34,5 +34,6 @@ static const char SETTINGS_ID[] = "ClangFormat"; static const char FORMAT_CODE_INSTEAD_OF_INDENT_ID[] = "ClangFormat.FormatCodeInsteadOfIndent"; static const char FORMAT_WHILE_TYPING_ID[] = "ClangFormat.FormatWhileTyping"; static const char FORMAT_CODE_ON_SAVE_ID[] = "ClangFormat.FormatCodeOnSave"; +static const char OVERRIDE_FILE_ID[] = "ClangFormat.OverrideFile"; } // namespace Constants } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatindenter.cpp b/src/plugins/clangformat/clangformatindenter.cpp index 42cedb7b6c9..381762ee7f2 100644 --- a/src/plugins/clangformat/clangformatindenter.cpp +++ b/src/plugins/clangformat/clangformatindenter.cpp @@ -57,7 +57,7 @@ bool ClangFormatIndenter::formatWhileTyping() const Utils::optional ClangFormatIndenter::tabSettings() const { - FormatStyle style = currentProjectStyle(); + FormatStyle style = styleForFile(); TabSettings tabSettings; switch (style.UseTab) { diff --git a/src/plugins/clangformat/clangformatsettings.cpp b/src/plugins/clangformat/clangformatsettings.cpp index 8be2b3cefee..8f70130ae3a 100644 --- a/src/plugins/clangformat/clangformatsettings.cpp +++ b/src/plugins/clangformat/clangformatsettings.cpp @@ -46,6 +46,8 @@ ClangFormatSettings::ClangFormatSettings() .toBool(); m_formatOnSave = settings->value(QLatin1String(Constants::FORMAT_CODE_ON_SAVE_ID), false) .toBool(); + m_overrideDefaultFile = settings->value(QLatin1String(Constants::OVERRIDE_FILE_ID), false) + .toBool(); settings->endGroup(); } @@ -57,6 +59,7 @@ void ClangFormatSettings::write() const m_formatCodeInsteadOfIndent); settings->setValue(QLatin1String(Constants::FORMAT_WHILE_TYPING_ID), m_formatWhileTyping); settings->setValue(QLatin1String(Constants::FORMAT_CODE_ON_SAVE_ID), m_formatOnSave); + settings->setValue(QLatin1String(Constants::OVERRIDE_FILE_ID), m_overrideDefaultFile); settings->endGroup(); } @@ -90,4 +93,14 @@ bool ClangFormatSettings::formatOnSave() const return m_formatOnSave; } +void ClangFormatSettings::setOverrideDefaultFile(bool enable) +{ + m_overrideDefaultFile = enable; +} + +bool ClangFormatSettings::overrideDefaultFile() const +{ + return m_overrideDefaultFile; +} + } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatsettings.h b/src/plugins/clangformat/clangformatsettings.h index 0ea9ed9747e..9344cf34a96 100644 --- a/src/plugins/clangformat/clangformatsettings.h +++ b/src/plugins/clangformat/clangformatsettings.h @@ -25,6 +25,8 @@ #pragma once +#include + namespace ClangFormat { class ClangFormatSettings @@ -43,10 +45,14 @@ public: void setFormatOnSave(bool enable); bool formatOnSave() const; + + void setOverrideDefaultFile(bool enable); + bool overrideDefaultFile() const; private: bool m_formatCodeInsteadOfIndent = false; bool m_formatWhileTyping = false; bool m_formatOnSave = false; + bool m_overrideDefaultFile = false; }; } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatutils.cpp b/src/plugins/clangformat/clangformatutils.cpp index babe94da24f..36bcf2f97d0 100644 --- a/src/plugins/clangformat/clangformatutils.cpp +++ b/src/plugins/clangformat/clangformatutils.cpp @@ -26,6 +26,7 @@ #include "clangformatutils.h" #include "clangformatconstants.h" +#include "clangformatsettings.h" #include #include @@ -33,6 +34,8 @@ #include #include +#include + using namespace clang; using namespace format; using namespace llvm; @@ -92,13 +95,26 @@ static void applyCppCodeStyleSettings(clang::format::FormatStyle &style, : - static_cast(style.IndentWidth); } -static Utils::FileName projectPath() +static bool useGlobalOverriddenSettings() +{ + return ClangFormatSettings::instance().overrideDefaultFile(); +} + +QString currentProjectUniqueId() { const Project *project = SessionManager::startupProject(); - if (project) - return project->projectDirectory(); + if (!project) + return QString(); - return Utils::FileName(); + return QString::fromUtf8(QCryptographicHash::hash(project->projectFilePath().toString().toUtf8(), + QCryptographicHash::Md5) + .toHex(0)); +} + +static bool useProjectOverriddenSettings() +{ + const Project *project = SessionManager::startupProject(); + return project ? project->namedSettings(Constants::OVERRIDE_FILE_ID).toBool() : false; } static Utils::FileName globalPath() @@ -106,25 +122,40 @@ static Utils::FileName globalPath() return Utils::FileName::fromString(Core::ICore::userResourcePath()); } -static QString configForFile(Utils::FileName fileName) +static Utils::FileName projectPath() { - Utils::FileName topProjectPath = projectPath(); - if (topProjectPath.isEmpty()) - return QString(); + const Project *project = SessionManager::startupProject(); + if (project) + return globalPath().appendPath("clang-format").appendPath(currentProjectUniqueId()); - QDir projectDir(fileName.parentDir().toString()); - while (!projectDir.exists(Constants::SETTINGS_FILE_NAME) - && !projectDir.exists(Constants::SETTINGS_FILE_ALT_NAME)) { - if (projectDir.path() == topProjectPath.toString() - || !Utils::FileName::fromString(projectDir.path()).isChildOf(topProjectPath) - || !projectDir.cdUp()) { - return QString(); - } + return Utils::FileName(); +} + +static QString configForFile(Utils::FileName fileName, bool checkForSettings) +{ + QDir overrideDir; + if (!checkForSettings || useProjectOverriddenSettings()) { + overrideDir = projectPath().toString(); + if (!overrideDir.isEmpty() && overrideDir.exists(Constants::SETTINGS_FILE_NAME)) + return overrideDir.filePath(Constants::SETTINGS_FILE_NAME); } - if (projectDir.exists(Constants::SETTINGS_FILE_NAME)) - return projectDir.filePath(Constants::SETTINGS_FILE_NAME); - return projectDir.filePath(Constants::SETTINGS_FILE_ALT_NAME); + if (!checkForSettings || useGlobalOverriddenSettings()) { + overrideDir = globalPath().toString(); + if (!overrideDir.isEmpty() && overrideDir.exists(Constants::SETTINGS_FILE_NAME)) + return overrideDir.filePath(Constants::SETTINGS_FILE_NAME); + } + + QDir parentDir(fileName.parentDir().toString()); + while (!parentDir.exists(Constants::SETTINGS_FILE_NAME) + && !parentDir.exists(Constants::SETTINGS_FILE_ALT_NAME)) { + if (!parentDir.cdUp()) + return QString(); + } + + if (parentDir.exists(Constants::SETTINGS_FILE_NAME)) + return parentDir.filePath(Constants::SETTINGS_FILE_NAME); + return parentDir.filePath(Constants::SETTINGS_FILE_ALT_NAME); } static clang::format::FormatStyle constructStyle(bool isGlobal, @@ -169,6 +200,7 @@ void createStyleFileIfNeeded(bool isGlobal) if (QFile::exists(configFile)) return; + QDir().mkpath(path.parentDir().toString()); std::fstream newStyleFile(configFile.toStdString(), std::fstream::out); if (newStyleFile.is_open()) { newStyleFile << clang::format::configurationAsText(constructStyle(isGlobal)); @@ -198,16 +230,11 @@ static QByteArray configBaseStyleName(const QString &configFile) .trimmed(); } -clang::format::FormatStyle styleForFile(Utils::FileName fileName) +static clang::format::FormatStyle styleForFile(Utils::FileName fileName, bool checkForSettings) { - bool isGlobal = false; - QString configFile = configForFile(fileName); - if (configFile.isEmpty()) { - Utils::FileName path = fileName = globalPath(); - fileName.appendPath(Constants::SAMPLE_FILE_NAME); - createStyleFileIfNeeded(true); - configFile = path.appendPath(Constants::SETTINGS_FILE_NAME).toString(); - } + QString configFile = configForFile(fileName, checkForSettings); + if (configFile.isEmpty()) + return constructStyle(true); Expected style = format::getStyle("file", fileName.toString().toStdString(), @@ -219,17 +246,22 @@ clang::format::FormatStyle styleForFile(Utils::FileName fileName) // do nothing }); - return constructStyle(isGlobal, configBaseStyleName(configFile)); + return constructStyle(true, configBaseStyleName(configFile)); +} + +clang::format::FormatStyle styleForFile(Utils::FileName fileName) +{ + return styleForFile(fileName, true); } clang::format::FormatStyle currentProjectStyle() { - return styleForFile(projectPath().appendPath(Constants::SAMPLE_FILE_NAME)); + return styleForFile(projectPath().appendPath(Constants::SAMPLE_FILE_NAME), false); } clang::format::FormatStyle currentGlobalStyle() { - return styleForFile(globalPath().appendPath(Constants::SAMPLE_FILE_NAME)); + return styleForFile(globalPath().appendPath(Constants::SAMPLE_FILE_NAME), false); } } diff --git a/src/plugins/clangformat/clangformatutils.h b/src/plugins/clangformat/clangformatutils.h index 41e2b5611fa..2c818ffe6aa 100644 --- a/src/plugins/clangformat/clangformatutils.h +++ b/src/plugins/clangformat/clangformatutils.h @@ -25,6 +25,7 @@ #pragma once +#include #include #include @@ -37,6 +38,8 @@ namespace ClangFormat { // Creates the style for the current project or the global style if needed. void createStyleFileIfNeeded(bool isGlobal); +QString currentProjectUniqueId(); + clang::format::FormatStyle currentProjectStyle(); clang::format::FormatStyle currentGlobalStyle();