Add support for rendering help info for input and output

This commit is contained in:
Paul Schaub 2024-09-18 15:50:17 +02:00
parent 84404d629f
commit ada77be955
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
4 changed files with 115 additions and 5 deletions

View file

@ -83,16 +83,20 @@ class SopCLI {
return CommandLine(SopCLI::class.java) return CommandLine(SopCLI::class.java)
.apply { .apply {
// explicitly set help command resource bundle
subcommands["help"]?.setResourceBundle(ResourceBundle.getBundle("msg_help"))
// Hide generate-completion command // Hide generate-completion command
subcommands["generate-completion"]?.commandSpec?.usageMessage()?.hidden(true) subcommands["generate-completion"]?.commandSpec?.usageMessage()?.hidden(true)
// render Input/Output sections in help command
subcommands.values.filter { (it.getCommand() as Any) is AbstractSopCmd } // Only for AbstractSopCmd objects
.forEach { (it.getCommand() as AbstractSopCmd).installIORenderer(it) }
// overwrite executable name // overwrite executable name
commandName = EXECUTABLE_NAME commandName = EXECUTABLE_NAME
// setup exception handling // setup exception handling
executionExceptionHandler = SOPExecutionExceptionHandler() executionExceptionHandler = SOPExecutionExceptionHandler()
exitCodeExceptionMapper = SOPExceptionExitCodeMapper() exitCodeExceptionMapper = SOPExceptionExitCodeMapper()
isCaseInsensitiveEnumValuesAllowed = true isCaseInsensitiveEnumValuesAllowed = true
} }.execute(*args)
.execute(*args)
} }
} }

View file

@ -7,6 +7,11 @@ package sop.cli.picocli.commands
import java.io.* import java.io.*
import java.text.ParseException import java.text.ParseException
import java.util.* import java.util.*
import picocli.CommandLine
import picocli.CommandLine.Help
import picocli.CommandLine.Help.Column
import picocli.CommandLine.Help.TextTable
import picocli.CommandLine.IHelpSectionRenderer
import sop.cli.picocli.commands.AbstractSopCmd.EnvironmentVariableResolver import sop.cli.picocli.commands.AbstractSopCmd.EnvironmentVariableResolver
import sop.exception.SOPGPException.* import sop.exception.SOPGPException.*
import sop.util.UTCUtil.Companion.parseUTCDate import sop.util.UTCUtil.Companion.parseUTCDate
@ -215,11 +220,106 @@ abstract class AbstractSopCmd(locale: Locale = Locale.getDefault()) : Runnable {
} }
} }
/**
* See
* [Example](https://github.com/remkop/picocli/blob/main/picocli-examples/src/main/java/picocli/examples/customhelp/EnvironmentVariablesSection.java)
*/
class InputOutputHelpSectionRenderer(private val argument: Pair<String?, String?>) :
IHelpSectionRenderer {
override fun render(help: Help): String {
return argument.let {
val calcLen =
help.calcLongOptionColumnWidth(
help.commandSpec().options(),
help.commandSpec().positionalParameters(),
help.colorScheme())
val keyLength =
help
.commandSpec()
.usageMessage()
.longOptionsMaxWidth()
.coerceAtMost(calcLen - 1)
val table =
TextTable.forColumns(
help.colorScheme(),
Column(keyLength + 7, 6, Column.Overflow.SPAN),
Column(width(help) - (keyLength + 7), 0, Column.Overflow.WRAP))
table.setAdjustLineBreaksForWideCJKCharacters(adjustCJK(help))
table.addRowValues("@|yellow ${argument.first}|@", argument.second ?: "")
table.toString()
}
}
private fun adjustCJK(help: Help) =
help.commandSpec().usageMessage().adjustLineBreaksForWideCJKCharacters()
private fun width(help: Help) = help.commandSpec().usageMessage().width()
}
fun installIORenderer(cmd: CommandLine) {
val inputName = getResString(cmd, "standardInput")
if (inputName != null) {
cmd.helpSectionMap[SECTION_KEY_STANDARD_INPUT_HEADING] = IHelpSectionRenderer {
getResString(cmd, "standardInputHeading")
}
cmd.helpSectionMap[SECTION_KEY_STANDARD_INPUT_DETAILS] =
InputOutputHelpSectionRenderer(
inputName to getResString(cmd, "standardInputDescription"))
cmd.helpSectionKeys =
insertKey(
cmd.helpSectionKeys,
SECTION_KEY_STANDARD_INPUT_HEADING,
SECTION_KEY_STANDARD_INPUT_DETAILS)
}
val outputName = getResString(cmd, "standardOutput")
if (outputName != null) {
cmd.helpSectionMap[SECTION_KEY_STANDARD_OUTPUT_HEADING] = IHelpSectionRenderer {
getResString(cmd, "standardOutputHeading")
}
cmd.helpSectionMap[SECTION_KEY_STANDARD_OUTPUT_DETAILS] =
InputOutputHelpSectionRenderer(
outputName to getResString(cmd, "standardOutputDescription"))
cmd.helpSectionKeys =
insertKey(
cmd.helpSectionKeys,
SECTION_KEY_STANDARD_OUTPUT_HEADING,
SECTION_KEY_STANDARD_OUTPUT_DETAILS)
}
}
private fun insertKey(keys: List<String>, header: String, details: String): List<String> {
val index =
keys.indexOf(CommandLine.Model.UsageMessageSpec.SECTION_KEY_EXIT_CODE_LIST_HEADING)
val result = keys.toMutableList()
result.add(index, header)
result.add(index + 1, details)
return result
}
private fun getResString(cmd: CommandLine, key: String): String? =
try {
cmd.resourceBundle.getString(key)
} catch (m: MissingResourceException) {
try {
cmd.parent.resourceBundle.getString(key)
} catch (m: MissingResourceException) {
null
}
}
?.let { String.format(it) }
companion object { companion object {
const val PRFX_ENV = "@ENV:" const val PRFX_ENV = "@ENV:"
const val PRFX_FD = "@FD:" const val PRFX_FD = "@FD:"
const val SECTION_KEY_STANDARD_INPUT_HEADING = "standardInputHeading"
const val SECTION_KEY_STANDARD_INPUT_DETAILS = "standardInput"
const val SECTION_KEY_STANDARD_OUTPUT_HEADING = "standardOutputHeading"
const val SECTION_KEY_STANDARD_OUTPUT_DETAILS = "standardOutput"
@JvmField val DAWN_OF_TIME = Date(0) @JvmField val DAWN_OF_TIME = Date(0)
@JvmField @JvmField

View file

@ -9,10 +9,13 @@ locale=Locale for description texts
# Generic # Generic
usage.synopsisHeading=Usage:\u0020 usage.synopsisHeading=Usage:\u0020
usage.commandListHeading = %nCommands:%n usage.commandListHeading=%nCommands:%n
usage.optionListHeading = %nOptions:%n usage.optionListHeading=%nOptions:%n
usage.footerHeading=Powered by picocli%n usage.footerHeading=Powered by picocli%n
standardInputHeading=%nInput:%n
standardOutputHeading=%nOutput:%n
# Exit Codes # Exit Codes
usage.exitCodeListHeading=%nExit Codes:%n usage.exitCodeListHeading=%nExit Codes:%n
usage.exitCodeList.0=\u00200:Successful program execution usage.exitCodeList.0=\u00200:Successful program execution

View file

@ -10,9 +10,12 @@ locale=Gebietsschema f
# Generic # Generic
usage.synopsisHeading=Aufruf:\u0020 usage.synopsisHeading=Aufruf:\u0020
usage.commandListHeading=%nBefehle:%n usage.commandListHeading=%nBefehle:%n
usage.optionListHeading = %nOptionen:%n usage.optionListHeading=%nOptionen:%n
usage.footerHeading=Powered by Picocli%n usage.footerHeading=Powered by Picocli%n
standardInputHeading=%nEingabe:%n
standardOutputHeading=%nAusgabe:%n
# Exit Codes # Exit Codes
usage.exitCodeListHeading=%nExit Codes:%n usage.exitCodeListHeading=%nExit Codes:%n
usage.exitCodeList.0=\u00200:Erfolgreiche Programmausführung usage.exitCodeList.0=\u00200:Erfolgreiche Programmausführung