diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,58 @@
+## Version 0.14.0.0 (09 Jun 2017)
+
+- Upgrade `str` and related builders to be polymorphic over
+  `IsString`. This allows `Text` and `Bytestring` to be used
+  naturally with `strOption` and `strArgument` and friends.
+
+  *Note:* This change may require additional type signatures
+          in cases where the reader was necessary for type
+          inference.
+
+- Export public API explicitly from `Options.Applicative`
+  instead of re-exporting other modules.
+
+  *Note:* Minor changes to exports were made in conjunction
+          to this change. `ParserHelp` no longer requires an
+          an extra import, and some internally used functions
+          from `Options.Applicative.Common` are no longer
+          exported from the main module.
+
+- Add Zsh and Fish completions with rich descriptions for
+  options and commands.
+
+  Use "--zsh-completion-script" and "fish-completion-script"
+  to generate scripts for these shells.
+
+- Fix bash completions with quoted sections, tilde expansions
+  and completions after "--".
+
+- Add suggestions to error message when a user mistypes a
+  command or option.
+
+- Add `style` builder, for styling option descriptions.
+
+- Improve error message for options when a required argument
+  is not supplied.
+
+- Fix #242 regarding flags with long options, where a flag given
+  a long option could be interpreted incorrectly.
+
+- Fix `noIntersperse` to be more like its namesakes in other
+  libraries. When on, options will be accepted until an argument
+  is passed, after which all options will be treated as positional
+  arguments.
+
+- Add `forwardOptions` builder, which will allow unknown options
+  and flags to be passed to an argument builder.
+  This is useful to mixed parsing environments, or wrappers to
+  other commands.
+
+- Add `Semigroup` instances for `Completer` and `Chunk`.
+
+- Forwards compatibility with `MonadFail` proposal.
+
+- Doc
+
 ## Version 0.13.2.0 (9 Mar 2017)
 
 - Updated dependency bounds.
diff --git a/Options/Applicative.hs b/Options/Applicative.hs
--- a/Options/Applicative.hs
+++ b/Options/Applicative.hs
@@ -1,30 +1,222 @@
 module Options.Applicative (
   -- * Applicative option parsers
   --
-  -- | This is an empty module which simply re-exports all the public definitions
-  -- of this package.
+  -- | This module exports all one should need for defining and using
+  -- optparse-applicative command line option parsers.
   --
   -- See <https://github.com/pcapriotti/optparse-applicative> for a tutorial,
   -- and a general introduction to applicative option parsers.
   --
-  -- See the documentation of individual modules for more details.
+  -- See the sections below for more detail
 
   -- * Exported modules
   --
   -- | The standard @Applicative@ module is re-exported here for convenience.
   module Control.Applicative,
 
-  -- | Parser type and low-level parsing functionality.
-  module Options.Applicative.Common,
+  -- * Option Parsers
+  --
+  -- | A 'Parser' is the core type in optparse-applicative. A value of type
+  -- @Parser a@ represents a specification for a set of options, which will
+  -- yield a value of type a when the command line arguments are successfully
+  -- parsed.
+  --
+  -- There are several types of primitive 'Parser'.
+  --
+  -- * Flags: simple no-argument options. When a flag is encountered on the
+  -- command line, its value is returned.
+  --
+  -- * Options: options with an argument. An option can define a /reader/,
+  -- which converts its argument from String to the desired value, or throws a
+  -- parse error if the argument does not validate correctly.
+  --
+  -- * Arguments: positional arguments, validated in the same way as option
+  -- arguments.
+  --
+  -- * Commands. A command defines a completely independent sub-parser. When a
+  -- command is encountered, the whole command line is passed to the
+  -- corresponding parser.
+  --
+  -- See the "Parser Builders" section for how to construct and customise
+  -- these parsers.
+  Parser,
 
-  -- | Utilities to build parsers out of basic primitives.
-  module Options.Applicative.Builder,
+  -- ** Parser builders
+  --
+  -- | This section contains utility functions and combinators to create parsers
+  -- for individual options.
+  --
+  -- Each parser builder takes an option modifier. A modifier can be created by
+  -- composing the basic modifiers provided by here using the 'Monoid' operations
+  -- 'mempty' and 'mappend', or their aliases 'idm' and '<>'.
+  --
+  -- For example:
+  --
+  -- > out = strOption
+  -- >     ( long "output"
+  -- >    <> short 'o'
+  -- >    <> metavar "FILENAME" )
+  --
+  -- creates a parser for an option called \"output\".
+  flag,
+  flag',
+  switch,
 
-  -- | Common completion functions.
-  module Options.Applicative.Builder.Completer,
+  strOption,
+  option,
 
-  -- | Utilities to run parsers and display a help text.
-  module Options.Applicative.Extra,
+  strArgument,
+  argument,
+
+  subparser,
+  hsubparser,
+
+  abortOption,
+  infoOption,
+  helper,
+
+  -- ** Modifiers
+  --
+  -- | 'Parser' builders take a modifier, which represents a modification of the
+  -- properties of an option, and can be composed as a monoid.
+  --
+  -- Contraints are often used to ensure that the modifiers can be sensibly applied.
+  -- For example, positional arguments can't be specified by long or short names,
+  -- so the 'HasName' constraint is used to ensure we have a flag or option.
+  Mod,
+
+  short,
+  long,
+  help,
+  helpDoc,
+  value,
+  showDefaultWith,
+  showDefault,
+  metavar,
+  noArgError,
+  hidden,
+  internal,
+  style,
+  command,
+  commandGroup,
+  completeWith,
+  action,
+  completer,
+  idm,
+  mappend,
+
+  OptionFields,
+  FlagFields,
+  ArgumentFields,
+  CommandFields,
+
+  -- ** Readers
+  --
+  -- | A reader is used by the 'option' and 'argument' builders to parse
+  -- the data passed by the user on the command line into a data type.
+  --
+  -- The most common are 'str' which is used for 'String' like types,
+  -- including 'ByteString' and 'Text'; and 'auto', which uses the 'Read'
+  -- typeclass, and is good for simple types like 'Int' or 'Double'.
+  --
+  -- More complex types can use the 'eitherReader' or 'maybeReader'
+  -- functions to pattern match or use a more expressive parser like a
+  -- member of the 'Parsec' family.
+  ReadM,
+
+  auto,
+  str,
+  maybeReader,
+  eitherReader,
+  disabled,
+  readerAbort,
+  readerError,
+
+  -- * Program descriptions
+  --
+  -- ** 'ParserInfo'
+  --
+  -- | A 'ParserInfo' describes a command line program, used to generate a help
+  -- screen. Two help modes are supported: brief and full. In brief mode, only
+  -- an option and argument summary is displayed, while in full mode each
+  -- available option and command, including hidden ones, is described.
+  --
+  -- A 'ParserInfo' should be created with the 'info' function and a set of
+  -- 'InfoMod' modifiers.
+  --
+  info,
+
+  ParserInfo(..),
+
+  InfoMod,
+  fullDesc,
+  briefDesc,
+  header,
+  headerDoc,
+  footer,
+  footerDoc,
+  progDesc,
+  progDescDoc,
+  failureCode,
+  noIntersperse,
+  forwardOptions,
+
+  -- * Running parsers
+  --
+  -- | The execParser family of functions are used to run parsers
+  execParser,
+  customExecParser,
+  execParserPure,
+
+  -- ** Handling parser results manually
+  getParseResult,
+  handleParseResult,
+  parserFailure,
+  renderFailure,
+  overFailure,
+
+  -- ** 'ParserPrefs'
+  --
+  -- | A 'ParserPrefs' contains general preferences for all command-line
+  -- options, and should be built with the 'prefs' function.
+  prefs,
+
+  ParserPrefs(..),
+
+  PrefsMod,
+  multiSuffix,
+  disambiguate,
+  showHelpOnError,
+  showHelpOnEmpty,
+  noBacktrack,
+  columns,
+  defaultPrefs,
+
+  -- * Completions
+  --
+  -- | optparse-applicative supplies a rich completion system for bash,
+  -- zsh, and fish shells.
+  --
+  -- 'Completer' functions are used for option and argument to complete
+  -- their values.
+  --
+  -- Use the 'completer' builder to use these.
+  -- The 'action' and 'completeWith' builders are also provided for
+  -- convenience, to use 'bashCompleter' and 'listCompleter' as a 'Mod'.
+  Completer,
+  mkCompleter,
+  listIOCompleter,
+
+  listCompleter,
+  bashCompleter,
+
+  -- * Types
+  ParseError(..),
+  ParserHelp(..),
+  ParserFailure(..),
+  ParserResult(..),
+  CompletionResult(..)
+
   ) where
 
 -- reexport Applicative here for convenience
@@ -34,5 +226,6 @@
 import Options.Applicative.Builder
 import Options.Applicative.Builder.Completer
 import Options.Applicative.Extra
+import Options.Applicative.Types
 
 {-# ANN module "HLint: ignore Use import/export shortcut" #-}
diff --git a/Options/Applicative/BashCompletion.hs b/Options/Applicative/BashCompletion.hs
--- a/Options/Applicative/BashCompletion.hs
+++ b/Options/Applicative/BashCompletion.hs
@@ -9,15 +9,28 @@
 
 import Control.Applicative
 import Prelude
-import Data.Foldable (asum)
-import Data.List (isPrefixOf)
-import Data.Maybe (fromMaybe, listToMaybe)
+import Data.Foldable ( asum )
+import Data.List ( isPrefixOf )
+import Data.Maybe ( fromMaybe, listToMaybe )
 
 import Options.Applicative.Builder
 import Options.Applicative.Common
 import Options.Applicative.Internal
 import Options.Applicative.Types
+import Options.Applicative.Help.Pretty
+import Options.Applicative.Help.Chunk
 
+-- | Provide basic or rich command completions
+data Richness
+  = Standard
+  -- ^ Add no help descriptions to the completions
+  | Enriched Int Int
+  -- ^ Include tab separated description for options
+  --   and commands when available.
+  --   Takes option description length and command
+  --   description length.
+  deriving (Eq, Ord, Show)
+
 bashCompletionParser :: ParserInfo a -> ParserPrefs -> Parser CompletionResult
 bashCompletionParser pinfo pprefs = complParser
   where
@@ -26,38 +39,112 @@
 
     complParser = asum
       [ failure <$>
-        (   bashCompletionQuery pinfo pprefs
-        <$> (many . strOption) (long "bash-completion-word"
+        (  bashCompletionQuery pinfo pprefs
+        -- To get rich completions, one just needs the first
+        -- command. To customise the lengths, use either of
+        -- the `desc-length` options.
+        -- zsh commands can go on a single line, so they might
+        -- want to be longer.
+        <$> ( flag' Enriched (long "bash-completion-enriched" `mappend` internal)
+                <*> option auto (long "bash-completion-option-desc-length" `mappend` internal `mappend` value 40)
+                <*> option auto (long "bash-completion-command-desc-length" `mappend` internal `mappend` value 40)
+          <|> pure Standard
+          )
+        <*> (many . strOption) (long "bash-completion-word"
                                   `mappend` internal)
         <*> option auto (long "bash-completion-index" `mappend` internal) )
       , failure <$>
           (bashCompletionScript <$>
-            strOption (long "bash-completion-script" `mappend` internal)) ]
+            strOption (long "bash-completion-script" `mappend` internal))
+      , failure <$>
+          (fishCompletionScript <$>
+            strOption (long "fish-completion-script" `mappend` internal))
+      , failure <$>
+          (zshCompletionScript <$>
+            strOption (long "zsh-completion-script" `mappend` internal))
+      ]
 
-bashCompletionQuery :: ParserInfo a -> ParserPrefs -> [String] -> Int -> String -> IO [String]
-bashCompletionQuery pinfo pprefs ws i _ = case runCompletion compl pprefs of
-  Just (Left (SomeParser p)) -> list_options p
-  Just (Right c)             -> run_completer c
-  _                          -> return []
+bashCompletionQuery :: ParserInfo a -> ParserPrefs -> Richness -> [String] -> Int -> String -> IO [String]
+bashCompletionQuery pinfo pprefs richness ws i _ = case runCompletion compl pprefs of
+  Just (Left (SomeParser p, a))
+    -> list_options a p
+  Just (Right c)
+    -> run_completer c
+  Nothing
+    -> return []
   where
-    list_options =
-        fmap concat
+    compl = runParserInfo pinfo (drop 1 ws')
+
+    list_options a
+      = fmap concat
       . sequence
-      . mapParser (const opt_completions)
+      . mapParser (opt_completions a)
 
-    opt_completions opt = case optMain opt of
-      OptReader ns _ _ -> return $ show_names ns
-      FlagReader ns _  -> return $ show_names ns
-      ArgReader rdr    -> run_completer (crCompleter rdr)
-      CmdReader _ ns _ -> return $ filter_names ns
+    --
+    -- Prior to 0.14 there was a subtle bug which would
+    -- mean that completions from positional arguments
+    -- further into the parse would be shown.
+    --
+    -- We therefore now check to see that
+    -- hinfoUnreachableArgs is off before running the
+    -- completion for position arguments.
+    --
+    -- For options and flags, ensure that the user
+    -- hasn't disabled them with `--`.
+    opt_completions argPolicy hinfo opt = case optMain opt of
+      OptReader ns _ _
+         | argPolicy /= AllPositionals
+        -> return . add_opt_help opt $ show_names ns
+         | otherwise
+        -> return []
+      FlagReader ns _
+         | argPolicy /= AllPositionals
+        -> return . add_opt_help opt $ show_names ns
+         | otherwise
+        -> return []
+      ArgReader rdr
+         | hinfoUnreachableArgs hinfo
+        -> return []
+         | otherwise
+        -> run_completer (crCompleter rdr)
+      CmdReader _ ns p
+         | hinfoUnreachableArgs hinfo
+        -> return []
+         | otherwise
+        -> return . add_cmd_help p $ filter_names ns
 
-    show_name :: OptName -> String
-    show_name (OptShort c) = '-':[c]
-    show_name (OptLong name) = "--" ++ name
+    -- When doing enriched completions, add any help specified
+    -- to the completion variables (tab separated).
+    add_opt_help :: Functor f => Option a -> f String -> f String
+    add_opt_help opt = case richness of
+      Standard
+        -> id
+      Enriched len _
+        -> fmap (\o -> let h = unChunk $ optHelp opt
+                       in  maybe o (\h' -> o ++ "\t" ++ render_line len h') h)
 
+    -- When doing enriched completions, add the command description
+    -- to the completion variables (tab separated).
+    add_cmd_help :: Functor f => (String -> Maybe (ParserInfo a)) -> f String -> f String
+    add_cmd_help p = case richness of
+      Standard
+        -> id
+      Enriched _ len
+        -> fmap (\cmd -> let h = p cmd >>= unChunk . infoProgDesc
+                         in  maybe cmd (\h' -> cmd ++ "\t" ++ render_line len h') h)
+
     show_names :: [OptName] -> [String]
-    show_names = filter_names . map show_name
+    show_names = filter_names . map showOption
 
+    -- We only want to show a single line in the completion results description.
+    -- If there was a line break, it would come across as a different completion
+    -- possibility.
+    render_line :: Int -> Doc -> String
+    render_line len doc = case lines (displayS (renderPretty 1 len doc) "") of
+      [] -> ""
+      [x] -> x
+      x : _ -> x ++ "..."
+
     filter_names :: [String] -> [String]
     filter_names = filter is_completion
 
@@ -72,14 +159,12 @@
         w:_ -> isPrefixOf w
         _ -> const True
 
-    compl = runParserInfo pinfo (drop 1 ws')
-
 bashCompletionScript :: String -> String -> IO [String]
 bashCompletionScript prog progn = return
   [ "_" ++ progn ++ "()"
   , "{"
-  , "    local cmdline"
-  , "    local IFS=$'\n'"
+  , "    local CMDLINE"
+  , "    local IFS=$'\\n'"
   , "    CMDLINE=(--bash-completion-index $COMP_CWORD)"
   , ""
   , "    for arg in ${COMP_WORDS[@]}; do"
@@ -90,3 +175,80 @@
   , "}"
   , ""
   , "complete -o filenames -F _" ++ progn ++ " " ++ progn ]
+
+{-
+/Note/: Fish Shell
+
+Derived from Drezil's post in #169.
+
+@
+commandline
+-c or --cut-at-cursor only print selection up until the current cursor position
+-o or --tokenize tokenize the selection and print one string-type token per line
+@
+
+We tokenize so that the call to count (and hence --bash-completion-index)
+gets the right number use cut-at-curstor to not bother sending anything
+after the cursor position, which allows for completion of the middle of
+words.
+
+Tab characters separate items from descriptions.
+-}
+fishCompletionScript :: String -> String -> IO [String]
+fishCompletionScript prog progn = return
+  [ " function _" ++ progn
+  , "    set -l cl (commandline --tokenize --current-process)"
+  , "    # Hack around fish issue #3934"
+  , "    set -l cn (commandline --tokenize --cut-at-cursor --current-process)"
+  , "    set -l cn (count $cn)"
+  , "    set -l tmpline --bash-completion-enriched --bash-completion-index $cn"
+  , "    for arg in $cl"
+  , "      set tmpline $tmpline --bash-completion-word $arg"
+  , "    end"
+  , "    for opt in (" ++ prog ++ " $tmpline)"
+  , "      if test -d $opt"
+  , "        echo -E \"$opt/\""
+  , "      else"
+  , "        echo -E \"$opt\""
+  , "      end"
+  , "    end"
+  , "end"
+  , ""
+  , "complete --no-files --command " ++ progn ++ " --arguments '(_"  ++ progn ++  ")'"
+  ]
+
+zshCompletionScript :: String -> String -> IO [String]
+zshCompletionScript prog progn = return
+  [ "#compdef " ++ progn
+  , ""
+  , "local request"
+  , "local completions"
+  , "local word"
+  , "local index=$((CURRENT - 1))"
+  , ""
+  , "request=(--bash-completion-enriched --bash-completion-index $index)"
+  , "for arg in ${words[@]}; do"
+  , "  request=(${request[@]} --bash-completion-word $arg)"
+  , "done"
+  , ""
+  , "IFS=$'\\n' completions=($( " ++ prog ++ " \"${request[@]}\" ))"
+  , ""
+  , "for word in $completions; do"
+  , "  local -a parts"
+  , ""
+  , "  # Split the line at a tab if there is one."
+  , "  IFS=$'\\t' parts=($( echo $word ))"
+  , ""
+  , "  if [[ -n $parts[2] ]]; then"
+  , "     if [[ $word[1] == \"-\" ]]; then"
+  , "       local desc=(\"$parts[1] ($parts[2])\")"
+  , "       compadd -d desc -- $parts[1]"
+  , "     else"
+  , "       local desc=($(print -f  \"%-019s -- %s\" $parts[1] $parts[2]))"
+  , "       compadd -l -d desc -- $parts[1]"
+  , "     fi"
+  , "  else"
+  , "    compadd -f -- $word"
+  , "  fi"
+  , "done"
+  ]
diff --git a/Options/Applicative/Builder.hs b/Options/Applicative/Builder.hs
--- a/Options/Applicative/Builder.hs
+++ b/Options/Applicative/Builder.hs
@@ -41,6 +41,7 @@
   ParseError(..),
   hidden,
   internal,
+  style,
   command,
   commandGroup,
   completeWith,
@@ -72,6 +73,7 @@
   progDescDoc,
   failureCode,
   noIntersperse,
+  forwardOptions,
   info,
 
   -- * Builder for 'ParserPrefs'
@@ -96,6 +98,7 @@
 
 import Control.Applicative
 import Data.Semigroup hiding (option)
+import Data.String (fromString, IsString)
 
 import Options.Applicative.Builder.Completer
 import Options.Applicative.Builder.Internal
@@ -104,7 +107,7 @@
 import Options.Applicative.Help.Pretty
 import Options.Applicative.Help.Chunk
 
--- readers --
+-- Readers --
 
 -- | 'Option' reader based on the 'Read' type class.
 auto :: Read a => ReadM a
@@ -113,17 +116,28 @@
   _         -> Left $ "cannot parse value `" ++ arg ++ "'"
 
 -- | String 'Option' reader.
-str :: ReadM String
-str = readerAsk
+--
+--   Polymorphic over the `IsString` type class since 0.14.
+str :: IsString s => ReadM s
+str = fromString <$> readerAsk
 
--- | Convert a function in the 'Either' monad to a reader.
+-- | Convert a function producing an 'Either' into a reader.
+--
+-- As an example, one can create a ReadM from an attoparsec Parser
+-- easily with
+--
+-- > import qualified Data.Attoparsec.Text as A
+-- > import qualified Data.Text as T
+-- > attoparsecReader :: A.Parser a => ReadM a
+-- > attoparsecReader p = eitherReader (A.parseOnly p . T.pack)
 eitherReader :: (String -> Either String a) -> ReadM a
 eitherReader f = readerAsk >>= either readerError return . f
 
--- | Convert a function in the 'Maybe' monad to a reader.
+-- | Convert a function producing a 'Maybe' into a reader.
 maybeReader :: (String -> Maybe a) -> ReadM a
-maybeReader f = eitherReader $ \arg ->
-  maybe (Left $ "cannot parse value `" ++ arg ++ "'") pure . f $ arg
+maybeReader f = do
+  arg  <- readerAsk
+  maybe (readerError $ "cannot parse value `" ++ arg ++ "'") return . f $ arg
 
 -- | Null 'Option' reader. All arguments will fail validation.
 disabled :: ReadM a
@@ -170,7 +184,7 @@
 
 -- | Specify the error to display when no argument is provided to this option.
 noArgError :: ParseError -> Mod OptionFields a
-noArgError e = fieldMod $ \p -> p { optNoArgError = e }
+noArgError e = fieldMod $ \p -> p { optNoArgError = const e }
 
 -- | Specify a metavariable for the argument.
 --
@@ -184,26 +198,55 @@
 hidden = optionMod $ \p ->
   p { propVisibility = min Hidden (propVisibility p) }
 
+-- | Apply a function to the option description in the usage text.
+--
+-- > import Options.Applicative.Help
+-- > flag' () (short 't' <> style bold)
+--
+-- /NOTE/: This builder is more flexible than its name and example
+-- allude. One of the motivating examples for its addition was to
+-- used `const` to completely replace the usage text of an option.
+style :: ( Doc -> Doc ) -> Mod f a
+style x = optionMod $ \p ->
+  p { propDescMod = Just x }
+
 -- | Add a command to a subparser option.
+--
+-- Suggested usage for multiple commands is to add them to a single subparser. e.g.
+--
+-- @
+-- sample :: Parser Sample
+-- sample = subparser
+--        ( command "hello"
+--          (info hello (progDesc "Print greeting"))
+--       <> command "goodbye"
+--          (info goodbye (progDesc "Say goodbye"))
+--        )
+-- @
 command :: String -> ParserInfo a -> Mod CommandFields a
 command cmd pinfo = fieldMod $ \p ->
   p { cmdCommands = (cmd, pinfo) : cmdCommands p }
 
 -- | Add a description to a group of commands.
+--
+-- Advanced feature for separating logical groups of commands on the parse line.
+--
+-- If using the same `metavar` for each group of commands, it may yield a more
+-- attractive usage text combined with `hidden` for some groups.
 commandGroup :: String -> Mod CommandFields a
 commandGroup g = fieldMod $ \p ->
   p { cmdGroup = Just g }
 
 -- | Add a list of possible completion values.
 completeWith :: HasCompleter f => [String] -> Mod f a
-completeWith xs = completer (listCompleter xs)
+completeWith = completer . listCompleter
 
 -- | Add a bash completion action. Common actions include @file@ and
 -- @directory@. See
 -- <http://www.gnu.org/software/bash/manual/html_node/Programmable-Completion-Builtins.html#Programmable-Completion-Builtins>
 -- for a complete list.
 action :: HasCompleter f => String -> Mod f a
-action act = completer (bashCompleter act)
+action = completer . bashCompleter
 
 -- | Add a completer to an argument.
 --
@@ -231,7 +274,7 @@
     rdr = CReader compl p
 
 -- | Builder for a 'String' argument.
-strArgument :: Mod ArgumentFields String -> Parser String
+strArgument :: IsString s => Mod ArgumentFields s -> Parser s
 strArgument = argument str
 
 -- | Builder for a flag parser.
@@ -298,7 +341,7 @@
 infoOption = abortOption . InfoMsg
 
 -- | Builder for an option taking a 'String' argument.
-strOption :: Mod OptionFields String -> Parser String
+strOption :: IsString s => Mod OptionFields s -> Parser s
 strOption = option str
 
 -- | Same as 'option'.
@@ -317,7 +360,7 @@
 option r m = mkParser d g rdr
   where
     Mod f d g = metavar "ARG" `mappend` m
-    fields = f (OptionFields [] mempty (ErrorMsg ""))
+    fields = f (OptionFields [] mempty ExpectsArgError)
     crdr = CReader (optCompleter fields) r
     rdr = OptReader (optNames fields) crdr (optNoArgError fields)
 
@@ -371,10 +414,23 @@
 failureCode :: Int -> InfoMod a
 failureCode n = InfoMod $ \i -> i { infoFailureCode = n }
 
--- | Disable parsing of regular options after arguments
+-- | Disable parsing of regular options after arguments. After a positional
+--   argument is parsed, all remaining options and arguments will be treated
+--   as a positional arguments. Not recommended in general as users often
+--   expect to be able to freely intersperse regular options and flags within
+--   command line options.
 noIntersperse :: InfoMod a
-noIntersperse = InfoMod $ \p -> p { infoIntersperse = False }
+noIntersperse = InfoMod $ \p -> p { infoPolicy = NoIntersperse }
 
+-- | Intersperse matched options and arguments normally, but allow unmatched
+--   options to be treated as positional arguments.
+--   This is sometimes useful if one is wrapping a third party cli tool and
+--   needs to pass options through, while also providing a handful of their
+--   own options. Not recommended in general as typos by the user may not
+--   yield a parse error and cause confusion.
+forwardOptions :: InfoMod a
+forwardOptions = InfoMod $ \p -> p { infoPolicy = ForwardOptions }
+
 -- | Create a 'ParserInfo' given a 'Parser' and a modifier.
 info :: Parser a -> InfoMod a -> ParserInfo a
 info parser m = applyInfoMod m base
@@ -386,7 +442,7 @@
       , infoHeader = mempty
       , infoFooter = mempty
       , infoFailureCode = 1
-      , infoIntersperse = True }
+      , infoPolicy = Intersperse }
 
 newtype PrefsMod = PrefsMod
   { applyPrefsMod :: ParserPrefs -> ParserPrefs }
@@ -398,24 +454,39 @@
 instance Semigroup PrefsMod where
   m1 <> m2 = PrefsMod $ applyPrefsMod m2 . applyPrefsMod m1
 
+-- | Include a suffix to attach to the metavar when multiple values
+--   can be entered.
 multiSuffix :: String -> PrefsMod
 multiSuffix s = PrefsMod $ \p -> p { prefMultiSuffix = s }
 
+-- | Turn on disambiguation.
+--
+--   See
+--   https://github.com/pcapriotti/optparse-applicative#disambiguation
 disambiguate :: PrefsMod
 disambiguate = PrefsMod $ \p -> p { prefDisambiguate = True }
 
+-- | Show full help text on any error.
 showHelpOnError :: PrefsMod
 showHelpOnError = PrefsMod $ \p -> p { prefShowHelpOnError = True }
 
+-- | Show the help text if the user enters only the program name or
+--   subcommand.
+--
+--   This will suppress a "Missing:" error and show the full usage
+--   instead if a user just types the name of the program.
 showHelpOnEmpty :: PrefsMod
 showHelpOnEmpty = PrefsMod $ \p -> p { prefShowHelpOnEmpty = True }
 
+-- | Turn off backtracking after subcommand is parsed.
 noBacktrack :: PrefsMod
 noBacktrack = PrefsMod $ \p -> p { prefBacktrack = False }
 
+-- | Set the maximum width of the generated help text.
 columns :: Int -> PrefsMod
 columns cols = PrefsMod $ \p -> p { prefColumns = cols }
 
+-- | Create a `ParserPrefs` given a modifier
 prefs :: PrefsMod -> ParserPrefs
 prefs m = applyPrefsMod m base
   where
@@ -427,7 +498,7 @@
       , prefBacktrack = True
       , prefColumns = 80 }
 
--- convenience shortcuts
+-- Convenience shortcuts
 
 -- | Trivial option modifier.
 idm :: Monoid m => m
diff --git a/Options/Applicative/Builder/Completer.hs b/Options/Applicative/Builder/Completer.hs
--- a/Options/Applicative/Builder/Completer.hs
+++ b/Options/Applicative/Builder/Completer.hs
@@ -14,18 +14,109 @@
 
 import Options.Applicative.Types
 
+-- | Create a 'Completer' from an IO action
 listIOCompleter :: IO [String] -> Completer
 listIOCompleter ss = Completer $ \s ->
   filter (isPrefixOf s) <$> ss
 
+-- | Create a 'Completer' from a constant
+-- list of strings.
 listCompleter :: [String] -> Completer
 listCompleter = listIOCompleter . pure
 
+-- | Run a compgen completion action.
+--
+-- Common actions include @file@ and
+-- @directory@. See
+-- <http://www.gnu.org/software/bash/manual/html_node/Programmable-Completion-Builtins.html#Programmable-Completion-Builtins>
+-- for a complete list.
 bashCompleter :: String -> Completer
 bashCompleter action = Completer $ \word -> do
-  let cmd = unwords ["compgen", "-A", action, "--", word]
+  let cmd = unwords ["compgen", "-A", action, "--", requote word]
   result <- tryIO $ readProcess "bash" ["-c", cmd] ""
   return . lines . either (const []) id $ result
 
 tryIO :: IO a -> IO (Either IOException a)
 tryIO = try
+
+-- | Strongly quote the string we pass to compgen.
+--
+-- We need to do this so bash doesn't expand out any ~ or other
+-- chars we want to complete on, or emit an end of line error
+-- when seeking the close to the quote.
+requote :: String -> String
+requote s =
+  let
+    -- Bash doesn't appear to allow "mixed" escaping
+    -- in bash completions. So we don't have to really
+    -- worry about people swapping between strong and
+    -- weak quotes.
+    unescaped =
+      case s of
+        -- It's already strongly quoted, so we
+        -- can use it mostly as is, but we must
+        -- ensure it's closed off at the end and
+        -- there's no single quotes in the
+        -- middle which might confuse bash.
+        ('\'': rs) -> unescapeN rs
+
+        -- We're weakly quoted.
+        ('"': rs)  -> unescapeD rs
+
+        -- We're not quoted at all.
+        -- We need to unescape some characters like
+        -- spaces and quotation marks.
+        elsewise   -> unescapeU elsewise
+  in
+    strong unescaped
+
+  where
+    strong ss = '\'' : foldr go "'" ss
+      where
+        -- If there's a single quote inside the
+        -- command: exit from the strong quote and
+        -- emit it the quote escaped, then resume.
+        go '\'' t = "'\\''" ++ t
+        go h t    = h : t
+
+    -- Unescape a strongly quoted string
+    -- We have two recursive functions, as we
+    -- can enter and exit the strong escaping.
+    unescapeN = goX
+      where
+        goX ('\'' : xs) = goN xs
+        goX (x : xs) = x : goX xs
+        goX [] = []
+
+        goN ('\\' : '\'' : xs) = '\'' : goN xs
+        goN ('\'' : xs) = goX xs
+        goN (x : xs) = x : goN xs
+        goN [] = []
+
+    -- Unescape an unquoted string
+    unescapeU = goX
+      where
+        goX [] = []
+        goX ('\\' : x : xs) = x : goX xs
+        goX (x : xs) = x : goX xs
+
+    -- Unescape a weakly quoted string
+    unescapeD = goX
+      where
+        -- Reached an escape character
+        goX ('\\' : x : xs)
+          -- If it's true escapable, strip the
+          -- slashes, as we're going to strong
+          -- escape instead.
+          | x `elem` "$`\"\\\n" = x : goX xs
+          | otherwise = '\\' : x : goX xs
+        -- We've ended quoted section, so we
+        -- don't recurse on goX, it's done.
+        goX ('"' : xs)
+          = xs
+        -- Not done, but not a special character
+        -- just continue the fold.
+        goX (x : xs)
+          = x : goX xs
+        goX []
+          = []
diff --git a/Options/Applicative/Builder/Internal.hs b/Options/Applicative/Builder/Internal.hs
--- a/Options/Applicative/Builder/Internal.hs
+++ b/Options/Applicative/Builder/Internal.hs
@@ -34,7 +34,7 @@
 data OptionFields a = OptionFields
   { optNames :: [OptName]
   , optCompleter :: Completer
-  , optNoArgError :: ParseError }
+  , optNoArgError :: String -> ParseError }
 
 data FlagFields a = FlagFields
   { flagNames :: [OptName]
@@ -118,7 +118,7 @@
 --
 -- Modifiers are instances of 'Monoid', and can be composed as such.
 --
--- You rarely need to deal with modifiers directly, as most of the times it is
+-- One rarely needs to deal with modifiers directly, as most of the times it is
 -- sufficient to pass them to builders (such as 'strOption' or 'flag') to
 -- create options (see 'Options.Applicative.Builder').
 data Mod f a = Mod (f a -> f a)
@@ -145,7 +145,9 @@
   { propMetaVar = ""
   , propVisibility = Visible
   , propHelp = mempty
-  , propShowDefault = Nothing }
+  , propShowDefault = Nothing
+  , propDescMod = Nothing
+  }
 
 mkCommand :: Mod CommandFields a -> (Maybe String, [String], String -> Maybe (ParserInfo a))
 mkCommand m = (group, map fst cmds, (`lookup` cmds))
diff --git a/Options/Applicative/Common.hs b/Options/Applicative/Common.hs
--- a/Options/Applicative/Common.hs
+++ b/Options/Applicative/Common.hs
@@ -55,7 +55,7 @@
 import Control.Monad.Trans.Class (lift)
 import Control.Monad.Trans.State (StateT(..), get, put, runStateT)
 import Data.List (isPrefixOf)
-import Data.Maybe (maybeToList, isJust)
+import Data.Maybe (maybeToList, isJust, isNothing)
 import Prelude
 
 import Options.Applicative.Internal
@@ -89,7 +89,7 @@
       prefs <- getPrefs
       let runSubparser
             | prefBacktrack prefs = \i a ->
-                runParser (getPolicy i) CmdStart (infoParser i) a
+                runParser (infoPolicy i) CmdStart (infoParser i) a
             | otherwise = \i a
             -> (,) <$> runParserInfo i a <*> pure []
       enterContext arg subp *> runSubparser subp args <* exitContext
@@ -102,12 +102,18 @@
     Just $ do
       args <- get
       let mb_args = uncons $ maybeToList val ++ args
-      let missing_arg = lift $ missingArgP no_arg_err (crCompleter rdr)
-      (arg', args') <- maybe missing_arg return mb_args
+      let missing_arg = missingArgP (no_arg_err $ showOption arg1) (crCompleter rdr)
+      (arg', args') <- maybe (lift missing_arg) return mb_args
       put args'
       lift $ runReadM (withReadM (errorFor arg1) (crReader rdr)) arg'
+
   FlagReader names x -> do
     guard $ has_name arg1 names
+    -- #242 Flags/switches succeed incorrectly when given an argument.
+    -- We'll not match a long option for a flag if there's a word attached.
+    -- This was revealing an implementation detail as
+    -- `--foo=val` was being parsed as `--foo -val`, which is gibberish.
+    guard $ is_short arg1 || isNothing val
     Just $ do
       args <- get
       let val' = (\s -> '-' : s) <$> val
@@ -117,6 +123,9 @@
   where
     errorFor name msg = "option " ++ showOption name ++ ": " ++ msg
 
+    is_short (OptShort _) = True
+    is_short (OptLong _)  = False
+
     has_name a
       | disambiguate = any (isOptionPrefix a)
       | otherwise = elem a
@@ -179,52 +188,52 @@
 
 stepParser :: MonadP m => ParserPrefs -> ArgPolicy -> String
            -> Parser a -> NondetT (StateT Args m) (Parser a)
-stepParser pprefs SkipOpts arg p = case parseWord arg of
+stepParser _ AllPositionals arg p =
+  searchArg arg p
+stepParser pprefs ForwardOptions arg p = case parseWord arg of
+  Just w -> searchOpt pprefs w p <|> searchArg arg p
+  Nothing -> searchArg arg p
+stepParser pprefs _ arg p = case parseWord arg of
   Just w -> searchOpt pprefs w p
   Nothing -> searchArg arg p
-stepParser _ AllowOpts arg p =
-  searchArg arg p
 
+
 -- | Apply a 'Parser' to a command line, and return a result and leftover
 -- arguments.  This function returns an error if any parsing error occurs, or
 -- if any options are missing and don't have a default value.
 runParser :: MonadP m => ArgPolicy -> IsCmdStart -> Parser a -> Args -> m (a, Args)
-runParser SkipOpts _ p ("--" : argt) = runParser AllowOpts CmdCont p argt
+runParser policy _ p ("--" : argt) | policy /= AllPositionals
+                                   = runParser AllPositionals CmdCont p argt
 runParser policy isCmdStart p args = case args of
-  [] -> exitP isCmdStart p result
+  [] -> exitP isCmdStart policy p result
   (arg : argt) -> do
     prefs <- getPrefs
     (mp', args') <- do_step prefs arg argt
     case mp' of
-      Nothing -> hoistMaybe result <|> parseError arg
-      Just p' -> runParser policy CmdCont p' args'
+      Nothing -> hoistMaybe result <|> parseError arg p
+      Just p' -> runParser (newPolicy arg) CmdCont p' args'
   where
     result = (,) <$> evalParser p <*> pure args
     do_step prefs arg argt = (`runStateT` argt)
                            . disamb (not (prefDisambiguate prefs))
                            $ stepParser prefs policy arg p
 
-parseError :: MonadP m => String -> m a
-parseError arg = errorP . ErrorMsg $ msg
-  where
-    msg = case arg of
-      ('-':_) -> "Invalid option `" ++ arg ++ "'"
-      _       -> "Invalid argument `" ++ arg ++ "'"
+    newPolicy a = case policy of
+      NoIntersperse -> if isJust (parseWord a) then NoIntersperse else AllPositionals
+      x             -> x
 
-getPolicy :: ParserInfo a -> ArgPolicy
-getPolicy i = if infoIntersperse i
-  then SkipOpts
-  else AllowOpts
+parseError :: MonadP m => String -> Parser x -> m a
+parseError arg = errorP . UnexpectedError arg . SomeParser
 
 runParserInfo :: MonadP m => ParserInfo a -> Args -> m a
-runParserInfo i = runParserFully (getPolicy i) (infoParser i)
+runParserInfo i = runParserFully (infoPolicy i) (infoParser i)
 
 runParserFully :: MonadP m => ArgPolicy -> Parser a -> Args -> m a
 runParserFully policy p args = do
   (r, args') <- runParser policy CmdStart p args
   case args' of
     []  -> return r
-    a:_ -> parseError a
+    a:_ -> parseError a (pure ())
 
 -- | The default value of a 'Parser'.  This function returns an error if any of
 -- the options don't have a default value.
@@ -238,7 +247,7 @@
 -- | Map a polymorphic function over all the options of a parser, and collect
 -- the results in a list.
 mapParser :: (forall x. OptHelpInfo -> Option x -> b)
-              -> Parser a -> [b]
+          -> Parser a -> [b]
 mapParser f = flatten . treeMapParser f
   where
     flatten (Leaf x) = [x]
@@ -249,25 +258,44 @@
 treeMapParser :: (forall x . OptHelpInfo -> Option x -> b)
           -> Parser a
           -> OptTree b
-treeMapParser g = simplify . go False False g
+treeMapParser g = simplify . go False False False g
   where
     has_default :: Parser a -> Bool
     has_default p = isJust (evalParser p)
 
-    go :: Bool -> Bool
+    go :: Bool -> Bool -> Bool
        -> (forall x . OptHelpInfo -> Option x -> b)
        -> Parser a
        -> OptTree b
-    go _ _ _ (NilP _) = MultNode []
-    go m d f (OptP opt)
+    go _ _ _ _ (NilP _) = MultNode []
+    go m d r f (OptP opt)
       | optVisibility opt > Internal
-      = Leaf (f (OptHelpInfo m d) opt)
+      = Leaf (f (OptHelpInfo m d r) opt)
       | otherwise
       = MultNode []
-    go m d f (MultP p1 p2) = MultNode [go m d f p1, go m d f p2]
-    go m d f (AltP p1 p2) = AltNode [go m d' f p1, go m d' f p2]
+    go m d r f (MultP p1 p2) = MultNode [go m d r f p1, go m d r' f p2]
+      where r' = r || has_positional p1
+    go m d r f (AltP p1 p2) = AltNode [go m d' r f p1, go m d' r f p2]
       where d' = d || has_default p1 || has_default p2
-    go _ d f (BindP p _) = go True d f p
+    go _ d r f (BindP p k) =
+      let go' = go True d r f p
+      in case evalParser p of
+        Nothing -> go'
+        Just aa -> MultNode [ go', go True d r f (k aa) ]
+
+    has_positional :: Parser a -> Bool
+    has_positional (NilP _) = False
+    has_positional (OptP p) = (is_positional . optMain) p
+    has_positional (MultP p1 p2) = has_positional p1 || has_positional p2
+    has_positional (AltP p1 p2) = has_positional p1 || has_positional p2
+    has_positional (BindP p _) = has_positional p
+
+    is_positional :: OptReader a -> Bool
+    is_positional (OptReader {})  = False
+    is_positional (FlagReader {}) = False
+    is_positional (ArgReader {})  = True
+    is_positional (CmdReader {})  = True
+
 
 simplify :: OptTree a -> OptTree a
 simplify (Leaf x) = Leaf x
diff --git a/Options/Applicative/Extra.hs b/Options/Applicative/Extra.hs
--- a/Options/Applicative/Extra.hs
+++ b/Options/Applicative/Extra.hs
@@ -29,7 +29,7 @@
 import System.IO (hPutStrLn, stderr)
 
 import Options.Applicative.BashCompletion
-import Options.Applicative.Builder hiding (briefDesc)
+import Options.Applicative.Builder
 import Options.Applicative.Builder.Internal
 import Options.Applicative.Common
 import Options.Applicative.Help
@@ -38,6 +38,13 @@
 import Options.Applicative.Types
 
 -- | A hidden \"helper\" option which always fails.
+--
+-- A common usage pattern is to apply this applicatively when
+-- creating a 'ParserInfo'
+--
+-- > opts :: ParserInfo Sample
+-- > opts = info (sample <**> helper) mempty
+
 helper :: Parser (a -> a)
 helper = abortOption ShowHelpText $ mconcat
   [ long "help"
@@ -143,15 +150,18 @@
   let h = with_context ctx pinfo $ \names pinfo' -> mconcat
             [ base_help pinfo'
             , usage_help progn names pinfo'
+            , suggestion_help
             , error_help ]
   in (h, exit_code, prefColumns pprefs)
   where
     exit_code = case msg of
-      ErrorMsg _       -> ExitFailure (infoFailureCode pinfo)
-      UnknownError     -> ExitFailure (infoFailureCode pinfo)
-      MissingError _ _ -> ExitFailure (infoFailureCode pinfo)
-      ShowHelpText     -> ExitSuccess
-      InfoMsg  _       -> ExitSuccess
+      ErrorMsg {}        -> ExitFailure (infoFailureCode pinfo)
+      UnknownError       -> ExitFailure (infoFailureCode pinfo)
+      MissingError {}    -> ExitFailure (infoFailureCode pinfo)
+      ExpectsArgError {} -> ExitFailure (infoFailureCode pinfo)
+      UnexpectedError {} -> ExitFailure (infoFailureCode pinfo)
+      ShowHelpText       -> ExitSuccess
+      InfoMsg {}         -> ExitSuccess
 
     with_context :: [Context]
                  -> ParserInfo a
@@ -161,20 +171,103 @@
     with_context c@(Context _ i:_) _ f = f (contextNames c) i
 
     usage_help progn names i = case msg of
-      InfoMsg _ -> mempty
-      _         -> usageHelp $ vcatChunks
-        [ pure . parserUsage pprefs (infoParser i) . unwords $ progn : names
-        , fmap (indent 2) . infoProgDesc $ i ]
+      InfoMsg _
+        -> mempty
+      _
+        -> usageHelp $ vcatChunks
+          [ pure . parserUsage pprefs (infoParser i) . unwords $ progn : names
+          , fmap (indent 2) . infoProgDesc $ i ]
 
     error_help = errorHelp $ case msg of
-      ShowHelpText                  -> mempty
-      ErrorMsg m                    -> stringChunk m
-      InfoMsg  m                    -> stringChunk m
-      MissingError CmdStart _        | prefShowHelpOnEmpty pprefs
-                                    -> mempty
-      MissingError _ (SomeParser x) -> stringChunk "Missing:" <<+>> missingDesc pprefs x
-      UnknownError                  -> mempty
+      ShowHelpText
+        -> mempty
 
+      ErrorMsg m
+        -> stringChunk m
+
+      InfoMsg  m
+        -> stringChunk m
+
+      MissingError CmdStart _
+        | prefShowHelpOnEmpty pprefs
+        -> mempty
+
+      MissingError _ (SomeParser x)
+        -> stringChunk "Missing:" <<+>> missingDesc pprefs x
+
+      ExpectsArgError x
+        -> stringChunk $ "The option `" ++ x ++ "` expects an argument."
+
+      UnexpectedError arg _
+        -> stringChunk msg'
+          where
+            --
+            -- This gives us the same error we have always
+            -- reported
+            msg' = case arg of
+              ('-':_) -> "Invalid option `" ++ arg ++ "'"
+              _       -> "Invalid argument `" ++ arg ++ "'"
+
+      UnknownError
+        -> mempty
+
+
+    suggestion_help = suggestionsHelp $ case msg of
+      UnexpectedError arg (SomeParser x)
+        --
+        -- We have an unexpected argument and the parser which
+        -- it's running over.
+        --
+        -- We can make a good help suggestion here if we do
+        -- a levenstein distance between all possible suggestions
+        -- and the supplied option or argument.
+        -> suggestions
+          where
+            --
+            -- Not using chunked here, as we don't want to
+            -- show "Did you mean" if there's nothing there
+            -- to show
+            suggestions = (.$.) <$> prose
+                                <*> (indent 4 <$> (vcatChunks . fmap stringChunk $ good ))
+
+            --
+            -- We won't worry about the 0 case, it won't be
+            -- shown anyway.
+            prose       = if length good < 2
+                            then stringChunk "Did you mean this?"
+                            else stringChunk "Did you mean one of these?"
+            --
+            -- Suggestions we will show, they're close enough
+            -- to what the user wrote
+            good        = filter isClose possibles
+
+            --
+            -- Bit of an arbitrary decision here.
+            -- Edit distances of 1 or 2 will give hints
+            isClose a   = editDistance a arg < 3
+
+            --
+            -- Similar to how bash completion works.
+            -- We map over the parser and get the names
+            -- ( no IO here though, unlike for completers )
+            possibles   = concat $ mapParser opt_completions x
+
+            --
+            -- Look at the option and give back the possible
+            -- things the user could type. If it's a command
+            -- reader also ensure that it can be immediately
+            -- reachable from where the error was given.
+            opt_completions hinfo opt = case optMain opt of
+              OptReader ns _ _ -> fmap showOption ns
+              FlagReader ns _  -> fmap showOption ns
+              ArgReader _      -> []
+              CmdReader _ ns _  | hinfoUnreachableArgs hinfo
+                               -> []
+                                | otherwise
+                               -> ns
+      _
+        -> mempty
+
     base_help :: ParserInfo a -> ParserHelp
     base_help i
       | show_full_help
@@ -187,7 +280,7 @@
 
     show_full_help = case msg of
       ShowHelpText             -> True
-      MissingError CmdStart  _ | prefShowHelpOnEmpty pprefs
+      MissingError CmdStart  _  | prefShowHelpOnEmpty pprefs
                                -> True
       _                        -> prefShowHelpOnError pprefs
 
diff --git a/Options/Applicative/Help.hs b/Options/Applicative/Help.hs
--- a/Options/Applicative/Help.hs
+++ b/Options/Applicative/Help.hs
@@ -16,9 +16,13 @@
   -- | Core implementation of the help text
   --   generator.
   module Options.Applicative.Help.Core,
+
+  -- | Edit distance calculations for suggestions
+  module Options.Applicative.Help.Levenshtein
   ) where
 
-import Options.Applicative.Help.Pretty
 import Options.Applicative.Help.Chunk
-import Options.Applicative.Help.Types
 import Options.Applicative.Help.Core
+import Options.Applicative.Help.Levenshtein
+import Options.Applicative.Help.Pretty
+import Options.Applicative.Help.Types
diff --git a/Options/Applicative/Help/Chunk.hs b/Options/Applicative/Help/Chunk.hs
--- a/Options/Applicative/Help/Chunk.hs
+++ b/Options/Applicative/Help/Chunk.hs
@@ -17,7 +17,7 @@
 import Control.Applicative
 import Control.Monad
 import Data.Maybe
-import Data.Monoid
+import Data.Semigroup
 import Prelude
 
 import Options.Applicative.Help.Pretty
@@ -45,6 +45,13 @@
   return = pure
   m >>= f = Chunk $ unChunk m >>= unChunk . f
 
+instance Monoid a => Semigroup (Chunk a) where
+  (<>) = chunked mappend
+
+instance Monoid a => Monoid (Chunk a) where
+  mempty = Chunk Nothing
+  mappend = (<>)
+
 instance MonadPlus Chunk where
   mzero = Chunk mzero
   mplus m1 m2 = Chunk $ mplus (unChunk m1) (unChunk m2)
@@ -65,10 +72,6 @@
 listToChunk :: Monoid a => [a] -> Chunk a
 listToChunk [] = mempty
 listToChunk xs = pure (mconcat xs)
-
-instance Monoid a => Monoid (Chunk a) where
-  mempty = Chunk Nothing
-  mappend = chunked mappend
 
 -- | Part of a constrained comonad instance.
 --
diff --git a/Options/Applicative/Help/Core.hs b/Options/Applicative/Help/Core.hs
--- a/Options/Applicative/Help/Core.hs
+++ b/Options/Applicative/Help/Core.hs
@@ -7,6 +7,7 @@
   ParserHelp(..),
   errorHelp,
   headerHelp,
+  suggestionsHelp,
   usageHelp,
   bodyHelp,
   footerHelp,
@@ -64,7 +65,7 @@
         = mappend chunk suffix
         | otherwise
         = mappend (fmap parens chunk) suffix
-  in render desc'
+  in maybe id fmap (optDescMod opt) (render desc')
 
 -- | Generate descriptions for commands.
 cmdDesc :: Parser a -> [(Maybe String, Chunk Doc)]
@@ -131,19 +132,22 @@
       , descSurround = False }
 
 errorHelp :: Chunk Doc -> ParserHelp
-errorHelp chunk = ParserHelp chunk mempty mempty mempty mempty
+errorHelp chunk = mempty { helpError = chunk }
 
 headerHelp :: Chunk Doc -> ParserHelp
-headerHelp chunk = ParserHelp mempty chunk mempty mempty mempty
+headerHelp chunk = mempty { helpHeader = chunk }
 
+suggestionsHelp :: Chunk Doc -> ParserHelp
+suggestionsHelp chunk = mempty { helpSuggestions = chunk }
+
 usageHelp :: Chunk Doc -> ParserHelp
-usageHelp chunk = ParserHelp mempty mempty chunk mempty mempty
+usageHelp chunk = mempty { helpUsage = chunk }
 
 bodyHelp :: Chunk Doc -> ParserHelp
-bodyHelp chunk = ParserHelp mempty mempty mempty chunk mempty
+bodyHelp chunk = mempty { helpBody = chunk }
 
 footerHelp :: Chunk Doc -> ParserHelp
-footerHelp chunk = ParserHelp mempty mempty mempty mempty chunk
+footerHelp chunk = mempty { helpFooter = chunk }
 
 -- | Generate the help text for a program.
 parserHelp :: ParserPrefs -> Parser a -> ParserHelp
diff --git a/Options/Applicative/Help/Levenshtein.hs b/Options/Applicative/Help/Levenshtein.hs
new file mode 100644
--- /dev/null
+++ b/Options/Applicative/Help/Levenshtein.hs
@@ -0,0 +1,61 @@
+module Options.Applicative.Help.Levenshtein (
+    editDistance
+  ) where
+
+-- | Calculate the Damerau-Levenshtein edit distance
+--   between two lists (strings).
+--
+--   This is modified from
+--   https://wiki.haskell.org/Edit_distance
+--   and is originally from Lloyd Allison's paper
+--   "Lazy Dynamic-Programming can be Eager"
+--
+--   It's been changed though from Levenshtein to
+--   Damerau-Levenshtein, which treats transposition
+--   of adjacent characters as one change instead of
+--   two.
+editDistance :: Eq a => [a] -> [a] -> Int
+editDistance a b = last $
+  case () of
+    _ | lab == 0
+     -> mainDiag
+      | lab > 0
+     -> lowers !! (lab - 1)
+      | otherwise
+     -> uppers !! (-1 - lab)
+  where
+    mainDiag = oneDiag a b (head uppers) (-1 : head lowers)
+    uppers = eachDiag a b (mainDiag : uppers) -- upper diagonals
+    lowers = eachDiag b a (mainDiag : lowers) -- lower diagonals
+    eachDiag _ [] _ = []
+    eachDiag _ _ [] = []
+    eachDiag a' (_:bs) (lastDiag:diags) =
+      oneDiag a' bs nextDiag lastDiag : eachDiag a' bs diags
+      where
+        nextDiag = head (tail diags)
+    oneDiag a' b' diagAbove diagBelow = thisdiag
+      where
+        doDiag [] _ _ _ _ = []
+        doDiag _ [] _ _ _ = []
+        -- Check for a transposition
+        -- We don't add anything to nw here, the next character
+        -- will be different however and the transposition
+        -- will have an edit distance of 1.
+        doDiag (ach:ach':as) (bch:bch':bs) nw n w
+          | ach' == bch && ach == bch'
+          = nw : (doDiag (ach' : as) (bch' : bs) nw (tail n) (tail w))
+        -- Standard case
+        doDiag (ach:as) (bch:bs) nw n w =
+          me : (doDiag as bs me (tail n) (tail w))
+          where
+            me =
+              if ach == bch
+                then nw
+                else 1 + min3 (head w) nw (head n)
+        firstelt = 1 + head diagBelow
+        thisdiag = firstelt : doDiag a' b' firstelt diagAbove (tail diagBelow)
+    lab = length a - length b
+    min3 x y z =
+      if x < y
+        then x
+        else min y z
diff --git a/Options/Applicative/Help/Types.hs b/Options/Applicative/Help/Types.hs
--- a/Options/Applicative/Help/Types.hs
+++ b/Options/Applicative/Help/Types.hs
@@ -1,6 +1,6 @@
 module Options.Applicative.Help.Types (
-  ParserHelp(..),
-  renderHelp
+    ParserHelp (..)
+  , renderHelp
   ) where
 
 import Data.Semigroup
@@ -11,6 +11,7 @@
 
 data ParserHelp = ParserHelp
   { helpError :: Chunk Doc
+  , helpSuggestions :: Chunk Doc
   , helpHeader :: Chunk Doc
   , helpUsage :: Chunk Doc
   , helpBody :: Chunk Doc
@@ -20,17 +21,17 @@
   showsPrec _ h = showString (renderHelp 80 h)
 
 instance Monoid ParserHelp where
-  mempty = ParserHelp mempty mempty mempty mempty mempty
+  mempty = ParserHelp mempty mempty mempty mempty mempty mempty
   mappend = (<>)
 
 instance Semigroup ParserHelp where
-  (ParserHelp e1 h1 u1 b1 f1) <> (ParserHelp e2 h2 u2 b2 f2)
-    = ParserHelp (mappend e1 e2) (mappend h1 h2)
-                 (mappend u1 u2) (mappend b1 b2)
-                 (mappend f1 f2)
+  (ParserHelp e1 s1 h1 u1 b1 f1) <> (ParserHelp e2 s2 h2 u2 b2 f2)
+    = ParserHelp (mappend e1 e2) (mappend s1 s2)
+                 (mappend h1 h2) (mappend u1 u2)
+                 (mappend b1 b2) (mappend f1 f2)
 
 helpText :: ParserHelp -> Doc
-helpText (ParserHelp e h u b f) = extractChunk . vsepChunks $ [e, h, u, b, f]
+helpText (ParserHelp e s h u b f) = extractChunk . vsepChunks $ [e, s, h, u, b, f]
 
 -- | Convert a help text to 'String'.
 renderHelp :: Int -> ParserHelp -> String
diff --git a/Options/Applicative/Internal.hs b/Options/Applicative/Internal.hs
--- a/Options/Applicative/Internal.hs
+++ b/Options/Applicative/Internal.hs
@@ -46,7 +46,7 @@
   missingArgP :: ParseError -> Completer -> m a
   tryP :: m a -> m (Either ParseError a)
   errorP :: ParseError -> m a
-  exitP :: IsCmdStart -> Parser b -> Maybe a -> m a
+  exitP :: IsCmdStart -> ArgPolicy -> Parser b -> Maybe a -> m a
 
 newtype P a = P (ExceptT ParseError (StateT [Context] (Reader ParserPrefs)) a)
 
@@ -62,7 +62,7 @@
   P x <|> P y = P $ x <|> y
 
 instance Monad P where
-  return a = P $ return a
+  return = pure
   P x >>= k = P $ x >>= \a -> case k a of P y -> y
 
 instance MonadPlus P where
@@ -81,7 +81,7 @@
 
   missingArgP e _ = errorP e
   tryP (P p) = P $ lift $ runExceptT p
-  exitP i p = P . maybe (throwE . MissingError i . SomeParser $ p) return
+  exitP i _ p = P . maybe (throwE . MissingError i . SomeParser $ p) return
   errorP = P . throwE
 
 hoistMaybe :: MonadPlus m => Maybe a -> m a
@@ -107,7 +107,7 @@
     f' e = e
 
 data ComplResult a
-  = ComplParser SomeParser
+  = ComplParser SomeParser ArgPolicy
   | ComplOption Completer
   | ComplResult a
 
@@ -122,7 +122,7 @@
   return = pure
   m >>= f = case m of
     ComplResult r -> f r
-    ComplParser p -> ComplParser p
+    ComplParser p a -> ComplParser p a
     ComplOption c -> ComplOption c
 
 newtype Completion a =
@@ -140,7 +140,7 @@
   Completion x <|> Completion y = Completion $ x <|> y
 
 instance Monad Completion where
-  return a = Completion $ return a
+  return = pure
   Completion x >>= k = Completion $ x >>= \a -> case k a of Completion y -> y
 
 instance MonadPlus Completion where
@@ -154,13 +154,13 @@
 
   missingArgP _ = Completion . lift . lift . ComplOption
   tryP (Completion p) = Completion $ catchE (Right <$> p) (return . Left)
-  exitP _ p _ = Completion . lift . lift . ComplParser $ SomeParser p
+  exitP _ a p _ = Completion . lift . lift $ ComplParser (SomeParser p) a
   errorP = Completion . throwE
 
-runCompletion :: Completion r -> ParserPrefs -> Maybe (Either SomeParser Completer)
+runCompletion :: Completion r -> ParserPrefs -> Maybe (Either (SomeParser, ArgPolicy) Completer)
 runCompletion (Completion c) prefs = case runReaderT (runExceptT c) prefs of
   ComplResult _ -> Nothing
-  ComplParser p' -> Just $ Left p'
+  ComplParser p' a' -> Just $ Left (p', a')
   ComplOption compl -> Just $ Right compl
 
 -- A "ListT done right" implementation
diff --git a/Options/Applicative/Types.hs b/Options/Applicative/Types.hs
--- a/Options/Applicative/Types.hs
+++ b/Options/Applicative/Types.hs
@@ -39,7 +39,8 @@
   optVisibility,
   optMetaVar,
   optHelp,
-  optShowDefault
+  optShowDefault,
+  optDescMod
   ) where
 
 import Control.Applicative
@@ -47,6 +48,7 @@
 import Control.Monad.Trans.Except (Except, throwE)
 import Control.Monad.Trans.Class (lift)
 import Control.Monad.Trans.Reader (ReaderT, ask)
+import qualified Control.Monad.Fail as Fail
 import Data.Semigroup hiding (Option)
 import Prelude
 
@@ -63,6 +65,8 @@
   | ShowHelpText
   | UnknownError
   | MissingError IsCmdStart SomeParser
+  | ExpectsArgError String
+  | UnexpectedError String SomeParser
 
 data IsCmdStart = CmdStart | CmdCont
   deriving Show
@@ -84,8 +88,8 @@
   , infoHeader :: Chunk Doc   -- ^ header of the full parser description
   , infoFooter :: Chunk Doc   -- ^ footer of the full parser description
   , infoFailureCode :: Int    -- ^ exit code for a parser failure
-  , infoIntersperse :: Bool   -- ^ allow regular options and flags to occur
-                              -- after arguments (default: True)
+  , infoPolicy :: ArgPolicy   -- ^ allow regular options and flags to occur
+                              -- after arguments (default: InterspersePolicy)
   }
 
 instance Functor ParserInfo where
@@ -123,8 +127,18 @@
   , propHelp :: Chunk Doc                 -- ^ help text for this option
   , propMetaVar :: String                 -- ^ metavariable for this option
   , propShowDefault :: Maybe String       -- ^ what to show in the help text as the default
-  } deriving Show
+  , propDescMod :: Maybe ( Doc -> Doc )   -- ^ a function to run over the brief description
+  }
 
+instance Show OptProperties where
+  showsPrec p (OptProperties pV pH pMV pSD _)
+    = showParen (p >= 11)
+    $ showString "OptProperties { propVisibility = " . showsPrec 0 pV
+    . showString ", propHelp = " . showsPrec 0 pH
+    . showString ", propMetaVar = " . showsPrec 0 pMV
+    . showString ", propShowDefault = " . showsPrec 0 pSD
+    . showString ", propDescMod = _ }"
+
 -- | A single option of a parser.
 data Option a = Option
   { optMain :: OptReader a               -- ^ reader for this option
@@ -161,6 +175,9 @@
 instance Monad ReadM where
   return = pure
   ReadM r >>= f = ReadM $ r >>= unReadM . f
+  fail = Fail.fail
+
+instance Fail.MonadFail ReadM where
   fail = readerError
 
 instance MonadPlus ReadM where
@@ -188,11 +205,14 @@
 
 -- | An 'OptReader' defines whether an option matches an command line argument.
 data OptReader a
-  = OptReader [OptName] (CReader a) ParseError          -- ^ option reader
-  | FlagReader [OptName] !a                             -- ^ flag reader
-  | ArgReader (CReader a)                               -- ^ argument reader
-  | CmdReader (Maybe String)
-              [String] (String -> Maybe (ParserInfo a)) -- ^ command reader
+  = OptReader [OptName] (CReader a) (String -> ParseError)
+  -- ^ option reader
+  | FlagReader [OptName] !a
+  -- ^ flag reader
+  | ArgReader (CReader a)
+  -- ^ argument reader
+  | CmdReader (Maybe String) [String] (String -> Maybe (ParserInfo a))
+  -- ^ command reader
 
 instance Functor OptReader where
   fmap f (OptReader ns cr e) = OptReader ns (fmap f cr) e
@@ -223,14 +243,14 @@
   { runParserM :: forall x . (r -> Parser x) -> Parser x }
 
 instance Monad ParserM where
-  return x = ParserM $ \k -> k x
+  return = pure
   ParserM f >>= g = ParserM $ \k -> f (\x -> runParserM (g x) k)
 
 instance Functor ParserM where
   fmap = liftM
 
 instance Applicative ParserM where
-  pure = return
+  pure x = ParserM $ \k -> k x
   (<*>) = ap
 
 fromM :: ParserM a -> Parser a
@@ -255,16 +275,21 @@
   many p = fromM $ manyM p
   some p = fromM $ (:) <$> oneM p <*> manyM p
 
+-- | A shell complete function.
 newtype Completer = Completer
   { runCompleter :: String -> IO [String] }
 
+-- | Smart constructor for a 'Completer'
 mkCompleter :: (String -> IO [String]) -> Completer
 mkCompleter = Completer
 
+instance Semigroup Completer where
+  (Completer c1) <> (Completer c2) =
+    Completer $ \s -> (++) <$> c1 s <*> c2 s
+
 instance Monoid Completer where
   mempty = Completer $ \_ -> return []
-  mappend (Completer c1) (Completer c2) =
-    Completer $ \s -> (++) <$> c1 s <*> c2 s
+  mappend = (<>)
 
 newtype CompletionResult = CompletionResult
   { execCompletion :: String -> IO String }
@@ -319,16 +344,34 @@
 
 -- | Policy for how to handle options within the parse
 data ArgPolicy
-  = SkipOpts  -- ^ Inputs beginning with `-` or `--` are treated
-              --   as options or flags, and can be mixed with arguments.
-  | AllowOpts -- ^ All input is treated as positional arguments.
-              --   Used after a bare `--` input, and also with
-              --   `noIntersperse` policy.
-  deriving (Eq, Show)
+  = Intersperse
+  -- ^ The default policy, options and arguments can
+  --   be interspersed.
+  --   A `--` option can be passed to ensure all following
+  --   commands are treated as arguments.
+  | NoIntersperse
+  -- ^ Options must all come before arguments, once a
+  --   single positional argument or subcommand is parsed,
+  --   all remaining arguments are treated as positionals.
+  --   A `--` option can be passed if the first positional
+  --   one needs starts with `-`.
+  | AllPositionals
+  -- ^ No options are parsed at all, all arguments are
+  --   treated as positionals.
+  --   Is the policy used after `--` is encountered.
+  | ForwardOptions
+  -- ^ Options and arguments can be interspersed, but if
+  --   a given option is not found, it is treated as a
+  --   positional argument. This is sometimes useful if
+  --   one is passing through most options to another tool,
+  --   but are supplying just a few of their own options.
+  deriving (Eq, Ord, Show)
 
 data OptHelpInfo = OptHelpInfo
-  { hinfoMulti :: Bool
-  , hinfoDefault :: Bool
+  { hinfoMulti :: Bool    -- ^ Whether this is part of a many or some (approximately)
+  , hinfoDefault :: Bool  -- ^ Whether this option has a default value
+  , hinfoUnreachableArgs :: Bool -- ^ If the result is a positional, if it can't be
+                                 --   accessed in the current parser position ( first arg )
   } deriving (Eq, Show)
 
 data OptTree a
@@ -348,3 +391,6 @@
 
 optShowDefault :: Option a -> Maybe String
 optShowDefault = propShowDefault . optProps
+
+optDescMod :: Option a -> Maybe ( Doc -> Doc )
+optDescMod = propDescMod . optProps
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -2,6 +2,7 @@
 
 [![Continuous Integration status][status-png]][status]
 [![Hackage page (downloads and API reference)][hackage-png]][hackage]
+[![Hackage-Deps][hackage-deps-png]][hackage-deps]
 
 optparse-applicative is a haskell library for parsing options on
 the command line, providing a powerful [applicative] interface
@@ -16,7 +17,7 @@
 
 - [Introduction](#introduction)
 - [Quick Start](#quick-start)
-- [Basics](#Basics)
+- [Basics](#basics)
     - [Parsers](#parsers)
     - [Applicative](#applicative)
     - [Alternative](#alternative)
@@ -75,9 +76,9 @@
 import Data.Semigroup ((<>))
 
 data Sample = Sample
-  { hello  :: String
-  , quiet  :: Bool
-  , repeat :: Int }
+  { hello      :: String
+  , quiet      :: Bool
+  , enthusiasm :: Int }
 
 sample :: Parser Sample
 sample = Sample
@@ -90,8 +91,8 @@
          <> short 'q'
          <> help "Whether to be quiet" )
       <*> option auto
-          ( long "repeat"
-         <> help "Repeats for greeting"
+          ( long "enthusiasm"
+         <> help "How enthusiastically to greet"
          <> showDefault
          <> value 1
          <> metavar "INT" )
@@ -100,8 +101,8 @@
 The parser is built using an [applicative] style starting from a
 set of basic combinators. In this example, hello is defined as an
 option with a `String` argument, while quiet is a boolean flag
-(called switch) and repeat gets parsed as an `Int` with help of the
-`Read` type class.
+(called a switch) and enthusiasm gets parsed as an `Int` with help
+of the `Read` type class.
 
 
 The parser can be used like this:
@@ -116,7 +117,7 @@
      <> header "hello - a test for optparse-applicative" )
 
 greet :: Sample -> IO ()
-greet (Sample h False n) = replicateM_ n . putStrLn $ "Hello, " ++ h
+greet (Sample h False n) = putStrLn $ "Hello, " ++ h ++ replicate n '!'
 greet _ = return ()
 ```
 
@@ -131,7 +132,7 @@
 
     Missing: --hello TARGET
 
-    Usage: hello --hello TARGET [-q|--quiet] [--repeat INT]
+    Usage: hello --hello TARGET [-q|--quiet] [--enthusiasm INT]
       Print a greeting for TARGET
 
 Running the program with the `--help` option will display the full help text
@@ -140,13 +141,13 @@
 ```
     hello - a test for optparse-applicative
 
-    Usage: hello --hello TARGET [-q|--quiet] [--repeat INT]
+    Usage: hello --hello TARGET [-q|--quiet] [--enthusiasm INT]
       Print a greeting for TARGET
 
     Available options:
       --hello TARGET           Target for the greeting
       -q,--quiet               Whether to be quiet
-      --repeat INT             Repeats for greeting (default: 1)
+      --enthusiasm INT         How enthusiastically to greet (default: 1)
       -h,--help                Show this help text
 ```
 
@@ -165,7 +166,7 @@
 ```haskell
 target :: Parser String
 target = strOption
-  (  long "target"
+  (  long "hello"
   <> metavar "TARGET"
   <> help "Target for the greeting" )
 ```
@@ -175,7 +176,7 @@
 and the given help text. This means that the `target` parser defined
 above will require an option like
 
-    --target world
+    --hello world
 
 on the command line. The metavariable and the help text will appear
 in the generated help text, but don't otherwise affect the behaviour
@@ -1001,6 +1002,8 @@
  [blog]: http://paolocapriotti.com/blog/2012/04/27/applicative-option-parser/
  [hackage]: http://hackage.haskell.org/package/optparse-applicative
  [hackage-png]: http://img.shields.io/hackage/v/optparse-applicative.svg
+ [hackage-deps]: http://packdeps.haskellers.com/reverse/optparse-applicative
+ [hackage-deps-png]: https://img.shields.io/hackage-deps/v/optparse-applicative.svg
  [monoid]: http://hackage.haskell.org/package/base/docs/Data-Monoid.html
  [semigroup]: http://hackage.haskell.org/package/base/docs/Data-Semigroup.html
  [parsec]: http://hackage.haskell.org/package/parsec
diff --git a/optparse-applicative.cabal b/optparse-applicative.cabal
--- a/optparse-applicative.cabal
+++ b/optparse-applicative.cabal
@@ -1,5 +1,5 @@
 name:                optparse-applicative
-version:             0.13.2.0
+version:             0.14.0.0
 synopsis:            Utilities and combinators for parsing command line options
 description:
     optparse-applicative is a haskell library for parsing options
@@ -17,9 +17,9 @@
 license:             BSD3
 license-file:        LICENSE
 author:              Paolo Capriotti, Huw Campbell
-maintainer:          paolo@capriotti.io
+maintainer:          huw.campbell@gmail.com
 copyright:           (c) 2012-2017 Paolo Capriotti <paolo@capriotti.io>
-category:            System
+category:            System, CLI, Options, Parsing
 build-type:          Simple
 cabal-version:       >= 1.8
 extra-source-files:  CHANGELOG.md
@@ -51,11 +51,12 @@
   location: https://github.com/pcapriotti/optparse-applicative.git
 
 library
-  if impl(ghc >= 8)
-    ghc-options:       -Wall -Wno-redundant-constraints
-  else
-    ghc-options:       -Wall
+  ghc-options:         -Wall
 
+  -- See https://ghc.haskell.org/trac/ghc/wiki/Migration/8.0#base-4.9.0.0
+  if impl(ghc >= 8.0)
+    ghc-options:  -Wno-redundant-constraints -Wcompat -Wnoncanonical-monad-instances -Wnoncanonical-monadfail-instances
+
   exposed-modules:     Options.Applicative,
                        Options.Applicative.Arrows,
                        Options.Applicative.BashCompletion,
@@ -65,9 +66,10 @@
                        Options.Applicative.Common,
                        Options.Applicative.Extra,
                        Options.Applicative.Help,
-                       Options.Applicative.Help.Pretty,
                        Options.Applicative.Help.Chunk,
                        Options.Applicative.Help.Core,
+                       Options.Applicative.Help.Levenshtein,
+                       Options.Applicative.Help.Pretty,
                        Options.Applicative.Help.Types,
                        Options.Applicative.Types,
                        Options.Applicative.Internal
@@ -81,7 +83,7 @@
 
   if !impl(ghc >= 8)
     build-depends:     semigroups                      >= 0.10 && < 0.19
-
+                     , fail                            == 4.9.*
 
 test-suite optparse-applicative-tests
   type:                exitcode-stdio-1.0
@@ -94,6 +96,7 @@
                        tests
 
   build-depends:       base
+                     , bytestring                      == 0.10.*
                      , optparse-applicative
                      , QuickCheck                      >= 2.8 && < 2.11
 
diff --git a/tests/commands_header.err.txt b/tests/commands_header.err.txt
--- a/tests/commands_header.err.txt
+++ b/tests/commands_header.err.txt
@@ -1,3 +1,6 @@
-Invalid option `-zzz'
+Invalid option `-zello'
+
+Did you mean this?
+    hello
 
 Usage: commands_header COMMAND
diff --git a/tests/commands_header_full.err.txt b/tests/commands_header_full.err.txt
--- a/tests/commands_header_full.err.txt
+++ b/tests/commands_header_full.err.txt
@@ -1,4 +1,7 @@
-Invalid option `-zzz'
+Invalid option `-zello'
+
+Did you mean this?
+    hello
 
 foo
 
diff --git a/tests/test.hs b/tests/test.hs
--- a/tests/test.hs
+++ b/tests/test.hs
@@ -12,8 +12,10 @@
 
 import           Control.Applicative
 import           Control.Monad
+import           Data.ByteString (ByteString)
 import           Data.List hiding (group)
 import           Data.Semigroup hiding (option)
+import           Data.String
 
 import           System.Exit
 import           Test.QuickCheck hiding (Success, Failure)
@@ -24,6 +26,7 @@
 import           Options.Applicative.Help.Pretty (Doc, SimpleDoc(..))
 import qualified Options.Applicative.Help.Pretty as Doc
 import           Options.Applicative.Help.Chunk
+import           Options.Applicative.Help.Levenshtein
 
 import           Prelude
 
@@ -73,9 +76,9 @@
 prop_cmd_header = once $
   let i  = info (helper <*> Commands.sample) (header "foo")
       r1 = checkHelpTextWith (ExitFailure 1) defaultPrefs
-                    "commands_header" i ["-zzz"]
+                    "commands_header" i ["-zello"]
       r2 = checkHelpTextWith (ExitFailure 1) (prefs showHelpOnError)
-                    "commands_header_full" i ["-zzz"]
+                    "commands_header_full" i ["-zello"]
   in  (r1 .&&. r2)
 
 prop_cabal_conf :: Property
@@ -129,7 +132,8 @@
 
 prop_alt_help :: Property
 prop_alt_help = once $
-  let p = p1 <|> p2 <|> p3
+  let p :: Parser (Maybe (Either String String))
+      p = p1 <|> p2 <|> p3
       p1 = (Just . Left)
         <$> strOption ( long "virtual-machine"
                      <> metavar "VM"
@@ -144,7 +148,8 @@
 
 prop_nested_commands :: Property
 prop_nested_commands = once $
-  let p3 = strOption (short 'a' <> metavar "A")
+  let p3 :: Parser String
+      p3 = strOption (short 'a' <> metavar "A")
       p2 = subparser (command "b" (info p3 idm))
       p1 = subparser (command "c" (info p2 idm))
       i = info (p1 <**> helper) idm
@@ -152,7 +157,8 @@
 
 prop_drops_back_contexts :: Property
 prop_drops_back_contexts = once $
-  let p3 = strOption (short 'a' <> metavar "A")
+  let p3 :: Parser String
+      p3 = strOption (short 'a' <> metavar "A")
       p2 = subparser (command "b" (info p3 idm)  <> metavar "B")
       p1 = subparser (command "c" (info p3 idm)  <> metavar "C")
       p0 = (,) <$> p2 <*> p1
@@ -161,7 +167,8 @@
 
 prop_context_carry :: Property
 prop_context_carry = once $
-  let p3 = strOption (short 'a' <> metavar "A")
+  let p3 :: Parser String
+      p3 = strOption (short 'a' <> metavar "A")
       p2 = subparser (command "b" (info p3 idm)  <> metavar "B")
       p1 = subparser (command "c" (info p3 idm)  <> metavar "C")
       p0 = (,) <$> p2 <*> p1
@@ -170,7 +177,8 @@
 
 prop_help_on_empty :: Property
 prop_help_on_empty = once $
-  let p3 = strOption (short 'a' <> metavar "A")
+  let p3 :: Parser String
+      p3 = strOption (short 'a' <> metavar "A")
       p2 = subparser (command "b" (info p3 idm)  <> metavar "B")
       p1 = subparser (command "c" (info p3 idm)  <> metavar "C")
       p0 = (,) <$> p2 <*> p1
@@ -179,7 +187,8 @@
 
 prop_help_on_empty_sub :: Property
 prop_help_on_empty_sub = once $
-  let p3 = strOption (short 'a' <> metavar "A" <> help "both commands require this")
+  let p3 :: Parser String
+      p3 = strOption (short 'a' <> metavar "A" <> help "both commands require this")
       p2 = subparser (command "b" (info p3 idm)  <> metavar "B")
       p1 = subparser (command "c" (info p3 idm)  <> metavar "C")
       p0 = (,) <$> p2 <*> p1
@@ -188,7 +197,8 @@
 
 prop_many_args :: Property
 prop_many_args = forAll (choose (0,2000)) $ \nargs ->
-  let p = many (argument str idm)
+  let p :: Parser [String]
+      p = many (argument str idm)
       i = info p idm
       result = run i (replicate nargs "foo")
   in  assertResult result (\xs -> nargs === length xs)
@@ -225,9 +235,104 @@
     Failure _   -> return $ counterexample "unexpected failure" failed
     Success val -> return $ counterexample ("unexpected result " ++ show val) failed
 
+prop_completion_opt_after_double_dash :: Property
+prop_completion_opt_after_double_dash = once . ioProperty $
+  let p = (,)
+        <$> strOption (long "foo" <> value "")
+        <*> argument readerAsk (completeWith ["bar"])
+      i = info p idm
+      result = run i ["--bash-completion-index", "2"
+                    , "--bash-completion-word", "test"
+                    , "--bash-completion-word", "--"]
+  in case result of
+    CompletionInvoked (CompletionResult err) -> do
+      completions <- lines <$> err "test"
+      return $ ["bar"] === completions
+    Failure _   -> return $ counterexample "unexpected failure" failed
+    Success val -> return $ counterexample ("unexpected result " ++ show val) failed
+
+prop_completion_only_reachable :: Property
+prop_completion_only_reachable = once . ioProperty $
+  let p :: Parser (String,String)
+      p = (,)
+        <$> strArgument (completeWith ["reachable"])
+        <*> strArgument (completeWith ["unreachable"])
+      i = info p idm
+      result = run i ["--bash-completion-index", "0"]
+  in case result of
+    CompletionInvoked (CompletionResult err) -> do
+      completions <- lines <$> err "test"
+      return $ ["reachable"] === completions
+    Failure _   -> return $ counterexample "unexpected failure" failed
+    Success val -> return $ counterexample ("unexpected result " ++ show val) failed
+
+prop_completion_only_reachable_deep :: Property
+prop_completion_only_reachable_deep = once . ioProperty $
+  let p :: Parser (String,String)
+      p = (,)
+        <$> strArgument (completeWith ["seen"])
+        <*> strArgument (completeWith ["now-reachable"])
+      i = info p idm
+      result = run i [ "--bash-completion-index", "2"
+                     , "--bash-completion-word", "test-prog"
+                     , "--bash-completion-word", "seen" ]
+  in case result of
+    CompletionInvoked (CompletionResult err) -> do
+      completions <- lines <$> err "test"
+      return $ ["now-reachable"] === completions
+    Failure _   -> return $ counterexample "unexpected failure" failed
+    Success val -> return $ counterexample ("unexpected result " ++ show val) failed
+
+prop_completion_multi :: Property
+prop_completion_multi = once . ioProperty $
+  let p :: Parser [String]
+      p = many (strArgument (completeWith ["reachable"]))
+      i = info p idm
+      result = run i [ "--bash-completion-index", "3"
+                     , "--bash-completion-word", "test-prog"
+                     , "--bash-completion-word", "nope" ]
+  in case result of
+    CompletionInvoked (CompletionResult err) -> do
+      completions <- lines <$> err "test"
+      return $ ["reachable"] === completions
+    Failure _   -> return $ counterexample "unexpected failure" failed
+    Success val -> return $ counterexample ("unexpected result " ++ show val) failed
+
+prop_completion_rich :: Property
+prop_completion_rich = once . ioProperty $
+  let p = (,)
+        <$> option readerAsk (long "foo" <> help "Fo?")
+        <*> option readerAsk (long "bar" <> help "Ba?")
+      i = info p idm
+      result = run i ["--bash-completion-enriched", "--bash-completion-index", "0"]
+  in case result of
+    CompletionInvoked (CompletionResult err) -> do
+      completions <- lines <$> err "test"
+      return $ ["--foo\tFo?", "--bar\tBa?"] === completions
+    Failure _   -> return $ counterexample "unexpected failure" failed
+    Success val -> return $ counterexample ("unexpected result " ++ show val) failed
+
+prop_completion_rich_lengths :: Property
+prop_completion_rich_lengths = once . ioProperty $
+  let p = (,)
+        <$> option readerAsk (long "foo" <> help "Foo hide this")
+        <*> option readerAsk (long "bar" <> help "Bar hide this")
+      i = info p idm
+      result = run i [ "--bash-completion-enriched"
+                     , "--bash-completion-index=0"
+                     , "--bash-completion-option-desc-length=3"
+                     , "--bash-completion-command-desc-length=30"]
+  in case result of
+    CompletionInvoked (CompletionResult err) -> do
+      completions <- lines <$> err "test"
+      return $ ["--foo\tFoo...", "--bar\tBar..."] === completions
+    Failure _   -> return $ counterexample "unexpected failure" failed
+    Success val -> return $ counterexample ("unexpected result " ++ show val) failed
+
 prop_bind_usage :: Property
 prop_bind_usage = once $
-  let p = many (argument str (metavar "ARGS..."))
+  let p :: Parser [String]
+      p = many (argument str (metavar "ARGS..."))
       i = info (p <**> helper) briefDesc
       result = run i ["--help"]
   in assertError result $ \failure ->
@@ -245,21 +350,24 @@
 
 prop_arguments1_none :: Property
 prop_arguments1_none =
-  let p = some (argument str idm)
+  let p :: Parser [String]
+      p = some (argument str idm)
       i = info (p <**> helper) idm
       result = run i []
   in assertError result $ \_ -> property succeeded
 
 prop_arguments1_some :: Property
 prop_arguments1_some = once $
-  let p = some (argument str idm)
+  let p :: Parser [String]
+      p = some (argument str idm)
       i = info (p <**> helper) idm
       result = run i ["foo", "--", "bar", "baz"]
   in  assertResult result (["foo", "bar", "baz"] ===)
 
 prop_arguments_switch :: Property
 prop_arguments_switch = once $
-  let p =  switch (short 'x')
+  let p :: Parser [String]
+      p =  switch (short 'x')
         *> many (argument str idm)
       i = info p idm
       result = run i ["--", "-x"]
@@ -404,11 +512,33 @@
              ( info (many (argument str (metavar "ARGS")))
                     idm ) )
       i = info p idm
-      result1 = run i ["run", "-x", "foo"]
-      result2 = run i ["test", "-x", "bar"]
-  in conjoin [ assertResult result1 $ \args -> ["-x", "foo"] === args
+      result1 = run i ["run", "foo", "-x"]
+      result2 = run i ["test", "bar", "-x"]
+  in conjoin [ assertResult result1 $ \args -> ["foo", "-x"] === args
              , assertError result2 $ \_ -> property succeeded ]
 
+prop_intersperse_3 :: Property
+prop_intersperse_3 = once $
+  let p = (,,) <$> switch ( long "foo" )
+               <*> strArgument ( metavar "FILE" )
+               <*> many ( strArgument ( metavar "ARGS..." ) )
+      i = info p noIntersperse
+      result = run i ["--foo", "myfile", "-a", "-b", "-c"]
+  in assertResult result $ \(b,f,as) ->
+     conjoin [ ["-a", "-b", "-c"] === as
+             , True               === b
+             , "myfile"           === f ]
+
+prop_forward_options :: Property
+prop_forward_options = once $
+  let p = (,) <$> switch ( long "foo" )
+              <*> many ( strArgument ( metavar "ARGS..." ) )
+      i = info p forwardOptions
+      result = run i ["--fo", "--foo", "myfile"]
+  in assertResult result $ \(b,a) ->
+     conjoin [ True               === b
+             , ["--fo", "myfile"] === a ]
+
 prop_issue_52 :: Property
 prop_issue_52 = once $
   let p = subparser
@@ -452,7 +582,8 @@
 
 prop_missing_flags_described :: Property
 prop_missing_flags_described = once $
-  let p = (,,)
+  let p :: Parser (String, String, Maybe String)
+      p = (,,)
        <$> option str (short 'a')
        <*> option str (short 'b')
        <*> optional (option str (short 'c'))
@@ -463,7 +594,8 @@
 
 prop_many_missing_flags_described :: Property
 prop_many_missing_flags_described = once $
-  let p = (,)
+  let p :: Parser (String, String)
+      p = (,)
         <$> option str (short 'a')
         <*> option str (short 'b')
       i = info p idm
@@ -473,15 +605,26 @@
 
 prop_alt_missing_flags_described :: Property
 prop_alt_missing_flags_described = once $
-  let p = option str (short 'a') <|> option str (short 'b')
+  let p :: Parser String
+      p = option str (short 'a') <|> option str (short 'b')
       i = info p idm
   in assertError (run i []) $ \failure ->
     let text = head . lines . fst $ renderFailure failure "test"
     in  "Missing: (-a ARG | -b ARG)" === text
 
+prop_missing_option_parameter_err :: Property
+prop_missing_option_parameter_err = once $
+  let p :: Parser String
+      p = option str (short 'a')
+      i = info p idm
+  in assertError (run i ["-a"]) $ \failure ->
+    let text = head . lines . fst $ renderFailure failure "test"
+    in  "The option `-a` expects an argument." === text
+
 prop_many_pairs_success :: Property
 prop_many_pairs_success = once $
-  let p = many $ (,) <$> argument str idm <*> argument str idm
+  let p :: Parser [(String, String)]
+      p = many $ (,) <$> argument str idm <*> argument str idm
       i = info p idm
       nargs = 10000
       result = run i (replicate nargs "foo")
@@ -489,7 +632,8 @@
 
 prop_many_pairs_failure :: Property
 prop_many_pairs_failure = once $
-  let p = many $ (,) <$> argument str idm <*> argument str idm
+  let p :: Parser [(String, String)]
+      p = many $ (,) <$> argument str idm <*> argument str idm
       i = info p idm
       nargs = 9999
       result = run i (replicate nargs "foo")
@@ -497,11 +641,34 @@
 
 prop_many_pairs_lazy_progress :: Property
 prop_many_pairs_lazy_progress = once $
-  let p = many $ (,) <$> optional (option str (short 'a')) <*> argument str idm
+  let p :: Parser [(Maybe String, String)]
+      p = many $ (,) <$> optional (option str (short 'a')) <*> argument str idm
       i = info p idm
       result = run i ["foo", "-abar", "baz"]
   in assertResult result $ \xs -> [(Just "bar", "foo"), (Nothing, "baz")] === xs
 
+prop_suggest :: Property
+prop_suggest = once $
+  let p2 = subparser (command "reachable"   (info (pure ()) idm))
+      p1 = subparser (command "unreachable" (info (pure ()) idm))
+      p  = (,) <$> p2 <*> p1
+      i  = info p idm
+      result = run i ["ureachable"]
+  in assertError result $ \failure ->
+    let (msg, _)  = renderFailure failure "prog"
+    in  counterexample msg
+       $  isInfixOf "Did you mean this?\n    reachable" msg
+      .&. not (isInfixOf "unreachable" msg)
+
+prop_bytestring_reader :: Property
+prop_bytestring_reader = once $
+  let t = "testValue"
+      p :: Parser ByteString
+      p = argument str idm
+      i = info p idm
+      result = run i ["testValue"]
+  in assertResult result $ \xs -> fromString t === xs
+
 ---
 
 deriving instance Arbitrary a => Arbitrary (Chunk a)
@@ -534,6 +701,38 @@
 
 prop_paragraph :: String -> Property
 prop_paragraph s = isEmpty (paragraph s) === null (words s)
+
+---
+
+--
+-- From
+-- https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance
+--
+-- In information theory and computer science, the Damerau–Levenshtein
+-- distance is a distance (string metric) between two strings, i.e.,
+-- finite sequence of symbols, given by counting the minimum number
+-- of operations needed to transform one string into the other, where
+-- an operation is defined as an insertion, deletion, or substitution
+-- of a single character, or a transposition of two adjacent characters.
+--
+prop_edit_distance_gezero :: String -> String -> Bool
+prop_edit_distance_gezero a b = editDistance a b >= 0
+
+prop_edit_insertion :: [Char] -> Char -> [Char] -> Property
+prop_edit_insertion as i bs =
+  editDistance (as ++ bs) (as ++ [i] ++ bs) === 1
+
+prop_edit_symmetric :: [Char] -> [Char] -> Property
+prop_edit_symmetric as bs =
+  editDistance as bs === editDistance bs as
+
+prop_edit_substitution :: [Char] -> [Char] -> Char -> Char -> Property
+prop_edit_substitution as bs a b = a /= b ==>
+  editDistance (as ++ [a] ++ bs) (as ++ [b] ++ bs) === 1
+
+prop_edit_transposition :: [Char] -> [Char] -> Char -> Char -> Property
+prop_edit_transposition as bs a b = a /= b ==>
+  editDistance (as ++ [a] ++ [b] ++ bs) (as ++ [b] ++ [a] ++ bs) === 1
 
 ---
 
