mdoc 0.4.1.4 → 0.5.0.0
raw patch · 47 files changed
+461/−416 lines, 47 filesdep −file-embedPVP ok
version bump matches the API change (PVP)
Dependencies removed: file-embed
API changes (from Hackage documentation)
- Mdoc.Data.Page: addCommand :: Command -> Page -> Page
- Mdoc.Data.Page: setEpilogue :: Mdoc -> Page -> Page
- Mdoc.Data.Page: setPrologue :: Mdoc -> Page -> Page
- Mdoc.Data.Page: setSynopsis :: Mdoc -> Page -> Page
- Mdoc.Data.Synopsis: custom :: Mdoc -> Synopsis
+ Mdoc.Data.Synopsis: command :: Synopsis
+ Mdoc.Examples.MdocDump: mdocDump1 :: Named
+ Mdoc.Syntax.Mdoc: textToMdocPp :: Text -> Maybe Mdoc
Files
- README.lhs +10/−16
- README.md +10/−16
- data/commands.mustache +8/−0
- data/configs.mustache +26/−0
- data/environment.mustache +3/−0
- data/man1.mustache +31/−0
- data/man1.template +0/−80
- data/man5.mustache +6/−0
- data/man5.template +0/−47
- data/name.mustache +6/−0
- data/options.mustache +12/−0
- data/tag-list.mustache +8/−0
- data/title1.mustache +3/−0
- data/title5.mustache +3/−0
- examples/crontab.5 +4/−3
- examples/docker.1 +0/−1
- examples/example.1 +0/−1
- examples/examplerc.5 +0/−5
- examples/grep.1 +0/−1
- examples/mandoc.1 +0/−1
- examples/mdoc-dump.1 +1/−2
- examples/mdoc-dump.1.html +2/−5
- examples/pass.1 +0/−1
- examples/person.5 +0/−1
- mdoc.cabal +17/−4
- src/Autodocodec/Schema/Mdoc.hs +3/−3
- src/Env/Mdoc.hs +2/−2
- src/Mdoc/Data/Page.hs +15/−28
- src/Mdoc/Data/Synopsis.hs +54/−38
- src/Mdoc/Examples/Grep.hs +1/−20
- src/Mdoc/Examples/MdocDump.hs +18/−0
- src/Mdoc/Examples/OptEnvConf.hs +5/−32
- src/Mdoc/Examples/Pass.hs +1/−46
- src/Mdoc/Examples/Person.hs +1/−13
- src/Mdoc/Syntax/MacroArg.hs +1/−1
- src/Mdoc/Syntax/Mdoc.hs +31/−6
- src/Mdoc/Syntax/MdocLine.hs +5/−2
- src/Mdoc/Template.hs +4/−11
- src/OptEnvConf/Mdoc.hs +9/−9
- src/Options/Applicative/Mdoc.hs +3/−3
- templates/crontab.5.mustache +12/−0
- templates/grep.1.mustache +26/−0
- templates/mdoc-dump.1.mustache +3/−0
- templates/pass.1.mustache +69/−0
- templates/person.5.mustache +10/−0
- test/Mdoc/Data/ConfigSpec.hs +1/−8
- test/Mdoc/ExamplesSpec.hs +37/−10
README.lhs view
@@ -41,30 +41,24 @@ import Prelude -import Data.Function ((&)) import Env.Mdoc qualified as Env import Mdoc-import Mdoc.Data.Page (setEpilogue)+import Mdoc.Template qualified as Template import Mdoc.Dump.Env (envParser) import Mdoc.Dump.Options (optionsInfo, optionsParser) import Options.Applicative.Mdoc qualified as Opt -mdocDump1 :: Named-mdocDump1 =- mempty- & (<> Opt.getPage optionsParser)- & (<> Env.getPage envParser)- & setEpilogue- ( Mdoc- [ MacroLine Sh ["SEE ALSO"]- , MacroLine Xr ["mdoc", "7"]- ]- )- & name "mdoc-dump" optionsInfo- main :: IO () main = do- mdoc <- renderTemplateThrow man1 mdocDump1+ -- Use a custom template so we can add "SEE ALSO"+ t <- Template.custom "templates/mdoc-dump.1.mustache"++ -- Render template with options and environment from our parsers+ mdoc <- renderTemplateThrow t+ $ name "mdoc-dump" optionsInfo+ $ Opt.getPage optionsParser <> Env.getPage envParser++ -- Update the Mdocdate and pretty-print putMdoc =<< updateMdocdate mdoc ```
README.md view
@@ -41,30 +41,24 @@ import Prelude -import Data.Function ((&)) import Env.Mdoc qualified as Env import Mdoc-import Mdoc.Data.Page (setEpilogue)+import Mdoc.Template qualified as Template import Mdoc.Dump.Env (envParser) import Mdoc.Dump.Options (optionsInfo, optionsParser) import Options.Applicative.Mdoc qualified as Opt -mdocDump1 :: Named-mdocDump1 =- mempty- & (<> Opt.getPage optionsParser)- & (<> Env.getPage envParser)- & setEpilogue- ( Mdoc- [ MacroLine Sh ["SEE ALSO"]- , MacroLine Xr ["mdoc", "7"]- ]- )- & name "mdoc-dump" optionsInfo- main :: IO () main = do- mdoc <- renderTemplateThrow man1 mdocDump1+ -- Use a custom template so we can add "SEE ALSO"+ t <- Template.custom "templates/mdoc-dump.1.mustache"++ -- Render template with options and environment from our parsers+ mdoc <- renderTemplateThrow t+ $ name "mdoc-dump" optionsInfo+ $ Opt.getPage optionsParser <> Env.getPage envParser++ -- Update the Mdocdate and pretty-print putMdoc =<< updateMdocdate mdoc ```
+ data/commands.mustache view
@@ -0,0 +1,8 @@+.Bl -tag -width {{{width}}}+{{#items}}+.It Cm {{{name}}}+{{#description}}+{{{description}}}+{{/description}}+{{/items}}+.El
+ data/configs.mustache view
@@ -0,0 +1,26 @@+.Bl -tag -width {{{width}}}+{{#items}}+.It {{{item}}}+{{#help}}+{{{help}}}+{{/help}}+{{#optionality.required}}+(required)+{{/optionality.required}}+{{^optionality.required}}+{{#optionality.default}}+(optional, default: {{{.}}})+{{/optionality.default}}+{{^optionality.default}}+(optional)+{{/optionality.default}}+{{/optionality.required}}+{{#example}}+.Pp+Example:+.Bd -literal -offset indent+{{{.}}}+.Ed+{{/example}}+{{/items}}+.El
+ data/environment.mustache view
@@ -0,0 +1,3 @@+The following environment variables affect the execution of+.Nm :+{{>tag-list}}
+ data/man1.mustache view
@@ -0,0 +1,31 @@+{{>title1}}+{{>name}}+.Sh SYNOPSIS+.Nm+{{#page.synopsis}}+{{{page.synopsis}}}+{{/page.synopsis}}+{{#page.commands}}+{{#items}}+.Pp+.Nm+.Cm {{{name}}}+{{#synopsis}}+{{{.}}}+{{/synopsis}}+{{/items}}+{{/page.commands}}+.Sh DESCRIPTION+{{#page.options}}+{{>options}}+{{/page.options}}+{{#page.commands}}+.Ss COMMANDS+{{>commands}}+{{/page.commands}}+{{#page.environment}}+.Sh ENVIRONMENT+{{>environment}}+{{/page.environment}}+.Sh EXIT STATUS+.Ex -std
− data/man1.template
@@ -1,80 +0,0 @@-.\" vim: ft=nroff.mustache-.Dd $Mdocdate$-.Dt {{{title}}} 1-.Os-.Sh NAME-{{#name.init}}-.Nm {{{.}}} ,-{{/name.init}}-.Nm {{{name.last}}}-.Nd {{{name.description}}}-.Sh SYNOPSIS-.Nm-.Bk -words-{{#page.synopsis}}-{{{page.synopsis}}}-{{/page.synopsis}}-{{^page.commands}}-.Ek-{{/page.commands}}-{{#page.commands}}-.Ar command-.Ek-{{#page.commands.items}}-.Pp-.Nm-.Cm {{{name}}}-{{#synopsis}}-.Bk -words-{{{.}}}-.Ek-{{/synopsis}}-{{/page.commands.items}}-{{/page.commands}}-.Sh DESCRIPTION-{{#page.prologue}}-{{{.}}}-{{/page.prologue}}-{{#page.options}}-The options are as follows:-.Bl -tag -width {{{page.options.width}}}-{{#page.options.items}}-.It {{{item}}}-{{#help}}-{{{help}}}-{{#optionality.default}}-(default: {{{.}}})-{{/optionality.default}}-{{/help}}-{{/page.options.items}}-.El-{{/page.options}}-{{#page.commands}}-.Ss COMMANDS-.Bl -tag -width {{{page.commands.width}}}-{{#page.commands.items}}-.It Cm {{{name}}}-{{#description}}-{{{description}}}-{{/description}}-{{/page.commands.items}}-.El-{{/page.commands}}-{{#page.environment}}-.Sh ENVIRONMENT-The following environment variables affect the execution of-.Nm :-.Bl -tag -width {{{page.environment.width}}}-{{#page.environment.items}}-.It {{{item}}}-{{#help}}-{{{help}}}-{{/help}}-{{/page.environment.items}}-.El-{{/page.environment}}-.Sh EXIT STATUS-.Ex -std-{{#page.epilogue}}-{{{.}}}-{{/page.epilogue}}
+ data/man5.mustache view
@@ -0,0 +1,6 @@+{{>title5}}+{{>name}}+.Sh DESCRIPTION+{{#page.configs}}+{{>configs}}+{{/page.configs}}
− data/man5.template
@@ -1,47 +0,0 @@-.\" vim: ft=nroff.mustache-.Dd $Mdocdate$-.Dt {{{title}}} 5-.Os-.Sh NAME-{{#name.init}}-.Nm {{{.}}} ,-{{/name.init}}-.Nm {{{name.last}}}-.Nd {{{name.description}}}-{{#page.synopsis}}-.Sh SYNOPSIS-{{{.}}}-{{/page.synopsis}}-.Sh DESCRIPTION-{{#page.prologue}}-{{{.}}}-{{/page.prologue}}-.Bl -tag -width {{{page.configs.width}}}-{{#page.configs.items}}-.It {{{item}}}-{{#help}}-{{{help}}}-{{/help}}-{{#optionality.required}}-(required)-{{/optionality.required}}-{{^optionality.required}}-{{#optionality.default}}-(optional, default: {{{.}}})-{{/optionality.default}}-{{^optionality.default}}-(optional)-{{/optionality.default}}-{{/optionality.required}}-{{#example}}-.Pp-Example:-.Bd -literal -offset indent-{{{.}}}-.Ed-{{/example}}-{{/page.configs.items}}-.El-{{#page.epilogue}}-{{{.}}}-{{/page.epilogue}}
+ data/name.mustache view
@@ -0,0 +1,6 @@+.Sh NAME+{{#name.init}}+.Nm {{{.}}} ,+{{/name.init}}+.Nm {{{name.last}}}+.Nd {{{name.description}}}
+ data/options.mustache view
@@ -0,0 +1,12 @@+The options are as follows:+.Bl -tag -width {{{page.options.width}}}+{{#page.options.items}}+.It {{{item}}}+{{#help}}+{{{help}}}+{{#optionality.default}}+(default: {{{.}}})+{{/optionality.default}}+{{/help}}+{{/page.options.items}}+.El
+ data/tag-list.mustache view
@@ -0,0 +1,8 @@+.Bl -tag -width {{{width}}}+{{#items}}+.It {{{item}}}+{{#help}}+{{{help}}}+{{/help}}+{{/items}}+.El
+ data/title1.mustache view
@@ -0,0 +1,3 @@+.Dd $Mdocdate$+.Dt {{{title}}} 1+.Os
+ data/title5.mustache view
@@ -0,0 +1,3 @@+.Dd $Mdocdate$+.Dt {{{title}}} 5+.Os
examples/crontab.5 view
@@ -1,4 +1,3 @@-.\" vim: ft=nroff.mustache .Dd $Mdocdate: September 20 2026 $ .Dt CRONTAB 5 .Os@@ -20,10 +19,12 @@ Example: .Bd -literal -offset indent # enable bat-bat: 1+bar:+ bat: 1 .Pp # disable bat-bat: 0+bar:+ bat: 0 .Ed .It Cm bar.baz : Ar boolean Bar's baz of bazzle
examples/docker.1 view
@@ -1,4 +1,3 @@-.\" vim: ft=nroff.mustache .Dd $Mdocdate: September 20 2026 $ .Dt DOCKER 1 .Os
examples/example.1 view
@@ -1,4 +1,3 @@-.\" vim: ft=nroff.mustache .Dd $Mdocdate: September 20 2026 $ .Dt EXAMPLE 1 .Os
examples/examplerc.5 view
@@ -1,14 +1,9 @@-.\" vim: ft=nroff.mustache .Dd $Mdocdate: September 20 2026 $ .Dt EXAMPLERC 5 .Os .Sh NAME .Nm examplerc .Nd opt\-env\-conf example config-.Sh SYNOPSIS-.Bl -tag -width indent -compact-.It Pa .examplerc.yaml-.El .Sh DESCRIPTION .Bl -tag -width indent .It Cm debug : Ar boolean
examples/grep.1 view
@@ -1,4 +1,3 @@-.\" vim: ft=nroff.mustache .Dd $Mdocdate: September 20 2026 $ .Dt GREP 1 .Os
examples/mandoc.1 view
@@ -1,4 +1,3 @@-.\" vim: ft=nroff.mustache .Dd $Mdocdate: September 20 2026 $ .Dt MANDOC 1 .Os
examples/mdoc-dump.1 view
@@ -1,5 +1,4 @@-.\" vim: ft=nroff.mustache-.Dd $Mdocdate: September 29 2026 $+.Dd $Mdocdate: October 12 2026 $ .Dt MDOC\-DUMP 1 .Os .Sh NAME
examples/mdoc-dump.1.html view
@@ -1,8 +1,5 @@ <!DOCTYPE html> <html>-<!-- This is an automatically generated file. Do not edit.- vim: ft=nroff.mustache- --> <head> <meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>@@ -79,8 +76,8 @@ </div> <table class="foot"> <tr>- <td class="foot-date">September 22, 2026</td>- <td class="foot-os">Linux 6.19.11-arch1-1</td>+ <td class="foot-date">October 12, 2026</td>+ <td class="foot-os">Linux 7.2.6-arch2-1</td> </tr> </table> </body>
examples/pass.1 view
@@ -1,4 +1,3 @@-.\" vim: ft=nroff.mustache .Dd $Mdocdate: September 20 2026 $ .Dt PASS 1 .Os
examples/person.5 view
@@ -1,4 +1,3 @@-.\" vim: ft=nroff.mustache .Dd $Mdocdate: September 20 2026 $ .Dt PERSON 5 .Os
mdoc.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.18 name: mdoc-version: 0.4.1.4+version: 0.5.0.0 license: AGPL-3 license-file: COPYING maintainer: Pat Brisbin@@ -31,8 +31,16 @@ category: Text build-type: Simple extra-source-files:- data/man1.template- data/man5.template+ data/commands.mustache+ data/configs.mustache+ data/environment.mustache+ data/man1.mustache+ data/man5.mustache+ data/name.mustache+ data/options.mustache+ data/tag-list.mustache+ data/title1.mustache+ data/title5.mustache examples/crontab.5 examples/docker.1 examples/example.1@@ -44,6 +52,11 @@ examples/pass.1 examples/person.5 examples/style.css+ templates/crontab.5.mustache+ templates/grep.1.mustache+ templates/mdoc-dump.1.mustache+ templates/pass.1.mustache+ templates/person.5.mustache extra-doc-files: README.md@@ -79,6 +92,7 @@ Mdoc.Examples.Docker Mdoc.Examples.Grep Mdoc.Examples.Mandoc+ Mdoc.Examples.MdocDump Mdoc.Examples.OptEnvConf Mdoc.Examples.Pass Mdoc.Examples.Person@@ -133,7 +147,6 @@ containers >=0.6.8, envparse >=0.6.0, extra >=1.7.16,- file-embed >=0.0.16.0, filepath >=1.4.301.0, generic-optics >=2.2.1.0, megaparsec >=9.6.1,
src/Autodocodec/Schema/Mdoc.hs view
@@ -35,7 +35,7 @@ import Mdoc.Data.Optionality import Mdoc.Data.Page import Mdoc.Optics-import Mdoc.Syntax (textToMdoc)+import Mdoc.Syntax (textToMdocPp) getPage :: JSONSchema -> Page getPage js = foldr addConfig mempty $ getConfigs Nothing js@@ -70,7 +70,7 @@ { item = Config {name = renderPrefix <$> mPrefix, schema} , optionality = Required , multiple = False- , help = textToMdoc =<< comment+ , help = textToMdocPp =<< comment , example = Nothing } @@ -145,7 +145,7 @@ JSONSchema.Required -> Required _ -> Optional , multiple = False- , help = textToMdoc =<< mcomment+ , help = textToMdocPp =<< mcomment , example = Nothing } ]
src/Env/Mdoc.hs view
@@ -20,7 +20,7 @@ import Mdoc.Data.EnvVar import Mdoc.Data.Optionality import Mdoc.Data.Page-import Mdoc.Syntax (textToMdoc)+import Mdoc.Syntax (textToMdocPp) getPage :: Parser e a -> Page getPage = flip appEndo mempty . foldAlt (Endo . addEnvVar . describeVarF) . unParser@@ -31,6 +31,6 @@ { item = EnvVar {names = pure $ varfName v, argument = Nothing} , optionality = maybe Required Defaulted (varfHelpDef v) , multiple = False- , help = textToMdoc . pack =<< varfHelp v+ , help = textToMdocPp . pack =<< varfHelp v , example = Nothing }
src/Mdoc/Data/Page.hs view
@@ -16,19 +16,15 @@ , addOption , addArgument , addAlternation- , addCommand , addCommands , addConfig -- * @ENVIRONMENT@ , addEnvVar - -- * Other, rarely-used elements- , getPositionals+ -- * Access necessary for building 'Command's , getSynopsis- , setSynopsis- , setPrologue- , setEpilogue+ , getPositionals ) where import Mdoc.Prelude@@ -45,16 +41,13 @@ import Mdoc.Data.Synopsis (Synopsis) import Mdoc.Data.Synopsis qualified as Synopsis import Mdoc.Optics-import Mdoc.Syntax data Page = Page { synopsis :: Maybe Synopsis- , prologue :: Maybe Mdoc , options :: Maybe (List Indent (Described Positional)) , commands :: Maybe (List Indent Command) , configs :: Maybe (List Indent (Described Config)) , environment :: Maybe (List ByItem (Described EnvVar))- , epilogue :: Maybe Mdoc } deriving stock (Eq, Generic, Show) deriving anyclass (ToJSON)@@ -90,31 +83,25 @@ (PositionalSwitch {}, Defaulted {}) -> described {optionality = Optional} _ -> described -addCommand :: Command -> Page -> Page-addCommand c m = m & field @"commands" <?>~ List.singleton c- addCommands :: [Command] -> Page -> Page-addCommands = appEndo . foldMap (Endo . addCommand)+addCommands cs =+ appEndo+ $ Endo (field @"synopsis" <?>~ Synopsis.command)+ <> foldMap addCommand cs+ where+ addCommand :: Command -> Endo Page+ addCommand c = Endo $ field @"commands" <?>~ List.singleton c addConfig :: Described Config -> Page -> Page-addConfig described m =- m & field @"configs" <?>~ List.singleton described+addConfig described =+ field @"configs" <?>~ List.singleton described addEnvVar :: Described EnvVar -> Page -> Page-addEnvVar described m =- m & field @"environment" <?>~ List.singleton described--getPositionals :: Page -> [Described Positional]-getPositionals p = maybe [] (toList . (.items)) p.options+addEnvVar described =+ field @"environment" <?>~ List.singleton described getSynopsis :: Page -> Synopsis getSynopsis p = fromMaybe mempty p.synopsis -setSynopsis :: Mdoc -> Page -> Page-setSynopsis s = field @"synopsis" ?~ Synopsis.custom s--setPrologue :: Mdoc -> Page -> Page-setPrologue p = field @"prologue" ?~ p--setEpilogue :: Mdoc -> Page -> Page-setEpilogue p = field @"epilogue" ?~ p+getPositionals :: Page -> [Described Positional]+getPositionals p = maybe [] (toList . (.items)) p.options
src/Mdoc/Data/Synopsis.hs view
@@ -10,7 +10,7 @@ ( Synopsis , singleton , alternation- , custom+ , command ) where import Mdoc.Prelude@@ -25,28 +25,20 @@ import Mdoc.Pretty import Mdoc.Syntax -data Synopsis- = Synopsis [SynopsisItem]- | SynopsisCustom Mdoc+newtype Synopsis = Synopsis+ { items :: [SynopsisItem]+ } deriving stock (Eq, Show)+ deriving newtype (Monoid, Semigroup) 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 items -> renderUsage items- SynopsisCustom ls -> pretty ls+ pretty = renderUsage . (.items) data SynopsisItem = Single (Described Positional) | Alternation Optionality [Described Positional]+ | Command deriving stock (Eq, Show) instance Ord SynopsisItem where@@ -64,20 +56,48 @@ alternation :: Optionality -> [Described Positional] -> Synopsis alternation o = Synopsis . pure . Alternation o -custom :: Mdoc -> Synopsis-custom = SynopsisCustom+command :: Synopsis+command = Synopsis [Command] data Usage ann = Usage { chars :: [Char]- , items :: [Doc ann]+ -- ^ list those [short] options not taking arguments first, in alphabetical+ -- order+ , shorts :: [Doc ann]+ -- ^ After that, list all [short] options taking arguments+ , longs :: [Doc ann]+ -- ^ If there are long options, list them after the short options that take+ -- arguments, just before the command line arguments+ , args :: [Doc ann]+ -- ^ Arguments follow the last option+ , command :: [Doc ann] } deriving stock (Generic, Show) deriving (Monoid, Semigroup) via Generically (Usage ann) +usageSetter :: Positional -> Lens' (Usage ann) [Doc ann]+usageSetter = \case+ PositionalOption (Option {flag = Flag {}}) -> field @"shorts"+ PositionalOption (Option {flag = GNUFlag {}}) -> field @"longs"+ PositionalSwitch (GNUFlag {}) -> field @"longs"+ PositionalSwitch (Flag {}) -> field @"shorts" -- edge-case+ PositionalArgument {} -> field @"args"+ renderUsage :: [SynopsisItem] -> Doc ann renderUsage = go . buildUsage where- go u = vsep $ maybeToList (pChars <$> nonEmpty u.chars) <> u.items+ go u =+ let ws =+ nonEmpty+ $ maybeToList (pChars <$> nonEmpty u.chars)+ <> u.shorts+ <> u.longs+ <> u.args+ <> u.command+ in case ws of+ Nothing -> mempty+ Just ne -> vsep $ ".Bk -words" : toList ne <> [".Ek"]+ pChars cs = ".Op Fl" <+> pretty (fixMacro $ esc $ pack $ toList cs) -- | Fix for when short-switches accidentally become a macro name@@ -101,30 +121,26 @@ addUsage :: Usage ann -> SynopsisItem -> Usage ann addUsage acc = \case- Single d- | Just c <- getPositionalSwitchChar d.item ->- acc & field @"chars" %~ sortOn toLower . (<> [c])- | otherwise ->- acc- & field @"items"- <>~ [ opLine d- $ ellipsis d- $ pretty- $ Positional1 d.item- ]- Alternation o ps ->+ Single (Described {item = PositionalSwitch (Flag c _)}) ->+ acc & field @"chars" %~ sortOn toLower . (<> [c])+ Single d -> acc- & field @"items"+ & usageSetter d.item+ <>~ [ opLine d+ $ ellipsis d+ $ pretty+ $ Positional1 d.item+ ]+ Alternation _ [] -> acc+ Alternation o ds@(d : _) ->+ acc+ & usageSetter d.item <>~ [ opLine' o $ mconcat $ punctuate " | "- $ map (pretty . Positional1 . (.item)) ps+ $ map (pretty . Positional1 . (.item)) ds ]--getPositionalSwitchChar :: Positional -> Maybe Char-getPositionalSwitchChar = \case- PositionalSwitch (Flag c _) -> Just c- _ -> Nothing+ Command -> acc & field @"command" <>~ [".Ar command"] opLine :: Described a -> Doc ann -> Doc ann opLine = opLine' . (.optionality)
src/Mdoc/Examples/Grep.hs view
@@ -15,31 +15,12 @@ import Env qualified import Env.Mdoc qualified as Env import Mdoc.Data.Named-import Mdoc.Data.Page-import Mdoc.Syntax import Options.Applicative import Options.Applicative.Mdoc -- | Recreates <https://github.com/arp242/bsdgrep/blob/master/grep.1> grep1 :: Named-grep1 =- mempty- & (<> getPage grepOpt)- & (<> Env.getPage grepEnv)- & setEpilogue- ( Mdoc- [ MacroLine Sh ["SEE ALSO"]- , MacroLine Xr ["ed", "1", ","]- , MacroLine Xr ["ex", "1", ","]- , MacroLine Xr ["sed", "1", ","]- , MacroLine Xr ["zgrep", "1", ","]- , MacroLine Xr ["re_format", "7"]- ]- )- & name "grep" "file pattern searcher"- & addSecondary "egrep"- & addSecondary "fgrep"- & addSecondary "rgrep"+grep1 = name "grep" "" $ getPage grepOpt <> Env.getPage grepEnv {- FOURMOLU_DISABLE -} grepOpt :: Parser ()
+ src/Mdoc/Examples/MdocDump.hs view
@@ -0,0 +1,18 @@+module Mdoc.Examples.MdocDump+ ( mdocDump1+ ) where++import Mdoc.Prelude++import Env.Mdoc qualified as Env+import Mdoc+import Mdoc.Dump.Env (envParser)+import Mdoc.Dump.Options (optionsInfo, optionsParser)+import Options.Applicative.Mdoc qualified as Opt++mdocDump1 :: Named+mdocDump1 =+ mempty+ & (<> Opt.getPage optionsParser)+ & (<> Env.getPage envParser)+ & name "mdoc-dump" optionsInfo
src/Mdoc/Examples/OptEnvConf.hs view
@@ -15,25 +15,11 @@ import Mdoc.Prelude import Mdoc.Data.Named-import Mdoc.Data.Page-import Mdoc.Syntax import OptEnvConf hiding (name) import OptEnvConf.Mdoc crontab5 :: Named-crontab5 =- mempty- & (<> getPage confParser)- & setSynopsis- ( Mdoc- [ MacroLine Bl ["-tag", "-width", "indent", "-compact"]- , MacroLine It [Callable Pa, "/etc/cron.daily", ","]- , MacroLine It [Callable Pa, "/etc/cron.hourly", ","]- , MacroLine It [Callable Pa, "/etc/cron.weekly"]- , MacroLine El []- ]- )- & name "crontab" "tables for driving cron"+crontab5 = name "crontab" "tables for driving cron" $ getPage confParser {- FOURMOLU_DISABLE -} confParser :: Parser ()@@ -42,29 +28,16 @@ <*> subConfig "bar" ((,) <$> setting [ conf @Bool "baz", help "Bar's baz of bazzle"] <*> setting [ conf @[Int] "bat", help "Bar's bat is better than that"- , example "# enable bat\nbat: 1"- , example "# disable bat\nbat: 0"+ , example "# enable bat\nbar:\n bat: 1"+ , example "# disable bat\nbar:\n bat: 0" ]) {- FOURMOLU_ENABLE -} example1 :: Named-example1 =- mempty- & (<> getPage exampleParser)- & name "example" "opt-env-conf example"+example1 = name "example" "opt-env-conf example" $ getPage exampleParser examplerc5 :: Named-examplerc5 =- mempty- & (<> getPage exampleParser)- & setSynopsis- ( Mdoc- [ MacroLine Bl ["-tag", "-width", "indent", "-compact"]- , MacroLine It [Callable Pa, ".examplerc.yaml"]- , MacroLine El []- ]- )- & name "examplerc" "opt-env-conf example config"+examplerc5 = name "examplerc" "opt-env-conf example config" $ getPage exampleParser {- FOURMOLU_DISABLE -} exampleParser :: Parser ()
src/Mdoc/Examples/Pass.hs view
@@ -13,56 +13,11 @@ import Mdoc.Prelude import Mdoc.Data.Named-import Mdoc.Data.Page (setPrologue)-import Mdoc.Syntax import Options.Applicative import Options.Applicative.Mdoc pass1 :: Named-pass1 =- mempty- & (<> getPage passOpt)- & setPrologue- ( Mdoc- [ MacroLine Nm []- , TextLine "is a very simple password store that keeps passwords inside"- , MacroLine Xr ["gpg2", "1"]- , TextLine "encrypted files inside a simple directory tree residing at"- , MacroLine Pa ["~/.password-store", "."]- , TextLine- "The pass utility provides a series of commands for manipulating the password store, allowing the user to add, remove, edit, synchronize, generate, and manipulate passwords."- , MacroLine Pp []- , TextLine "If no COMMAND is specified, COMMAND defaults to either"- , MacroLine Cm ["show"]- , TextLine "or"- , MacroLine Cm ["ls", ","]- , TextLine "depending on the type of specifier in ARGS. Alternatively, if"- , MacroLine Ar ["PASSWORD_STORE_ENABLE_EXTENSIONS"]- , TextLine "is set to \"true\", and the file"- , MacroLine Pa [".extensions/COMMAND.bash"]- , TextLine- "exists inside the password store and is executable, then it is sourced into the environment, passing any arguments and environment variables. Extensions existing in a system- wide directory, only installable by the administrator, are always enabled."- , MacroLine Pp []- , TextLine "Otherwise COMMAND must be one of the valid commands listed below."- , MacroLine Pp []- , TextLine- "Several of the commands below rely on or provide additional functionality if the password store directory is also a git repository. If the password store directory is a git repository, all password store modification commands will cause a corresponding git commit. Sub- directories may be separate nested git repositories, and pass will use the inner-most directory relative to the current password. See the"- , MacroLine Ar ["EXTENDED", "GIT", "EXAMPLE"]- , TextLine "section for a detailed description using init and"- , MacroLine Xr ["git", "1"]- , MacroLine Pp []- , TextLine "The"- , MacroLine Cm ["init"]- , TextLine- "command must be run before other commands in order to initialize the password store with the correct gpg key id. Passwords are encrypted using the gpg key set with"- , MacroLine Cm ["init", "."]- , MacroLine Pp []- , TextLine- "There is a corresponding bash completion script for use with tab completing password names in"- , MacroLine Xr ["bash", "1", "."]- ]- )- & name "pass" "stores, retrieves, generates, and synchronizes passwords securely"+pass1 = name "pass" "" $ getPage passOpt {- FOURMOLU_DISABLE -} passOpt :: Parser ()
src/Mdoc/Examples/Person.hs view
@@ -15,8 +15,6 @@ import Autodocodec (HasCodec (..), object, optionalField, requiredField, (.=)) import Autodocodec.Schema.Mdoc (getPageViaCodec) import Mdoc.Data.Named-import Mdoc.Data.Page-import Mdoc.Syntax data Person = Person { name :: Text@@ -51,14 +49,4 @@ {- FOURMOLU_ENABLE -} person5 :: Named-person5 =- mempty- & (<> getPageViaCodec @Person)- & setSynopsis- ( Mdoc- [ MacroLine Bl ["-tag", "-width", "indent", "-compact"]- , MacroLine It [Callable Pa, "person.schema.json"]- , MacroLine El []- ]- )- & name "person" "person example via Codec"+person5 = name "person" "person example via Codec" $ getPageViaCodec @Person
src/Mdoc/Syntax/MacroArg.hs view
@@ -9,7 +9,7 @@ module Mdoc.Syntax.MacroArg ( MacroArg (..) - -- * Lenses+ -- * Optics , _Mdocdate -- * Pretty
src/Mdoc/Syntax/Mdoc.hs view
@@ -14,11 +14,13 @@ -- * Util , textToMdoc+ , textToMdocPp ) where import Mdoc.Prelude import Data.Text qualified as T+import Mdoc.Optics import Mdoc.Pretty import Mdoc.Syntax.MacroName import Mdoc.Syntax.MdocLine@@ -36,13 +38,36 @@ instance Pretty Mdoc where pretty = unAnnotate . prettyMdoc +-- | Map 'Text' to an 'Mdoc' of 'TextLine's+--+-- @+-- Some text with+-- newlines+-- @+--+-- @+-- Some text with+-- newlines+-- @ textToMdoc :: Text -> Maybe Mdoc textToMdoc =- fmap- ( Mdoc- . intersperse (MacroLine Pp [])- . map (TextLine . esc)- . toList- )+ fmap (Mdoc . map (TextLine . esc) . toList) . nonEmpty . T.lines++-- | Map 'Text' to an 'Mdoc' of 'TextLine's, injecting a 'Pp' between each+--+-- @+-- Some text with+-- newlines+-- @+--+-- @+-- Some text with+-- .Pp+-- newlines+-- @+textToMdocPp :: Text -> Maybe Mdoc+textToMdocPp = fmap go . textToMdoc+ where+ go = field @"lines" %~ intersperse (MacroLine Pp [])
src/Mdoc/Syntax/MdocLine.hs view
@@ -9,7 +9,7 @@ module Mdoc.Syntax.MdocLine ( MdocLine (..) - -- * Lenses+ -- * Optics , _Dd -- * Pretty@@ -18,6 +18,7 @@ import Mdoc.Prelude +import Data.Text qualified as T import Mdoc.Optics import Mdoc.Pretty import Mdoc.Syntax.MacroArg@@ -46,7 +47,9 @@ $ annotate AnnComment "." <> annotate AnnCommand (prettyMacroName name) : map prettyMacroArg args- TextLine x -> reflow x+ TextLine x ->+ let (leading, rest) = T.break (/= ' ') x+ in pretty leading <> reflow rest TableLines ls -> prettyTableLines ls TroffMacro m -> prettyTroffMacro m
src/Mdoc/Template.hs view
@@ -28,8 +28,6 @@ import Mdoc.Prelude -import Data.FileEmbed-import Data.Text.Encoding (decodeUtf8) import Data.Text.Lazy.Encoding (encodeUtf8) import Mdoc.Data.Named import Mdoc.Input@@ -37,6 +35,7 @@ import Mdoc.Syntax import System.IO (hPutStrLn, stderr) import Text.Mustache+import Text.Mustache.Compile.TH qualified as TH data RenderResult = Rendered Mdoc@@ -62,19 +61,13 @@ RenderedError err -> throwIO err custom :: MonadIO m => FilePath -> m Template-custom = compileMustacheFile+custom p = (<> man1) <$> compileMustacheFile p man1 :: Template-man1 =- either (error . errorBundlePretty) id- $ compileMustacheText "man1"- $ decodeUtf8 $(embedFile "data/man1.template")+man1 = $(TH.compileMustacheDir "man1" "data") man5 :: Template-man5 =- either (error . errorBundlePretty) id- $ compileMustacheText "man5"- $ decodeUtf8 $(embedFile "data/man5.template")+man5 = $(TH.compileMustacheDir "man5" "data") putMustacheWarnings :: MonadIO m => NonEmpty MustacheWarning -> m () putMustacheWarnings = liftIO . hPutStrLn stderr . displayMustacheWarnings
src/OptEnvConf/Mdoc.hs view
@@ -49,7 +49,6 @@ import Autodocodec.Schema.Mdoc qualified as JSONSchema import Control.Monad (foldM) import Control.Monad.State (MonadState (..), evalState, modify)-import Data.Text qualified as T import Mdoc.Data.Argument import Mdoc.Data.Command import Mdoc.Data.Config@@ -72,7 +71,7 @@ Command { index , name = commandDocArgument doc- , description = textToMdoc $ pack $ commandDocHelp doc+ , description = textToMdocPp $ pack $ commandDocHelp doc , synopsis = getSynopsis $ foldSetDocs addToPage $ commandDocs doc } @@ -155,7 +154,7 @@ { item = d , optionality = maybe Required Defaulted (setDocDefault d) , multiple = False- , help = textToMdoc . pack =<< setDocHelp d+ , help = textToMdocPp . pack =<< setDocHelp d , example = exampleMdoc <$> nonEmpty (setDocExamples d) } where@@ -169,12 +168,13 @@ } exampleMdoc :: NonEmpty String -> Mdoc-exampleMdoc ne =- ne- & toList -- [x]- & map (map TextLine . T.lines . pack) -- [[x, y], [z]]- & intercalate [MacroLine Pp []] -- [x, y, Pp, z]- & Mdoc+exampleMdoc =+ mconcat+ -- Inject a Pp between each example+ . intersperse (Mdoc [MacroLine Pp []])+ -- Map each example to Mdoc without injecting Pps between lines+ . mapMaybe (textToMdoc . pack)+ . toList dashedFlags :: [Dashed] -> Maybe Flag dashedFlags = fmap go . nonEmpty
src/Options/Applicative/Mdoc.hs view
@@ -26,7 +26,7 @@ import Mdoc.Data.Option import Mdoc.Data.Optionality import Mdoc.Data.Page-import Mdoc.Syntax (textToMdoc)+import Mdoc.Syntax (textToMdocPp) import Options.Applicative (Parser) import Options.Applicative.Common (treeMapParser) import Options.Applicative.Help.Chunk (Chunk (..), unChunk)@@ -43,7 +43,7 @@ Command { index , name- , description = textToMdoc =<< docToText doc+ , description = textToMdocPp =<< docToText doc , synopsis = getSynopsis $ getPage $ O.infoParser pinfo } where@@ -121,7 +121,7 @@ { item = o , optionality = maybe Required Defaulted (O.optShowDefault o) , multiple = False- , help = textToMdoc =<< docToText (O.optHelp o)+ , help = textToMdocPp =<< docToText (O.optHelp o) , example = Nothing } O.Internal -> pure acc
+ templates/crontab.5.mustache view
@@ -0,0 +1,12 @@+{{>title5}}+{{>name}}+.Sh SYNOPSIS+.Bl -tag -width indent -compact+.It Pa /etc/cron.daily ,+.It Pa /etc/cron.hourly ,+.It Pa /etc/cron.weekly+.El+.Sh DESCRIPTION+{{#page.configs}}+{{>configs}}+{{/page.configs}}
+ templates/grep.1.mustache view
@@ -0,0 +1,26 @@+{{>title1}}+.Sh NAME+.Nm grep ,+.Nm egrep ,+.Nm fgrep ,+.Nm rgrep+.Nd file pattern searcher+.Sh SYNOPSIS+.Nm+{{{page.synopsis}}}+.Sh DESCRIPTION+{{#page.options}}+{{>options}}+{{/page.options}}+.Sh ENVIRONMENT+{{#page.environment}}+{{>environment}}+{{/page.environment}}+.Sh EXIT STATUS+.Ex -std+.Sh SEE ALSO+.Xr ed 1 ,+.Xr ex 1 ,+.Xr sed 1 ,+.Xr zgrep 1 ,+.Xr re_format 7
+ templates/mdoc-dump.1.mustache view
@@ -0,0 +1,3 @@+{{>man1}}+.Sh SEE ALSO+.Xr mdoc 7
+ templates/pass.1.mustache view
@@ -0,0 +1,69 @@+{{>title1}}+.Sh NAME+.Nm pass+.Nd stores, retrieves, generates, and synchronizes passwords securely+.Sh SYNOPSIS+.Nm+{{{page.synopsis}}}+{{#page.commands.items}}+.Pp+.Nm+.Cm {{{name}}}+{{#synopsis}}+{{{.}}}+{{/synopsis}}+{{/page.commands.items}}+.Sh DESCRIPTION+.Nm+is a very simple password store that keeps passwords inside+.Xr gpg2 1+encrypted files inside a simple directory tree residing at+.Pa ~/.password-store .+The pass utility provides a series of commands for manipulating the password+store, allowing the user to add, remove, edit, synchronize, generate, and+manipulate passwords.+.Pp+If no COMMAND is specified, COMMAND defaults to either+.Cm show+or+.Cm ls ,+depending on the type of specifier in ARGS. Alternatively, if+.Ar PASSWORD_STORE_ENABLE_EXTENSIONS+is set to "true", and the file+.Pa .extensions/COMMAND.bash+exists inside the password store and is executable, then it is sourced into the+environment, passing any arguments and environment variables. Extensions+existing in a system- wide directory, only installable by the administrator, are+always enabled.+.Pp+Otherwise COMMAND must be one of the valid commands listed below.+.Pp+Several of the commands below rely on or provide additional functionality if the+password store directory is also a git repository. If the password store+directory is a git repository, all password store modification commands will+cause a corresponding git commit. Sub- directories may be separate nested git+repositories, and pass will use the inner-most directory relative to the current+password. See the+.Ar EXTENDED GIT EXAMPLE+section for a detailed description using init and+.Xr git 1+.Pp+The+.Cm init+command must be run before other commands in order to initialize the password+store with the correct gpg key id. Passwords are encrypted using the gpg key set+with+.Cm init .+.Pp+There is a corresponding bash completion script for use with tab completing+password names in+.Xr bash 1 .+{{#page.options}}+{{>options}}+{{/page.options}}+{{#page.commands}}+.Ss COMMANDS+{{>commands}}+{{/page.commands}}+.Sh EXIT STATUS+.Ex -std
+ templates/person.5.mustache view
@@ -0,0 +1,10 @@+{{>title5}}+{{>name}}+.Sh SYNOPSIS+.Bl -tag -width indent -compact+.It Pa person.schema.json+.El+.Sh DESCRIPTION+{{#page.configs}}+{{>configs}}+{{/page.configs}}
test/Mdoc/Data/ConfigSpec.hs view
@@ -32,14 +32,7 @@ , optionality = Required , multiple = False , help = Just $ Mdoc [TextLine "Push to git remote"]- , example =- textToMdoc- $ mconcat- [ "# disable pushing\n"- , "git:\n"- , " push: false\n"- , "\n"- ]+ , example = Nothing } renderPlain (prettyDescribed config)
test/Mdoc/ExamplesSpec.hs view
@@ -22,6 +22,7 @@ import Mdoc.Examples.Docker import Mdoc.Examples.Grep import Mdoc.Examples.Mandoc+import Mdoc.Examples.MdocDump import Mdoc.Examples.OptEnvConf import Mdoc.Examples.Pass import Mdoc.Examples.Person@@ -37,15 +38,38 @@ spec :: Spec spec = do- it "grep.1" $ exampleGolden man1 grep1 1- it "example.1" $ exampleGolden man1 example1 1- it "pass.1" $ exampleGolden man1 pass1 1- it "docker.1" $ exampleGolden man1 docker1 1- it "mandoc.1" $ exampleGolden man1 mandoc1 1- it "crontab.5" $ exampleGolden man5 crontab5 5- it "examplerc.5" $ exampleGolden man5 examplerc5 5- it "person.5" $ exampleGolden man5 person5 5+ it "grep.1" $ do+ t <- custom "templates/grep.1.mustache"+ exampleGolden t grep1 1 + it "example.1" $ do+ exampleGolden man1 example1 1++ it "pass.1" $ do+ t <- custom "templates/pass.1.mustache"+ exampleGolden t pass1 1++ it "docker.1" $ do+ exampleGolden man1 docker1 1++ it "mandoc.1" $ do+ exampleGolden man1 mandoc1 1++ it "mdoc-dump.1" $ do+ t <- custom "templates/mdoc-dump.1.mustache"+ exampleGolden t mdocDump1 1++ it "crontab.5" $ do+ t <- custom "templates/crontab.5.mustache"+ exampleGolden t crontab5 5++ it "examplerc.5" $ do+ exampleGolden man5 examplerc5 5++ it "person.5" $ do+ t <- custom "templates/person.5.mustache"+ exampleGolden t person5 5+ exampleGolden :: Template -> Named -> Int -> IO (Golden Mdoc) exampleGolden template named section = do case renderTemplate template named of@@ -53,9 +77,12 @@ pure @IO Golden { output = fixMdocDate mdoc- , encodePretty = unpack . renderPlain . prettyMdoc+ , encodePretty = show -- unpack . renderPlain . prettyMdoc , writeToFile = \fp -> T.writeFile fp . (<> "\n") . renderPlain . prettyMdoc- , readFromFile = \fp -> either exitParseError pure . parseMdocBytes fp =<< BSL.readFile fp+ , readFromFile = \fp ->+ either exitParseError (pure . fixMdocDate)+ . parseMdocBytes fp+ =<< BSL.readFile fp , goldenFile = "examples" </> path , actualFile = Nothing , failFirstTime = False