diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,24 @@
-## Version 0.17.1.0 (21 May 2023)
+## Version 0.18.0.0 (22 May 2023)
 
+- Move to 'prettyprinter` library for pretty printing.
+
+  This is a potentially breaking change when one uses the '*Doc' family of functions
+  (like `headerDoc`) from `Options.Applicative`. However, as versions of
+  'ansi-wl-pprint > 1.0' export a compatible `Doc` type, this can be mitigated by
+  using a recent version.
+
+  One can also either import directly from `Options.Applicative.Help` or from the
+  `Prettyprinter` module of 'prettyprinter'.
+
+- Allow commands to be disambiguated in a similar manner to flags when the
+  `disambiguate` modifier is used.
+
+  This is a potentially breaking change as the internal `CmdReader` constructor
+  has been adapted so it is able to be inspected to a greater degree to support
+  finding prefix matches.
+
+## Version 0.17.1.0 (22 May 2023)
+
 - Widen bounds for `ansi-wl-pprint`. This supports the use of `prettyprinter`
   in a non-breaking way, as the `ansi-wl-pprint > 1.0` support the newer
   library.
@@ -11,6 +30,8 @@
 - Add `simpleVersioner` utility for adding a '--version' option to a parser.
 
 - Improve documentation.
+
+- Drop support for GHC 7.0 and 7.2.
 
 ## Version 0.17.0.0 (1 Feb 2022)
 
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.17.1.0
+version:             0.18.0.0
 synopsis:            Utilities and combinators for parsing command line options
 description:
     optparse-applicative is a haskell library for parsing options
@@ -100,10 +100,11 @@
                      , Options.Applicative.Types
                      , Options.Applicative.Internal
 
-  build-depends:       base                            == 4.*
+  build-depends:       base                            >= 4.5 && < 5
                      , transformers                    >= 0.2 && < 0.7
                      , transformers-compat             >= 0.3 && < 0.8
-                     , ansi-wl-pprint                  >= 0.6.8 && < 1.1
+                     , prettyprinter                   >= 1.7 && < 1.8
+                     , prettyprinter-ansi-terminal     >= 1.1 && < 1.2
 
   if flag(process)
     build-depends:     process                         >= 1.0 && < 1.7
diff --git a/src/Options/Applicative/BashCompletion.hs b/src/Options/Applicative/BashCompletion.hs
--- a/src/Options/Applicative/BashCompletion.hs
+++ b/src/Options/Applicative/BashCompletion.hs
@@ -1,4 +1,3 @@
-{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}
 -- | You don't need to import this module to enable bash completion.
 --
 -- See
@@ -114,11 +113,11 @@
         -> return []
          | otherwise
         -> run_completer (crCompleter rdr)
-      CmdReader _ ns p
+      CmdReader _ ns
          | argumentIsUnreachable reachability
         -> return []
          | otherwise
-        -> return . add_cmd_help p $ filter_names ns
+        -> return . with_cmd_help $ filter (is_completion . fst) ns
 
     -- When doing enriched completions, add any help specified
     -- to the completion variables (tab separated).
@@ -133,29 +132,27 @@
 
     -- 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
+    with_cmd_help :: Functor f => f (String, ParserInfo a) -> f String
+    with_cmd_help =
+      case richness of
+        Standard ->
+          fmap fst
+        Enriched _ len ->
+          fmap $ \(cmd, cmdInfo) ->
+            let h = unChunk (infoProgDesc cmdInfo)
+            in  maybe cmd (\h' -> cmd ++ "\t" ++ render_line len h') h
 
     show_names :: [OptName] -> [String]
-    show_names = filter_names . map showOption
+    show_names = filter is_completion . 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
+    render_line len doc = case lines (prettyString 1 len doc) of
       [] -> ""
       [x] -> x
       x : _ -> x ++ "..."
-
-    filter_names :: [String] -> [String]
-    filter_names = filter is_completion
 
     run_completer :: Completer -> IO [String]
     run_completer c = runCompleter c (fromMaybe "" (listToMaybe ws''))
diff --git a/src/Options/Applicative/Builder.hs b/src/Options/Applicative/Builder.hs
--- a/src/Options/Applicative/Builder.hs
+++ b/src/Options/Applicative/Builder.hs
@@ -282,8 +282,8 @@
 subparser m = mkParser d g rdr
   where
     Mod _ d g = metavar "COMMAND" `mappend` m
-    (groupName, cmds, subs) = mkCommand m
-    rdr = CmdReader groupName cmds subs
+    (groupName, cmds) = mkCommand m
+    rdr = CmdReader groupName cmds
 
 -- | Builder for an argument parser.
 argument :: ReadM a -> Mod ArgumentFields a -> Parser a
diff --git a/src/Options/Applicative/Builder/Internal.hs b/src/Options/Applicative/Builder/Internal.hs
--- a/src/Options/Applicative/Builder/Internal.hs
+++ b/src/Options/Applicative/Builder/Internal.hs
@@ -152,8 +152,8 @@
   , propShowGlobal = True
   }
 
-mkCommand :: Mod CommandFields a -> (Maybe String, [String], String -> Maybe (ParserInfo a))
-mkCommand m = (group, map fst cmds, (`lookup` cmds))
+mkCommand :: Mod CommandFields a -> (Maybe String, [(String, ParserInfo a)])
+mkCommand m = (group, cmds)
   where
     Mod f _ _ = m
     CommandFields cmds group = f (CommandFields [] Nothing)
diff --git a/src/Options/Applicative/Common.hs b/src/Options/Applicative/Common.hs
--- a/src/Options/Applicative/Common.hs
+++ b/src/Options/Applicative/Common.hs
@@ -166,23 +166,28 @@
   searchParser $ \opt -> do
     when (isArg (optMain opt)) cut
     case optMain opt of
-      CmdReader _ _ f ->
-        case (f arg, prefBacktrack prefs) of
-          (Just subp, NoBacktrack) -> lift $ do
+      CmdReader _ cs -> do
+        subp <- hoistList (cmdMatches cs)
+        case prefBacktrack prefs of
+          NoBacktrack -> lift $ do
             args <- get <* put []
             fmap pure . lift $ enterContext arg subp *> runParserInfo subp args <* exitContext
 
-          (Just subp, Backtrack) -> fmap pure . lift . StateT $ \args ->
+          Backtrack -> fmap pure . lift . StateT $ \args ->
             enterContext arg subp *> runParser (infoPolicy subp) CmdStart (infoParser subp) args <* exitContext
 
-          (Just subp, SubparserInline) -> lift $ do
+          SubparserInline -> lift $ do
             lift $ enterContext arg subp
             return $ infoParser subp
 
-          (Nothing, _)  -> mzero
       ArgReader rdr ->
         fmap pure . lift . lift $ runReadM (crReader rdr) arg
       _ -> mzero
+
+  where
+    cmdMatches cs
+      | prefDisambiguate prefs = snd <$> filter (isPrefixOf arg . fst) cs
+      | otherwise = maybeToList (lookup arg cs)
 
 stepParser :: MonadP m => ParserPrefs -> ArgPolicy -> String
            -> Parser a -> NondetT (StateT Args m) (Parser a)
diff --git a/src/Options/Applicative/Extra.hs b/src/Options/Applicative/Extra.hs
--- a/src/Options/Applicative/Extra.hs
+++ b/src/Options/Applicative/Extra.hs
@@ -89,8 +89,8 @@
 hsubparser m = mkParser d g rdr
   where
     Mod _ d g = metavar "COMMAND" `mappend` m
-    (groupName, cmds, subs) = mkCommand m
-    rdr = CmdReader groupName cmds (fmap add_helper . subs)
+    (groupName, cmds) = mkCommand m
+    rdr = CmdReader groupName ((fmap . fmap) add_helper cmds)
     add_helper pinfo = pinfo
       { infoParser = infoParser pinfo <**> helper }
 
@@ -317,10 +317,10 @@
               OptReader ns _ _ -> fmap showOption ns
               FlagReader ns _  -> fmap showOption ns
               ArgReader _      -> []
-              CmdReader _ ns _  | argumentIsUnreachable reachability
+              CmdReader _ ns    | argumentIsUnreachable reachability
                                -> []
                                 | otherwise
-                               -> ns
+                               -> fst <$> ns
       _
         -> mempty
 
diff --git a/src/Options/Applicative/Help/Chunk.hs b/src/Options/Applicative/Help/Chunk.hs
--- a/src/Options/Applicative/Help/Chunk.hs
+++ b/src/Options/Applicative/Help/Chunk.hs
@@ -1,4 +1,3 @@
-{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}
 module Options.Applicative.Help.Chunk
   ( Chunk(..)
   , chunked
@@ -116,7 +115,7 @@
 -- > extractChunk . stringChunk = string
 stringChunk :: String -> Chunk Doc
 stringChunk "" = mempty
-stringChunk s = pure (string s)
+stringChunk s = pure (pretty s)
 
 -- | Convert a paragraph into a 'Chunk'.  The resulting chunk is composed by the
 -- words of the original paragraph separated by softlines, so it will be
diff --git a/src/Options/Applicative/Help/Core.hs b/src/Options/Applicative/Help/Core.hs
--- a/src/Options/Applicative/Help/Core.hs
+++ b/src/Options/Applicative/Help/Core.hs
@@ -1,5 +1,4 @@
 {-# LANGUAGE CPP #-}
-{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}
 module Options.Applicative.Help.Core (
   cmdDesc,
   briefDesc,
@@ -25,7 +24,7 @@
 import Data.Function (on)
 import Data.List (sort, intersperse, groupBy)
 import Data.Foldable (any, foldl')
-import Data.Maybe (catMaybes, fromMaybe, maybeToList)
+import Data.Maybe (catMaybes, fromMaybe)
 #if !MIN_VERSION_base(4,8,0)
 import Data.Monoid (mempty)
 #endif
@@ -58,7 +57,7 @@
       meta =
         stringChunk $ optMetaVar opt
       descs =
-        map (string . showOption) names
+        map (pretty . showOption) names
       descriptions =
         listToChunk (intersperse (descSep style) descs)
       desc
@@ -95,12 +94,11 @@
   where
     desc _ opt =
       case optMain opt of
-        CmdReader gn cmds p ->
+        CmdReader gn cmds ->
           (,) gn $
             tabulate (prefTabulateFill pprefs)
-              [ (string cmd, align (extractChunk d))
-                | cmd <- reverse cmds,
-                  d <- maybeToList . fmap infoProgDesc $ p cmd
+              [ (pretty nm, align (extractChunk (infoProgDesc cmd)))
+              | (nm, cmd) <- reverse cmds
               ]
         _ -> mempty
 
@@ -128,7 +126,7 @@
       | otherwise =
         filterOptional
     style = OptDescStyle
-      { descSep = string "|",
+      { descSep = pretty '|',
         descHidden = False,
         descGlobal = False
       }
@@ -205,9 +203,9 @@
         n = fst $ optDesc pprefs style info opt
         h = optHelp opt
         hdef = Chunk . fmap show_def . optShowDefault $ opt
-        show_def s = parens (string "default:" <+> string s)
+        show_def s = parens (pretty "default:" <+> pretty s)
     style = OptDescStyle
-      { descSep = string ",",
+      { descSep = pretty ',',
         descHidden = True,
         descGlobal = global
       }
@@ -252,7 +250,7 @@
     group_title _ = mempty
 
     with_title :: String -> Chunk Doc -> Chunk Doc
-    with_title title = fmap (string title .$.)
+    with_title title = fmap (pretty title .$.)
 
 
 parserGlobals :: ParserPrefs -> Parser a -> ParserHelp
@@ -268,8 +266,8 @@
 parserUsage pprefs p progn =
   group $
     hsep
-      [ string "Usage:",
-        string progn,
+      [ pretty "Usage:",
+        pretty progn,
         hangAtIfOver 9 35 (extractChunk (briefDesc pprefs p))
       ]
 
diff --git a/src/Options/Applicative/Help/Pretty.hs b/src/Options/Applicative/Help/Pretty.hs
--- a/src/Options/Applicative/Help/Pretty.hs
+++ b/src/Options/Applicative/Help/Pretty.hs
@@ -1,40 +1,41 @@
 {-# LANGUAGE CPP #-}
-{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}
 module Options.Applicative.Help.Pretty
-  ( module Text.PrettyPrint.ANSI.Leijen
+  ( module Prettyprinter
+  , module Prettyprinter.Render.Terminal
   , Doc
-  , indent
-  , renderPretty
-  , displayS
+  , SimpleDoc
+
   , (.$.)
+  , (</>)
+
   , groupOrNestLine
   , altSep
   , hangAtIfOver
+
+  , prettyString
   ) where
 
 #if !MIN_VERSION_base(4,11,0)
-import           Data.Semigroup ((<>))
+import           Data.Semigroup ((<>), mempty)
 #endif
 
-import           Text.PrettyPrint.ANSI.Leijen hiding (Doc, (<$>), (<>), columns, indent, renderPretty, displayS)
-import qualified Text.PrettyPrint.ANSI.Leijen as PP
+import           Prettyprinter hiding (Doc)
+import qualified Prettyprinter as PP
+import qualified Prettyprinter.Render.String as PP
+import           Prettyprinter.Render.Terminal
 
 import           Prelude
 
-type Doc = PP.Doc
-
-indent :: Int -> PP.Doc -> PP.Doc
-indent = PP.indent
-
-renderPretty :: Float -> Int -> PP.Doc -> SimpleDoc
-renderPretty = PP.renderPretty
+type Doc = PP.Doc Prettyprinter.Render.Terminal.AnsiStyle
+type SimpleDoc = SimpleDocStream AnsiStyle
 
-displayS :: SimpleDoc -> ShowS
-displayS = PP.displayS
+linebreak :: Doc
+linebreak = flatAlt line mempty
 
 (.$.) :: Doc -> Doc -> Doc
-(.$.) = (PP.<$>)
-
+x .$. y = x <> line <> y
+(</>) :: Doc -> Doc -> Doc
+x </> y = x <> softline <> y
 
 -- | Apply the function if we're not at the
 --   start of our nesting level.
@@ -58,7 +59,6 @@
         then f doc
         else g doc
 
-
 -- | Render flattened text on this line, or start
 --   a new line before rendering any text.
 --
@@ -81,7 +81,7 @@
 --   next line.
 altSep :: Doc -> Doc -> Doc
 altSep x y =
-  group (x <+> char '|' <> line) <//> y
+  group (x <+> pretty '|' <> line) <> group linebreak <>  y
 
 
 -- | Printer hacks to get nice indentation for long commands
@@ -102,3 +102,18 @@
       align d
     else
       linebreak <> ifAtRoot (indent i) d
+
+
+renderPretty :: Double -> Int -> Doc -> SimpleDocStream AnsiStyle
+renderPretty ribbonFraction lineWidth
+  = layoutSmart LayoutOptions
+      { layoutPageWidth = AvailablePerLine lineWidth ribbonFraction }
+
+prettyString :: Double -> Int -> Doc -> String
+prettyString ribbonFraction lineWidth
+  = streamToString
+  . renderPretty ribbonFraction lineWidth
+
+streamToString :: SimpleDocStream AnsiStyle -> String
+streamToString stream =
+  PP.renderShowS stream ""
diff --git a/src/Options/Applicative/Help/Types.hs b/src/Options/Applicative/Help/Types.hs
--- a/src/Options/Applicative/Help/Types.hs
+++ b/src/Options/Applicative/Help/Types.hs
@@ -42,6 +42,5 @@
 -- | Convert a help text to 'String'.
 renderHelp :: Int -> ParserHelp -> String
 renderHelp cols
-  = (`displayS` "")
-  . renderPretty 1.0 cols
+  = prettyString 1.0 cols
   . helpText
diff --git a/src/Options/Applicative/Internal.hs b/src/Options/Applicative/Internal.hs
--- a/src/Options/Applicative/Internal.hs
+++ b/src/Options/Applicative/Internal.hs
@@ -18,6 +18,7 @@
   , ListT
   , takeListT
   , runListT
+  , hoistList
 
   , NondetT
   , cut
@@ -172,9 +173,6 @@
 bimapTStep _ _ TNil = TNil
 bimapTStep f g (TCons a x) = TCons (f a) (g x)
 
-hoistList :: Monad m => [a] -> ListT m a
-hoistList = foldr (\x xt -> ListT (return (TCons x xt))) mzero
-
 takeListT :: Monad m => Int -> ListT m a -> ListT m a
 takeListT 0 = const mzero
 takeListT n = ListT . liftM (bimapTStep id (takeListT (n - 1))) . stepListT
@@ -192,7 +190,7 @@
          . stepListT
 
 instance Monad m => Applicative (ListT m) where
-  pure = hoistList . pure
+  pure a = ListT (return (TCons a mzero))
   (<*>) = ap
 
 instance Monad m => Monad (ListT m) where
@@ -263,3 +261,8 @@
   return $ case xs' of
     [x] -> Just x
     _   -> Nothing
+
+hoistList :: Alternative m => [a] -> m a
+hoistList = foldr cons empty
+  where
+    cons x xs = pure x <|> xs
diff --git a/src/Options/Applicative/Types.hs b/src/Options/Applicative/Types.hs
--- a/src/Options/Applicative/Types.hs
+++ b/src/Options/Applicative/Types.hs
@@ -242,14 +242,14 @@
   -- ^ flag reader
   | ArgReader (CReader a)
   -- ^ argument reader
-  | CmdReader (Maybe String) [String] (String -> Maybe (ParserInfo a))
+  | CmdReader (Maybe String) [(String, ParserInfo a)]
   -- ^ command reader
 
 instance Functor OptReader where
   fmap f (OptReader ns cr e) = OptReader ns (fmap f cr) e
   fmap f (FlagReader ns x) = FlagReader ns (f x)
   fmap f (ArgReader cr) = ArgReader (fmap f cr)
-  fmap f (CmdReader n cs g) = CmdReader n cs ((fmap . fmap) f . g)
+  fmap f (CmdReader n cs) = CmdReader n ((fmap . fmap . fmap) f cs)
 
 -- | A @Parser a@ is an option parser returning a value of type 'a'.
 data Parser a
diff --git a/tests/test.hs b/tests/test.hs
--- a/tests/test.hs
+++ b/tests/test.hs
@@ -28,7 +28,7 @@
 
 
 import qualified Options.Applicative.Help as H
-import           Options.Applicative.Help.Pretty (Doc, SimpleDoc(..))
+import           Options.Applicative.Help.Pretty (Doc)
 import qualified Options.Applicative.Help.Pretty as Doc
 import           Options.Applicative.Help.Chunk
 import           Options.Applicative.Help.Levenshtein
@@ -318,6 +318,49 @@
       result = execParserPure (prefs disambiguate) i ["--ba"]
   in  assertError result (\_ -> property succeeded)
 
+
+prop_disambiguate_in_same_subparsers :: Property
+prop_disambiguate_in_same_subparsers = once $
+  let p0 = subparser (command "oranges" (info (pure "oranges") idm) <> command "apples" (info (pure "apples") idm) <> metavar "B")
+      i = info (p0 <**> helper) idm
+      result = execParserPure (prefs disambiguate) i ["orang"]
+  in  assertResult result ((===) "oranges")
+
+prop_disambiguate_commands_in_separate_subparsers :: Property
+prop_disambiguate_commands_in_separate_subparsers = once $
+  let p2 = subparser (command "oranges" (info (pure "oranges") idm)  <> metavar "B")
+      p1 = subparser (command "apples" (info (pure "apples") idm)  <> metavar "C")
+      p0 = p1 <|> p2
+      i = info (p0 <**> helper) idm
+      result = execParserPure (prefs disambiguate) i ["orang"]
+  in  assertResult result ((===) "oranges")
+
+prop_fail_ambiguous_commands_in_same_subparser :: Property
+prop_fail_ambiguous_commands_in_same_subparser = once $
+  let p0 = subparser (command "oranges" (info (pure ()) idm) <> command "orangutans" (info (pure ()) idm) <> metavar "B")
+      i = info (p0 <**> helper) idm
+      result = execParserPure (prefs disambiguate) i ["orang"]
+  in  assertError result (\_ -> property succeeded)
+
+prop_fail_ambiguous_commands_in_separate_subparser :: Property
+prop_fail_ambiguous_commands_in_separate_subparser = once $
+  let p2 = subparser (command "oranges" (info (pure ()) idm)  <> metavar "B")
+      p1 = subparser (command "orangutans" (info (pure ()) idm)  <> metavar "C")
+      p0 = p1 <|> p2
+      i = info (p0 <**> helper) idm
+      result = execParserPure (prefs disambiguate) i ["orang"]
+  in  assertError result (\_ -> property succeeded)
+
+prop_without_disambiguation_same_named_commands_should_parse_in_order :: Property
+prop_without_disambiguation_same_named_commands_should_parse_in_order = once $
+  let p3 = subparser (command "b" (info (pure ()) idm)  <> metavar "B")
+      p2 = subparser (command "a" (info (pure ()) idm)  <> metavar "B")
+      p1 = subparser (command "a" (info (pure ()) idm)  <> metavar "C")
+      p0 = (,,) <$> p1 <*> p2 <*> p3
+      i = info (p0 <**> helper) idm
+      result = execParserPure defaultPrefs i ["b", "a", "a"]
+  in  assertResult result ((===) ((), (), ()))
+
 prop_completion :: Property
 prop_completion = once . ioProperty $
   let p = (,)
@@ -903,15 +946,14 @@
             , "to fit the size of the terminal" ]) )
   in checkHelpTextWith ExitSuccess (prefs (columns 50)) "formatting-long-subcommand" i ["hello-very-long-sub", "--help"]
 
-
 ---
 
 deriving instance Arbitrary a => Arbitrary (Chunk a)
 
 
-equalDocs :: Float -> Int -> Doc -> Doc -> Property
-equalDocs f w d1 d2 = Doc.displayS (Doc.renderPretty f w d1) ""
-                  === Doc.displayS (Doc.renderPretty f w d2) ""
+equalDocs :: Double -> Int -> Doc -> Doc -> Property
+equalDocs f w d1 d2 = Doc.prettyString f w d1
+                  === Doc.prettyString f w d2
 
 prop_listToChunk_1 :: [String] -> Property
 prop_listToChunk_1 xs = isEmpty (listToChunk xs) === null xs
@@ -925,10 +967,10 @@
 prop_extractChunk_2 :: Chunk String -> Property
 prop_extractChunk_2 x = extractChunk (fmap pure x) === x
 
-prop_stringChunk_1 :: Positive Float -> Positive Int -> String -> Property
+prop_stringChunk_1 :: Positive Double -> Positive Int -> String -> Property
 prop_stringChunk_1 (Positive f) (Positive w) s =
   equalDocs f w (extractChunk (stringChunk s))
-                (Doc.string s)
+                (Doc.pretty s)
 
 prop_stringChunk_2 :: String -> Property
 prop_stringChunk_2 s = isEmpty (stringChunk s) === null s
