diff --git a/language/codegen/sourcefiletemplate.h b/language/codegen/sourcefiletemplate.h --- a/language/codegen/sourcefiletemplate.h +++ b/language/codegen/sourcefiletemplate.h @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -214,6 +215,22 @@ }; /** + * Describes a group of configuration options + */ + struct ConfigOptionGroup + { + /** + * A unique identifier for this option group + */ + QString name; + + /** + * The list of options in this group + */ + QVector options; + }; + + /** * Creates a SourceFileTemplate representing the template archive with * description file @p templateDescription. * @@ -274,9 +291,9 @@ bool hasCustomOptions() const; /** - * Return the custom options this template exposes + * @return the custom options this template exposes, in the order as defined in the config file **/ - QHash > customOptions(TemplateRenderer* renderer) const; + QVector customOptions(TemplateRenderer* renderer) const; /** * @return The type of this template. @@ -318,5 +335,6 @@ Q_DECLARE_TYPEINFO(KDevelop::SourceFileTemplate::OutputFile, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(KDevelop::SourceFileTemplate::ConfigOption, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(KDevelop::SourceFileTemplate::ConfigOptionGroup, Q_MOVABLE_TYPE); #endif // KDEVPLATFORM_SOURCEFILETEMPLATE_H diff --git a/language/codegen/sourcefiletemplate.cpp b/language/codegen/sourcefiletemplate.cpp --- a/language/codegen/sourcefiletemplate.cpp +++ b/language/codegen/sourcefiletemplate.cpp @@ -285,19 +285,19 @@ return hasOptions; } -QHash< QString, QList > SourceFileTemplate::customOptions(TemplateRenderer* renderer) const +QVector SourceFileTemplate::customOptions(TemplateRenderer* renderer) const { Q_ASSERT(isValid()); KConfig templateConfig(d->descriptionFileName); KConfigGroup cg(&templateConfig, "General"); const KArchiveEntry* entry = d->archive->directory()->entry(cg.readEntry("OptionsFile", "options.kcfg")); - QHash > options; + QVector optionGroups; if (!entry->isFile()) { - return options; + return optionGroups; } const KArchiveFile* file = static_cast(entry); @@ -311,32 +311,34 @@ if ( !doc.setContent( file->data(), &errorMsg, &errorRow, &errorCol ) ) { qCDebug(LANGUAGE) << "Unable to load document."; qCDebug(LANGUAGE) << "Parse error in line " << errorRow << ", col " << errorCol << ": " << errorMsg; - return options; + return optionGroups; } QDomElement cfgElement = doc.documentElement(); if ( cfgElement.isNull() ) { qCDebug(LANGUAGE) << "No document in kcfg file"; - return options; + return optionGroups; } QDomNodeList groups = cfgElement.elementsByTagName(QStringLiteral("group")); + optionGroups.reserve(groups.size()); for (int i = 0; i < groups.size(); ++i) { QDomElement group = groups.at(i).toElement(); - QList optionGroup; - QString groupName = group.attribute(QStringLiteral("name")); + ConfigOptionGroup optionGroup; + optionGroup.name = group.attribute(QStringLiteral("name")); QDomNodeList entries = group.elementsByTagName(QStringLiteral("entry")); + optionGroup.options.reserve(entries.size()); for (int j = 0; j < entries.size(); ++j) { QDomElement entry = entries.at(j).toElement(); - optionGroup << d->readEntry(entry, renderer); + optionGroup.options << d->readEntry(entry, renderer); } - options.insert(groupName, optionGroup); + optionGroups << optionGroup; } - return options; + return optionGroups; } void SourceFileTemplate::addAdditionalSearchLocation(const QString& location) diff --git a/language/codegen/tests/data/kdevcodegentest/templates/CMakeLists.txt b/language/codegen/tests/data/kdevcodegentest/templates/CMakeLists.txt --- a/language/codegen/tests/data/kdevcodegentest/templates/CMakeLists.txt +++ b/language/codegen/tests/data/kdevcodegentest/templates/CMakeLists.txt @@ -1,6 +1,7 @@ set( test_filetemplate_DIRS test_yaml test_cpp + test_options ) include(KDevPlatformMacros) diff --git a/language/codegen/tests/data/kdevcodegentest/templates/test_options/options.kcfg b/language/codegen/tests/data/kdevcodegentest/templates/test_options/options.kcfg new file mode 100644 --- /dev/null +++ b/language/codegen/tests/data/kdevcodegentest/templates/test_options/options.kcfg @@ -0,0 +1,38 @@ + + + + + + + true + + + + Test + + + + + + + + + Second + + + + + + false + + + + + + true + + + diff --git a/language/codegen/tests/data/kdevcodegentest/templates/test_options/template.txt b/language/codegen/tests/data/kdevcodegentest/templates/test_options/template.txt new file mode 100644 --- /dev/null +++ b/language/codegen/tests/data/kdevcodegentest/templates/test_options/template.txt @@ -0,0 +1,7 @@ +Template + +Name: {{ name }} + +Bool option: "{% if bool_option %}true{% else %}false{% endif %}" +String option: "{{ string_option }}" +Enum option: "{{ enum_option }}" diff --git a/language/codegen/tests/data/kdevcodegentest/templates/test_options/test_options.desktop b/language/codegen/tests/data/kdevcodegentest/templates/test_options/test_options.desktop new file mode 100644 --- /dev/null +++ b/language/codegen/tests/data/kdevcodegentest/templates/test_options/test_options.desktop @@ -0,0 +1,11 @@ +[General] +Name=Options Test Template +Comment= +Category=Foo/Bar +Files=Template +OptionsFile=options.kcfg + +[Template] +Name=Template +File=template.txt +OutputFile=output.txt diff --git a/language/codegen/tests/test_templateclassgenerator.cpp b/language/codegen/tests/test_templateclassgenerator.cpp --- a/language/codegen/tests/test_templateclassgenerator.cpp +++ b/language/codegen/tests/test_templateclassgenerator.cpp @@ -131,6 +131,55 @@ { TemplateClassGenerator* generator = loadTemplate(QStringLiteral("test_yaml")); QCOMPARE(generator->sourceFileTemplate().hasCustomOptions(), false); + + generator = loadTemplate(QStringLiteral("test_options")); + QCOMPARE(generator->sourceFileTemplate().hasCustomOptions(), true); + + // test if option data loaded with all values in same order as in kcfg file + struct ExpectedOption + { + const char* name; const char* label; const char* type; const char* value; QStringList values; + }; + const struct { + const char* name; + QVector expectedOptions; + } expectedGroupDataList[] = { + { name: "A Group", expectedOptions: { + {name: "bool_option", label: "A Bool", type: "Bool", value: "true", values: {}}, + {name: "string_option", label: "Zzz String", type: "String", value: "Test", values: {}}, + {name: "enum_option", label: "Bb Enum", type: "Enum", value: "Second", values: { + {QLatin1String("First")}, {QLatin1String("Second")}, {QLatin1String("Last")} + }} + }}, + { name: "Zzz Group", expectedOptions: { + {name: "z_option", label: "Z Bool", type: "Bool", value: "false", values: {}} + }}, + { name: "Bb Group", expectedOptions: { + {name: "b_option", label: "B Bool", type: "Bool", value: "true", values: {}} + }} + }; + const int expectedGroupDataCount = sizeof(expectedGroupDataList)/sizeof(expectedGroupDataList[0]); + + const auto customOptionGroups = generator->sourceFileTemplate().customOptions(generator->renderer()); + + QCOMPARE(customOptionGroups.count(), expectedGroupDataCount); + for (int i = 0; i < expectedGroupDataCount; ++i) { + const auto& customOptionGroup = customOptionGroups[i]; + const auto& expectedGroupData = expectedGroupDataList[i]; + + QCOMPARE(customOptionGroup.name, QString::fromLatin1(expectedGroupData.name)); + QCOMPARE(customOptionGroup.options.count(), expectedGroupData.expectedOptions.count()); + for (int j = 0; j < expectedGroupData.expectedOptions.count(); ++j) { + const auto& customOption = customOptionGroup.options[j]; + const auto& expectedOptionData = expectedGroupData.expectedOptions[j]; + + QCOMPARE(customOption.name, QString::fromLatin1(expectedOptionData.name)); + QCOMPARE(customOption.label, QString::fromLatin1(expectedOptionData.label)); + QCOMPARE(customOption.type, QString::fromLatin1(expectedOptionData.type)); + QCOMPARE(customOption.value.toString(), QString::fromLatin1(expectedOptionData.value)); + QCOMPARE(customOption.values, expectedOptionData.values); + } + } } void TestTemplateClassGenerator::templateVariablesCpp() diff --git a/plugins/filetemplates/templateoptionspage.cpp b/plugins/filetemplates/templateoptionspage.cpp --- a/plugins/filetemplates/templateoptionspage.cpp +++ b/plugins/filetemplates/templateoptionspage.cpp @@ -41,7 +41,7 @@ class KDevelop::TemplateOptionsPagePrivate { public: - QList entries; + QVector entries; QHash controls; QHash typeProperties; QWidget *firstEditWidget; @@ -71,19 +71,15 @@ d->firstEditWidget = nullptr; QVBoxLayout* layout = new QVBoxLayout(); - QHash > options = fileTemplate.customOptions(renderer); - QHash >::const_iterator it; - for (it = options.constBegin(); it != options.constEnd(); ++it) - { + for (const auto& optionGroup : fileTemplate.customOptions(renderer)) { QGroupBox* box = new QGroupBox(this); - box->setTitle(it.key()); + box->setTitle(optionGroup.name); QFormLayout* formLayout = new QFormLayout; - d->entries << it.value(); - foreach (const SourceFileTemplate::ConfigOption& entry, it.value()) - { + d->entries << optionGroup.options; + for (const auto& entry : optionGroup.options) { QWidget* control = nullptr; const QString type = entry.type; if (type == QLatin1String("String")) diff --git a/plugins/filetemplates/templateselectionpage.cpp b/plugins/filetemplates/templateselectionpage.cpp --- a/plugins/filetemplates/templateselectionpage.cpp +++ b/plugins/filetemplates/templateselectionpage.cpp @@ -100,8 +100,8 @@ // set default option values if (fileTemplate.hasCustomOptions()) { QVariantHash extraVars; - for (const auto& option : fileTemplate.customOptions(&renderer)) { - for (const auto& entry : option) { + for (const auto& optionGroup : fileTemplate.customOptions(&renderer)) { + for (const auto& entry : optionGroup.options) { extraVars[entry.name] = entry.value; } }