Repository: urfave/cli
Pull request #1168 (2020-07-30 10:14:07)

Report the source of a value when we cannot parse it
## What type of PR is this?

- feature

## What this PR does / why we need it

If you allow a flag to be set from environment variables or files and a parse error occurs from one of them, it is very useful for the error message to mention where the value came from.

Without this, it can be difficult to notice an error caused by an unexpected environment variable being set.

Implements #1167.

The existing function `flagFromEnvOrFile` takes a slice of strings (environment variable names) and a file path, and checks each to see if they exist. If they do, then it returns the value and a bool which is true if a value was found.

This patch modifies it to also return a string description of where the value was found. Callers have been updated to use this extra value when reporting errors.

## Which issue(s) this PR fixes:

Implements #1167.

## Special notes for your reviewer:

I haven't quite figured out what should be done in flag_path.go and flag_string.go yet- any advice?

## Testing

Existing tests have been updated to expect the new error strings.

## Release Notes

```release-note
NONE
```

stale[bot]: This issue or PR has been automatically marked as stale because it has not had recent activity. Please add a comment bumping this if you're still interested in it's resolution! Thanks for your help, please let us know if you need anything else.

mostynb: Bump.
stale[bot]: This issue or PR has been bumped and is no longer marked as stale! Feel free to bump it again in the future, if it's still relevant.


Patch:
diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md
index b968075..bca4fea 100644
--- a/.github/ISSUE_TEMPLATE/question.md
+++ b/.github/ISSUE_TEMPLATE/question.md
@@ -1,10 +1,12 @@
 ---
 name: ask a question
-about: ask a question - assume stackoverflow's guidelines apply here
-title: your question title goes here
+about: ask us question - assume stackoverflow's guidelines apply here
+title: 'q: ( your question title goes here )'
 labels: 'kind/question, status/triage, area/v2'
 assignees: ''
 
 ---
 
-my question is...
+## my question is...
+
+_**( Put the question text here )**_
diff --git a/.github/ISSUE_TEMPLATE/v1-bug-report.md b/.github/ISSUE_TEMPLATE/v1-bug-report.md
index 1205d38..aabfe82 100644
--- a/.github/ISSUE_TEMPLATE/v1-bug-report.md
+++ b/.github/ISSUE_TEMPLATE/v1-bug-report.md
@@ -1,32 +1,28 @@
 ---
 name: v1 bug report
 about: Create a report to help us fix v1 bugs
-title: 'your bug title goes here'
+title: 'v1 bug: ( your bug title goes here )'
 labels: 'kind/bug, status/triage, area/v1'
 assignees: ''
 
 ---
 
-## My urfave/cli version is
+## my urfave/cli version is
 
 _**( Put the version of urfave/cli that you are using here )**_
 
 ## Checklist
 
-- [ ] Are you running the latest v1 release? The list of releases is [here](https://github.com/urfave/cli/releases).
-- [ ] Did you check the manual for your release? The v1 manual is [here](https://github.com/urfave/cli/blob/main/docs/v1/manual.md).
-- [ ] Did you perform a search about this problem? Here's the [Github guide](https://help.github.com/en/github/managing-your-work-on-github/using-search-to-filter-issues-and-pull-requests) about searching.
+* [ ] Are you running the latest v1 release? The list of releases is [here](https://github.com/urfave/cli/releases).
+* [ ] Did you check the manual for your release? The v1 manual is [here](https://github.com/urfave/cli/blob/master/docs/v1/manual.md)
+* [ ] Did you perform a search about this problem? Here's the [Github guide](https://help.github.com/en/github/managing-your-work-on-github/using-search-to-filter-issues-and-pull-requests) about searching.
 
 ## Dependency Management
 
-<!--
-  Delete any of the following that do not apply:
--->
-
-- My project is using go modules.
-- My project is using vendoring.
-- My project is automatically downloading the latest version.
-- I am unsure of what my dependency management setup is.
+- [ ] My project is using go modules.
+- [ ] My project is using vendoring.
+- [ ] My project is automatically downloading the latest version.
+- [ ] I am unsure of what my dependency management setup is.
 
 ## Describe the bug
 
@@ -38,30 +34,23 @@ Describe the steps or code required to reproduce the behavior
 
 ## Observed behavior
 
-What did you see happen immediately after the reproduction steps
-above?
+What did you see happen immediately after the reproduction steps above?
 
 ## Expected behavior
 
-What would you have expected to happen immediately after the
-reproduction steps above?
+What would you have expected to happen immediately after the reproduction steps above?
 
 ## Additional context
 
 Add any other context about the problem here.
 
-If the issue relates to a specific open source Github repo, please
-link that repo here.
+If the issue relates to a specific open source Github repo, please link that repo here.
 
-If you can reproduce this issue with a public CI system, please
-link a failing build here.
+If you can reproduce this issue with a public CI system, please link a failing build here.
 
 ## Want to fix this yourself?
 
-We'd love to have more contributors on this project! If the fix for
-this bug is easily explained and very small, free free to create a
-pull request for it. You'll want to base the PR off the `v1`
-branch, all `v1` bug fix releases will be made from that branch.
+We'd love to have more contributors on this project! If the fix for this bug is easily explained and very small, free free to create a pull request for it. You'll want to base the PR off the `v1` branch, all `v1` bug fix releases will be made from that branch.
 
 ## Run `go version` and paste its output here
 
diff --git a/.github/ISSUE_TEMPLATE/v2-bug-report.md b/.github/ISSUE_TEMPLATE/v2-bug-report.md
index 5c120e1..9944769 100644
--- a/.github/ISSUE_TEMPLATE/v2-bug-report.md
+++ b/.github/ISSUE_TEMPLATE/v2-bug-report.md
@@ -1,32 +1,28 @@
 ---
 name: v2 bug report
 about: Create a report to help us fix v2 bugs
-title: 'your bug title goes here'
+title: 'v2 bug: ( your bug title goes here )'
 labels: 'kind/bug, area/v2, status/triage'
 assignees: ''
 
 ---
 
-## My urfave/cli version is
+## my urfave/cli version is
 
 _**( Put the version of urfave/cli that you are using here )**_
 
 ## Checklist
 
-- [ ] Are you running the latest v2 release? The list of releases is [here](https://github.com/urfave/cli/releases).
-- [ ] Did you check the manual for your release? The v2 manual is [here](https://github.com/urfave/cli/blob/main/docs/v2/manual.md)
-- [ ] Did you perform a search about this problem? Here's the [Github guide](https://help.github.com/en/github/managing-your-work-on-github/using-search-to-filter-issues-and-pull-requests) about searching.
+* [ ] Are you running the latest v2 release? The list of releases is [here](https://github.com/urfave/cli/releases).
+* [ ] Did you check the manual for your release? The v2 manual is [here](https://github.com/urfave/cli/blob/master/docs/v2/manual.md)
+* [ ] Did you perform a search about this problem? Here's the [Github guide](https://help.github.com/en/github/managing-your-work-on-github/using-search-to-filter-issues-and-pull-requests) about searching.
 
 ## Dependency Management
 
-<!--
-  Delete any of the following that do not apply:
--->
-
-- My project is using go modules.
-- My project is using vendoring.
-- My project is automatically downloading the latest version.
-- I am unsure of what my dependency management setup is.
+- [ ] My project is using go modules.
+- [ ] My project is using vendoring.
+- [ ] My project is automatically downloading the latest version.
+- [ ] I am unsure of what my dependency management setup is.
 
 ## Describe the bug
 
@@ -38,30 +34,23 @@ Describe the steps or code required to reproduce the behavior
 
 ## Observed behavior
 
-What did you see happen immediately after the reproduction steps
-above?
+What did you see happen immediately after the reproduction steps above?
 
 ## Expected behavior
 
-What would you have expected to happen immediately after the
-reproduction steps above?
+What would you have expected to happen immediately after the reproduction steps above?
 
 ## Additional context
 
 Add any other context about the problem here.
 
-If the issue relates to a specific open source Github repo, please
-link that repo here.
+If the issue relates to a specific open source Github repo, please link that repo here.
 
-If you can reproduce this issue with a public CI system, please
-link a failing build here.
+If you can reproduce this issue with a public CI system, please link a failing build here.
 
 ## Want to fix this yourself?
 
-We'd love to have more contributors on this project! If the fix for
-this bug is easily explained and very small, free free to create a
-pull request for it.
-
+We'd love to have more contributors on this project! If the fix for this bug is easily explained and very small, free free to create a pull request for it.
 ## Run `go version` and paste its output here
 
 ```
diff --git a/.github/ISSUE_TEMPLATE/v2-feature-request.md b/.github/ISSUE_TEMPLATE/v2-feature-request.md
index 2b1e727..bc527b1 100644
--- a/.github/ISSUE_TEMPLATE/v2-feature-request.md
+++ b/.github/ISSUE_TEMPLATE/v2-feature-request.md
@@ -1,7 +1,7 @@
 ---
 name: v2 feature request
 about: Suggest an improvement for v2
-title: 'your feature title goes here'
+title: 'v2 feature: ( your feature title goes here )'
 labels: 'type/feature, area/v2, status/triage'
 assignees: ''
 
@@ -10,19 +10,16 @@ assignees: ''
 ## Checklist
 
 * [ ] Are you running the latest v2 release? The list of releases is [here](https://github.com/urfave/cli/releases).
-* [ ] Did you check the manual for your release? The v2 manual is [here](https://github.com/urfave/cli/blob/main/docs/v2/manual.md).
+* [ ] Did you check the manual for your release? The v2 manual is [here](https://github.com/urfave/cli/blob/master/docs/v2/manual.md)
 * [ ] Did you perform a search about this feature? Here's the [Github guide](https://help.github.com/en/github/managing-your-work-on-github/using-search-to-filter-issues-and-pull-requests) about searching.
 
 ## What problem does this solve?
 
 A clear and concise description of what problem this feature would solve. For example:
 
-- needing to type out the full flag name takes a long time, so I
-  would like to suggest adding auto-complete
-- I use (osx, windows, linux) and would like support for (some
-  existing feature) to be extended to my platform
-- the terminal output for a particular error case is confusing, and
-  I think it could be improved
+- needing to type out the full flag name takes a long time, so I would like to suggest adding auto-complete
+- I use (osx, windows, linux) and would like support for (some existing feature) to be extended to my platform
+- the terminal output for a particular error case is confusing, and I think it could be improved
 
 ## Solution description
 
@@ -30,5 +27,4 @@ A detailed description of what you want to happen.
 
 ## Describe alternatives you've considered
 
-A clear and concise description of any alternative solutions or
-features you've considered.
+A clear and concise description of any alternative solutions or features you've considered.
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 47348f8..4e76725 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -8,14 +8,10 @@
 
 _(REQUIRED)_
 
-<!--
-  Delete any of the following that do not apply:
- -->
-
-- bug
-- cleanup
-- documentation
-- feature
+- [ ] bug
+- [ ] cleanup  
+- [ ] documentation
+- [ ] feature
 
 ## What this PR does / why we need it:
 
@@ -32,7 +28,6 @@ _(REQUIRED)_
 ## Which issue(s) this PR fixes:
 
 _(REQUIRED)_
-
 <!--
 If this PR fixes one of more issues, list them here.
 One line each, like so:
diff --git a/.github/stale.yml b/.github/stale.yml
index f050e9b..a3282a2 100644
--- a/.github/stale.yml
+++ b/.github/stale.yml
@@ -1,17 +1,63 @@
-# Number of days of inactivity before an issue becomes stale
-daysUntilStale: 365
-# Number of days of inactivity before a stale issue is closed
-daysUntilClose: 90
-# Issues with these labels will never be considered stale
+# Configuration for probot-stale - https://github.com/probot/stale
+
+# Number of days of inactivity before an Issue or Pull Request becomes stale
+daysUntilStale: 90
+
+# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
+# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
+daysUntilClose: 30
+
+# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
+onlyLabels: []
+
+# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
 exemptLabels:
   - pinned
   - security
-# Label to use when marking an issue as stale
-staleLabel: wontfix
-# Comment to post when marking an issue as stale. Set to `false` to disable
+  - "kind/maintenance" 
+
+# Set to true to ignore issues in a project (defaults to false)
+exemptProjects: false
+
+# Set to true to ignore issues in a milestone (defaults to false)
+exemptMilestones: false
+
+# Set to true to ignore issues with an assignee (defaults to false)
+exemptAssignees: false
+
+# Label to use when marking as stale
+staleLabel: "status/stale"
+
+# Comment to post when marking as stale. Set to `false` to disable
 markComment: >
-  This issue has been automatically marked as stale because it has not had
-  recent activity. It will be closed if no further activity occurs. Thank you
-  for your contributions.
-# Comment to post when closing a stale issue. Set to `false` to disable
-closeComment: false
+  This issue or PR has been automatically marked as stale because it has not had
+  recent activity. Please add a comment bumping this if you're still
+  interested in it's resolution! Thanks for your help, please let us know
+  if you need anything else.
+
+# Comment to post when removing the stale label.
+unmarkComment: >
+  This issue or PR has been bumped and is no longer marked as stale! Feel free
+  to bump it again in the future, if it's still relevant.
+
+# Comment to post when closing a stale Issue or Pull Request.
+closeComment: >
+  Closing this as it has become stale.
+
+# Limit the number of actions per hour, from 1-30. Default is 30
+limitPerRun: 30
+
+# Limit to only `issues` or `pulls`
+# only: issues
+
+# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
+# pulls:
+#   daysUntilStale: 30
+#   markComment: >
+#     This pull request has been automatically marked as stale because it has not had
+#     recent activity. It will be closed if no further activity occurs. Thank you
+#     for your contributions.
+
+# issues:
+#   exemptLabels:
+#     - confirmed
diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml
index e9a45c2..9fa056e 100644
--- a/.github/workflows/cli.yml
+++ b/.github/workflows/cli.yml
@@ -3,35 +3,43 @@ name: Run Tests
 on:
   push:
     branches:
-      - main
-    tags:
-      - v2.*
+      - master
+      - v1
   pull_request:
     branches:
-      - main
+      - master
+      - v1
 
 jobs:
+
   test:
     strategy:
       matrix:
         os: [ubuntu-latest, macos-latest, windows-latest]
-        go: [1.16.x, 1.17.x, 1.18.x]
+        go: [1.13, 1.14, 1.15]
     name: ${{ matrix.os }} @ Go ${{ matrix.go }}
     runs-on: ${{ matrix.os }}
     steps:
       - name: Set up Go ${{ matrix.go }}
-        uses: actions/setup-go@v3
+        uses: actions/setup-go@v1
         with:
           go-version: ${{ matrix.go }}
 
-      - name: Set PATH
-        run: echo "${GITHUB_WORKSPACE}/.local/bin" >>"${GITHUB_PATH}"
+      - name: Set GOPATH, PATH and ENV
+        run: |
+          echo "::set-env name=GOPATH::$(dirname $GITHUB_WORKSPACE)"
+          echo "::set-env name=GO111MODULE::on"
+          echo "::set-env name=GOPROXY::https://proxy.golang.org"
+          echo "::add-path::$(dirname $GITHUB_WORKSPACE)/bin"
+        shell: bash
 
       - name: Checkout Code
-        uses: actions/checkout@v3
+        uses: actions/checkout@v1
+        with:
+          ref: ${{ github.ref }}
 
       - name: GOFMT Check
-        if: matrix.go == '1.17.x' && matrix.os == 'ubuntu-latest'
+        if: matrix.go == 1.15 && matrix.os == 'ubuntu-latest'
         run: test -z $(gofmt -l .)
 
       - name: vet
@@ -44,40 +52,54 @@ jobs:
         run: go run internal/build/build.go check-binary-size
 
       - name: Upload coverage to Codecov
-        if: success() && matrix.go == '1.17.x' && matrix.os == 'ubuntu-latest'
-        uses: codecov/codecov-action@v2
+        if: success() && matrix.go == 1.14 && 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
-        uses: actions/setup-go@v3
+      - name: Set up Go 1.15
+        uses: actions/setup-go@v1
         with:
-          go-version: 1.18.x
+          go-version: 1.15
 
-      - name: Use Node.js 16
-        uses: actions/setup-node@v3
+      - name: Use Node.js 12.x
+        uses: actions/setup-node@v1
         with:
-          node-version: '16'
+          node-version: 12.x
 
-      - name: Set PATH
-        run: echo "${GITHUB_WORKSPACE}/.local/bin" >>"${GITHUB_PATH}"
+      - name: Set GOPATH, PATH and ENV
+        run: |
+          echo "::set-env name=GOPATH::$(dirname $GITHUB_WORKSPACE)"
+          echo "::set-env name=GO111MODULE::on"
+          echo "::set-env name=GOPROXY::https://proxy.golang.org"
+          echo "::add-path::$(dirname $GITHUB_WORKSPACE)/bin"
+        shell: bash
 
       - name: Checkout Code
-        uses: actions/checkout@v3
+        uses: actions/checkout@v1
+        with:
+          ref: ${{ github.ref }}
 
       - name: Install Dependencies
-        run:
-          mkdir -p "${GITHUB_WORKSPACE}/.local/bin" &&
-          curl -fsSL -o "${GITHUB_WORKSPACE}/.local/bin/gfmrun" "https://github.com/urfave/gfmrun/releases/download/v1.3.0/gfmrun-$(go env GOOS)-$(go env GOARCH)-v1.3.0" &&
-          chmod +x "${GITHUB_WORKSPACE}/.local/bin/gfmrun" &&
+        run: |
+          mkdir -p $GOPATH/bin
+          curl -L -o $GOPATH/bin/gfmrun "https://github.com/urfave/gfmrun/releases/download/v1.2.14/gfmrun-$(go env GOOS)-amd64-v1.2.14"
+          chmod +x $GOPATH/bin/gfmrun
           npm install -g markdown-toc@1.2.0
 
-      - name: gfmrun
-        run: go run internal/build/build.go gfmrun docs/v2/manual.md
-
-      - name: toc
-        run: go run internal/build/build.go toc docs/v2/manual.md
+      - name: Run Tests (v1)
+        if: contains(github.base_ref, 'v1')
+        run: |
+          go run internal/build/build.go gfmrun docs/v1/manual.md
+          go run internal/build/build.go toc docs/v1/manual.md
+
+      - name: Run Tests (v2)
+        if: contains(github.base_ref, 'master')
+        run: |
+          go run internal/build/build.go gfmrun docs/v2/manual.md
+          go run internal/build/build.go toc docs/v2/manual.md
diff --git a/.gitignore b/.gitignore
index e0c50ab..2d5e149 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,3 @@ vendor
 .idea
 internal/*/built-example
 coverage.txt
-/.local/
-
-*.exe
diff --git a/README.md b/README.md
index 5b8d2b6..408668b 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
 cli
 ===
 
-[![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://pkg.go.dev/github.com/urfave/cli/v2)
+[![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://godoc.org/github.com/urfave/cli)
 [![codebeat](https://codebeat.co/badges/0a8f30aa-f975-404b-b878-5fab3ae1cc5f)](https://codebeat.co/projects/github-com-urfave-cli)
 [![Go Report Card](https://goreportcard.com/badge/urfave/cli)](https://goreportcard.com/report/urfave/cli)
-[![codecov](https://codecov.io/gh/urfave/cli/branch/main/graph/badge.svg)](https://codecov.io/gh/urfave/cli)
+[![codecov](https://codecov.io/gh/urfave/cli/branch/master/graph/badge.svg)](https://codecov.io/gh/urfave/cli)
 
 cli is a simple, fast, and fun package for building command line apps in Go. The
 goal is to enable developers to write fast and distributable command line
@@ -12,7 +12,7 @@ applications in an expressive way.
 
 ## Usage Documentation
 
-Usage documentation exists for each major version. Don't know what version you're on? You're probably using the version from the `main` branch, which is currently `v2`.
+Usage documentation exists for each major version. Don't know what version you're on? You're probably using the version from the `master` branch, which is currently `v2`.
 
 - `v2` - [./docs/v2/manual.md](./docs/v2/manual.md)
 - `v1` - [./docs/v1/manual.md](./docs/v1/manual.md)
@@ -30,7 +30,7 @@ Go Modules are required when using this package. [See the go blog guide on using
 ### Using `v2` releases
 
 ```
-$ go get github.com/urfave/cli/v2
+$ GO111MODULE=on go get github.com/urfave/cli/v2
 ```
 
 ```go
@@ -44,7 +44,7 @@ import (
 ### Using `v1` releases
 
 ```
-$ go get github.com/urfave/cli
+$ GO111MODULE=on go get github.com/urfave/cli
 ```
 
 ```go
@@ -67,4 +67,4 @@ export PATH=$PATH:$GOPATH/bin
 
 cli is tested against multiple versions of Go on Linux, and against the latest
 released version of Go on OS X and Windows. This project uses Github Actions for
-builds. To see our currently supported go versions and platforms, look at the [./.github/workflows/cli.yml](https://github.com/urfave/cli/blob/main/.github/workflows/cli.yml).
+builds. To see our currently supported go versions and platforms, look at the [./.github/workflows/cli.yml](https://github.com/urfave/cli/blob/master/.github/workflows/cli.yml).
diff --git a/app.go b/app.go
index 6abcc08..f801fce 100644
--- a/app.go
+++ b/app.go
@@ -12,7 +12,7 @@ import (
 )
 
 var (
-	changeLogURL            = "https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md"
+	changeLogURL            = "https://github.com/urfave/cli/blob/master/docs/CHANGELOG.md"
 	appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL)
 	contactSysadmin         = "This is an error in the application.  Please contact the distributor of this application if this is not you."
 	errInvalidActionType    = NewExitError("ERROR invalid Action type. "+
@@ -140,7 +140,7 @@ func (a *App) Setup() {
 	}
 
 	if a.HelpName == "" {
-		a.HelpName = a.Name
+		a.HelpName = filepath.Base(os.Args[0])
 	}
 
 	if a.Usage == "" {
@@ -278,7 +278,7 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) {
 		return nil
 	}
 
-	cerr := context.checkRequiredFlags(a.Flags)
+	cerr := checkRequiredFlags(a.Flags, context)
 	if cerr != nil {
 		_ = ShowAppHelp(context)
 		return cerr
@@ -328,7 +328,7 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) {
 // RunAndExitOnError calls .Run() and exits non-zero if an error was returned
 //
 // Deprecated: instead you should return an error that fulfills cli.ExitCoder
-// to cli.App.Run. This will cause the application to exit with the given error
+// to cli.App.Run. This will cause the application to exit with the given eror
 // code in the cli.ExitCoder
 func (a *App) RunAndExitOnError() {
 	if err := a.Run(os.Args); err != nil {
@@ -397,7 +397,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
 		}
 	}
 
-	cerr := context.checkRequiredFlags(a.Flags)
+	cerr := checkRequiredFlags(a.Flags, context)
 	if cerr != nil {
 		_ = ShowSubcommandHelp(context)
 		return cerr
diff --git a/command.go b/command.go
index 3477686..dda2f49 100644
--- a/command.go
+++ b/command.go
@@ -127,7 +127,7 @@ func (c *Command) Run(ctx *Context) (err error) {
 		return nil
 	}
 
-	cerr := context.checkRequiredFlags(c.Flags)
+	cerr := checkRequiredFlags(c.Flags, context)
 	if cerr != nil {
 		_ = ShowCommandHelp(context, c.Name)
 		return cerr
@@ -227,7 +227,6 @@ func (c *Command) startApp(ctx *Context) error {
 	}
 
 	app.Usage = c.Usage
-	app.UsageText = c.UsageText
 	app.Description = c.Description
 	app.ArgsUsage = c.ArgsUsage
 
@@ -244,7 +243,6 @@ func (c *Command) startApp(ctx *Context) error {
 	app.Version = ctx.App.Version
 	app.HideVersion = true
 	app.Compiled = ctx.App.Compiled
-	app.Reader = ctx.App.Reader
 	app.Writer = ctx.App.Writer
 	app.ErrWriter = ctx.App.ErrWriter
 	app.ExitErrHandler = ctx.App.ExitErrHandler
diff --git a/context.go b/context.go
index da090e8..74ed519 100644
--- a/context.go
+++ b/context.go
@@ -2,7 +2,9 @@ package cli
 
 import (
 	"context"
+	"errors"
 	"flag"
+	"fmt"
 	"strings"
 )
 
@@ -51,18 +53,20 @@ 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 := c.lookupFlagSet(name); fs != nil {
-		isSet := false
-		fs.Visit(func(f *flag.Flag) {
-			if f.Name == name {
-				isSet = true
+	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 isSet {
-			return true
 		}
 
-		f := c.lookupFlag(name)
+		f := lookupFlag(name, c)
 		if f == nil {
 			return false
 		}
@@ -104,10 +108,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 := c.lookupFlagSet(name); fs != nil {
-		return fs.Lookup(name).Value.(flag.Getter).Get()
-	}
-	return nil
+	return c.flagSet.Lookup(name).Value.(flag.Getter).Get()
 }
 
 // Args returns the command line arguments associated with the context.
@@ -121,7 +122,7 @@ func (c *Context) NArg() int {
 	return c.Args().Len()
 }
 
-func (ctx *Context) lookupFlag(name string) Flag {
+func lookupFlag(name string, ctx *Context) Flag {
 	for _, c := range ctx.Lineage() {
 		if c.Command == nil {
 			continue
@@ -149,11 +150,8 @@ func (ctx *Context) lookupFlag(name string) Flag {
 	return nil
 }
 
-func (ctx *Context) lookupFlagSet(name string) *flag.FlagSet {
+func lookupFlagSet(name string, ctx *Context) *flag.FlagSet {
 	for _, c := range ctx.Lineage() {
-		if c.flagSet == nil {
-			continue
-		}
 		if f := c.flagSet.Lookup(name); f != nil {
 			return c.flagSet
 		}
@@ -162,7 +160,89 @@ func (ctx *Context) lookupFlagSet(name string) *flag.FlagSet {
 	return nil
 }
 
-func (context *Context) checkRequiredFlags(flags []Flag) requiredFlagsErr {
+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 {
 	var missingFlags []string
 	for _, f := range flags {
 		if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() {
@@ -191,21 +271,3 @@ func (context *Context) checkRequiredFlags(flags []Flag) 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 9f82fc6..dc16fc8 100644
--- a/docs.go
+++ b/docs.go
@@ -15,39 +15,31 @@ import (
 // The function errors if either parsing or writing of the string fails.
 func (a *App) ToMarkdown() (string, error) {
 	var w bytes.Buffer
-	if err := a.writeDocTemplate(&w, 0); err != nil {
+	if err := a.writeDocTemplate(&w); err != nil {
 		return "", err
 	}
 	return w.String(), nil
 }
 
-// ToMan creates a man page string with section number for the `*App`
+// ToMan creates a man page string for the `*App`
 // The function errors if either parsing or writing of the string fails.
-func (a *App) ToManWithSection(sectionNumber int) (string, error) {
+func (a *App) ToMan() (string, error) {
 	var w bytes.Buffer
-	if err := a.writeDocTemplate(&w, sectionNumber); err != nil {
+	if err := a.writeDocTemplate(&w); err != nil {
 		return "", err
 	}
 	man := md2man.Render(w.Bytes())
 	return string(man), nil
 }
 
-// ToMan creates a man page string for the `*App`
-// The function errors if either parsing or writing of the string fails.
-func (a *App) ToMan() (string, error) {
-	man, err := a.ToManWithSection(8)
-	return man, err
-}
-
 type cliTemplate struct {
 	App          *App
-	SectionNum   int
 	Commands     []string
 	GlobalArgs   []string
 	SynopsisArgs []string
 }
 
-func (a *App) writeDocTemplate(w io.Writer, sectionNum int) error {
+func (a *App) writeDocTemplate(w io.Writer) error {
 	const name = "cli"
 	t, err := template.New(name).Parse(MarkdownDocTemplate)
 	if err != nil {
@@ -55,7 +47,6 @@ func (a *App) writeDocTemplate(w io.Writer, sectionNum int) error {
 	}
 	return t.ExecuteTemplate(w, name, &cliTemplate{
 		App:          a,
-		SectionNum:   sectionNum,
 		Commands:     prepareCommands(a.Commands, 0),
 		GlobalArgs:   prepareArgsWithValues(a.VisibleFlags()),
 		SynopsisArgs: prepareArgsSynopsis(a.VisibleFlags()),
@@ -68,16 +59,15 @@ func prepareCommands(commands []*Command, level int) []string {
 		if command.Hidden {
 			continue
 		}
+		usage := ""
+		if command.Usage != "" {
+			usage = command.Usage
+		}
 
-		usageText := prepareUsageText(command)
-
-		usage := prepareUsage(command, usageText)
-
-		prepared := fmt.Sprintf("%s %s\n\n%s%s",
+		prepared := fmt.Sprintf("%s %s\n\n%s\n",
 			strings.Repeat("#", level+2),
 			strings.Join(command.Names(), ", "),
 			usage,
-			usageText,
 		)
 
 		flags := prepareArgsWithValues(command.Flags)
@@ -156,40 +146,3 @@ 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/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
index 41671a9..9a4640a 100644
--- a/docs/CONTRIBUTING.md
+++ b/docs/CONTRIBUTING.md
@@ -6,7 +6,7 @@ Feel free to put up a pull request to fix a bug or maybe add a feature. We will
 give it a code review and make sure that it does not break backwards
 compatibility. If collaborators agree that it is in line with
 the vision of the project, we will work with you to get the code into
-a mergeable state and merge it into the main branch.
+a mergeable state and merge it into the master branch.
 
 If you have contributed something significant to the project, we will most
 likely add you as a collaborator. As a collaborator you are given the ability
diff --git a/docs/migrate-v1-to-v2.md b/docs/migrate-v1-to-v2.md
index c555468..74f68f5 100644
--- a/docs/migrate-v1-to-v2.md
+++ b/docs/migrate-v1-to-v2.md
@@ -15,18 +15,15 @@ consider sending a PR to help improve this guide.
 
   * [Flags before args](#flags-before-args)
   * [Import string changed](#import-string-changed)
-  * [Flag aliases are done differently](#flag-aliases-are-done-differently)
+  * [Flag aliases are done differently.](#flag-aliases-are-done-differently)
   * [EnvVar is now a list (EnvVars)](#envvar-is-now-a-list-envvars)
-  * [Actions returns errors](#actions-returns-errors)
-  * [cli.Flag changed](#cliflag-changed)
   * [Commands are now lists of pointers](#commands-are-now-lists-of-pointers)
   * [Lists of commands should be pointers](#lists-of-commands-should-be-pointers)
+  * [cli.Flag changed](#cliflag-changed)
   * [Appending Commands](#appending-commands)
-  * [GlobalString, GlobalBool and its likes are deprecated](#globalstring-globalbool-and-its-likes-are-deprecated)
-  * [BoolTFlag and BoolT are deprecated](#booltflag-and-boolt-are-deprecated)
-  * [&cli.StringSlice{""} replaced with cli.NewStringSlice("")](#clistringslice-replaced-with-clinewstringslice)
-  * [Replace deprecated functions](#replace-deprecated-functions)
+  * [Actions returns errors](#actions-returns-errors)
   * [Everything else](#everything-else)
+  * [Full API Example](#full-api-example)
 
 <!-- tocstop -->
 
@@ -56,7 +53,7 @@ Check each file for this and make the change.
 
 Shell command to find them all: `fgrep -rl github.com/urfave/cli *`
 
-# Flag aliases are done differently
+# Flag aliases are done differently.
 
 Change `Name: "foo, f"` to `Name: "foo", Aliases: []string{"f"}`
 
@@ -184,52 +181,6 @@ Compiler messages you might see:
 cannot use c (type *cli.Command) as type cli.Command in append
 ```
 
-# GlobalString, GlobalBool and its likes are deprecated
-
-Use simply `String` instead of `GlobalString`, `Bool` instead of `GlobalBool` 
-
-# BoolTFlag and BoolT are deprecated
-
-BoolTFlag was a Bool Flag with its default value set to true and BoolT was used to find any BoolTFlag used locally, so both are deprecated.
-
-* OLD: 
-
-```go
-cli.BoolTFlag{
-		Name:   FlagName,
-		Usage:  FlagUsage,
-		EnvVar: "FLAG_ENV_VAR",
-}
-```
-* NEW: 
-```go
-cli.BoolFlag{
-		Name:   FlagName,
-		Value:  true,
-		Usage:  FlagUsage,
-		EnvVar: "FLAG_ENV_VAR",
-}
-```
-
-
-# &cli.StringSlice{""} replaced with cli.NewStringSlice("")
-
-Example: 
-
-* OLD: 
-
-```go
-Value: &cli.StringSlice{""},
-```
-* NEW: 
-```go
-Value: cli.NewStringSlice(""),
-}
-```
-# Replace deprecated functions
-
-`cli.NewExitError()` is deprecated.  Use `cli.Exit()` instead.  ([Staticcheck](https://staticcheck.io/) detects this automatically and recommends replacement code.)
-
 # Everything else
 
 Compile the code and work through any errors. Most should
diff --git a/docs/v1/manual.md b/docs/v1/manual.md
index dd22bdb..05ea370 100644
--- a/docs/v1/manual.md
+++ b/docs/v1/manual.md
@@ -612,7 +612,7 @@ given sources.
 Here is a more complete sample of a command using YAML support:
 
 <!-- {
-  "args": ["&#45;&#45;help"],
+  "args": ["test-cmd", "&#45;&#45;help"],
   "output": "&#45&#45;test value.*default: 0"
 } -->
 ``` go
diff --git a/docs/v2/manual.md b/docs/v2/manual.md
index b480dd6..e99afc1 100644
--- a/docs/v2/manual.md
+++ b/docs/v2/manual.md
@@ -45,7 +45,7 @@ cli v2 manual
 There are a small set of breaking changes between v1 and v2.
 Converting is relatively straightforward and typically takes less than
 an hour. Specific steps are included in
-[Migration Guide: v1 to v2](../migrate-v1-to-v2.md). Also see the [pkg.go.dev docs](https://pkg.go.dev/github.com/urfave/cli/v2) for v2 API documentation.
+[Migration Guide: v1 to v2](../migrate-v1-to-v2.md).
 
 ## Getting Started
 
@@ -514,7 +514,7 @@ func main() {
 ```
 
 If `EnvVars` contains more than one string, the first environment variable that
-resolves is used.
+resolves is used as the default.
 
 <!-- {
   "args": ["&#45;&#45;help"],
@@ -627,7 +627,7 @@ given sources.
 Here is a more complete sample of a command using YAML support:
 
 <!-- {
-  "args": ["&#45;&#45;help"],
+  "args": ["test-cmd", "&#45;&#45;help"],
   "output": "&#45&#45;test value.*default: 0"
 } -->
 ``` go
@@ -649,7 +649,7 @@ func main() {
 
   app := &cli.App{
     Action: func(c *cli.Context) error {
-      fmt.Println("--test value.*default: 0")
+      fmt.Println("yaml ist rad")
       return nil
     },
     Before: altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("load")),
@@ -674,10 +674,8 @@ Take for example this app that requires the `lang` flag:
 package main
 
 import (
-  "fmt"
   "log"
   "os"
-
   "github.com/urfave/cli/v2"
 )
 
diff --git a/errors.go b/errors.go
index 8f641fb..751ef9b 100644
--- a/errors.go
+++ b/errors.go
@@ -47,28 +47,6 @@ 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 49682f2..35ec217 100644
--- a/flag.go
+++ b/flag.go
@@ -1,7 +1,6 @@
 package cli
 
 import (
-	"errors"
 	"flag"
 	"fmt"
 	"io/ioutil"
@@ -119,14 +118,6 @@ 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)
 
@@ -139,52 +130,11 @@ 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 {
-		if vf, ok := f.(VisibleFlag); ok && vf.IsVisible() {
+		field := flagValue(f).FieldByName("Hidden")
+		if !field.IsValid() || !field.Bool() {
 			visible = append(visible, f)
 		}
 	}
@@ -409,11 +359,7 @@ func stringifySliceFlag(usage string, names, defaultVals []string) string {
 	}
 
 	usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal))
-	multiInputString := "(accepts multiple inputs)"
-	if usageWithDefault != "" {
-		multiInputString = "\t" + multiInputString
-	}
-	return fmt.Sprintf("%s\t%s%s", prefixedNames(names, placeholder), usageWithDefault, multiInputString)
+	return fmt.Sprintf("%s\t%s", prefixedNames(names, placeholder), usageWithDefault)
 }
 
 func hasFlag(flags []Flag, fl Flag) bool {
@@ -426,19 +372,20 @@ func hasFlag(flags []Flag, fl Flag) bool {
 	return false
 }
 
-func flagFromEnvOrFile(envVars []string, filePath string) (val string, ok bool) {
+// Return the first value from a list of environment variables and files
+// (which may or may not exist), a description of where the value was found,
+// and a boolean which is true if a value was found.
+func flagFromEnvOrFile(envVars []string, filePath string) (value string, fromWhere string, found bool) {
 	for _, envVar := range envVars {
 		envVar = strings.TrimSpace(envVar)
-		if val, ok := syscall.Getenv(envVar); ok {
-			return val, true
+		if value, found := syscall.Getenv(envVar); found {
+			return value, fmt.Sprintf("environment variable %q", envVar), true
 		}
 	}
 	for _, fileVar := range strings.Split(filePath, ",") {
-		if fileVar != "" {
-			if data, err := ioutil.ReadFile(fileVar); err == nil {
-				return string(data), true
-			}
+		if data, err := ioutil.ReadFile(fileVar); err == nil {
+			return string(data), fmt.Sprintf("file %q", filePath), true
 		}
 	}
-	return "", false
+	return "", "", false
 }
diff --git a/flag_bool.go b/flag_bool.go
index 8bd5820..9462e06 100644
--- a/flag_bool.go
+++ b/flag_bool.go
@@ -58,19 +58,14 @@ 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 {
+	if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
 		if val != "" {
 			valBool, err := strconv.ParseBool(val)
 
 			if err != nil {
-				return fmt.Errorf("could not parse %q as bool value for flag %s: %s", val, f.Name, err)
+				return fmt.Errorf("could not parse %q as bool value from %s for flag %s: %s", val, source, f.Name, err)
 			}
 
 			f.Value = valBool
@@ -92,7 +87,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 := c.lookupFlagSet(name); fs != nil {
+	if fs := lookupFlagSet(name, c); fs != nil {
 		return lookupBool(name, fs)
 	}
 	return false
diff --git a/flag_duration.go b/flag_duration.go
index 28f3978..040b5d6 100644
--- a/flag_duration.go
+++ b/flag_duration.go
@@ -58,19 +58,14 @@ 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 {
+	if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
 		if val != "" {
 			valDuration, err := time.ParseDuration(val)
 
 			if err != nil {
-				return fmt.Errorf("could not parse %q as duration value for flag %s: %s", val, f.Name, err)
+				return fmt.Errorf("could not parse %q as duration value from %s for flag %s: %s", val, source, f.Name, err)
 			}
 
 			f.Value = valDuration
@@ -91,7 +86,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 := c.lookupFlagSet(name); fs != nil {
+	if fs := lookupFlagSet(name, c); fs != nil {
 		return lookupDuration(name, fs)
 	}
 	return 0
diff --git a/flag_float64.go b/flag_float64.go
index 352f02b..14df60d 100644
--- a/flag_float64.go
+++ b/flag_float64.go
@@ -58,18 +58,14 @@ 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 {
+	if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
 		if val != "" {
-			valFloat, err := strconv.ParseFloat(val, 64)
+			valFloat, err := strconv.ParseFloat(val, 10)
+
 			if err != nil {
-				return fmt.Errorf("could not parse %q as float64 value for flag %s: %s", val, f.Name, err)
+				return fmt.Errorf("could not parse %q as float64 value from %s for flag %s: %s", val, source, f.Name, err)
 			}
 
 			f.Value = valFloat
@@ -91,7 +87,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 := c.lookupFlagSet(name); fs != nil {
+	if fs := lookupFlagSet(name, c); fs != nil {
 		return lookupFloat64(name, fs)
 	}
 	return 0
diff --git a/flag_float64_slice.go b/flag_float64_slice.go
index 385732e..0382229 100644
--- a/flag_float64_slice.go
+++ b/flag_float64_slice.go
@@ -19,16 +19,6 @@ 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 {
@@ -127,36 +117,27 @@ 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 {
+	if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
 		if val != "" {
 			f.Value = &Float64Slice{}
 
 			for _, s := range strings.Split(val, ",") {
 				if err := f.Value.Set(strings.TrimSpace(s)); err != nil {
-					return fmt.Errorf("could not parse %q as float64 slice value for flag %s: %s", f.Value, f.Name, err)
+					return fmt.Errorf("could not parse %q as float64 slice value from %s for flag %s: %s", f.Value, source, f.Name, err)
 				}
 			}
 
-			// Set this to false so that we reset the slice if we then set values from
-			// flags that have already been set by the environment.
-			f.Value.hasBeenSet = false
 			f.HasBeenSet = true
 		}
 	}
 
-	if f.Value == nil {
-		f.Value = &Float64Slice{}
-	}
-	copyValue := f.Value.clone()
 	for _, name := range f.Names() {
-		set.Var(copyValue, name, f.Usage)
+		if f.Value == nil {
+			f.Value = &Float64Slice{}
+		}
+		set.Var(f.Value, name, f.Usage)
 	}
 
 	return nil
@@ -165,7 +146,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 := c.lookupFlagSet(name); fs != nil {
+	if fs := lookupFlagSet(name, c); fs != nil {
 		return lookupFloat64Slice(name, fs)
 	}
 	return nil
diff --git a/flag_generic.go b/flag_generic.go
index fdf586d..b92c4de 100644
--- a/flag_generic.go
+++ b/flag_generic.go
@@ -66,18 +66,13 @@ 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 {
-	if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
+	if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
 		if val != "" {
 			if err := f.Value.Set(val); err != nil {
-				return fmt.Errorf("could not parse %q as value for flag %s: %s", val, f.Name, err)
+				return fmt.Errorf("could not parse %q from %s as value for flag %s: %s", val, source, f.Name, err)
 			}
 
 			f.HasBeenSet = true
@@ -94,7 +89,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 := c.lookupFlagSet(name); fs != nil {
+	if fs := lookupFlagSet(name, c); fs != nil {
 		return lookupGeneric(name, fs)
 	}
 	return nil
diff --git a/flag_int.go b/flag_int.go
index 1ebe356..8e0ee59 100644
--- a/flag_int.go
+++ b/flag_int.go
@@ -58,19 +58,14 @@ 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 {
+	if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
 		if val != "" {
 			valInt, err := strconv.ParseInt(val, 0, 64)
 
 			if err != nil {
-				return fmt.Errorf("could not parse %q as int value for flag %s: %s", val, f.Name, err)
+				return fmt.Errorf("could not parse %q as int value from %s for flag %s: %s", val, source, f.Name, err)
 			}
 
 			f.Value = int(valInt)
@@ -92,7 +87,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 := c.lookupFlagSet(name); fs != nil {
+	if fs := lookupFlagSet(name, c); fs != nil {
 		return lookupInt(name, fs)
 	}
 	return 0
diff --git a/flag_int64.go b/flag_int64.go
index ecf0e9e..0dda82e 100644
--- a/flag_int64.go
+++ b/flag_int64.go
@@ -58,19 +58,14 @@ 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 {
+	if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
 		if val != "" {
 			valInt, err := strconv.ParseInt(val, 0, 64)
 
 			if err != nil {
-				return fmt.Errorf("could not parse %q as int value for flag %s: %s", val, f.Name, err)
+				return fmt.Errorf("could not parse %q as int value from %s for flag %s: %s", val, source, f.Name, err)
 			}
 
 			f.Value = valInt
@@ -91,7 +86,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 := c.lookupFlagSet(name); fs != nil {
+	if fs := lookupFlagSet(name, c); fs != nil {
 		return lookupInt64(name, fs)
 	}
 	return 0
diff --git a/flag_int64_slice.go b/flag_int64_slice.go
index f5c6939..cfb29eb 100644
--- a/flag_int64_slice.go
+++ b/flag_int64_slice.go
@@ -19,16 +19,6 @@ 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 {
@@ -128,34 +118,25 @@ 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 {
+	if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
 		f.Value = &Int64Slice{}
 
 		for _, s := range strings.Split(val, ",") {
 			if err := f.Value.Set(strings.TrimSpace(s)); err != nil {
-				return fmt.Errorf("could not parse %q as int64 slice value for flag %s: %s", val, f.Name, err)
+				return fmt.Errorf("could not parse %q as int64 slice value from %s for flag %s: %s", val, source, f.Name, err)
 			}
 		}
 
-		// Set this to false so that we reset the slice if we then set values from
-		// flags that have already been set by the environment.
-		f.Value.hasBeenSet = false
 		f.HasBeenSet = true
 	}
 
-	if f.Value == nil {
-		f.Value = &Int64Slice{}
-	}
-	copyValue := f.Value.clone()
 	for _, name := range f.Names() {
-		set.Var(copyValue, name, f.Usage)
+		if f.Value == nil {
+			f.Value = &Int64Slice{}
+		}
+		set.Var(f.Value, name, f.Usage)
 	}
 
 	return nil
@@ -164,10 +145,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 := c.lookupFlagSet(name); fs != nil {
-		return lookupInt64Slice(name, fs)
-	}
-	return nil
+	return lookupInt64Slice(name, c.flagSet)
 }
 
 func lookupInt64Slice(name string, set *flag.FlagSet) []int64 {
diff --git a/flag_int_slice.go b/flag_int_slice.go
index 94c668e..f379039 100644
--- a/flag_int_slice.go
+++ b/flag_int_slice.go
@@ -19,16 +19,6 @@ 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) {
@@ -139,34 +129,25 @@ 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 {
+	if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
 		f.Value = &IntSlice{}
 
 		for _, s := range strings.Split(val, ",") {
 			if err := f.Value.Set(strings.TrimSpace(s)); err != nil {
-				return fmt.Errorf("could not parse %q as int slice value for flag %s: %s", val, f.Name, err)
+				return fmt.Errorf("could not parse %q as int slice value from %s for flag %s: %s", val, source, f.Name, err)
 			}
 		}
 
-		// Set this to false so that we reset the slice if we then set values from
-		// flags that have already been set by the environment.
-		f.Value.hasBeenSet = false
 		f.HasBeenSet = true
 	}
 
-	if f.Value == nil {
-		f.Value = &IntSlice{}
-	}
-	copyValue := f.Value.clone()
 	for _, name := range f.Names() {
-		set.Var(copyValue, name, f.Usage)
+		if f.Value == nil {
+			f.Value = &IntSlice{}
+		}
+		set.Var(f.Value, name, f.Usage)
 	}
 
 	return nil
@@ -175,8 +156,8 @@ 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 := c.lookupFlagSet(name); fs != nil {
-		return lookupIntSlice(name, fs)
+	if fs := lookupFlagSet(name, c); fs != nil {
+		return lookupIntSlice(name, c.flagSet)
 	}
 	return nil
 }
diff --git a/flag_path.go b/flag_path.go
index b3aa919..bc8d5a2 100644
--- a/flag_path.go
+++ b/flag_path.go
@@ -54,14 +54,9 @@ 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 {
+	if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
 		f.Value = val
 		f.HasBeenSet = true
 	}
@@ -80,7 +75,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 := c.lookupFlagSet(name); fs != nil {
+	if fs := lookupFlagSet(name, c); fs != nil {
 		return lookupPath(name, fs)
 	}
 
diff --git a/flag_string.go b/flag_string.go
index aad4c43..5d79ce8 100644
--- a/flag_string.go
+++ b/flag_string.go
@@ -55,14 +55,9 @@ 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 {
+	if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
 		f.Value = val
 		f.HasBeenSet = true
 	}
@@ -81,7 +76,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 := c.lookupFlagSet(name); fs != nil {
+	if fs := lookupFlagSet(name, c); fs != nil {
 		return lookupString(name, fs)
 	}
 	return ""
@@ -90,7 +85,10 @@ func (c *Context) String(name string) string {
 func lookupString(name string, set *flag.FlagSet) string {
 	f := set.Lookup(name)
 	if f != nil {
-		parsed := f.Value.String()
+		parsed, err := f.Value.String(), error(nil)
+		if err != nil {
+			return ""
+		}
 		return parsed
 	}
 	return ""
diff --git a/flag_string_slice.go b/flag_string_slice.go
index 5269643..e081c4f 100644
--- a/flag_string_slice.go
+++ b/flag_string_slice.go
@@ -18,16 +18,6 @@ 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 {
@@ -124,11 +114,6 @@ 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 {
 
@@ -138,7 +123,7 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error {
 
 	}
 
-	if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
+	if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
 		if f.Value == nil {
 			f.Value = &StringSlice{}
 		}
@@ -149,7 +134,7 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error {
 
 		for _, s := range strings.Split(val, ",") {
 			if err := destination.Set(strings.TrimSpace(s)); err != nil {
-				return fmt.Errorf("could not parse %q as string value for flag %s: %s", val, f.Name, err)
+				return fmt.Errorf("could not parse %q as string value from %s for flag %s: %s", val, source, f.Name, err)
 			}
 		}
 
@@ -159,15 +144,17 @@ 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() {
-		set.Var(setValue, name, f.Usage)
+		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)
 	}
 
 	return nil
@@ -176,7 +163,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 := c.lookupFlagSet(name); fs != nil {
+	if fs := lookupFlagSet(name, c); fs != nil {
 		return lookupStringSlice(name, fs)
 	}
 	return nil
diff --git a/flag_timestamp.go b/flag_timestamp.go
index 7458a79..fb174ee 100644
--- a/flag_timestamp.go
+++ b/flag_timestamp.go
@@ -71,7 +71,6 @@ 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
@@ -114,11 +113,6 @@ 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 == "" {
@@ -129,23 +123,14 @@ 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 val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
 		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)
+			return fmt.Errorf("could not parse %q as timestamp value from %s for flag %s: %s", val, source, f.Name, err)
 		}
 		f.HasBeenSet = true
 	}
 
 	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
@@ -153,7 +138,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 := c.lookupFlagSet(name); fs != nil {
+	if fs := lookupFlagSet(name, c); fs != nil {
 		return lookupTimestamp(name, fs)
 	}
 	return nil
diff --git a/flag_uint.go b/flag_uint.go
index 23a70a7..f64cbe4 100644
--- a/flag_uint.go
+++ b/flag_uint.go
@@ -52,18 +52,13 @@ 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 {
+	if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
 		if val != "" {
 			valInt, err := strconv.ParseUint(val, 0, 64)
 			if err != nil {
-				return fmt.Errorf("could not parse %q as uint value for flag %s: %s", val, f.Name, err)
+				return fmt.Errorf("could not parse %q as uint value from %s for flag %s: %s", val, source, f.Name, err)
 			}
 
 			f.Value = uint(valInt)
@@ -91,7 +86,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 := c.lookupFlagSet(name); fs != nil {
+	if fs := lookupFlagSet(name, c); fs != nil {
 		return lookupUint(name, fs)
 	}
 	return 0
diff --git a/flag_uint64.go b/flag_uint64.go
index a2df024..a6bf63d 100644
--- a/flag_uint64.go
+++ b/flag_uint64.go
@@ -52,18 +52,13 @@ 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 {
+	if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
 		if val != "" {
 			valInt, err := strconv.ParseUint(val, 0, 64)
 			if err != nil {
-				return fmt.Errorf("could not parse %q as uint64 value for flag %s: %s", val, f.Name, err)
+				return fmt.Errorf("could not parse %q as uint64 value from %s for flag %s: %s", val, source, f.Name, err)
 			}
 
 			f.Value = valInt
@@ -91,7 +86,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 := c.lookupFlagSet(name); fs != nil {
+	if fs := lookupFlagSet(name, c); fs != nil {
 		return lookupUint64(name, fs)
 	}
 	return 0
diff --git a/funcs.go b/funcs.go
index 842b4aa..474c48f 100644
--- a/funcs.go
+++ b/funcs.go
@@ -17,7 +17,7 @@ type ActionFunc func(*Context) error
 // CommandNotFoundFunc is executed if the proper command cannot be found
 type CommandNotFoundFunc func(*Context, string)
 
-// OnUsageErrorFunc is executed if a usage error occurs. This is useful for displaying
+// OnUsageErrorFunc is executed if an usage error occurs. This is useful for displaying
 // customized usage error messages.  This function is able to replace the
 // original error messages.  If this function is not set, the "Incorrect usage"
 // is displayed and the execution is interrupted.
diff --git a/go.mod b/go.mod
index 71c38a6..1139664 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,6 @@ go 1.11
 
 require (
 	github.com/BurntSushi/toml v0.3.1
-	github.com/cpuguy83/go-md2man/v2 v2.0.1
-	gopkg.in/yaml.v2 v2.2.8
+	github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d
+	gopkg.in/yaml.v2 v2.2.3
 )
diff --git a/go.sum b/go.sum
index ad7e1ce..663ad72 100644
--- a/go.sum
+++ b/go.sum
@@ -1,10 +1,14 @@
 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
-github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
-github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3 h1:fvjTMHxHEw/mxHbtzPi3JCcKXQRAnQTBRo6YCJSVHKI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/internal/build/build.go b/internal/build/build.go
index 9fd2cf9..d0be855 100644
--- a/internal/build/build.go
+++ b/internal/build/build.go
@@ -194,7 +194,7 @@ func checkBinarySizeActionFunc(c *cli.Context) (err error) {
 		helloSourceFilePath  = "./internal/example-hello-world/example-hello-world.go"
 		helloBuiltFilePath   = "./internal/example-hello-world/built-example"
 		desiredMinBinarySize = 1.9
-		desiredMaxBinarySize = 2.2
+		desiredMaxBinarySize = 2.1
 		badNewsEmoji         = "🚨"
 		goodNewsEmoji        = "✨"
 		checksPassedEmoji    = "✅"
diff --git a/template.go b/template.go
index 39fa4db..31c03f8 100644
--- a/template.go
+++ b/template.go
@@ -7,7 +7,7 @@ var AppHelpTemplate = `NAME:
    {{.Name}}{{if .Usage}} - {{.Usage}}{{end}}
 
 USAGE:
-   {{if .UsageText}}{{.UsageText | nindent 3 | trim}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}
+   {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}
 
 VERSION:
    {{.Version}}{{end}}{{end}}{{if .Description}}
@@ -39,7 +39,7 @@ var CommandHelpTemplate = `NAME:
    {{.HelpName}} - {{.Usage}}
 
 USAGE:
-   {{if .UsageText}}{{.UsageText | nindent 3 | trim}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}}
+   {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}}
 
 CATEGORY:
    {{.Category}}{{end}}{{if .Description}}
@@ -59,7 +59,7 @@ var SubcommandHelpTemplate = `NAME:
    {{.HelpName}} - {{.Usage}}
 
 USAGE:
-   {{if .UsageText}}{{.UsageText | nindent 3 | trim}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}}
+   {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}}
 
 DESCRIPTION:
    {{.Description | nindent 3 | trim}}{{end}}
@@ -74,9 +74,9 @@ OPTIONS:
    {{end}}{{end}}
 `
 
-var MarkdownDocTemplate = `{{if gt .SectionNum 0}}% {{ .App.Name }} {{ .SectionNum }}
+var MarkdownDocTemplate = `% {{ .App.Name }} 8
 
-{{end}}# NAME
+# NAME
 
 {{ .App.Name }}{{ if .App.Usage }} - {{ .App.Usage }}{{ end }}
 
@@ -86,18 +86,16 @@ var MarkdownDocTemplate = `{{if gt .SectionNum 0}}% {{ .App.Name }} {{ .SectionN
 {{ if .SynopsisArgs }}
 ` + "```" + `
 {{ range $v := .SynopsisArgs }}{{ $v }}{{ end }}` + "```" + `
-{{ end }}{{ if .App.Description }}
+{{ end }}{{ if .App.UsageText }}
 # DESCRIPTION
 
-{{ .App.Description }}
+{{ .App.UsageText }}
 {{ end }}
 **Usage**:
 
-` + "```" + `{{ if .App.UsageText }}
-{{ .App.UsageText }}
-{{ else }}
+` + "```" + `
 {{ .App.Name }} [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
-{{ end }}` + "```" + `
+` + "```" + `
 {{ if .GlobalArgs }}
 # GLOBAL OPTIONS
 {{ range $v := .GlobalArgs }}


Test patch:
diff --git a/app_test.go b/app_test.go
index 76e211d..57c850d 100644
--- a/app_test.go
+++ b/app_test.go
@@ -315,6 +315,7 @@ func ExampleApp_Run_bashComplete_withMultipleLongFlag() {
 }
 
 func ExampleApp_Run_bashComplete() {
+	// set args for examples sake
 	// set args for examples sake
 	os.Args = []string{"greet", "--generate-bash-completion"}
 
@@ -476,18 +477,18 @@ func TestApp_RunAsSubCommandIncorrectUsage(t *testing.T) {
 	a := App{
 		Name: "cmd",
 		Flags: []Flag{
-			&StringFlag{Name: "foo"},
+			&StringFlag{Name: "--foo"},
 		},
 		Writer: bytes.NewBufferString(""),
 	}
 
 	set := flag.NewFlagSet("", flag.ContinueOnError)
-	_ = set.Parse([]string{"", "-bar"})
+	_ = set.Parse([]string{"", "---foo"})
 	c := &Context{flagSet: set}
 
 	err := a.RunAsSubcommand(c)
 
-	expect(t, err.Error(), "flag provided but not defined: -bar")
+	expect(t, err, errors.New("bad flag syntax: ---foo"))
 }
 
 func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) {
@@ -896,39 +897,6 @@ func TestApp_SetStdin(t *testing.T) {
 	}
 }
 
-func TestApp_SetStdin_Subcommand(t *testing.T) {
-	buf := make([]byte, 12)
-
-	app := &App{
-		Name:   "test",
-		Reader: strings.NewReader("Hello World!"),
-		Commands: []*Command{
-			{
-				Name: "command",
-				Subcommands: []*Command{
-					{
-						Name: "subcommand",
-						Action: func(c *Context) error {
-							_, err := c.App.Reader.Read(buf)
-							return err
-						},
-					},
-				},
-			},
-		},
-	}
-
-	err := app.Run([]string{"test", "command", "subcommand"})
-
-	if err != nil {
-		t.Fatalf("Run error: %s", err)
-	}
-
-	if string(buf) != "Hello World!" {
-		t.Error("App did not read input from desired reader.")
-	}
-}
-
 func TestApp_SetStdout(t *testing.T) {
 	var w bytes.Buffer
 
diff --git a/context_test.go b/context_test.go
index 8475706..61d6268 100644
--- a/context_test.go
+++ b/context_test.go
@@ -112,8 +112,6 @@ func TestContext_String(t *testing.T) {
 	c := NewContext(nil, set, parentCtx)
 	expect(t, c.String("myflag"), "hello world")
 	expect(t, c.String("top-flag"), "hai veld")
-	c = NewContext(nil, nil, parentCtx)
-	expect(t, c.String("top-flag"), "hai veld")
 }
 
 func TestContext_Path(t *testing.T) {
@@ -138,18 +136,6 @@ func TestContext_Bool(t *testing.T) {
 	expect(t, c.Bool("top-flag"), true)
 }
 
-func TestContext_Value(t *testing.T) {
-	set := flag.NewFlagSet("test", 0)
-	set.Int("myflag", 12, "doc")
-	parentSet := flag.NewFlagSet("test", 0)
-	parentSet.Int("top-flag", 13, "doc")
-	parentCtx := NewContext(nil, parentSet, nil)
-	c := NewContext(nil, set, parentCtx)
-	expect(t, c.Value("myflag"), 12)
-	expect(t, c.Value("top-flag"), 13)
-	expect(t, c.Value("unknown-flag"), nil)
-}
-
 func TestContext_Args(t *testing.T) {
 	set := flag.NewFlagSet("test", 0)
 	set.Bool("myflag", false, "doc")
@@ -318,13 +304,13 @@ func TestContext_lookupFlagSet(t *testing.T) {
 	_ = set.Parse([]string{"--local-flag"})
 	_ = parentSet.Parse([]string{"--top-flag"})
 
-	fs := ctx.lookupFlagSet("top-flag")
+	fs := lookupFlagSet("top-flag", ctx)
 	expect(t, fs, parentCtx.flagSet)
 
-	fs = ctx.lookupFlagSet("local-flag")
+	fs = lookupFlagSet("local-flag", ctx)
 	expect(t, fs, ctx.flagSet)
 
-	if fs := ctx.lookupFlagSet("frob"); fs != nil {
+	if fs := lookupFlagSet("frob", ctx); fs != nil {
 		t.Fail()
 	}
 }
@@ -578,7 +564,7 @@ func TestCheckRequiredFlags(t *testing.T) {
 			ctx.Command.Flags = test.flags
 
 			// logic under test
-			err := ctx.checkRequiredFlags(test.flags)
+			err := checkRequiredFlags(test.flags, ctx)
 
 			// assertions
 			if test.expectedAnError && err == nil {
diff --git a/docs_test.go b/docs_test.go
index adccbbb..46e38dc 100644
--- a/docs_test.go
+++ b/docs_test.go
@@ -2,7 +2,6 @@ package cli
 
 import (
 	"bytes"
-	"errors"
 	"io/ioutil"
 	"testing"
 )
@@ -67,50 +66,8 @@ 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.Description = `Description of the application.`
 	app.Usage = "Some app"
 	app.Authors = []*Author{
 		{Name: "Harrison", Email: "harrison@lolwut.com"},
@@ -119,13 +76,13 @@ Should be a part of the same code block
 	return app
 }
 
-func expectFileContent(t *testing.T, file, got string) {
+func expectFileContent(t *testing.T, file, expected string) {
 	data, err := ioutil.ReadFile(file)
 	// Ignore windows line endings
 	// TODO: Replace with bytes.ReplaceAll when support for Go 1.11 is dropped
 	data = bytes.Replace(data, []byte("\r\n"), []byte("\n"), -1)
 	expect(t, err, nil)
-	expect(t, got, string(data))
+	expect(t, string(data), expected)
 }
 
 func TestToMarkdownFull(t *testing.T) {
@@ -179,19 +136,6 @@ func TestToMarkdownNoAuthors(t *testing.T) {
 	expectFileContent(t, "testdata/expected-doc-no-authors.md", res)
 }
 
-func TestToMarkdownNoUsageText(t *testing.T) {
-	// Given
-	app := testApp()
-	app.UsageText = ""
-
-	// When
-	res, err := app.ToMarkdown()
-
-	// Then
-	expect(t, err, nil)
-	expectFileContent(t, "testdata/expected-doc-no-usagetext.md", res)
-}
-
 func TestToMan(t *testing.T) {
 	// Given
 	app := testApp()
@@ -203,137 +147,3 @@ func TestToMan(t *testing.T) {
 	expect(t, err, nil)
 	expectFileContent(t, "testdata/expected-doc-full.man", res)
 }
-
-func TestToManParseError(t *testing.T) {
-	// Given
-	app := testApp()
-
-	// When
-	// temporarily change the global variable for testing
-	tmp := MarkdownDocTemplate
-	MarkdownDocTemplate = `{{ .App.Name`
-	_, err := app.ToMan()
-	MarkdownDocTemplate = tmp
-
-	// Then
-	expect(t, err, errors.New(`template: cli:1: unclosed action`))
-}
-
-func TestToManWithSection(t *testing.T) {
-	// Given
-	app := testApp()
-
-	// When
-	res, err := app.ToManWithSection(8)
-
-	// Then
-	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 c563d6f..8600ba4 100644
--- a/flag_test.go
+++ b/flag_test.go
@@ -52,21 +52,15 @@ func TestBoolFlagApply_SetsAllNames(t *testing.T) {
 }
 
 func TestFlagsFromEnv(t *testing.T) {
-	newSetFloat64Slice := func(defaults ...float64) Float64Slice {
-		s := NewFloat64Slice(defaults...)
-		s.hasBeenSet = false
-		return *s
-	}
-
 	newSetIntSlice := func(defaults ...int) IntSlice {
 		s := NewIntSlice(defaults...)
-		s.hasBeenSet = false
+		s.hasBeenSet = true
 		return *s
 	}
 
 	newSetInt64Slice := func(defaults ...int64) Int64Slice {
 		s := NewInt64Slice(defaults...)
-		s.hasBeenSet = false
+		s.hasBeenSet = true
 		return *s
 	}
 
@@ -85,33 +79,30 @@ func TestFlagsFromEnv(t *testing.T) {
 		{"", false, &BoolFlag{Name: "debug", EnvVars: []string{"DEBUG"}}, ""},
 		{"1", true, &BoolFlag{Name: "debug", EnvVars: []string{"DEBUG"}}, ""},
 		{"false", false, &BoolFlag{Name: "debug", EnvVars: []string{"DEBUG"}}, ""},
-		{"foobar", true, &BoolFlag{Name: "debug", EnvVars: []string{"DEBUG"}}, `could not parse "foobar" as bool value for flag debug: .*`},
+		{"foobar", true, &BoolFlag{Name: "debug", EnvVars: []string{"DEBUG"}}, `could not parse "foobar" as bool value from environment variable "DEBUG" for flag debug: .*`},
 
 		{"1s", 1 * time.Second, &DurationFlag{Name: "time", EnvVars: []string{"TIME"}}, ""},
-		{"foobar", false, &DurationFlag{Name: "time", EnvVars: []string{"TIME"}}, `could not parse "foobar" as duration value for flag time: .*`},
+		{"foobar", false, &DurationFlag{Name: "time", EnvVars: []string{"TIME"}}, `could not parse "foobar" as duration value from environment variable "TIME" for flag time: .*`},
 
 		{"1.2", 1.2, &Float64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
 		{"1", 1.0, &Float64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
-		{"foobar", 0, &Float64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as float64 value for flag seconds: .*`},
+		{"foobar", 0, &Float64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as float64 value from environment variable "SECONDS" for flag seconds: .*`},
 
 		{"1", int64(1), &Int64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
-		{"1.2", 0, &Int64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as int value for flag seconds: .*`},
-		{"foobar", 0, &Int64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int value for flag seconds: .*`},
+		{"1.2", 0, &Int64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as int value from environment variable "SECONDS" for flag seconds: .*`},
+		{"foobar", 0, &Int64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int value from environment variable "SECONDS" for flag seconds: .*`},
 
 		{"1", 1, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
-		{"1.2", 0, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as int value for flag seconds: .*`},
-		{"foobar", 0, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int value for flag seconds: .*`},
-
-		{"1.0,2", newSetFloat64Slice(1, 2), &Float64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
-		{"foobar", newSetFloat64Slice(), &Float64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "\[\]float64{}" as float64 slice value for flag seconds: .*`},
+		{"1.2", 0, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as int value from environment variable "SECONDS" for flag seconds: .*`},
+		{"foobar", 0, &IntFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int value from environment variable "SECONDS" for flag seconds: .*`},
 
 		{"1,2", newSetIntSlice(1, 2), &IntSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
-		{"1.2,2", newSetIntSlice(), &IntSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2,2" as int slice value for flag seconds: .*`},
-		{"foobar", newSetIntSlice(), &IntSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int slice value for flag seconds: .*`},
+		{"1.2,2", newSetIntSlice(), &IntSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2,2" as int slice value from environment variable "SECONDS" for flag seconds: .*`},
+		{"foobar", newSetIntSlice(), &IntSliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int slice value from environment variable "SECONDS" for flag seconds: .*`},
 
 		{"1,2", newSetInt64Slice(1, 2), &Int64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
-		{"1.2,2", newSetInt64Slice(), &Int64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2,2" as int64 slice value for flag seconds: .*`},
-		{"foobar", newSetInt64Slice(), &Int64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int64 slice value for flag seconds: .*`},
+		{"1.2,2", newSetInt64Slice(), &Int64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2,2" as int64 slice value from environment variable "SECONDS" for flag seconds: .*`},
+		{"foobar", newSetInt64Slice(), &Int64SliceFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as int64 slice value from environment variable "SECONDS" for flag seconds: .*`},
 
 		{"foo", "foo", &StringFlag{Name: "name", EnvVars: []string{"NAME"}}, ""},
 		{"path", "path", &PathFlag{Name: "path", EnvVars: []string{"PATH"}}, ""},
@@ -119,12 +110,12 @@ func TestFlagsFromEnv(t *testing.T) {
 		{"foo,bar", newSetStringSlice("foo", "bar"), &StringSliceFlag{Name: "names", EnvVars: []string{"NAMES"}}, ""},
 
 		{"1", uint(1), &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
-		{"1.2", 0, &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as uint value for flag seconds: .*`},
-		{"foobar", 0, &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as uint value for flag seconds: .*`},
+		{"1.2", 0, &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as uint value from environment variable "SECONDS" for flag seconds: .*`},
+		{"foobar", 0, &UintFlag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as uint value from environment variable "SECONDS" for flag seconds: .*`},
 
 		{"1", uint64(1), &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, ""},
-		{"1.2", 0, &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as uint64 value for flag seconds: .*`},
-		{"foobar", 0, &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as uint64 value for flag seconds: .*`},
+		{"1.2", 0, &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "1.2" as uint64 value from environment variable "SECONDS" for flag seconds: .*`},
+		{"foobar", 0, &Uint64Flag{Name: "seconds", EnvVars: []string{"SECONDS"}}, `could not parse "foobar" as uint64 value from environment variable "SECONDS" for flag seconds: .*`},
 
 		{"foo,bar", &Parser{"foo", "bar"}, &GenericFlag{Name: "names", Value: &Parser{}, EnvVars: []string{"NAMES"}}, ""},
 	}
@@ -349,11 +340,11 @@ var stringSliceFlagTests = []struct {
 	value    *StringSlice
 	expected string
 }{
-	{"foo", nil, NewStringSlice(""), "--foo value\t(accepts multiple inputs)"},
-	{"f", nil, NewStringSlice(""), "-f value\t(accepts multiple inputs)"},
-	{"f", nil, NewStringSlice("Lipstick"), "-f value\t(default: \"Lipstick\")\t(accepts multiple inputs)"},
-	{"test", nil, NewStringSlice("Something"), "--test value\t(default: \"Something\")\t(accepts multiple inputs)"},
-	{"dee", []string{"d"}, NewStringSlice("Inka", "Dinka", "dooo"), "--dee value, -d value\t(default: \"Inka\", \"Dinka\", \"dooo\")\t(accepts multiple inputs)"},
+	{"foo", nil, NewStringSlice(""), "--foo value\t"},
+	{"f", nil, NewStringSlice(""), "-f value\t"},
+	{"f", nil, NewStringSlice("Lipstick"), "-f value\t(default: \"Lipstick\")"},
+	{"test", nil, NewStringSlice("Something"), "--test value\t(default: \"Something\")"},
+	{"dee", []string{"d"}, NewStringSlice("Inka", "Dinka", "dooo"), "--dee value, -d value\t(default: \"Inka\", \"Dinka\", \"dooo\")"},
 }
 
 func TestStringSliceFlagHelpOutput(t *testing.T) {
@@ -639,9 +630,9 @@ var intSliceFlagTests = []struct {
 	value    *IntSlice
 	expected string
 }{
-	{"heads", nil, NewIntSlice(), "--heads value\t(accepts multiple inputs)"},
-	{"H", nil, NewIntSlice(), "-H value\t(accepts multiple inputs)"},
-	{"H", []string{"heads"}, NewIntSlice(9, 3), "-H value, --heads value\t(default: 9, 3)\t(accepts multiple inputs)"},
+	{"heads", nil, NewIntSlice(), "--heads value\t"},
+	{"H", nil, NewIntSlice(), "-H value\t"},
+	{"H", []string{"heads"}, NewIntSlice(9, 3), "-H value, --heads value\t(default: 9, 3)"},
 }
 
 func TestIntSliceFlagHelpOutput(t *testing.T) {
@@ -683,55 +674,16 @@ func TestIntSliceFlagApply_SetsAllNames(t *testing.T) {
 	expect(t, err, nil)
 }
 
-func TestIntSliceFlagApply_ParentContext(t *testing.T) {
-	_ = (&App{
-		Flags: []Flag{
-			&IntSliceFlag{Name: "numbers", Aliases: []string{"n"}, Value: NewIntSlice(1, 2, 3)},
-		},
-		Commands: []*Command{
-			{
-				Name: "child",
-				Action: func(ctx *Context) error {
-					expected := []int{1, 2, 3}
-					if !reflect.DeepEqual(ctx.IntSlice("numbers"), expected) {
-						t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.IntSlice("numbers"))
-					}
-					if !reflect.DeepEqual(ctx.IntSlice("n"), expected) {
-						t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.IntSlice("n"))
-					}
-					return nil
-				},
-			},
-		},
-	}).Run([]string{"run", "child"})
-}
-
-func TestIntSliceFlag_SetFromParentContext(t *testing.T) {
-	fl := &IntSliceFlag{Name: "numbers", Aliases: []string{"n"}, Value: NewIntSlice(1, 2, 3, 4)}
-	set := flag.NewFlagSet("test", 0)
-	_ = fl.Apply(set)
-	ctx := &Context{
-		parentContext: &Context{
-			flagSet: set,
-		},
-		flagSet: flag.NewFlagSet("empty", 0),
-	}
-	expected := []int{1, 2, 3, 4}
-	if !reflect.DeepEqual(ctx.IntSlice("numbers"), expected) {
-		t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.IntSlice("numbers"))
-	}
-}
-
 var int64SliceFlagTests = []struct {
 	name     string
 	aliases  []string
 	value    *Int64Slice
 	expected string
 }{
-	{"heads", nil, NewInt64Slice(), "--heads value\t(accepts multiple inputs)"},
-	{"H", nil, NewInt64Slice(), "-H value\t(accepts multiple inputs)"},
+	{"heads", nil, NewInt64Slice(), "--heads value\t"},
+	{"H", nil, NewInt64Slice(), "-H value\t"},
 	{"heads", []string{"H"}, NewInt64Slice(int64(2), int64(17179869184)),
-		"--heads value, -H value\t(default: 2, 17179869184)\t(accepts multiple inputs)"},
+		"--heads value, -H value\t(default: 2, 17179869184)"},
 }
 
 func TestInt64SliceFlagHelpOutput(t *testing.T) {
@@ -764,60 +716,6 @@ func TestInt64SliceFlagWithEnvVarHelpOutput(t *testing.T) {
 	}
 }
 
-func TestInt64SliceFlagApply_ParentContext(t *testing.T) {
-	_ = (&App{
-		Flags: []Flag{
-			&Int64SliceFlag{Name: "numbers", Aliases: []string{"n"}, Value: NewInt64Slice(1, 2, 3)},
-		},
-		Commands: []*Command{
-			{
-				Name: "child",
-				Action: func(ctx *Context) error {
-					expected := []int64{1, 2, 3}
-					if !reflect.DeepEqual(ctx.Int64Slice("numbers"), expected) {
-						t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.Int64Slice("numbers"))
-					}
-					if !reflect.DeepEqual(ctx.Int64Slice("n"), expected) {
-						t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.Int64Slice("n"))
-					}
-					return nil
-				},
-			},
-		},
-	}).Run([]string{"run", "child"})
-}
-
-func TestInt64SliceFlag_SetFromParentContext(t *testing.T) {
-	fl := &Int64SliceFlag{Name: "numbers", Aliases: []string{"n"}, Value: NewInt64Slice(1, 2, 3, 4)}
-	set := flag.NewFlagSet("test", 0)
-	_ = fl.Apply(set)
-	ctx := &Context{
-		parentContext: &Context{
-			flagSet: set,
-		},
-		flagSet: flag.NewFlagSet("empty", 0),
-	}
-	expected := []int64{1, 2, 3, 4}
-	if !reflect.DeepEqual(ctx.Int64Slice("numbers"), expected) {
-		t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.Int64Slice("numbers"))
-	}
-}
-func TestInt64SliceFlag_ReturnNil(t *testing.T) {
-	fl := &Int64SliceFlag{}
-	set := flag.NewFlagSet("test", 0)
-	_ = fl.Apply(set)
-	ctx := &Context{
-		parentContext: &Context{
-			flagSet: set,
-		},
-		flagSet: flag.NewFlagSet("empty", 0),
-	}
-	expected := []int64(nil)
-	if !reflect.DeepEqual(ctx.Int64Slice("numbers"), expected) {
-		t.Errorf("child context unable to view parent flag: %v != %v", expected, ctx.Int64Slice("numbers"))
-	}
-}
-
 var float64FlagTests = []struct {
 	name     string
 	expected string
@@ -873,10 +771,10 @@ var float64SliceFlagTests = []struct {
 	value    *Float64Slice
 	expected string
 }{
-	{"heads", nil, NewFloat64Slice(), "--heads value\t(accepts multiple inputs)"},
-	{"H", nil, NewFloat64Slice(), "-H value\t(accepts multiple inputs)"},
+	{"heads", nil, NewFloat64Slice(), "--heads value\t"},
+	{"H", nil, NewFloat64Slice(), "-H value\t"},
 	{"heads", []string{"H"}, NewFloat64Slice(0.1234, -10.5),
-		"--heads value, -H value\t(default: 0.1234, -10.5)\t(accepts multiple inputs)"},
+		"--heads value, -H value\t(default: 0.1234, -10.5)"},
 }
 
 func TestFloat64SliceFlagHelpOutput(t *testing.T) {
@@ -1860,7 +1758,7 @@ func TestFlagFromFile(t *testing.T) {
 	}
 
 	for _, filePathTest := range filePathTests {
-		got, _ := flagFromEnvOrFile(filePathTest.name, filePathTest.path)
+		got, _, _ := flagFromEnvOrFile(filePathTest.name, filePathTest.path)
 		if want := filePathTest.expected; got != want {
 			t.Errorf("Did not expect %v - Want %v", got, want)
 		}
@@ -1982,80 +1880,3 @@ 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/help_test.go b/help_test.go
index 8dd262d..407c269 100644
--- a/help_test.go
+++ b/help_test.go
@@ -511,36 +511,6 @@ func TestShowSubcommandHelp_CommandUsageText(t *testing.T) {
 	}
 }
 
-func TestShowSubcommandHelp_MultiLine_CommandUsageText(t *testing.T) {
-	app := &App{
-		Commands: []*Command{
-			{
-				Name: "frobbly",
-				UsageText: `This is a
-multi
-line
-UsageText`,
-			},
-		},
-	}
-
-	output := &bytes.Buffer{}
-	app.Writer = output
-
-	_ = app.Run([]string{"foo", "frobbly", "--help"})
-
-	expected := `USAGE:
-   This is a
-   multi
-   line
-   UsageText
-`
-
-	if !strings.Contains(output.String(), expected) {
-		t.Errorf("expected output to include usage text; got: %q", output.String())
-	}
-}
-
 func TestShowSubcommandHelp_SubcommandUsageText(t *testing.T) {
 	app := &App{
 		Commands: []*Command{
@@ -565,40 +535,6 @@ func TestShowSubcommandHelp_SubcommandUsageText(t *testing.T) {
 	}
 }
 
-func TestShowSubcommandHelp_MultiLine_SubcommandUsageText(t *testing.T) {
-	app := &App{
-		Commands: []*Command{
-			{
-				Name: "frobbly",
-				Subcommands: []*Command{
-					{
-						Name: "bobbly",
-						UsageText: `This is a
-multi
-line
-UsageText`,
-					},
-				},
-			},
-		},
-	}
-
-	output := &bytes.Buffer{}
-	app.Writer = output
-	_ = app.Run([]string{"foo", "frobbly", "bobbly", "--help"})
-
-	expected := `USAGE:
-   This is a
-   multi
-   line
-   UsageText
-`
-
-	if !strings.Contains(output.String(), expected) {
-		t.Errorf("expected output to include usage text; got: %q", output.String())
-	}
-}
-
 func TestShowAppHelp_HiddenCommand(t *testing.T) {
 	app := &App{
 		Commands: []*Command{
@@ -844,56 +780,6 @@ VERSION:
 	}
 }
 
-func TestShowAppHelp_UsageText(t *testing.T) {
-	app := &App{
-		UsageText: "This is a sinlge line of UsageText",
-		Commands: []*Command{
-			{
-				Name: "frobbly",
-			},
-		},
-	}
-
-	output := &bytes.Buffer{}
-	app.Writer = output
-
-	_ = app.Run([]string{"foo"})
-
-	if !strings.Contains(output.String(), "This is a sinlge line of UsageText") {
-		t.Errorf("expected output to include usage text; got: %q", output.String())
-	}
-}
-
-func TestShowAppHelp_MultiLine_UsageText(t *testing.T) {
-	app := &App{
-		UsageText: `This is a
-multi
-line
-App UsageText`,
-		Commands: []*Command{
-			{
-				Name: "frobbly",
-			},
-		},
-	}
-
-	output := &bytes.Buffer{}
-	app.Writer = output
-
-	_ = app.Run([]string{"foo"})
-
-	expected := `USAGE:
-   This is a
-   multi
-   line
-   App UsageText
-`
-
-	if !strings.Contains(output.String(), expected) {
-		t.Errorf("expected output to include usage text; got: %q", output.String())
-	}
-}
-
 func TestHideHelpCommand(t *testing.T) {
 	app := &App{
 		HideHelpCommand: true,
diff --git a/helpers_test.go b/helpers_test.go
index 9ecd8e1..9217e89 100644
--- a/helpers_test.go
+++ b/helpers_test.go
@@ -3,17 +3,24 @@ 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{}) {
-	t.Helper()
+	_, fn, line, _ := runtime.Caller(1)
+	fn = strings.Replace(fn, wd+"/", "", -1)
 
 	if !reflect.DeepEqual(a, b) {
-		t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
+		t.Errorf("(%s:%d) Expected %v (type %v) - Got %v (type %v)", fn, line, b, reflect.TypeOf(b), a, reflect.TypeOf(a))
 	}
 }
diff --git a/testdata/expected-doc-full.man b/testdata/expected-doc-full.man
index a2131f0..2bea507 100644
--- a/testdata/expected-doc-full.man
+++ b/testdata/expected-doc-full.man
@@ -3,7 +3,7 @@
 
 .SH NAME
 .PP
-greet - Some app
+greet \- Some app
 
 
 .SH SYNOPSIS
@@ -14,9 +14,9 @@ greet
 .RS
 
 .nf
-[--another-flag|-b]
-[--flag|--fl|-f]=[value]
-[--socket|-s]=[value]
+[\-\-another\-flag|\-b]
+[\-\-flag|\-\-fl|\-f]=[value]
+[\-\-socket|\-s]=[value]
 
 .fi
 .RE
@@ -24,7 +24,7 @@ greet
 
 .SH DESCRIPTION
 .PP
-Description of the application.
+app [first\_arg] [second\_arg]
 
 .PP
 \fBUsage\fP:
@@ -33,7 +33,7 @@ Description of the application.
 .RS
 
 .nf
-app [first_arg] [second_arg]
+greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
 
 .fi
 .RE
@@ -41,13 +41,13 @@ app [first_arg] [second_arg]
 
 .SH GLOBAL OPTIONS
 .PP
-\fB--another-flag, -b\fP: another usage text
+\fB\-\-another\-flag, \-b\fP: another usage text
 
 .PP
-\fB--flag, --fl, -f\fP="":
+\fB\-\-flag, \-\-fl, \-f\fP="":
 
 .PP
-\fB--socket, -s\fP="": some 'usage' text (default: value)
+\fB\-\-socket, \-s\fP="": some 'usage' text (default: value)
 
 
 .SH COMMANDS
@@ -56,65 +56,23 @@ app [first_arg] [second_arg]
 another usage test
 
 .PP
-\fB--another-flag, -b\fP: another usage text
+\fB\-\-another\-flag, \-b\fP: another usage text
 
 .PP
-\fB--flag, --fl, -f\fP="":
+\fB\-\-flag, \-\-fl, \-f\fP="":
 
-.SS sub-config, s, ss
+.SS sub\-config, s, ss
 .PP
 another usage test
 
 .PP
-\fB--sub-command-flag, -s\fP: some usage text
+\fB\-\-sub\-command\-flag, \-s\fP: some usage text
 
 .PP
-\fB--sub-flag, --sub-fl, -s\fP="":
+\fB\-\-sub\-flag, \-\-sub\-fl, \-s\fP="":
 
 .SH info, i, in
 .PP
 retrieve generic information
 
-.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
+.SH some\-command
\ No newline at end of file
diff --git a/testdata/expected-doc-full.md b/testdata/expected-doc-full.md
index 80ff6a7..f272274 100644
--- a/testdata/expected-doc-full.md
+++ b/testdata/expected-doc-full.md
@@ -1,3 +1,5 @@
+% greet 8
+
 # NAME
 
 greet - Some app
@@ -14,12 +16,12 @@ greet
 
 # DESCRIPTION
 
-Description of the application.
+app [first_arg] [second_arg]
 
 **Usage**:
 
 ```
-app [first_arg] [second_arg]
+greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
 ```
 
 # GLOBAL OPTIONS
@@ -56,29 +58,3 @@ 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 80ff6a7..f272274 100644
--- a/testdata/expected-doc-no-authors.md
+++ b/testdata/expected-doc-no-authors.md
@@ -1,3 +1,5 @@
+% greet 8
+
 # NAME
 
 greet - Some app
@@ -14,12 +16,12 @@ greet
 
 # DESCRIPTION
 
-Description of the application.
+app [first_arg] [second_arg]
 
 **Usage**:
 
 ```
-app [first_arg] [second_arg]
+greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
 ```
 
 # GLOBAL OPTIONS
@@ -56,29 +58,3 @@ 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-commands.md b/testdata/expected-doc-no-commands.md
index 4db2a37..adaedb4 100644
--- a/testdata/expected-doc-no-commands.md
+++ b/testdata/expected-doc-no-commands.md
@@ -1,3 +1,5 @@
+% greet 8
+
 # NAME
 
 greet - Some app
@@ -14,12 +16,12 @@ greet
 
 # DESCRIPTION
 
-Description of the application.
+app [first_arg] [second_arg]
 
 **Usage**:
 
 ```
-app [first_arg] [second_arg]
+greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
 ```
 
 # GLOBAL OPTIONS
diff --git a/testdata/expected-doc-no-flags.md b/testdata/expected-doc-no-flags.md
index 33d1275..2994593 100644
--- a/testdata/expected-doc-no-flags.md
+++ b/testdata/expected-doc-no-flags.md
@@ -1,3 +1,5 @@
+% greet 8
+
 # NAME
 
 greet - Some app
@@ -8,12 +10,12 @@ greet
 
 # DESCRIPTION
 
-Description of the application.
+app [first_arg] [second_arg]
 
 **Usage**:
 
 ```
-app [first_arg] [second_arg]
+greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
 ```
 
 # COMMANDS
@@ -41,29 +43,3 @@ 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-usagetext.md b/testdata/expected-doc-no-usagetext.md
deleted file mode 100644
index d09b69f..0000000
--- a/testdata/expected-doc-no-usagetext.md
+++ /dev/null
@@ -1,84 +0,0 @@
-# NAME
-
-greet - Some app
-
-# SYNOPSIS
-
-greet
-
-```
-[--another-flag|-b]
-[--flag|--fl|-f]=[value]
-[--socket|-s]=[value]
-```
-
-# DESCRIPTION
-
-Description of the application.
-
-**Usage**:
-
-```
-greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
-```
-
-# GLOBAL OPTIONS
-
-**--another-flag, -b**: another usage text
-
-**--flag, --fl, -f**="": 
-
-**--socket, -s**="": some 'usage' text (default: value)
-
-
-# COMMANDS
-
-## config, c
-
-another usage test
-
-**--another-flag, -b**: another usage text
-
-**--flag, --fl, -f**="": 
-
-### sub-config, s, ss
-
-another usage test
-
-**--sub-command-flag, -s**: some usage text
-
-**--sub-flag, --sub-fl, -s**="": 
-
-## info, i, in
-
-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 cc449c5..dc41e5a 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 usage u sub-usage su
+        if contains -- $i config c sub-config s ss info i in some-command
             return 1
         end
     end
@@ -27,10 +27,3 @@ 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'