Repository: urfave/cli
Pull request #1171 (2020-08-06 16:51:21)

feat(docs): add UsageText to docs output for markdown and man page generation
<!--
  This template provides some ideas of things to include in your PR description.
  To start, try providing a short summary of your changes in the Title above.
  If a section of the PR template does not apply to this PR, then delete that section.
 -->

## What type of PR is this?

_(REQUIRED)_

- [ ] bug
- [ ] cleanup  
- [x] documentation
- [X] feature

## What this PR does / why we need it:

_(REQUIRED)_

<!--
  What goal is this change working towards?
  Provide a bullet pointed summary of how each file was changed.
  Briefly explain any decisions you made with respect to the changes.
  Include anything here that you didn't include in *Release Notes*
  above, such as changes to CI or changes to internal methods.
-->

As a tools developer, I find the `UsageText` field to be useful in providing details on what a command does and how to use it. A good example is with [gwsm](https://github.com/GoodwayGroup/gwsm)

For example, the command `gwsm diff` has as detailed description that helps the user know how to use the command.
```
$ gwsm diff --help
NAME:
   gwsm diff - View diff of local vs. namespace

USAGE:

View the diff of the local environment against a given command running on a pod within a namespace.

This will retrieve the stored secrets within AWS Secrets Manager and map them via
the secrets.yml file used by the 'summon' CLI tool to generate the current state of
Environment Variables for a given stage.

The AWS Secrets Manager names are assumed to be stored as '<SECRETS_GROUP>_NAME'
in the ConfigMap. Example: 'RDS_SECRET_NAME: rds/staging/service-yolo'

From the root of the service, the required files are typically found below:

The path to the configmap.yaml file is within the kubernetes deployment.
This is typically .kube/<stage>/05-configmap.yaml

The path to the secrets.yml is typically .docker/secrets.yaml

It will then grab current environment for a specific process running within a Pod in a given Namespace.

This is achieved by inspecting the /proc/<PID>/environ for the given process. This method uses
'/bin/bash -c' as the base command to perform the PID inspection via 'ps faux'.

The 'filter-prefix' flag will exclude any values that start with the flagged prefixes from display.

The 'exclude' flag will exclude any values where the KEY matches exactly from display.


OPTIONS:
   --secrets value, -s value        Path to secrets.yml (default: ".docker/secrets.yml")
   --configmap value, -c value      Path to configmap.yaml
   --namespace value, -n value      Kube Namespace list Pods from
   --cmd value                      Command to inspect (default: "node")
   --filter-prefix value, -f value  List of prefixes (csv) used to filter values from display. Set to "" to remove any filters. (default: "npm_,KUBERNETES_,API_PORT")
   --exclude value                  List (csv) of specific env vars to exclude values from display. Set to "" to remove any exclusions. (default: "PATH,SHLVL,HOSTNAME")
   --secret-suffix value            Suffix used to find ENV variables that denote the Secret Manager Secrets to lookup (default: "_NAME")
   --help, -h                       show help (default: false)
```

I would like for the docs generation (`ToMarkdown` and `ToMan`) to include this help text in the docs.

See the before and after here:

| | before | after |
|---|---|---|
| markdown | https://gist.github.com/clok/ca1452874e372b02fe597f8ad65ca7f2 |  https://gist.github.com/clok/fe5e00c973575778f8e548263b5dba44 |
| man | https://gist.github.com/clok/4ad90b19868709c86612ff76ee0e1350 | https://gist.github.com/clok/a810114a454beee90d5134e9c8c533e8 |

## Which issue(s) this PR fixes:

_(REQUIRED)_
<!--
If this PR fixes one of more issues, list them here.
One line each, like so:

Fixes #123
Fixes #39
-->
N/A - New feature

## Testing

Updated a local copy of an existing tool ([gwsm](https://github.com/GoodwayGroup/gwsm)) with the delta applied and generated before/after docs.

See the before and after here:

| | before | after |
|---|---|---|
| markdown | https://gist.github.com/clok/ca1452874e372b02fe597f8ad65ca7f2 |  https://gist.github.com/clok/fe5e00c973575778f8e548263b5dba44 |
| man | https://gist.github.com/clok/4ad90b19868709c86612ff76ee0e1350 | https://gist.github.com/clok/a810114a454beee90d5134e9c8c533e8 |

- Updated test suite
- Generated local examples

## Release Notes

_(REQUIRED)_
<!--
  If this PR makes user facing changes, please describe them here. This
  description will be copied into the release notes/changelog, whenever the
  next version is released. Keep this section short, and focus on high level
  changes.

  Put your text between the block. To omit notes, use NONE within the block.
-->

```release-note
Add UsageText to docs output for markdown and man page generation
```


clok: @rliebz and @lynncyrin any chance that this can get merged?
clok: Thank you very much for the feedback and suggestions. I will get to updating tests and responding to the feedback. I had hoped to get to it last week, but life and work kept pulling me away. Sorry for the delay!
clok: @rliebz Rebased with current `master` and updated with your feedback. Thank you again for the suggestions and comments.  I noticed that the github actions are failing, but I can't tell if it is due to the changes I pushed. I am happy to address any issues that I can. Just let me know.

Patch:
diff --git a/.github/stale.yml b/.github/stale.yml
index a3282a2..0cc3007 100644
--- a/.github/stale.yml
+++ b/.github/stale.yml
@@ -14,6 +14,7 @@ onlyLabels: []
 exemptLabels:
   - pinned
   - security
+  - "help wanted"
   - "kind/maintenance" 
 
 # Set to true to ignore issues in a project (defaults to false)
diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml
index 06dbb80..edeebdf 100644
--- a/.github/workflows/cli.yml
+++ b/.github/workflows/cli.yml
@@ -15,7 +15,7 @@ jobs:
     strategy:
       matrix:
         os: [ubuntu-latest, macos-latest, windows-latest]
-        go: [1.13, 1.14, 1.15]
+        go: [1.14, 1.15, 1.16]
     name: ${{ matrix.os }} @ Go ${{ matrix.go }}
     runs-on: ${{ matrix.os }}
     steps:
@@ -38,7 +38,7 @@ jobs:
           ref: ${{ github.ref }}
 
       - name: GOFMT Check
-        if: matrix.go == 1.15 && matrix.os == 'ubuntu-latest'
+        if: matrix.go == 1.16 && matrix.os == 'ubuntu-latest'
         run: test -z $(gofmt -l .)
 
       - name: vet
@@ -51,19 +51,19 @@ jobs:
         run: go run internal/build/build.go check-binary-size
 
       - name: Upload coverage to Codecov
-        if: success() && matrix.go == 1.14 && matrix.os == 'ubuntu-latest'
+        if: success() && matrix.go == 1.16 && matrix.os == 'ubuntu-latest'
         uses: codecov/codecov-action@v1
         with:
-          token: 0a8cc73b-bb7c-480b-8626-38a461643761
           fail_ci_if_error: true
 
   test-docs:
     name: test-docs
     runs-on: ubuntu-latest
     steps:
-      - name: Set up Go 1.15
+      - name: Set up Go
         uses: actions/setup-go@v1
         with:
+          # Currently fails on 1.16
           go-version: 1.15
 
       - name: Use Node.js 12.x
diff --git a/.gitignore b/.gitignore
index 2d5e149..afdca41 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,5 @@ vendor
 .idea
 internal/*/built-example
 coverage.txt
+
+*.exe
diff --git a/app.go b/app.go
index 2c97251..5c616e6 100644
--- a/app.go
+++ b/app.go
@@ -278,7 +278,7 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) {
 		return nil
 	}
 
-	cerr := checkRequiredFlags(a.Flags, context)
+	cerr := context.checkRequiredFlags(a.Flags)
 	if cerr != nil {
 		_ = ShowAppHelp(context)
 		return cerr
@@ -397,7 +397,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
 		}
 	}
 
-	cerr := checkRequiredFlags(a.Flags, context)
+	cerr := context.checkRequiredFlags(a.Flags)
 	if cerr != nil {
 		_ = ShowSubcommandHelp(context)
 		return cerr
diff --git a/command.go b/command.go
index 3503a55..3477686 100644
--- a/command.go
+++ b/command.go
@@ -127,7 +127,7 @@ func (c *Command) Run(ctx *Context) (err error) {
 		return nil
 	}
 
-	cerr := checkRequiredFlags(c.Flags, context)
+	cerr := context.checkRequiredFlags(c.Flags)
 	if cerr != nil {
 		_ = ShowCommandHelp(context, c.Name)
 		return cerr
@@ -227,6 +227,7 @@ func (c *Command) startApp(ctx *Context) error {
 	}
 
 	app.Usage = c.Usage
+	app.UsageText = c.UsageText
 	app.Description = c.Description
 	app.ArgsUsage = c.ArgsUsage
 
diff --git a/context.go b/context.go
index 65e0d1e..94cbb65 100644
--- a/context.go
+++ b/context.go
@@ -2,9 +2,7 @@ package cli
 
 import (
 	"context"
-	"errors"
 	"flag"
-	"fmt"
 	"strings"
 )
 
@@ -53,20 +51,18 @@ func (c *Context) Set(name, value string) error {
 
 // IsSet determines if the flag was actually set
 func (c *Context) IsSet(name string) bool {
-	if fs := lookupFlagSet(name, c); fs != nil {
-		if fs := lookupFlagSet(name, c); fs != nil {
-			isSet := false
-			fs.Visit(func(f *flag.Flag) {
-				if f.Name == name {
-					isSet = true
-				}
-			})
-			if isSet {
-				return true
+	if fs := c.lookupFlagSet(name); fs != nil {
+		isSet := false
+		fs.Visit(func(f *flag.Flag) {
+			if f.Name == name {
+				isSet = true
 			}
+		})
+		if isSet {
+			return true
 		}
 
-		f := lookupFlag(name, c)
+		f := c.lookupFlag(name)
 		if f == nil {
 			return false
 		}
@@ -108,7 +104,7 @@ func (c *Context) Lineage() []*Context {
 
 // Value returns the value of the flag corresponding to `name`
 func (c *Context) Value(name string) interface{} {
-	if fs := lookupFlagSet(name, c); fs != nil {
+	if fs := c.lookupFlagSet(name); fs != nil {
 		return fs.Lookup(name).Value.(flag.Getter).Get()
 	}
 	return nil
@@ -125,7 +121,7 @@ func (c *Context) NArg() int {
 	return c.Args().Len()
 }
 
-func lookupFlag(name string, ctx *Context) Flag {
+func (ctx *Context) lookupFlag(name string) Flag {
 	for _, c := range ctx.Lineage() {
 		if c.Command == nil {
 			continue
@@ -153,7 +149,7 @@ func lookupFlag(name string, ctx *Context) Flag {
 	return nil
 }
 
-func lookupFlagSet(name string, ctx *Context) *flag.FlagSet {
+func (ctx *Context) lookupFlagSet(name string) *flag.FlagSet {
 	for _, c := range ctx.Lineage() {
 		if f := c.flagSet.Lookup(name); f != nil {
 			return c.flagSet
@@ -163,89 +159,7 @@ func lookupFlagSet(name string, ctx *Context) *flag.FlagSet {
 	return nil
 }
 
-func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) {
-	switch ff.Value.(type) {
-	case Serializer:
-		_ = set.Set(name, ff.Value.(Serializer).Serialize())
-	default:
-		_ = set.Set(name, ff.Value.String())
-	}
-}
-
-func normalizeFlags(flags []Flag, set *flag.FlagSet) error {
-	visited := make(map[string]bool)
-	set.Visit(func(f *flag.Flag) {
-		visited[f.Name] = true
-	})
-	for _, f := range flags {
-		parts := f.Names()
-		if len(parts) == 1 {
-			continue
-		}
-		var ff *flag.Flag
-		for _, name := range parts {
-			name = strings.Trim(name, " ")
-			if visited[name] {
-				if ff != nil {
-					return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name)
-				}
-				ff = set.Lookup(name)
-			}
-		}
-		if ff == nil {
-			continue
-		}
-		for _, name := range parts {
-			name = strings.Trim(name, " ")
-			if !visited[name] {
-				copyFlag(name, ff, set)
-			}
-		}
-	}
-	return nil
-}
-
-func makeFlagNameVisitor(names *[]string) func(*flag.Flag) {
-	return func(f *flag.Flag) {
-		nameParts := strings.Split(f.Name, ",")
-		name := strings.TrimSpace(nameParts[0])
-
-		for _, part := range nameParts {
-			part = strings.TrimSpace(part)
-			if len(part) > len(name) {
-				name = part
-			}
-		}
-
-		if name != "" {
-			*names = append(*names, name)
-		}
-	}
-}
-
-type requiredFlagsErr interface {
-	error
-	getMissingFlags() []string
-}
-
-type errRequiredFlags struct {
-	missingFlags []string
-}
-
-func (e *errRequiredFlags) Error() string {
-	numberOfMissingFlags := len(e.missingFlags)
-	if numberOfMissingFlags == 1 {
-		return fmt.Sprintf("Required flag %q not set", e.missingFlags[0])
-	}
-	joinedMissingFlags := strings.Join(e.missingFlags, ", ")
-	return fmt.Sprintf("Required flags %q not set", joinedMissingFlags)
-}
-
-func (e *errRequiredFlags) getMissingFlags() []string {
-	return e.missingFlags
-}
-
-func checkRequiredFlags(flags []Flag, context *Context) requiredFlagsErr {
+func (context *Context) checkRequiredFlags(flags []Flag) requiredFlagsErr {
 	var missingFlags []string
 	for _, f := range flags {
 		if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() {
@@ -274,3 +188,21 @@ func checkRequiredFlags(flags []Flag, context *Context) requiredFlagsErr {
 
 	return nil
 }
+
+func makeFlagNameVisitor(names *[]string) func(*flag.Flag) {
+	return func(f *flag.Flag) {
+		nameParts := strings.Split(f.Name, ",")
+		name := strings.TrimSpace(nameParts[0])
+
+		for _, part := range nameParts {
+			part = strings.TrimSpace(part)
+			if len(part) > len(name) {
+				name = part
+			}
+		}
+
+		if name != "" {
+			*names = append(*names, name)
+		}
+	}
+}
diff --git a/docs.go b/docs.go
index 4c0e1f6..021cc2b 100644
--- a/docs.go
+++ b/docs.go
@@ -68,15 +68,16 @@ func prepareCommands(commands []*Command, level int) []string {
 		if command.Hidden {
 			continue
 		}
-		usage := ""
-		if command.Usage != "" {
-			usage = command.Usage
-		}
 
-		prepared := fmt.Sprintf("%s %s\n\n%s\n",
+		usageText := prepareUsageText(command)
+
+		usage := prepareUsage(command, usageText)
+
+		prepared := fmt.Sprintf("%s %s\n\n%s%s",
 			strings.Repeat("#", level+2),
 			strings.Join(command.Names(), ", "),
 			usage,
+			usageText,
 		)
 
 		flags := prepareArgsWithValues(command.Flags)
@@ -155,3 +156,40 @@ func flagDetails(flag DocGenerationFlag) string {
 	}
 	return ": " + description
 }
+
+func prepareUsageText(command *Command) string {
+	if command.UsageText == "" {
+		return ""
+	}
+
+	// Remove leading and trailing newlines
+	preparedUsageText := strings.Trim(command.UsageText, "\n")
+
+	var usageText string
+	if strings.Contains(preparedUsageText, "\n") {
+		// Format multi-line string as a code block using the 4 space schema to allow for embedded markdown such
+		// that it will not break the continuous code block.
+		for _, ln := range strings.Split(preparedUsageText, "\n") {
+			usageText += fmt.Sprintf("    %s\n", ln)
+		}
+	} else {
+		// Style a single line as a note
+		usageText = fmt.Sprintf(">%s\n", preparedUsageText)
+	}
+
+	return usageText
+}
+
+func prepareUsage(command *Command, usageText string) string {
+	if command.Usage == "" {
+		return ""
+	}
+
+	usage := command.Usage + "\n"
+	// Add a newline to the Usage IFF there is a UsageText
+	if usageText != "" {
+		usage += "\n"
+	}
+
+	return usage
+}
diff --git a/errors.go b/errors.go
index 751ef9b..8f641fb 100644
--- a/errors.go
+++ b/errors.go
@@ -47,6 +47,28 @@ func (m *multiError) Errors() []error {
 	return errs
 }
 
+type requiredFlagsErr interface {
+	error
+	getMissingFlags() []string
+}
+
+type errRequiredFlags struct {
+	missingFlags []string
+}
+
+func (e *errRequiredFlags) Error() string {
+	numberOfMissingFlags := len(e.missingFlags)
+	if numberOfMissingFlags == 1 {
+		return fmt.Sprintf("Required flag %q not set", e.missingFlags[0])
+	}
+	joinedMissingFlags := strings.Join(e.missingFlags, ", ")
+	return fmt.Sprintf("Required flags %q not set", joinedMissingFlags)
+}
+
+func (e *errRequiredFlags) getMissingFlags() []string {
+	return e.missingFlags
+}
+
 // ErrorFormatter is the interface that will suitably format the error output
 type ErrorFormatter interface {
 	Format(s fmt.State, verb rune)
diff --git a/flag.go b/flag.go
index aff8d5b..125c6ce 100644
--- a/flag.go
+++ b/flag.go
@@ -1,6 +1,7 @@
 package cli
 
 import (
+	"errors"
 	"flag"
 	"fmt"
 	"io/ioutil"
@@ -118,6 +119,14 @@ type DocGenerationFlag interface {
 	GetValue() string
 }
 
+// VisibleFlag is an interface that allows to check if a flag is visible
+type VisibleFlag interface {
+	Flag
+
+	// IsVisible returns true if the flag is not hidden, otherwise false
+	IsVisible() bool
+}
+
 func flagSet(name string, flags []Flag) (*flag.FlagSet, error) {
 	set := flag.NewFlagSet(name, flag.ContinueOnError)
 
@@ -130,11 +139,52 @@ func flagSet(name string, flags []Flag) (*flag.FlagSet, error) {
 	return set, nil
 }
 
+func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) {
+	switch ff.Value.(type) {
+	case Serializer:
+		_ = set.Set(name, ff.Value.(Serializer).Serialize())
+	default:
+		_ = set.Set(name, ff.Value.String())
+	}
+}
+
+func normalizeFlags(flags []Flag, set *flag.FlagSet) error {
+	visited := make(map[string]bool)
+	set.Visit(func(f *flag.Flag) {
+		visited[f.Name] = true
+	})
+	for _, f := range flags {
+		parts := f.Names()
+		if len(parts) == 1 {
+			continue
+		}
+		var ff *flag.Flag
+		for _, name := range parts {
+			name = strings.Trim(name, " ")
+			if visited[name] {
+				if ff != nil {
+					return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name)
+				}
+				ff = set.Lookup(name)
+			}
+		}
+		if ff == nil {
+			continue
+		}
+		for _, name := range parts {
+			name = strings.Trim(name, " ")
+			if !visited[name] {
+				copyFlag(name, ff, set)
+			}
+		}
+	}
+	return nil
+}
+
 func visibleFlags(fl []Flag) []Flag {
 	var visible []Flag
 	for _, f := range fl {
-		field := flagValue(f).FieldByName("Hidden")
-		if !field.IsValid() || !field.Bool() {
+		if vf, ok := f.(VisibleFlag); ok && vf.IsVisible() {
 			visible = append(visible, f)
 		}
 	}
diff --git a/flag_bool.go b/flag_bool.go
index bc9ea35..8bd5820 100644
--- a/flag_bool.go
+++ b/flag_bool.go
@@ -58,6 +58,11 @@ func (f *BoolFlag) GetValue() string {
 	return ""
 }
 
+// IsVisible returns true if the flag is not hidden, otherwise false
+func (f *BoolFlag) IsVisible() bool {
+	return !f.Hidden
+}
+
 // Apply populates the flag given the flag set and environment
 func (f *BoolFlag) Apply(set *flag.FlagSet) error {
 	if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
@@ -87,7 +92,7 @@ func (f *BoolFlag) Apply(set *flag.FlagSet) error {
 // Bool looks up the value of a local BoolFlag, returns
 // false if not found
 func (c *Context) Bool(name string) bool {
-	if fs := lookupFlagSet(name, c); fs != nil {
+	if fs := c.lookupFlagSet(name); fs != nil {
 		return lookupBool(name, fs)
 	}
 	return false
diff --git a/flag_duration.go b/flag_duration.go
index 22a2e67..28f3978 100644
--- a/flag_duration.go
+++ b/flag_duration.go
@@ -58,6 +58,11 @@ func (f *DurationFlag) GetValue() string {
 	return f.Value.String()
 }
 
+// IsVisible returns true if the flag is not hidden, otherwise false
+func (f *DurationFlag) IsVisible() bool {
+	return !f.Hidden
+}
+
 // Apply populates the flag given the flag set and environment
 func (f *DurationFlag) Apply(set *flag.FlagSet) error {
 	if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
@@ -86,7 +91,7 @@ func (f *DurationFlag) Apply(set *flag.FlagSet) error {
 // Duration looks up the value of a local DurationFlag, returns
 // 0 if not found
 func (c *Context) Duration(name string) time.Duration {
-	if fs := lookupFlagSet(name, c); fs != nil {
+	if fs := c.lookupFlagSet(name); fs != nil {
 		return lookupDuration(name, fs)
 	}
 	return 0
diff --git a/flag_float64.go b/flag_float64.go
index 91c778c..10fb8df 100644
--- a/flag_float64.go
+++ b/flag_float64.go
@@ -58,6 +58,11 @@ func (f *Float64Flag) GetValue() string {
 	return fmt.Sprintf("%f", f.Value)
 }
 
+// IsVisible returns true if the flag is not hidden, otherwise false
+func (f *Float64Flag) IsVisible() bool {
+	return !f.Hidden
+}
+
 // Apply populates the flag given the flag set and environment
 func (f *Float64Flag) Apply(set *flag.FlagSet) error {
 	if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
@@ -87,7 +92,7 @@ func (f *Float64Flag) Apply(set *flag.FlagSet) error {
 // Float64 looks up the value of a local Float64Flag, returns
 // 0 if not found
 func (c *Context) Float64(name string) float64 {
-	if fs := lookupFlagSet(name, c); fs != nil {
+	if fs := c.lookupFlagSet(name); fs != nil {
 		return lookupFloat64(name, fs)
 	}
 	return 0
diff --git a/flag_float64_slice.go b/flag_float64_slice.go
index 706ee6c..f752ad7 100644
--- a/flag_float64_slice.go
+++ b/flag_float64_slice.go
@@ -19,6 +19,16 @@ func NewFloat64Slice(defaults ...float64) *Float64Slice {
 	return &Float64Slice{slice: append([]float64{}, defaults...)}
 }
 
+// clone allocate a copy of self object
+func (f *Float64Slice) clone() *Float64Slice {
+	n := &Float64Slice{
+		slice:      make([]float64, len(f.slice)),
+		hasBeenSet: f.hasBeenSet,
+	}
+	copy(n.slice, f.slice)
+	return n
+}
+
 // Set parses the value into a float64 and appends it to the list of values
 func (f *Float64Slice) Set(value string) error {
 	if !f.hasBeenSet {
@@ -117,6 +127,11 @@ func (f *Float64SliceFlag) GetValue() string {
 	return ""
 }
 
+// IsVisible returns true if the flag is not hidden, otherwise false
+func (f *Float64SliceFlag) IsVisible() bool {
+	return !f.Hidden
+}
+
 // Apply populates the flag given the flag set and environment
 func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error {
 	if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
@@ -133,11 +148,12 @@ func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error {
 		}
 	}
 
+	if f.Value == nil {
+		f.Value = &Float64Slice{}
+	}
+	copyValue := f.Value.clone()
 	for _, name := range f.Names() {
-		if f.Value == nil {
-			f.Value = &Float64Slice{}
-		}
-		set.Var(f.Value, name, f.Usage)
+		set.Var(copyValue, name, f.Usage)
 	}
 
 	return nil
@@ -146,7 +162,7 @@ func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error {
 // Float64Slice looks up the value of a local Float64SliceFlag, returns
 // nil if not found
 func (c *Context) Float64Slice(name string) []float64 {
-	if fs := lookupFlagSet(name, c); fs != nil {
+	if fs := c.lookupFlagSet(name); fs != nil {
 		return lookupFloat64Slice(name, fs)
 	}
 	return nil
diff --git a/flag_generic.go b/flag_generic.go
index b0c8ff4..fdf586d 100644
--- a/flag_generic.go
+++ b/flag_generic.go
@@ -66,6 +66,11 @@ func (f *GenericFlag) GetValue() string {
 	return ""
 }
 
+// IsVisible returns true if the flag is not hidden, otherwise false
+func (f *GenericFlag) IsVisible() bool {
+	return !f.Hidden
+}
+
 // Apply takes the flagset and calls Set on the generic flag with the value
 // provided by the user for parsing by the flag
 func (f GenericFlag) Apply(set *flag.FlagSet) error {
@@ -89,7 +94,7 @@ func (f GenericFlag) Apply(set *flag.FlagSet) error {
 // Generic looks up the value of a local GenericFlag, returns
 // nil if not found
 func (c *Context) Generic(name string) interface{} {
-	if fs := lookupFlagSet(name, c); fs != nil {
+	if fs := c.lookupFlagSet(name); fs != nil {
 		return lookupGeneric(name, fs)
 	}
 	return nil
diff --git a/flag_int.go b/flag_int.go
index ac39d4a..1ebe356 100644
--- a/flag_int.go
+++ b/flag_int.go
@@ -58,6 +58,11 @@ func (f *IntFlag) GetValue() string {
 	return fmt.Sprintf("%d", f.Value)
 }
 
+// IsVisible returns true if the flag is not hidden, otherwise false
+func (f *IntFlag) IsVisible() bool {
+	return !f.Hidden
+}
+
 // Apply populates the flag given the flag set and environment
 func (f *IntFlag) Apply(set *flag.FlagSet) error {
 	if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
@@ -87,7 +92,7 @@ func (f *IntFlag) Apply(set *flag.FlagSet) error {
 // Int looks up the value of a local IntFlag, returns
 // 0 if not found
 func (c *Context) Int(name string) int {
-	if fs := lookupFlagSet(name, c); fs != nil {
+	if fs := c.lookupFlagSet(name); fs != nil {
 		return lookupInt(name, fs)
 	}
 	return 0
diff --git a/flag_int64.go b/flag_int64.go
index e099912..ecf0e9e 100644
--- a/flag_int64.go
+++ b/flag_int64.go
@@ -58,6 +58,11 @@ func (f *Int64Flag) GetValue() string {
 	return fmt.Sprintf("%d", f.Value)
 }
 
+// IsVisible returns true if the flag is not hidden, otherwise false
+func (f *Int64Flag) IsVisible() bool {
+	return !f.Hidden
+}
+
 // Apply populates the flag given the flag set and environment
 func (f *Int64Flag) Apply(set *flag.FlagSet) error {
 	if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
@@ -86,7 +91,7 @@ func (f *Int64Flag) Apply(set *flag.FlagSet) error {
 // Int64 looks up the value of a local Int64Flag, returns
 // 0 if not found
 func (c *Context) Int64(name string) int64 {
-	if fs := lookupFlagSet(name, c); fs != nil {
+	if fs := c.lookupFlagSet(name); fs != nil {
 		return lookupInt64(name, fs)
 	}
 	return 0
diff --git a/flag_int64_slice.go b/flag_int64_slice.go
index 2c9a15a..b4c8bc1 100644
--- a/flag_int64_slice.go
+++ b/flag_int64_slice.go
@@ -19,6 +19,16 @@ func NewInt64Slice(defaults ...int64) *Int64Slice {
 	return &Int64Slice{slice: append([]int64{}, defaults...)}
 }
 
+// clone allocate a copy of self object
+func (i *Int64Slice) clone() *Int64Slice {
+	n := &Int64Slice{
+		slice:      make([]int64, len(i.slice)),
+		hasBeenSet: i.hasBeenSet,
+	}
+	copy(n.slice, i.slice)
+	return n
+}
+
 // Set parses the value into an integer and appends it to the list of values
 func (i *Int64Slice) Set(value string) error {
 	if !i.hasBeenSet {
@@ -118,6 +128,11 @@ func (f *Int64SliceFlag) GetValue() string {
 	return ""
 }
 
+// IsVisible returns true if the flag is not hidden, otherwise false
+func (f *Int64SliceFlag) IsVisible() bool {
+	return !f.Hidden
+}
+
 // Apply populates the flag given the flag set and environment
 func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error {
 	if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
@@ -132,11 +147,12 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error {
 		f.HasBeenSet = true
 	}
 
+	if f.Value == nil {
+		f.Value = &Int64Slice{}
+	}
+	copyValue := f.Value.clone()
 	for _, name := range f.Names() {
-		if f.Value == nil {
-			f.Value = &Int64Slice{}
-		}
-		set.Var(f.Value, name, f.Usage)
+		set.Var(copyValue, name, f.Usage)
 	}
 
 	return nil
@@ -145,7 +161,7 @@ func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error {
 // Int64Slice looks up the value of a local Int64SliceFlag, returns
 // nil if not found
 func (c *Context) Int64Slice(name string) []int64 {
-	if fs := lookupFlagSet(name, c); fs != nil {
+	if fs := c.lookupFlagSet(name); fs != nil {
 		return lookupInt64Slice(name, fs)
 	}
 	return nil
diff --git a/flag_int_slice.go b/flag_int_slice.go
index a73ca6b..d4889b3 100644
--- a/flag_int_slice.go
+++ b/flag_int_slice.go
@@ -19,6 +19,16 @@ func NewIntSlice(defaults ...int) *IntSlice {
 	return &IntSlice{slice: append([]int{}, defaults...)}
 }
 
+// clone allocate a copy of self object
+func (i *IntSlice) clone() *IntSlice {
+	n := &IntSlice{
+		slice:      make([]int, len(i.slice)),
+		hasBeenSet: i.hasBeenSet,
+	}
+	copy(n.slice, i.slice)
+	return n
+}
+
 // TODO: Consistently have specific Set function for Int64 and Float64 ?
 // SetInt directly adds an integer to the list of values
 func (i *IntSlice) SetInt(value int) {
@@ -129,6 +139,11 @@ func (f *IntSliceFlag) GetValue() string {
 	return ""
 }
 
+// IsVisible returns true if the flag is not hidden, otherwise false
+func (f *IntSliceFlag) IsVisible() bool {
+	return !f.Hidden
+}
+
 // Apply populates the flag given the flag set and environment
 func (f *IntSliceFlag) Apply(set *flag.FlagSet) error {
 	if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
@@ -143,11 +158,12 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) error {
 		f.HasBeenSet = true
 	}
 
+	if f.Value == nil {
+		f.Value = &IntSlice{}
+	}
+	copyValue := f.Value.clone()
 	for _, name := range f.Names() {
-		if f.Value == nil {
-			f.Value = &IntSlice{}
-		}
-		set.Var(f.Value, name, f.Usage)
+		set.Var(copyValue, name, f.Usage)
 	}
 
 	return nil
@@ -156,7 +172,7 @@ func (f *IntSliceFlag) Apply(set *flag.FlagSet) error {
 // IntSlice looks up the value of a local IntSliceFlag, returns
 // nil if not found
 func (c *Context) IntSlice(name string) []int {
-	if fs := lookupFlagSet(name, c); fs != nil {
+	if fs := c.lookupFlagSet(name); fs != nil {
 		return lookupIntSlice(name, fs)
 	}
 	return nil
diff --git a/flag_path.go b/flag_path.go
index 8070dc4..b3aa919 100644
--- a/flag_path.go
+++ b/flag_path.go
@@ -54,6 +54,11 @@ func (f *PathFlag) GetValue() string {
 	return f.Value
 }
 
+// IsVisible returns true if the flag is not hidden, otherwise false
+func (f *PathFlag) IsVisible() bool {
+	return !f.Hidden
+}
+
 // Apply populates the flag given the flag set and environment
 func (f *PathFlag) Apply(set *flag.FlagSet) error {
 	if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
@@ -75,7 +80,7 @@ func (f *PathFlag) Apply(set *flag.FlagSet) error {
 // Path looks up the value of a local PathFlag, returns
 // "" if not found
 func (c *Context) Path(name string) string {
-	if fs := lookupFlagSet(name, c); fs != nil {
+	if fs := c.lookupFlagSet(name); fs != nil {
 		return lookupPath(name, fs)
 	}
 
diff --git a/flag_string.go b/flag_string.go
index 400bb53..e7bd412 100644
--- a/flag_string.go
+++ b/flag_string.go
@@ -55,6 +55,11 @@ func (f *StringFlag) GetValue() string {
 	return f.Value
 }
 
+// IsVisible returns true if the flag is not hidden, otherwise false
+func (f *StringFlag) IsVisible() bool {
+	return !f.Hidden
+}
+
 // Apply populates the flag given the flag set and environment
 func (f *StringFlag) Apply(set *flag.FlagSet) error {
 	if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
@@ -76,7 +81,7 @@ func (f *StringFlag) Apply(set *flag.FlagSet) error {
 // String looks up the value of a local StringFlag, returns
 // "" if not found
 func (c *Context) String(name string) string {
-	if fs := lookupFlagSet(name, c); fs != nil {
+	if fs := c.lookupFlagSet(name); fs != nil {
 		return lookupString(name, fs)
 	}
 	return ""
diff --git a/flag_string_slice.go b/flag_string_slice.go
index 3549703..5269643 100644
--- a/flag_string_slice.go
+++ b/flag_string_slice.go
@@ -18,6 +18,16 @@ func NewStringSlice(defaults ...string) *StringSlice {
 	return &StringSlice{slice: append([]string{}, defaults...)}
 }
 
+// clone allocate a copy of self object
+func (s *StringSlice) clone() *StringSlice {
+	n := &StringSlice{
+		slice:      make([]string, len(s.slice)),
+		hasBeenSet: s.hasBeenSet,
+	}
+	copy(n.slice, s.slice)
+	return n
+}
+
 // Set appends the string value to the list of values
 func (s *StringSlice) Set(value string) error {
 	if !s.hasBeenSet {
@@ -114,6 +124,11 @@ func (f *StringSliceFlag) GetValue() string {
 	return ""
 }
 
+// IsVisible returns true if the flag is not hidden, otherwise false
+func (f *StringSliceFlag) IsVisible() bool {
+	return !f.Hidden
+}
+
 // Apply populates the flag given the flag set and environment
 func (f *StringSliceFlag) Apply(set *flag.FlagSet) error {
 
@@ -144,17 +159,15 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error {
 		f.HasBeenSet = true
 	}
 
+	if f.Value == nil {
+		f.Value = &StringSlice{}
+	}
+	setValue := f.Destination
+	if f.Destination == nil {
+		setValue = f.Value.clone()
+	}
 	for _, name := range f.Names() {
-		if f.Value == nil {
-			f.Value = &StringSlice{}
-		}
-
-		if f.Destination != nil {
-			set.Var(f.Destination, name, f.Usage)
-			continue
-		}
-
-		set.Var(f.Value, name, f.Usage)
+		set.Var(setValue, name, f.Usage)
 	}
 
 	return nil
@@ -163,7 +176,7 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error {
 // StringSlice looks up the value of a local StringSliceFlag, returns
 // nil if not found
 func (c *Context) StringSlice(name string) []string {
-	if fs := lookupFlagSet(name, c); fs != nil {
+	if fs := c.lookupFlagSet(name); fs != nil {
 		return lookupStringSlice(name, fs)
 	}
 	return nil
diff --git a/flag_timestamp.go b/flag_timestamp.go
index 0382a6b..7458a79 100644
--- a/flag_timestamp.go
+++ b/flag_timestamp.go
@@ -71,6 +71,7 @@ type TimestampFlag struct {
 	Value       *Timestamp
 	DefaultText string
 	HasBeenSet  bool
+	Destination *Timestamp
 }
 
 // IsSet returns whether or not the flag has been set through env or file
@@ -113,6 +114,11 @@ func (f *TimestampFlag) GetValue() string {
 	return ""
 }
 
+// IsVisible returns true if the flag is not hidden, otherwise false
+func (f *TimestampFlag) IsVisible() bool {
+	return !f.Hidden
+}
+
 // Apply populates the flag given the flag set and environment
 func (f *TimestampFlag) Apply(set *flag.FlagSet) error {
 	if f.Layout == "" {
@@ -123,6 +129,10 @@ func (f *TimestampFlag) Apply(set *flag.FlagSet) error {
 	}
 	f.Value.SetLayout(f.Layout)
 
+	if f.Destination != nil {
+		f.Destination.SetLayout(f.Layout)
+	}
+
 	if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
 		if err := f.Value.Set(val); err != nil {
 			return fmt.Errorf("could not parse %q as timestamp value for flag %s: %s", val, f.Name, err)
@@ -131,6 +141,11 @@ func (f *TimestampFlag) Apply(set *flag.FlagSet) error {
 	}
 
 	for _, name := range f.Names() {
+		if f.Destination != nil {
+			set.Var(f.Destination, name, f.Usage)
+			continue
+		}
+
 		set.Var(f.Value, name, f.Usage)
 	}
 	return nil
@@ -138,7 +153,7 @@ func (f *TimestampFlag) Apply(set *flag.FlagSet) error {
 
 // Timestamp gets the timestamp from a flag name
 func (c *Context) Timestamp(name string) *time.Time {
-	if fs := lookupFlagSet(name, c); fs != nil {
+	if fs := c.lookupFlagSet(name); fs != nil {
 		return lookupTimestamp(name, fs)
 	}
 	return nil
diff --git a/flag_uint.go b/flag_uint.go
index 2e5e76b..23a70a7 100644
--- a/flag_uint.go
+++ b/flag_uint.go
@@ -52,6 +52,11 @@ func (f *UintFlag) GetUsage() string {
 	return f.Usage
 }
 
+// IsVisible returns true if the flag is not hidden, otherwise false
+func (f *UintFlag) IsVisible() bool {
+	return !f.Hidden
+}
+
 // Apply populates the flag given the flag set and environment
 func (f *UintFlag) Apply(set *flag.FlagSet) error {
 	if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
@@ -86,7 +91,7 @@ func (f *UintFlag) GetValue() string {
 // Uint looks up the value of a local UintFlag, returns
 // 0 if not found
 func (c *Context) Uint(name string) uint {
-	if fs := lookupFlagSet(name, c); fs != nil {
+	if fs := c.lookupFlagSet(name); fs != nil {
 		return lookupUint(name, fs)
 	}
 	return 0
diff --git a/flag_uint64.go b/flag_uint64.go
index 8fc3289..a2df024 100644
--- a/flag_uint64.go
+++ b/flag_uint64.go
@@ -52,6 +52,11 @@ func (f *Uint64Flag) GetUsage() string {
 	return f.Usage
 }
 
+// IsVisible returns true if the flag is not hidden, otherwise false
+func (f *Uint64Flag) IsVisible() bool {
+	return !f.Hidden
+}
+
 // Apply populates the flag given the flag set and environment
 func (f *Uint64Flag) Apply(set *flag.FlagSet) error {
 	if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
@@ -86,7 +91,7 @@ func (f *Uint64Flag) GetValue() string {
 // Uint64 looks up the value of a local Uint64Flag, returns
 // 0 if not found
 func (c *Context) Uint64(name string) uint64 {
-	if fs := lookupFlagSet(name, c); fs != nil {
+	if fs := c.lookupFlagSet(name); fs != nil {
 		return lookupUint64(name, fs)
 	}
 	return 0


Test patch:
diff --git a/context_test.go b/context_test.go
index 35feefe..b37876c 100644
--- a/context_test.go
+++ b/context_test.go
@@ -316,13 +316,13 @@ func TestContext_lookupFlagSet(t *testing.T) {
 	_ = set.Parse([]string{"--local-flag"})
 	_ = parentSet.Parse([]string{"--top-flag"})
 
-	fs := lookupFlagSet("top-flag", ctx)
+	fs := ctx.lookupFlagSet("top-flag")
 	expect(t, fs, parentCtx.flagSet)
 
-	fs = lookupFlagSet("local-flag", ctx)
+	fs = ctx.lookupFlagSet("local-flag")
 	expect(t, fs, ctx.flagSet)
 
-	if fs := lookupFlagSet("frob", ctx); fs != nil {
+	if fs := ctx.lookupFlagSet("frob"); fs != nil {
 		t.Fail()
 	}
 }
@@ -576,7 +576,7 @@ func TestCheckRequiredFlags(t *testing.T) {
 			ctx.Command.Flags = test.flags
 
 			// logic under test
-			err := checkRequiredFlags(test.flags, ctx)
+			err := ctx.checkRequiredFlags(test.flags)
 
 			// assertions
 			if test.expectedAnError && err == nil {
diff --git a/docs_test.go b/docs_test.go
index fac106f..962c366 100644
--- a/docs_test.go
+++ b/docs_test.go
@@ -67,6 +67,47 @@ func testApp() *App {
 	}, {
 		Name:   "hidden-command",
 		Hidden: true,
+	}, {
+		Aliases: []string{"u"},
+		Flags: []Flag{
+			&StringFlag{
+				Name:      "flag",
+				Aliases:   []string{"fl", "f"},
+				TakesFile: true,
+			},
+			&BoolFlag{
+				Name:    "another-flag",
+				Aliases: []string{"b"},
+				Usage:   "another usage text",
+			},
+		},
+		Name:  "usage",
+		Usage: "standard usage text",
+		UsageText: `
+Usage for the usage text
+- formatted:  Based on the specified ConfigMap and summon secrets.yml
+- list:       Inspect the environment for a specific process running on a Pod
+- for_effect: Compare 'namespace' environment with 'local'
+
+` + "```" + `
+func() { ... }
+` + "```" + `
+
+Should be a part of the same code block
+`,
+		Subcommands: []*Command{{
+			Aliases: []string{"su"},
+			Flags: []Flag{
+				&BoolFlag{
+					Name:    "sub-command-flag",
+					Aliases: []string{"s"},
+					Usage:   "some usage text",
+				},
+			},
+			Name:      "sub-usage",
+			Usage:     "standard usage text",
+			UsageText: "Single line of UsageText",
+		}},
 	}}
 	app.UsageText = "app [first_arg] [second_arg]"
 	app.Usage = "Some app"
@@ -175,3 +216,110 @@ func TestToManWithSection(t *testing.T) {
 	expect(t, err, nil)
 	expectFileContent(t, "testdata/expected-doc-full.man", res)
 }
+
+func Test_prepareUsageText(t *testing.T) {
+	t.Run("no UsageText provided", func(t *testing.T) {
+		// Given
+		cmd := Command{}
+
+		// When
+		res := prepareUsageText(&cmd)
+
+		// Then
+		expect(t, res, "")
+	})
+
+	t.Run("single line UsageText", func(t *testing.T) {
+		// Given
+		cmd := Command{UsageText: "Single line usage text"}
+
+		// When
+		res := prepareUsageText(&cmd)
+
+		// Then
+		expect(t, res, ">Single line usage text\n")
+	})
+
+	t.Run("multiline UsageText", func(t *testing.T) {
+		// Given
+		cmd := Command{
+			UsageText: `
+Usage for the usage text
+- Should be a part of the same code block
+`,
+		}
+
+		// When
+		res := prepareUsageText(&cmd)
+
+		// Then
+		test := `    Usage for the usage text
+    - Should be a part of the same code block
+`
+		expect(t, res, test)
+	})
+
+	t.Run("multiline UsageText has formatted embedded markdown", func(t *testing.T) {
+		// Given
+		cmd := Command{
+			UsageText: `
+Usage for the usage text
+
+` + "```" + `
+func() { ... }
+` + "```" + `
+
+Should be a part of the same code block
+`,
+		}
+
+		// When
+		res := prepareUsageText(&cmd)
+
+		// Then
+		test := `    Usage for the usage text
+    
+    ` + "```" + `
+    func() { ... }
+    ` + "```" + `
+    
+    Should be a part of the same code block
+`
+		expect(t, res, test)
+	})
+}
+
+func Test_prepareUsage(t *testing.T) {
+	t.Run("no Usage provided", func(t *testing.T) {
+		// Given
+		cmd := Command{}
+
+		// When
+		res := prepareUsage(&cmd, "")
+
+		// Then
+		expect(t, res, "")
+	})
+
+	t.Run("simple Usage", func(t *testing.T) {
+		// Given
+		cmd := Command{Usage: "simple usage text"}
+
+		// When
+		res := prepareUsage(&cmd, "")
+
+		// Then
+		expect(t, res, cmd.Usage+"\n")
+	})
+
+	t.Run("simple Usage with UsageText", func(t *testing.T) {
+		// Given
+		cmd := Command{Usage: "simple usage text"}
+
+		// When
+		res := prepareUsage(&cmd, "a non-empty string")
+
+		// Then
+		expect(t, res, cmd.Usage+"\n\n")
+	})
+}
diff --git a/flag_test.go b/flag_test.go
index b3b0d7c..e46270d 100644
--- a/flag_test.go
+++ b/flag_test.go
@@ -1973,3 +1973,80 @@ func TestTimestampFlagApply_Fail_Parse_Wrong_Time(t *testing.T) {
 	err := set.Parse([]string{"--time", "2006-01-02T15:04:05Z"})
 	expect(t, err, fmt.Errorf("invalid value \"2006-01-02T15:04:05Z\" for flag -time: parsing time \"2006-01-02T15:04:05Z\" as \"Jan 2, 2006 at 3:04pm (MST)\": cannot parse \"2006-01-02T15:04:05Z\" as \"Jan\""))
 }
+
+type flagDefaultTestCase struct {
+	name    string
+	flag    Flag
+	toParse []string
+	expect  string
+}
+
+func TestFlagDefaultValue(t *testing.T) {
+	cases := []*flagDefaultTestCase{
+		&flagDefaultTestCase{
+			name:    "stringSclice",
+			flag:    &StringSliceFlag{Name: "flag", Value: NewStringSlice("default1", "default2")},
+			toParse: []string{"--flag", "parsed"},
+			expect: `--flag value	(default: "default1", "default2")	(accepts multiple inputs)`,
+		},
+		&flagDefaultTestCase{
+			name:    "float64Sclice",
+			flag:    &Float64SliceFlag{Name: "flag", Value: NewFloat64Slice(1.1, 2.2)},
+			toParse: []string{"--flag", "13.3"},
+			expect: `--flag value	(default: 1.1, 2.2)	(accepts multiple inputs)`,
+		},
+		&flagDefaultTestCase{
+			name:    "int64Sclice",
+			flag:    &Int64SliceFlag{Name: "flag", Value: NewInt64Slice(1, 2)},
+			toParse: []string{"--flag", "13"},
+			expect: `--flag value	(default: 1, 2)	(accepts multiple inputs)`,
+		},
+		&flagDefaultTestCase{
+			name:    "intSclice",
+			flag:    &IntSliceFlag{Name: "flag", Value: NewIntSlice(1, 2)},
+			toParse: []string{"--flag", "13"},
+			expect: `--flag value	(default: 1, 2)	(accepts multiple inputs)`,
+		},
+		&flagDefaultTestCase{
+			name:    "string",
+			flag:    &StringFlag{Name: "flag", Value: "default"},
+			toParse: []string{"--flag", "parsed"},
+			expect: `--flag value	(default: "default")`,
+		},
+		&flagDefaultTestCase{
+			name:    "bool",
+			flag:    &BoolFlag{Name: "flag", Value: true},
+			toParse: []string{"--flag", "false"},
+			expect: `--flag	(default: true)`,
+		},
+		&flagDefaultTestCase{
+			name:    "uint64",
+			flag:    &Uint64Flag{Name: "flag", Value: 1},
+			toParse: []string{"--flag", "13"},
+			expect: `--flag value	(default: 1)`,
+		},
+	}
+	for i, v := range cases {
+		set := flag.NewFlagSet("test", 0)
+		set.SetOutput(ioutil.Discard)
+		_ = v.flag.Apply(set)
+		if err := set.Parse(v.toParse); err != nil {
+			t.Error(err)
+		}
+		if got := v.flag.String(); got != v.expect {
+			t.Errorf("TestFlagDefaultValue %d %s\nexpect:%s\ngot:%s", i, v.name, v.expect, got)
+		}
+	}
+}
+
+func TestTimestampFlagApply_WithDestination(t *testing.T) {
+	var destination Timestamp
+	expectedResult, _ := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
+	fl := TimestampFlag{Name: "time", Aliases: []string{"t"}, Layout: time.RFC3339, Destination: &destination}
+	set := flag.NewFlagSet("test", 0)
+	_ = fl.Apply(set)
+
+	err := set.Parse([]string{"--time", "2006-01-02T15:04:05Z"})
+	expect(t, err, nil)
+	expect(t, *fl.Destination.timestamp, expectedResult)
+}
diff --git a/helpers_test.go b/helpers_test.go
index 9217e89..9ecd8e1 100644
--- a/helpers_test.go
+++ b/helpers_test.go
@@ -3,24 +3,17 @@ package cli
 import (
 	"os"
 	"reflect"
-	"runtime"
-	"strings"
 	"testing"
 )
 
-var (
-	wd, _ = os.Getwd()
-)
-
 func init() {
 	_ = os.Setenv("CLI_TEMPLATE_REPANIC", "1")
 }
 
 func expect(t *testing.T, a interface{}, b interface{}) {
-	_, fn, line, _ := runtime.Caller(1)
-	fn = strings.Replace(fn, wd+"/", "", -1)
+	t.Helper()
 
 	if !reflect.DeepEqual(a, b) {
-		t.Errorf("(%s:%d) Expected %v (type %v) - Got %v (type %v)", fn, line, b, reflect.TypeOf(b), a, reflect.TypeOf(a))
+		t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
 	}
 }
diff --git a/testdata/expected-doc-full.man b/testdata/expected-doc-full.man
index 2bea507..3ac3553 100644
--- a/testdata/expected-doc-full.man
+++ b/testdata/expected-doc-full.man
@@ -75,4 +75,46 @@ another usage test
 .PP
 retrieve generic information
 
-.SH some\-command
\ No newline at end of file
+.SH some\-command
+.SH usage, u
+.PP
+standard usage text
+
+.PP
+.RS
+
+.nf
+Usage for the usage text
+\- formatted:  Based on the specified ConfigMap and summon secrets.yml
+\- list:       Inspect the environment for a specific process running on a Pod
+\- for\_effect: Compare 'namespace' environment with 'local'
+
+```
+func() { ... }
+```
+
+Should be a part of the same code block
+
+.fi
+.RE
+
+.PP
+\fB\-\-another\-flag, \-b\fP: another usage text
+
+.PP
+\fB\-\-flag, \-\-fl, \-f\fP="":
+
+.SS sub\-usage, su
+.PP
+standard usage text
+
+.PP
+.RS
+
+.PP
+Single line of UsageText
+
+.RE
+
+.PP
+\fB\-\-sub\-command\-flag, \-s\fP: some usage text
diff --git a/testdata/expected-doc-full.md b/testdata/expected-doc-full.md
index f272274..7b8c0d7 100644
--- a/testdata/expected-doc-full.md
+++ b/testdata/expected-doc-full.md
@@ -58,3 +58,29 @@ retrieve generic information
 ## some-command
 
 
+## usage, u
+
+standard usage text
+
+    Usage for the usage text
+    - formatted:  Based on the specified ConfigMap and summon secrets.yml
+    - list:       Inspect the environment for a specific process running on a Pod
+    - for_effect: Compare 'namespace' environment with 'local'
+    
+    ```
+    func() { ... }
+    ```
+    
+    Should be a part of the same code block
+
+**--another-flag, -b**: another usage text
+
+**--flag, --fl, -f**="": 
+
+### sub-usage, su
+
+standard usage text
+
+>Single line of UsageText
+
+**--sub-command-flag, -s**: some usage text
diff --git a/testdata/expected-doc-no-authors.md b/testdata/expected-doc-no-authors.md
index f272274..7b8c0d7 100644
--- a/testdata/expected-doc-no-authors.md
+++ b/testdata/expected-doc-no-authors.md
@@ -58,3 +58,29 @@ retrieve generic information
 ## some-command
 
 
+## usage, u
+
+standard usage text
+
+    Usage for the usage text
+    - formatted:  Based on the specified ConfigMap and summon secrets.yml
+    - list:       Inspect the environment for a specific process running on a Pod
+    - for_effect: Compare 'namespace' environment with 'local'
+    
+    ```
+    func() { ... }
+    ```
+    
+    Should be a part of the same code block
+
+**--another-flag, -b**: another usage text
+
+**--flag, --fl, -f**="": 
+
+### sub-usage, su
+
+standard usage text
+
+>Single line of UsageText
+
+**--sub-command-flag, -s**: some usage text
diff --git a/testdata/expected-doc-no-flags.md b/testdata/expected-doc-no-flags.md
index 2994593..ebd0416 100644
--- a/testdata/expected-doc-no-flags.md
+++ b/testdata/expected-doc-no-flags.md
@@ -43,3 +43,29 @@ retrieve generic information
 ## some-command
 
 
+## usage, u
+
+standard usage text
+
+    Usage for the usage text
+    - formatted:  Based on the specified ConfigMap and summon secrets.yml
+    - list:       Inspect the environment for a specific process running on a Pod
+    - for_effect: Compare 'namespace' environment with 'local'
+    
+    ```
+    func() { ... }
+    ```
+    
+    Should be a part of the same code block
+
+**--another-flag, -b**: another usage text
+
+**--flag, --fl, -f**="": 
+
+### sub-usage, su
+
+standard usage text
+
+>Single line of UsageText
+
+**--sub-command-flag, -s**: some usage text
diff --git a/testdata/expected-fish-full.fish b/testdata/expected-fish-full.fish
index dc41e5a..cc449c5 100644
--- a/testdata/expected-fish-full.fish
+++ b/testdata/expected-fish-full.fish
@@ -2,7 +2,7 @@
 
 function __fish_greet_no_subcommand --description 'Test if there has been any subcommand yet'
     for i in (commandline -opc)
-        if contains -- $i config c sub-config s ss info i in some-command
+        if contains -- $i config c sub-config s ss info i in some-command usage u sub-usage su
             return 1
         end
     end
@@ -27,3 +27,10 @@ complete -c greet -n '__fish_seen_subcommand_from info i in' -f -l help -s h -d
 complete -r -c greet -n '__fish_greet_no_subcommand' -a 'info i in' -d 'retrieve generic information'
 complete -c greet -n '__fish_seen_subcommand_from some-command' -f -l help -s h -d 'show help'
 complete -r -c greet -n '__fish_greet_no_subcommand' -a 'some-command'
+complete -c greet -n '__fish_seen_subcommand_from usage u' -f -l help -s h -d 'show help'
+complete -r -c greet -n '__fish_greet_no_subcommand' -a 'usage u' -d 'standard usage text'
+complete -c greet -n '__fish_seen_subcommand_from usage u' -l flag -s fl -s f -r
+complete -c greet -n '__fish_seen_subcommand_from usage u' -f -l another-flag -s b -d 'another usage text'
+complete -c greet -n '__fish_seen_subcommand_from sub-usage su' -f -l help -s h -d 'show help'
+complete -r -c greet -n '__fish_seen_subcommand_from usage u' -a 'sub-usage su' -d 'standard usage text'
+complete -c greet -n '__fish_seen_subcommand_from sub-usage su' -f -l sub-command-flag -s s -d 'some usage text'