From d40d649ff175fb8c720035653d48ae8ad7c28058 Mon Sep 17 00:00:00 2001 From: Mikkel Munck Rasmussen Date: Thu, 7 Nov 2019 14:38:30 +0100 Subject: [PATCH 1/2] Support for flag parameters in comments of a flag This commit introduces the ability to add a parameter describtion for a flag, so it will appear in the in the syntax of the doc of the cli. This way you can write clis that gives better hinting of how to use the cli based app. Parameters are marked with '@param' following the Javadoc-styled documentation standard. The following example has a flag called 'path' that expects a directory name class MyFlags { /** @param Search in 'directory' **/ @:flag('path') public var path:String = null; . . . } Cli.getDoc(new MyFlags(), new tink.cli.doc.DefaultFormatter() will then give this output: Usage: --path, -p : Search in path of 'directory' - Only the first occurence of @param is used since a flag only accepts one value, - Lines with @params are removed from the comments before the are added to the doc of the flag. --- src/tink/cli/DocFormatter.hx | 1 + src/tink/cli/Macro.hx | 15 +++++++- src/tink/cli/doc/DefaultFormatter.hx | 15 ++++++-- tests/RunTests.hx | 1 + tests/TestDoc.hx | 56 ++++++++++++++++++++++++++++ 5 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 tests/TestDoc.hx diff --git a/src/tink/cli/DocFormatter.hx b/src/tink/cli/DocFormatter.hx index 7727906..76bbb62 100644 --- a/src/tink/cli/DocFormatter.hx +++ b/src/tink/cli/DocFormatter.hx @@ -21,4 +21,5 @@ typedef DocFlag = { aliases:Array, names:Array, doc:String, + paramDescription:String } \ No newline at end of file diff --git a/src/tink/cli/Macro.hx b/src/tink/cli/Macro.hx index cdc81de..e4e0c47 100644 --- a/src/tink/cli/Macro.hx +++ b/src/tink/cli/Macro.hx @@ -39,7 +39,19 @@ class Macro { function s2e(v:String) return macro $v{v}; function f2e(fields) return EObjectDecl(fields).at(); - + function getFlagParamDesc(doc : String) { + return if(doc != null) { + var ido = doc.indexOf('@param'); + if(ido > -1) { + var iap = ido + 6; + StringTools.trim(doc.substr(iap, doc.indexOf('\n', iap) - iap)); + } else { + ""; + } + } else { + ""; + } + } for(command in info.commands) { commands.push([ {field: 'isDefault', expr: macro $v{command.isDefault}}, @@ -54,6 +66,7 @@ class Macro { {field: 'names', expr: macro $a{flag.names.map(s2e)}}, {field: 'aliases', expr: macro $a{flag.aliases.map(String.fromCharCode).map(s2e)}}, {field: 'doc', expr: macro $v{flag.field.doc}}, + {field: 'paramDescription', expr: s2e(getFlagParamDesc(flag.field.doc))}, ]); } diff --git a/src/tink/cli/doc/DefaultFormatter.hx b/src/tink/cli/doc/DefaultFormatter.hx index 5a19144..8d85e9c 100644 --- a/src/tink/cli/doc/DefaultFormatter.hx +++ b/src/tink/cli/doc/DefaultFormatter.hx @@ -71,20 +71,27 @@ class DefaultFormatter implements DocFormatter { var maxFlagLength = spec.flags.fold(function(flag, max) { var name = nameOf(flag); + if(flag.paramDescription.length > 0) name += ' ${flag.paramDescription}'; if(name.length > max) max = name.length; return max; }, 0); - function addFlag(name:String, doc:String) { - if(doc == null) doc = ''; - addLine(indent(name.lpad(' ', maxFlagLength) + ' : ' + indent(doc, maxFlagLength + 3).trim(), 6)); + var ep = ~/^@param.*$/gim; + function addFlag(name:String, doc:String, paramDescription:String) { + if(doc == null) { + doc = ''; + } else { + //Filter out the lines with @param + doc = ep.map(doc, function(e : EReg) return ""); + } + addLine(indent(('$name $paramDescription').rpad(' ', maxFlagLength) + ' : ' + indent(doc, maxFlagLength + 3).trim(), 6)); } addLine(''); addLine(' Flags:'); for(flag in spec.flags) { - addFlag(nameOf(flag), formatDoc(flag.doc)); + addFlag(nameOf(flag), formatDoc(flag.doc), flag.paramDescription); } } diff --git a/tests/RunTests.hx b/tests/RunTests.hx index 55e3331..67d3260 100644 --- a/tests/RunTests.hx +++ b/tests/RunTests.hx @@ -13,6 +13,7 @@ class RunTests { new TestAliasDisabled(), new TestPrompt(), new TestOptional(), + new TestDoc(), ])).handle(Runner.exit); } diff --git a/tests/TestDoc.hx b/tests/TestDoc.hx new file mode 100644 index 0000000..45c9e14 --- /dev/null +++ b/tests/TestDoc.hx @@ -0,0 +1,56 @@ +package; + +import tink.cli.*; +import tink.Cli; +import tink.unit.Assert.*; +import haxe.ds.StringMap; + +using tink.CoreApi; + +@:asserts +class TestDoc { + public function new() {} + + public function doc() { + var command = new FlagDoc(); + + var result = + '\n' + + ' Usage: root\n\n' + + ' Flags:\n' + + ' --name, -n : No parameter\n' + + ' --path, -p : Do search in path\n' + + ' --unusedParameter, -u : Do search in path\n'; + + result = StringTools.replace(result, " ", ""); + asserts.assert(result == StringTools.replace(Cli.getDoc(command, new tink.cli.doc.DefaultFormatter("root")), " ", "")); + return asserts.done(); + } +} + +class FlagDoc extends DebugCommand { + + /** + No parameter + **/ + @:flag('name') + public var name:String = null; + + /** + @param + Do search in path + **/ + @:flag('path') + public var path:String = null; + + /** + @param + Do search in path + @param unused parameter + **/ + @:flag('unusedParameter') + public var unusedParameter:String = null; + + @:defaultCommand + public function run(args:Rest) {} +} \ No newline at end of file From 364cb13a088b3b843acb69e7f9430096d3a9afa4 Mon Sep 17 00:00:00 2001 From: Mikkel Munck Rasmussen Date: Sun, 10 Nov 2019 12:13:46 +0100 Subject: [PATCH 2/2] Removed the changes in Macro as discussed in https://github.com/haxetink/tink_cli/pull/18 --- src/tink/cli/DocFormatter.hx | 3 +-- src/tink/cli/Macro.hx | 21 +-------------------- src/tink/cli/doc/DefaultFormatter.hx | 18 ++++++++++++++++-- 3 files changed, 18 insertions(+), 24 deletions(-) diff --git a/src/tink/cli/DocFormatter.hx b/src/tink/cli/DocFormatter.hx index 76bbb62..aec53c4 100644 --- a/src/tink/cli/DocFormatter.hx +++ b/src/tink/cli/DocFormatter.hx @@ -20,6 +20,5 @@ typedef DocCommand = { typedef DocFlag = { aliases:Array, names:Array, - doc:String, - paramDescription:String + doc:String } \ No newline at end of file diff --git a/src/tink/cli/Macro.hx b/src/tink/cli/Macro.hx index e4e0c47..d19a75f 100644 --- a/src/tink/cli/Macro.hx +++ b/src/tink/cli/Macro.hx @@ -16,8 +16,6 @@ class Macro { static var infoCache = new TypeMap(); static var routerCache = new TypeMap(); static var docCache = new TypeMap(); - static var TYPE_STRING = Context.getType('String'); - static var TYPE_STRINGLY = Context.getType('tink.Stringly'); public static function build() { switch Context.getLocalType() { @@ -39,19 +37,7 @@ class Macro { function s2e(v:String) return macro $v{v}; function f2e(fields) return EObjectDecl(fields).at(); - function getFlagParamDesc(doc : String) { - return if(doc != null) { - var ido = doc.indexOf('@param'); - if(ido > -1) { - var iap = ido + 6; - StringTools.trim(doc.substr(iap, doc.indexOf('\n', iap) - iap)); - } else { - ""; - } - } else { - ""; - } - } + for(command in info.commands) { commands.push([ {field: 'isDefault', expr: macro $v{command.isDefault}}, @@ -66,7 +52,6 @@ class Macro { {field: 'names', expr: macro $a{flag.names.map(s2e)}}, {field: 'aliases', expr: macro $a{flag.aliases.map(String.fromCharCode).map(s2e)}}, {field: 'doc', expr: macro $v{flag.field.doc}}, - {field: 'paramDescription', expr: s2e(getFlagParamDesc(flag.field.doc))}, ]); } @@ -384,10 +369,6 @@ class Macro { // flag is marked as "required" (will be prompted when missing) if there is no default expr var isRequired = field.expr() == null && !field.meta.has(':optional'); - if(isRequired && !TYPE_STRINGLY.unifiesWith(field.type) && !TYPE_STRING.unifiesWith(field.type)) { - var type = field.type.toComplex().toString(); - field.pos.error('$type is not supported. Please use a custom abstract to handle it. See https://github.com/haxetink/tink_cli#data-types'); - } addFlag(flags, aliases, isRequired); diff --git a/src/tink/cli/doc/DefaultFormatter.hx b/src/tink/cli/doc/DefaultFormatter.hx index 8d85e9c..f5b284f 100644 --- a/src/tink/cli/doc/DefaultFormatter.hx +++ b/src/tink/cli/doc/DefaultFormatter.hx @@ -63,6 +63,20 @@ class DefaultFormatter implements DocFormatter { } if(spec.flags.length > 0) { + function getParamDesc(flag:DocFlag) { + return if(flag.doc != null) { + var ido = flag.doc.indexOf('@param'); + if(ido > -1) { + var iap = ido + 6; + StringTools.trim(flag.doc.substr(iap, flag.doc.indexOf('\n', iap) - iap)); + } else { + ""; + } + } else { + ""; + } + } + function nameOf(flag:DocFlag) { var variants = flag.names.join(', '); if(flag.aliases.length > 0) variants += ', ' + flag.aliases.map(function(a) return '-$a').join(', '); @@ -71,7 +85,7 @@ class DefaultFormatter implements DocFormatter { var maxFlagLength = spec.flags.fold(function(flag, max) { var name = nameOf(flag); - if(flag.paramDescription.length > 0) name += ' ${flag.paramDescription}'; + name += ' ${getParamDesc(flag)}'; if(name.length > max) max = name.length; return max; }, 0); @@ -91,7 +105,7 @@ class DefaultFormatter implements DocFormatter { addLine(' Flags:'); for(flag in spec.flags) { - addFlag(nameOf(flag), formatDoc(flag.doc), flag.paramDescription); + addFlag(nameOf(flag), formatDoc(flag.doc), getParamDesc(flag)); } }