From 8685f296ced1c33ddfd84a33c3fbf008ef43b7fa Mon Sep 17 00:00:00 2001 From: Elena Lepilkina Date: Fri, 29 Nov 2019 18:09:06 +0400 Subject: [PATCH 1/2] Added hidden options + sorted options in help --- core/commonMain/src/ArgParser.kt | 38 ++++++++++------ core/commonMain/src/Descriptors.kt | 3 +- core/commonMain/src/Options.kt | 16 +++---- core/commonTest/src/HelpTests.kt | 71 +++++++++++++++++++++++++----- 4 files changed, 93 insertions(+), 35 deletions(-) diff --git a/core/commonMain/src/ArgParser.kt b/core/commonMain/src/ArgParser.kt index 26a11ff..e60660a 100644 --- a/core/commonMain/src/ArgParser.kt +++ b/core/commonMain/src/ArgParser.kt @@ -95,7 +95,8 @@ open class ArgParser( val programName: String, var useDefaultHelpShortName: Boolean = true, var prefixStyle: OPTION_PREFIX_STYLE = OPTION_PREFIX_STYLE.LINUX, - var skipExtraArguments: Boolean = false + var skipExtraArguments: Boolean = false, + var hiddenOptionsHelpFlag: String = "help-hidden" ) { /** @@ -197,10 +198,11 @@ open class ArgParser( fullName: String? = null, shortName: String ? = null, description: String? = null, - deprecatedWarning: String? = null + deprecatedWarning: String? = null, + hidden: Boolean = false ): SingleNullableOption { val option = SingleNullableOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, - fullName, shortName, description, deprecatedWarning = deprecatedWarning), CLIEntityWrapper()) + fullName, shortName, description, deprecatedWarning = deprecatedWarning, hidden = hidden), CLIEntityWrapper()) option.owner.entity = option declaredOptions.add(option.owner) return option @@ -368,6 +370,7 @@ open class ArgParser( val helpOption = SingleNullableOption(helpDescriptor, CLIEntityWrapper()) helpOption.owner.entity = helpOption declaredOptions.add(helpOption.owner) + option(ArgType.Boolean, hiddenOptionsHelpFlag, description = "Display help for hidden options") // Add default list with arguments if there can be extra free arguments. if (skipExtraArguments) { @@ -441,8 +444,8 @@ open class ArgParser( } } else { // Boolean flags. - if (argValue.descriptor.fullName == "help") { - println(makeUsage()) + if (argValue.descriptor.fullName == "help" || argValue.descriptor.fullName == hiddenOptionsHelpFlag) { + println(makeUsage(argValue.descriptor.fullName == hiddenOptionsHelpFlag)) exitProcess(0) } saveAsOption(argValue, "true") @@ -478,19 +481,26 @@ open class ArgParser( /** * Creates a message with the usage information. */ - internal fun makeUsage(): String { + internal fun makeUsage(showHidden: Boolean = false): String { val result = StringBuilder() result.append("Usage: ${fullCommandName.joinToString(" ")} options_list\n") - if (arguments.isNotEmpty()) { - result.append("Arguments: \n") - arguments.forEach { - result.append(it.value.descriptor.helpMessage) + if (!showHidden) { + val shownArguments = arguments.values.sortedBy { it.descriptor.fullName } + if (shownArguments.isNotEmpty()) { + result.append("Arguments: \n") + shownArguments.forEach { + result.append(it.descriptor.helpMessage) + } } } - if (options.isNotEmpty()) { - result.append("Options: \n") - options.forEach { - result.append(it.value.descriptor.helpMessage) + val shownOptions = options.values.filter { (it.descriptor as OptionDescriptor<*, *>) + .let { if (showHidden) it.hidden else !it.hidden } + }.sortedBy { it.descriptor.fullName } + val optionsHeader = if (showHidden) "Hidden options: \n" else "Options: \n" + if (shownOptions.isNotEmpty()) { + result.append(optionsHeader) + shownOptions.forEach { + result.append(it.descriptor.helpMessage) } } return result.toString() diff --git a/core/commonMain/src/Descriptors.kt b/core/commonMain/src/Descriptors.kt index 6091afe..3f3e2f8 100644 --- a/core/commonMain/src/Descriptors.kt +++ b/core/commonMain/src/Descriptors.kt @@ -78,7 +78,8 @@ internal class OptionDescriptor( required: Boolean = false, val multiple: Boolean = false, val delimiter: String? = null, - deprecatedWarning: String? = null) : Descriptor(type, fullName, description, defaultValue, + deprecatedWarning: String? = null, + val hidden: Boolean = false) : Descriptor(type, fullName, description, defaultValue, required, deprecatedWarning) { override val textDescription: String diff --git a/core/commonMain/src/Options.kt b/core/commonMain/src/Options.kt index 41fe26f..c769cfd 100644 --- a/core/commonMain/src/Options.kt +++ b/core/commonMain/src/Options.kt @@ -88,7 +88,7 @@ fun AbstractSingleOption.multiple(): MultipleOpti val newOption = with((delegate as ParsingValue).descriptor as OptionDescriptor) { MultipleOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName, description, listOfNotNull(defaultValue), - required, true, delimiter, deprecatedWarning), owner) + required, true, delimiter, deprecatedWarning, hidden), owner) } owner.entity = newOption return newOption @@ -104,7 +104,7 @@ fun MultipleOption.multiple(): MultipleOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName, description, defaultValue?.toList() ?: listOf(), - required, true, delimiter, deprecatedWarning), owner) + required, true, delimiter, deprecatedWarning, hidden), owner) } owner.entity = newOption return newOption @@ -118,7 +118,7 @@ fun MultipleOption.multiple(): MultipleOption AbstractSingleOption.default(value: T): SingleOption { val newOption = with((delegate as ParsingValue).descriptor as OptionDescriptor) { SingleOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName, - description, value, required, multiple, delimiter, deprecatedWarning), owner) + description, value, required, multiple, delimiter, deprecatedWarning, hidden), owner) } owner.entity = newOption return newOption @@ -138,7 +138,7 @@ fun } MultipleOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName, description, value.toList(), - required, multiple, delimiter, deprecatedWarning), owner) + required, multiple, delimiter, deprecatedWarning, hidden), owner) } owner.entity = newOption return newOption @@ -151,7 +151,7 @@ fun SingleNullableOption.required(): SingleOption { val newOption = with((delegate as ParsingValue).descriptor as OptionDescriptor) { SingleOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName, description, defaultValue, - true, multiple, delimiter, deprecatedWarning), owner) + true, multiple, delimiter, deprecatedWarning, hidden), owner) } owner.entity = newOption return newOption @@ -165,7 +165,7 @@ fun val newOption = with((delegate as ParsingValue>).descriptor as OptionDescriptor) { MultipleOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName, description, defaultValue?.toList() ?: listOf(), - true, multiple, delimiter, deprecatedWarning), owner) + true, multiple, delimiter, deprecatedWarning, hidden), owner) } owner.entity = newOption return newOption @@ -180,7 +180,7 @@ fun AbstractSingleOption.delimiter(delimiterValue val newOption = with((delegate as ParsingValue).descriptor as OptionDescriptor) { MultipleOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName, description, listOfNotNull(defaultValue), - required, multiple, delimiterValue, deprecatedWarning), owner) + required, multiple, delimiterValue, deprecatedWarning, hidden), owner) } owner.entity = newOption return newOption @@ -195,7 +195,7 @@ fun MultipleOption.delimiter(delimiterValue: String val newOption = with((delegate as ParsingValue>).descriptor as OptionDescriptor) { MultipleOption(OptionDescriptor(optionFullFormPrefix, optionShortFromPrefix, type, fullName, shortName, description, defaultValue?.toList() ?: listOf(), - required, multiple, delimiterValue, deprecatedWarning), owner) + required, multiple, delimiterValue, deprecatedWarning, hidden), owner) } owner.entity = newOption return newOption diff --git a/core/commonTest/src/HelpTests.kt b/core/commonTest/src/HelpTests.kt index 973a0cd..d927381 100644 --- a/core/commonTest/src/HelpTests.kt +++ b/core/commonTest/src/HelpTests.kt @@ -33,15 +33,16 @@ class HelpTests { val expectedOutput = """ Usage: test options_list Arguments: - mainReport -> Main report for analysis { String } compareToReport -> Report to compare to (optional) { String } + mainReport -> Main report for analysis { String } Options: - --output, -o -> Output file { String } --eps, -e [$epsDefault] -> Meaningful performance changes { Double } - --short, -s [false] -> Show short version of report + --help, -h -> Usage info + --help-hidden -> Display help for hidden options + --output, -o -> Output file { String } --renders, -r [text] -> Renders for showing information { Value should be one of [text, html, teamcity, statistics, metrics] } + --short, -s [false] -> Show short version of report --user, -u -> User access information for authorization { String } - --help, -h -> Usage info """.trimIndent() assertEquals(expectedOutput, helpOutput) } @@ -85,18 +86,64 @@ Usage: test summary options_list Arguments: mainReport -> Main report for analysis { String } Options: - --exec [geomean] -> Execution time way of calculation { Value should be one of [samples, geomean] } - --exec-samples -> Samples used for execution time metric (value 'all' allows use all samples) { String } - --exec-normalize -> File with golden results which should be used for normalization { String } - --compile [geomean] -> Compile time way of calculation { Value should be one of [samples, geomean] } - --compile-samples -> Samples used for compile time metric (value 'all' allows use all samples) { String } - --compile-normalize -> File with golden results which should be used for normalization { String } --codesize [geomean] -> Code size way of calculation { Value should be one of [samples, geomean] } - --codesize-samples -> Samples used for code size metric (value 'all' allows use all samples) { String } --codesize-normalize -> File with golden results which should be used for normalization { String } - --user, -u -> User access information for authorization { String } + --codesize-samples -> Samples used for code size metric (value 'all' allows use all samples) { String } + --compile [geomean] -> Compile time way of calculation { Value should be one of [samples, geomean] } + --compile-normalize -> File with golden results which should be used for normalization { String } + --compile-samples -> Samples used for compile time metric (value 'all' allows use all samples) { String } + --exec [geomean] -> Execution time way of calculation { Value should be one of [samples, geomean] } + --exec-normalize -> File with golden results which should be used for normalization { String } + --exec-samples -> Samples used for execution time metric (value 'all' allows use all samples) { String } --help, -h -> Usage info + --help-hidden -> Display help for hidden options + --user, -u -> User access information for authorization { String } """.trimIndent() assertEquals(expectedOutput, helpOutput) } + + @Test + fun testHelpHiddenMessage() { + val argParser = ArgParser("test") + val mainReport by argParser.argument(ArgType.String, description = "Main report for analysis") + val compareToReport by argParser.argument(ArgType.String, description = "Report to compare to").optional() + + val output by argParser.option(ArgType.String, shortName = "o", description = "Output file") + val epsValue by argParser.option(ArgType.Double, "eps", "e", "Meaningful performance changes", + hidden = true) + .default(1.0) + val useShortForm by argParser.option(ArgType.Boolean, "short", "s", + "Show short version of report").default(false) + val renders by argParser.option(ArgType.Choice(listOf("text", "html", "teamcity", "statistics", "metrics")), + shortName = "r", description = "Renders for showing information").multiple().default(listOf("text")) + val user by argParser.option(ArgType.String, shortName = "u", description = "User access information for authorization", + hidden = true) + argParser.parse(arrayOf("main.txt")) + val helpOutput = argParser.makeUsage().trimIndent() + @Suppress("CanBeVal") // can't be val in order to build expectedOutput only in run time. + var epsDefault = 1.0 + val expectedOutputHelp = """ +Usage: test options_list +Arguments: + compareToReport -> Report to compare to (optional) { String } + mainReport -> Main report for analysis { String } +Options: + --help, -h -> Usage info + --help-hidden -> Display help for hidden options + --output, -o -> Output file { String } + --renders, -r [text] -> Renders for showing information { Value should be one of [text, html, teamcity, statistics, metrics] } + --short, -s [false] -> Show short version of report +""".trimIndent() + assertEquals(expectedOutputHelp, helpOutput) + + val helpHiddenOutput = argParser.makeUsage(true).trimIndent() + @Suppress("CanBeVal") // can't be val in order to build expectedOutput only in run time. + val expectedOutputHelpHidden = """ +Usage: test options_list +Hidden options: + --eps, -e [$epsDefault] -> Meaningful performance changes { Double } + --user, -u -> User access information for authorization { String } + """.trimIndent() + assertEquals(expectedOutputHelpHidden, helpHiddenOutput) + } } From 71033cb77a50c408c2fd08e79830f3ade726e9ac Mon Sep 17 00:00:00 2001 From: Elena Lepilkina Date: Wed, 18 Dec 2019 09:25:19 +0300 Subject: [PATCH 2/2] Add opportunity to set custom help message for hidden options --- core/commonMain/src/ArgParser.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/commonMain/src/ArgParser.kt b/core/commonMain/src/ArgParser.kt index e60660a..060c72d 100644 --- a/core/commonMain/src/ArgParser.kt +++ b/core/commonMain/src/ArgParser.kt @@ -96,7 +96,8 @@ open class ArgParser( var useDefaultHelpShortName: Boolean = true, var prefixStyle: OPTION_PREFIX_STYLE = OPTION_PREFIX_STYLE.LINUX, var skipExtraArguments: Boolean = false, - var hiddenOptionsHelpFlag: String = "help-hidden" + var hiddenOptionsHelpFlag: String = "help-hidden", + var hiddenOptionsHelpMessage: String = "Display help for hidden options" ) { /** @@ -370,7 +371,7 @@ open class ArgParser( val helpOption = SingleNullableOption(helpDescriptor, CLIEntityWrapper()) helpOption.owner.entity = helpOption declaredOptions.add(helpOption.owner) - option(ArgType.Boolean, hiddenOptionsHelpFlag, description = "Display help for hidden options") + option(ArgType.Boolean, hiddenOptionsHelpFlag, description = hiddenOptionsHelpMessage) // Add default list with arguments if there can be extra free arguments. if (skipExtraArguments) {