mdoc-0.2.0.0: src/Mdoc/Data/Synopsis.hs
-- |
--
-- Module : Mdoc.Data.Synopsis
-- Copyright : (c) 2026 Patrick Brisbin
-- License : AGPL-3
-- Maintainer : pbrisbin@gmail.com
-- Stability : experimental
-- Portability : POSIX
module Mdoc.Data.Synopsis
( Synopsis (..)
, fromList
, singleton
) where
import Mdoc.Prelude
import Data.Char (toLower)
import Data.List (sort, sortOn)
import Mdoc.Data.Described
import Mdoc.Data.Flag
import Mdoc.Data.Optionality
import Mdoc.Data.Positional
import Mdoc.Optics
import Mdoc.Pretty
import Mdoc.Syntax
data Synopsis
= Synopsis [Described Positional]
| SynopsisCustom Mdoc
deriving stock (Eq, Show)
deriving (ToJSON) via (Rendered Synopsis)
instance Semigroup Synopsis where
_ <> a@SynopsisCustom {} = a
a@SynopsisCustom {} <> _ = a
Synopsis a <> Synopsis b = Synopsis $ a <> b
instance Monoid Synopsis where
mempty = Synopsis mempty
instance Pretty Synopsis where
pretty = \case
Synopsis ps -> pretty $ buildUsage ps
SynopsisCustom ls -> pretty ls
fromList :: [Described Positional] -> Synopsis
fromList = foldMap singleton
singleton :: Described Positional -> Synopsis
singleton = Synopsis . pure
data Usage ann = Usage
{ chars :: [Char]
, shorts :: [Doc ann]
, longs :: [Doc ann]
, args :: [Doc ann]
}
deriving stock (Generic, Show)
deriving (Monoid, Semigroup) via Generically (Usage ann)
deriving (ToJSON) via (Rendered (Usage ann))
instance Pretty (Usage ann) where
pretty u =
unAnnotate
$ vsep
$ catMaybes
[ Just ".Nm"
, Just ".Bk -words"
, (".Op Fl" <+>) . pretty . esc . pack . toList <$> nonEmpty u.chars
, vsep . toList <$> nonEmpty (u.shorts <> u.longs <> u.args)
, Just ".Ek"
]
buildUsage :: [Described Positional] -> Usage ann
buildUsage = foldl go mempty . sort
go :: Usage ann -> Described Positional -> Usage ann
go acc d = case d.item of
PositionalSwitch f -> case f of
Flag c _ -> acc & field @"chars" %~ sortOn toLower . (<> [c])
GNUFlag {} -> acc & field @"longs" <>~ [opLine $ pretty $ Flag1 f]
PositionalOption o ->
let doc = opLine $ pretty $ Option1 o
in case o.flag of
Flag {} -> acc & field @"shorts" <>~ [doc]
GNUFlag {} -> acc & field @"longs" <>~ [doc]
PositionalArgument a -> acc & field @"args" <>~ [opLine $ ellipsis $ pretty a]
where
opLine = case d.optionality of
Required -> ("." <>)
_ -> (".Op" <+>)
ellipsis
| d.multiple = (<+> "...")
| otherwise = id