diff --git a/src/libs/utils/codegeneration.cpp b/src/libs/utils/codegeneration.cpp index 8842023a239..ad3b8bad18f 100644 --- a/src/libs/utils/codegeneration.cpp +++ b/src/libs/utils/codegeneration.cpp @@ -29,23 +29,6 @@ QTCREATOR_UTILS_EXPORT QString fileNameToCppIdentifier(const QString &s) return rc; } -QTCREATOR_UTILS_EXPORT QString headerGuard(const QString &file) -{ - return headerGuard(file, QStringList()); -} - -QTCREATOR_UTILS_EXPORT QString headerGuard(const QString &file, const QStringList &namespaceList) -{ - const QChar underscore = QLatin1Char('_'); - QString rc; - for (int i = 0; i < namespaceList.count(); i++) - rc += namespaceList.at(i).toUpper() + underscore; - - const QFileInfo fi(file); - rc += fileNameToCppIdentifier(fi.fileName()).toUpper(); - return rc; -} - QTCREATOR_UTILS_EXPORT void writeIncludeFileDirective(const QString &file, bool globalInclude, QTextStream &str) diff --git a/src/libs/utils/codegeneration.h b/src/libs/utils/codegeneration.h index 9ea0bb28c7e..e1c297eb92c 100644 --- a/src/libs/utils/codegeneration.h +++ b/src/libs/utils/codegeneration.h @@ -17,9 +17,6 @@ namespace Utils { // or replacing them by an underscore). QTCREATOR_UTILS_EXPORT QString fileNameToCppIdentifier(const QString &s); -QTCREATOR_UTILS_EXPORT QString headerGuard(const QString &file); -QTCREATOR_UTILS_EXPORT QString headerGuard(const QString &file, const QStringList &namespaceList); - QTCREATOR_UTILS_EXPORT void writeIncludeFileDirective(const QString &file, bool globalInclude, diff --git a/src/plugins/clangtools/clangtoolrunner.cpp b/src/plugins/clangtools/clangtoolrunner.cpp index c91d9e9cfa7..08858a0498e 100644 --- a/src/plugins/clangtools/clangtoolrunner.cpp +++ b/src/plugins/clangtools/clangtoolrunner.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include diff --git a/src/plugins/cppeditor/cppeditorplugin.cpp b/src/plugins/cppeditor/cppeditorplugin.cpp index 63122a538b3..bd8d7331711 100644 --- a/src/plugins/cppeditor/cppeditorplugin.cpp +++ b/src/plugins/cppeditor/cppeditorplugin.cpp @@ -227,7 +227,7 @@ void CppEditorPlugin::initialize() void CppEditorPlugin::extensionsInitialized() { setupCppQuickFixProjectPanel(); - setupCppFileSettings(); + setupCppFileSettings(*this); setupCppCodeModelProjectSettingsPanel(); if (CppModelManager::isClangCodeModelActive()) { diff --git a/src/plugins/cppeditor/cppfilesettingspage.cpp b/src/plugins/cppeditor/cppfilesettingspage.cpp index 3579e0b5f21..a5c01b400b1 100644 --- a/src/plugins/cppeditor/cppfilesettingspage.cpp +++ b/src/plugins/cppeditor/cppfilesettingspage.cpp @@ -6,15 +6,19 @@ #include "cppeditortr.h" #include "cppheadersource.h" +#include + #include #include #include #include +#include #include #include #include +#include #include #include #include @@ -29,10 +33,30 @@ #include #include +#ifdef WITH_TESTS +#include +#endif + using namespace ProjectExplorer; using namespace Utils; namespace CppEditor::Internal { +namespace { +class HeaderGuardExpander : public MacroExpander +{ +public: + HeaderGuardExpander(const FilePath &filePath) : m_filePath(filePath) + { + setDisplayName(Tr::tr("Header file variables")); + registerFileVariables("Header", Tr::tr("Header file"), [this] { + return m_filePath; + }); + } + +private: + const FilePath m_filePath; +}; +} // namespace const char projectSettingsKeyC[] = "CppEditorFileNames"; const char useGlobalKeyC[] = "UseGlobal"; @@ -44,6 +68,7 @@ const char headerSearchPathsKeyC[] = "HeaderSearchPaths"; const char sourceSearchPathsKeyC[] = "SourceSearchPaths"; const char headerPragmaOnceC[] = "HeaderPragmaOnce"; const char licenseTemplatePathKeyC[] = "LicenseTemplate"; +const char headerGuardTemplateKeyC[] = "HeaderGuardTemplate"; const char *licenseTemplateTemplate = QT_TRANSLATE_NOOP("QtC::CppEditor", "/**************************************************************************\n" @@ -67,6 +92,7 @@ void CppFileSettings::toSettings(QtcSettings *s) const s->setValueWithDefault(headerPragmaOnceC, headerPragmaOnce, def.headerPragmaOnce); s->setValueWithDefault(licenseTemplatePathKeyC, licenseTemplatePath.toSettings(), def.licenseTemplatePath.toSettings()); + s->setValueWithDefault(headerGuardTemplateKeyC, headerGuardTemplate, def.headerGuardTemplate); s->endGroup(); } @@ -84,6 +110,7 @@ void CppFileSettings::fromSettings(QtcSettings *s) headerPragmaOnce = s->value(headerPragmaOnceC, def.headerPragmaOnce).toBool(); licenseTemplatePath = FilePath::fromSettings(s->value(licenseTemplatePathKeyC, def.licenseTemplatePath.toSettings())); + headerGuardTemplate = s->value(headerGuardTemplateKeyC, def.headerGuardTemplate).toString(); s->endGroup(); } @@ -124,6 +151,7 @@ bool CppFileSettings::equals(const CppFileSettings &rhs) const && sourceSuffix == rhs.sourceSuffix && headerSearchPaths == rhs.headerSearchPaths && sourceSearchPaths == rhs.sourceSearchPaths + && headerGuardTemplate == rhs.headerGuardTemplate && licenseTemplatePath == rhs.licenseTemplatePath; } @@ -237,6 +265,11 @@ QString CppFileSettings::licenseTemplate() const return license; } +QString CppFileSettings::headerGuard(const Utils::FilePath &headerFilePath) const +{ + return HeaderGuardExpander(headerFilePath).expand(headerGuardTemplate); +} + // ------------------ CppFileSettingsWidget class CppFileSettingsWidget final : public Core::IOptionsPageWidget @@ -269,6 +302,8 @@ private: QLineEdit *m_sourcePrefixesEdit = nullptr; QCheckBox *m_lowerCaseFileNamesCheckBox = nullptr; PathChooser *m_licenseTemplatePathChooser = nullptr; + StringAspect m_headerGuardAspect; + HeaderGuardExpander m_headerGuardExpander{{}}; }; CppFileSettingsWidget::CppFileSettingsWidget(CppFileSettings *settings) @@ -276,7 +311,7 @@ CppFileSettingsWidget::CppFileSettingsWidget(CppFileSettings *settings) , m_headerSuffixComboBox(new QComboBox) , m_headerSearchPathsEdit(new QLineEdit) , m_headerPrefixesEdit(new QLineEdit) - , m_headerPragmaOnceCheckBox(new QCheckBox(Tr::tr("Use \"#pragma once\" instead of \"#ifndef\" guards"))) + , m_headerPragmaOnceCheckBox(new QCheckBox(Tr::tr("Use \"#pragma once\" instead"))) , m_sourceSuffixComboBox(new QComboBox) , m_sourceSearchPathsEdit(new QLineEdit) , m_sourcePrefixesEdit(new QLineEdit) @@ -301,6 +336,8 @@ CppFileSettingsWidget::CppFileSettingsWidget(CppFileSettings *settings) m_sourcePrefixesEdit->setToolTip(Tr::tr("Comma-separated list of source prefixes.\n" "\n" "These prefixes are used in addition to current file name on Switch Header/Source.")); + m_headerGuardAspect.setDisplayStyle(Utils::StringAspect::LineEditDisplay); + m_headerGuardAspect.setMacroExpander(&m_headerGuardExpander); using namespace Layouting; @@ -311,8 +348,8 @@ CppFileSettingsWidget::CppFileSettingsWidget(CppFileSettings *settings) Tr::tr("&Suffix:"), m_headerSuffixComboBox, st, br, Tr::tr("S&earch paths:"), m_headerSearchPathsEdit, br, Tr::tr("&Prefixes:"), m_headerPrefixesEdit, br, - Tr::tr("Include guards"), m_headerPragmaOnceCheckBox - } + Tr::tr("Include guard template:"), m_headerPragmaOnceCheckBox, m_headerGuardAspect + }, }, Group { title(Tr::tr("Sources")), @@ -361,12 +398,21 @@ CppFileSettingsWidget::CppFileSettingsWidget(CppFileSettings *settings) this, &CppFileSettingsWidget::userChange); connect(m_sourcePrefixesEdit, &QLineEdit::textEdited, this, &CppFileSettingsWidget::userChange); - connect(m_headerPragmaOnceCheckBox, &QCheckBox::stateChanged, - this, &CppFileSettingsWidget::userChange); connect(m_lowerCaseFileNamesCheckBox, &QCheckBox::stateChanged, this, &CppFileSettingsWidget::userChange); connect(m_licenseTemplatePathChooser, &PathChooser::textChanged, this, &CppFileSettingsWidget::userChange); + const auto updateHeaderGuardAspectState = [this] { + m_headerGuardAspect.setEnabled(!m_headerPragmaOnceCheckBox->isChecked()); + }; + connect(m_headerPragmaOnceCheckBox, &QCheckBox::stateChanged, + this, [this, updateHeaderGuardAspectState] { + updateHeaderGuardAspectState(); + emit userChange(); + }); + connect(&m_headerGuardAspect, &StringAspect::changed, + this, &CppFileSettingsWidget::userChange); + updateHeaderGuardAspectState(); } FilePath CppFileSettingsWidget::licenseTemplatePath() const @@ -417,6 +463,7 @@ void CppFileSettingsWidget::setSettings(const CppFileSettings &s) m_headerSearchPathsEdit->setText(s.headerSearchPaths.join(comma)); m_sourceSearchPathsEdit->setText(s.sourceSearchPaths.join(comma)); setLicenseTemplatePath(s.licenseTemplatePath); + m_headerGuardAspect.setValue(s.headerGuardTemplate); } CppFileSettings CppFileSettingsWidget::currentSettings() const @@ -431,6 +478,7 @@ CppFileSettings CppFileSettingsWidget::currentSettings() const rc.headerSearchPaths = trimmedPaths(m_headerSearchPathsEdit->text()); rc.sourceSearchPaths = trimmedPaths(m_sourceSearchPathsEdit->text()); rc.licenseTemplatePath = licenseTemplatePath(); + rc.headerGuardTemplate = m_headerGuardAspect.value(); return rc; } @@ -536,6 +584,8 @@ void CppFileSettingsForProject::loadSettings() m_customSettings.lowerCaseFiles).toBool(); m_customSettings.headerPragmaOnce = data.value(headerPragmaOnceC, m_customSettings.headerPragmaOnce).toBool(); + m_customSettings.headerGuardTemplate + = data.value(headerGuardTemplateKeyC, m_customSettings.headerGuardTemplate).toString(); m_customSettings.licenseTemplatePath = FilePath::fromSettings(data.value(licenseTemplatePathKeyC, m_customSettings.licenseTemplatePath.toSettings())); @@ -560,6 +610,7 @@ void CppFileSettingsForProject::saveSettings() data.insert(sourceSearchPathsKeyC, m_customSettings.sourceSearchPaths); data.insert(Constants::LOWERCASE_CPPFILES_KEY, m_customSettings.lowerCaseFiles); data.insert(headerPragmaOnceC, m_customSettings.headerPragmaOnce); + data.insert(headerGuardTemplateKeyC, m_customSettings.headerGuardTemplate); data.insert(licenseTemplatePathKeyC, m_customSettings.licenseTemplatePath.toSettings()); m_project->setNamedSettings(projectSettingsKeyC, data); } @@ -631,16 +682,6 @@ public: } }; -void setupCppFileSettings() -{ - static CppFileSettingsProjectPanelFactory theCppFileSettingsProjectPanelFactory; - - static CppFileSettingsPage theCppFileSettingsPage; - - globalCppFileSettings().fromSettings(Core::ICore::settings()); - globalCppFileSettings().addMimeInitializer(); -} - CppFileSettings &globalCppFileSettings() { // This is the global instance. There could be more. @@ -653,6 +694,59 @@ CppFileSettings cppFileSettingsForProject(ProjectExplorer::Project *project) return CppFileSettingsForProject(project).settings(); } +#ifdef WITH_TESTS +namespace { +class CppFileSettingsTest : public QObject +{ + Q_OBJECT + +private slots: + void testHeaderGuard_data() + { + QTest::addColumn("guardTemplate"); + QTest::addColumn("headerFile"); + QTest::addColumn("expectedGuard"); + + QTest::newRow("default template, .h") + << QString() << QString("/tmp/header.h") << QString("HEADER_H"); + QTest::newRow("default template, .hpp") + << QString() << QString("/tmp/header.hpp") << QString("HEADER_HPP"); + QTest::newRow("default template, two extensions") + << QString() << QString("/tmp/header.in.h") << QString("HEADER_IN_H"); + QTest::newRow("non-default template") + << QString("%{JS: '%{Header:FilePath}'.toUpperCase().replace(/[.]/, '_').replace(/[/]/g, '_')}") + << QString("/tmp/header.h") << QString("_TMP_HEADER_H"); + } + + void testHeaderGuard() + { + QFETCH(QString, guardTemplate); + QFETCH(QString, headerFile); + QFETCH(QString, expectedGuard); + + CppFileSettings settings; + if (!guardTemplate.isEmpty()) + settings.headerGuardTemplate = guardTemplate; + QCOMPARE(settings.headerGuard(FilePath::fromUserInput(headerFile)), expectedGuard); + } +}; +} // namespace +#endif // WITH_TESTS + +void setupCppFileSettings(ExtensionSystem::IPlugin &plugin) +{ + static CppFileSettingsProjectPanelFactory theCppFileSettingsProjectPanelFactory; + + static CppFileSettingsPage theCppFileSettingsPage; + + globalCppFileSettings().fromSettings(Core::ICore::settings()); + globalCppFileSettings().addMimeInitializer(); + +#ifdef WITH_TESTS + plugin.addTestCreator([] { return new CppFileSettingsTest; }); +#endif +} + } // namespace CppEditor::Internal #include diff --git a/src/plugins/cppeditor/cppfilesettingspage.h b/src/plugins/cppeditor/cppfilesettingspage.h index 5915b5383cf..044ac6fb450 100644 --- a/src/plugins/cppeditor/cppfilesettingspage.h +++ b/src/plugins/cppeditor/cppfilesettingspage.h @@ -9,6 +9,7 @@ #include +namespace ExtensionSystem { class IPlugin; } namespace ProjectExplorer { class Project; } namespace CppEditor::Internal { @@ -28,6 +29,7 @@ public: QDir::toNativeSeparators("../Src"), ".."}; Utils::FilePath licenseTemplatePath; + QString headerGuardTemplate = "%{JS: '%{Header:FileName}'.toUpperCase().replace(/[.]/g, '_')}"; bool headerPragmaOnce = false; bool lowerCaseFiles = Constants::LOWERCASE_CPPFILES_DEFAULT; @@ -39,6 +41,9 @@ public: // Convenience to return a license template completely formatted. QString licenseTemplate() const; + // Expanded headerGuardTemplate. + QString headerGuard(const Utils::FilePath &headerFilePath) const; + bool equals(const CppFileSettings &rhs) const; bool operator==(const CppFileSettings &s) const { return equals(s); } bool operator!=(const CppFileSettings &s) const { return !equals(s); } @@ -48,6 +53,6 @@ CppFileSettings &globalCppFileSettings(); CppFileSettings cppFileSettingsForProject(ProjectExplorer::Project *project); -void setupCppFileSettings(); +void setupCppFileSettings(ExtensionSystem::IPlugin &plugin); } // namespace CppEditor::Internal diff --git a/src/plugins/cppeditor/cpptoolsjsextension.cpp b/src/plugins/cppeditor/cpptoolsjsextension.cpp index c03a38ede2b..c3ed6e6286e 100644 --- a/src/plugins/cppeditor/cpptoolsjsextension.cpp +++ b/src/plugins/cppeditor/cpptoolsjsextension.cpp @@ -42,7 +42,7 @@ static QString fileName(const QString &path, const QString &extension) QString CppToolsJsExtension::headerGuard(const QString &in) const { - return Utils::headerGuard(in); + return fileSettings().headerGuard(Utils::FilePath::fromString(in)); } QString CppToolsJsExtension::licenseTemplate() const diff --git a/src/plugins/cppeditor/cpptoolsreuse.cpp b/src/plugins/cppeditor/cpptoolsreuse.cpp index 6e0802b4bbe..7be177c2c1b 100644 --- a/src/plugins/cppeditor/cpptoolsreuse.cpp +++ b/src/plugins/cppeditor/cpptoolsreuse.cpp @@ -358,6 +358,11 @@ CppCompletionAssistProcessor *getCppCompletionAssistProcessor() return new Internal::InternalCppCompletionAssistProcessor(); } +QString deriveHeaderGuard(const Utils::FilePath &filePath, ProjectExplorer::Project *project) +{ + return Internal::cppFileSettingsForProject(project).headerGuard(filePath); +} + bool fileSizeExceedsLimit(const FilePath &filePath, int sizeLimitInMb) { if (sizeLimitInMb <= 0) diff --git a/src/plugins/cppeditor/cpptoolsreuse.h b/src/plugins/cppeditor/cpptoolsreuse.h index 8b1da79113d..71aabab08df 100644 --- a/src/plugins/cppeditor/cpptoolsreuse.h +++ b/src/plugins/cppeditor/cpptoolsreuse.h @@ -6,7 +6,6 @@ #include "cppeditor_global.h" #include "clangdiagnosticconfig.h" -#include "compileroptionsbuilder.h" #include "projectpart.h" #include @@ -65,6 +64,9 @@ quickFixOperations(const TextEditor::AssistInterface *interface); CppCompletionAssistProcessor CPPEDITOR_EXPORT *getCppCompletionAssistProcessor(); +QString CPPEDITOR_EXPORT +deriveHeaderGuard(const Utils::FilePath &filePath, ProjectExplorer::Project *project); + enum class CacheUsage { ReadWrite, ReadOnly }; Utils::FilePath CPPEDITOR_EXPORT correspondingHeaderOrSource( diff --git a/src/plugins/cppeditor/quickfixes/moveclasstoownfile.cpp b/src/plugins/cppeditor/quickfixes/moveclasstoownfile.cpp index 4b83faaf2d4..ada2027971e 100644 --- a/src/plugins/cppeditor/quickfixes/moveclasstoownfile.cpp +++ b/src/plugins/cppeditor/quickfixes/moveclasstoownfile.cpp @@ -388,7 +388,7 @@ private: = Utils::transform(state->namespacePath, [&](const Namespace *ns) { return ov.prettyName(ns->name()); }); - const QString headerGuard = Utils::headerGuard(headerFileName); + const QString headerGuard = fileSettings.headerGuard(headerFilePath); if (fileSettings.headerPragmaOnce) { headerContent.append("#pragma once\n"); } else { diff --git a/src/plugins/designer/qtdesignerformclasscodegenerator.cpp b/src/plugins/designer/qtdesignerformclasscodegenerator.cpp index e0afdc45d97..d3937ff696d 100644 --- a/src/plugins/designer/qtdesignerformclasscodegenerator.cpp +++ b/src/plugins/designer/qtdesignerformclasscodegenerator.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -75,7 +76,8 @@ bool QtDesignerFormClassCodeGenerator::generateCpp(const FormClassWizardParamete const QString sourceLicense = CppEditor::AbstractEditorSupport::licenseTemplate( project, FilePath::fromString(parameters.sourceFile), parameters.className); // Include guards - const QString guard = Utils::headerGuard(parameters.headerFile); + const QString guard + = CppEditor::deriveHeaderGuard(FilePath::fromString(parameters.headerFile), project); const QString uiInclude = "ui_" + QFileInfo(parameters.uiFile).completeBaseName() + ".h";