mdoc 0.1.0.1 → 0.1.0.2
raw patch · 18 files changed
+257/−103 lines, 18 filesdep −semigroupsPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies removed: semigroups
API changes (from Hackage documentation)
- Mdoc.Gen.Flag: data FlagOrder
- Mdoc.Gen.Flag: flagOrder :: Flag -> FlagOrder
- Mdoc.Gen.Flag: instance GHC.Classes.Eq Mdoc.Gen.Flag.FlagOrder
- Mdoc.Gen.Flag: instance GHC.Classes.Ord Mdoc.Gen.Flag.FlagOrder
+ Mdoc.Gen.Flag: instance GHC.Classes.Ord Mdoc.Gen.Flag.Flag
+ Mdoc.Gen.Optionality: opLine :: MacroName -> [MacroArg] -> Optionality -> MdocLine
+ Mdoc.Pretty: ColorDefault :: Color
+ Mdoc.Pretty: instance GHC.Base.Semigroup Mdoc.Pretty.Color
- Mdoc.Dump.Env: Env :: Maybe Color -> Bool -> Env
+ Mdoc.Dump.Env: Env :: Color -> Bool -> Env
- Mdoc.Dump.Env: [color] :: Env -> Maybe Color
+ Mdoc.Dump.Env: [color] :: Env -> Color
- Mdoc.Dump.Options: Options :: Bool -> Bool -> Maybe Color -> Bool -> [FilePath] -> Options
+ Mdoc.Dump.Options: Options :: Bool -> Bool -> Color -> Bool -> [FilePath] -> Options
- Mdoc.Dump.Options: [color] :: Options -> Maybe Color
+ Mdoc.Dump.Options: [color] :: Options -> Color
- Mdoc.Gen.Description: optionLines :: Described Option -> (FlagOrder, [MdocLine])
+ Mdoc.Gen.Description: optionLines :: Described Option -> (Flag, [MdocLine])
- Mdoc.Gen.Description: switchLines :: Described Flag -> (FlagOrder, [MdocLine])
+ Mdoc.Gen.Description: switchLines :: Described Flag -> (Flag, [MdocLine])
Files
- mdoc.cabal +23/−3
- src/Mdoc/Dump/Env.hs +5/−3
- src/Mdoc/Dump/Main.hs +9/−12
- src/Mdoc/Dump/Options.hs +12/−12
- src/Mdoc/Gen/Argument.hs +12/−15
- src/Mdoc/Gen/CrossRef.hs +2/−1
- src/Mdoc/Gen/Description.hs +4/−4
- src/Mdoc/Gen/Environment.hs +8/−0
- src/Mdoc/Gen/ExitStatus.hs +10/−2
- src/Mdoc/Gen/Flag.hs +8/−14
- src/Mdoc/Gen/Optionality.hs +19/−0
- src/Mdoc/Gen/Synopsis.hs +8/−25
- src/Mdoc/Interpolated.hs +6/−9
- src/Mdoc/Parse.hs +1/−2
- src/Mdoc/Pretty.hs +19/−1
- test/Mdoc/Gen/ExitStatusSpec.hs +8/−0
- test/Mdoc/Gen/FlagSpec.hs +95/−0
- test/Mdoc/Test/Fixtures.hs +8/−0
mdoc.cabal view
@@ -1,10 +1,30 @@ cabal-version: 1.18 name: mdoc-version: 0.1.0.1+version: 0.1.0.2 license: AGPL-3 maintainer: Pat Brisbin synopsis: Parser and pretty-printer for the mdoc(7) language-description: Please see README.md+description:+ This library parses, represents, pretty-prints, or generates man-page content+ using the [mdoc(7)](https://mandoc.bsd.lv/man/mdoc.7.html) language.+ .+ To parse a man-page and work with its data in a structured way,+ .+ * See 'parseMdoc' and 'Mdoc'+ .+ To pretty-print parsed (or constructed) man-page data,+ .+ * Use 'prettyMdoc' (or 'putDoc')+ .+ To generate a man-page from a supported parser library, check the appropriate+ integration module,+ .+ * @envparse@ -- "Env.Mdoc"+ * @opt-env-conf@ -- "OptEnvConf.Mdoc"+ * @optparse-applicative@ -- "Options.Applicative.Mdoc"+ .+ An example and screenshots are present in the project [README](#readme).+ build-type: Simple extra-source-files: data/man1.template@@ -106,7 +126,6 @@ optparse-applicative >=0.18.1.0, prettyprinter >=1.7.1, prettyprinter-ansi-terminal >=1.1.3,- semigroups >=0.20, text >=2.1.1, time >=1.12.2, zlib >=0.7.1.0@@ -169,6 +188,7 @@ other-modules: Mdoc.Gen.DescriptionSpec Mdoc.Gen.ExitStatusSpec+ Mdoc.Gen.FlagSpec Mdoc.GenSpec Mdoc.Parse.MacroArgSpec Mdoc.Parse.MdocLineSpec
src/Mdoc/Dump/Env.hs view
@@ -21,7 +21,7 @@ import Mdoc.Pretty (Color (..), readColor) data Env = Env- { color :: Maybe Color+ { color :: Color , debug :: Bool } @@ -35,8 +35,10 @@ envParser = Env.prefixed "MDOC_DUMP_" $ Env- <$> optional colorEnv+ <$> colorEnv <*> switch "DEBUG" (help "Log more verbosely") colorEnv :: Parser Error Color-colorEnv = var (eitherReader readColor) "COLOR" $ help "When to colorize the output"+colorEnv =+ var (eitherReader readColor) "COLOR"+ $ help "When to colorize the output" <> def ColorDefault
src/Mdoc/Dump/Main.hs view
@@ -19,7 +19,7 @@ import Mdoc.Dump.Options import Mdoc.Input import Mdoc.Parse (exitParseError)-import Mdoc.Pretty (Ann (..), Color (..), putDoc, renderPlain)+import Mdoc.Pretty (Ann (..), putDoc, renderPlain) import Mdoc.UpdateMdocdate import Prettyprinter import System.Exit (exitFailure)@@ -29,19 +29,16 @@ main = do options <- parseOptions - let- color = fromMaybe ColorAuto options.color-- postProcess mdoc =- if options.updateMdocdate- then updateMdocdate mdoc- else pure mdoc+ let postProcess mdoc =+ if options.updateMdocdate+ then updateMdocdate mdoc+ else pure mdoc forInputs_ options.input $ \input -> do case input.parsed of SkipParse reason -> when options.debug- $ putDoc color stderr+ $ putDoc options.color stderr $ annotate AnnFile (pretty input.name) <> ":" <+> "refusing to parse"@@ -49,7 +46,7 @@ ParseError err -> exitParseError err Parsed mdoc -> do mdoc' <- postProcess mdoc- putDoc color stdout $ prettyMdoc mdoc'+ putDoc options.color stdout $ prettyMdoc mdoc' -- NB. The --check option operates on mdoc before any --update-mdocdate -- changes. Unclear what the least surprising behavior is here.@@ -61,7 +58,7 @@ case diffs of NoDifferences ->- putDoc color stdout $ prettyDifferences input.name diffs+ putDoc options.color stdout $ prettyDifferences input.name diffs Differences {} -> do- putDoc color stderr $ prettyDifferences input.name diffs+ putDoc options.color stderr $ prettyDifferences input.name diffs exitFailure
src/Mdoc/Dump/Options.hs view
@@ -20,13 +20,13 @@ import Prelude import Mdoc.Dump.Env-import Mdoc.Pretty (Color (..), readColor)+import Mdoc.Pretty (Color (..), readColor, showColor) import Options.Applicative data Options = Options { check :: Bool , updateMdocdate :: Bool- , color :: Maybe Color+ , color :: Color , debug :: Bool , input :: [FilePath] }@@ -41,7 +41,7 @@ pure (opt :: Options)- { color = opt.color <|> env.color+ { color = env.color <> opt.color , debug = env.debug || opt.debug } @@ -64,15 +64,15 @@ , help "Update any $Mdocdate$ macros in the output" ] )- <*> optional- ( option- (eitherReader readColor)- ( mconcat- [ long "color"- , help "When to colorize the output"- , metavar "auto|always|never"- ]- )+ <*> option+ (eitherReader readColor)+ ( mconcat+ [ long "color"+ , help "When to colorize the output"+ , metavar "auto|always|never"+ , value ColorDefault+ , showDefaultWith showColor+ ] ) <*> switch ( mconcat
src/Mdoc/Gen/Argument.hs view
@@ -44,18 +44,15 @@ renderArgumentForLong :: String -> Argument -> [MacroArg] renderArgumentForLong x arg@Argument {optionality} =- case optionality of- Required ->- [ Bare $ esc $ pack $ x <> "="- , Callable Ns- ]- <> renderArgument arg- _ ->- [ Bare $ esc $ pack x- , Callable Ns- , Callable Op- , Callable Cm- , Bare "="- , Callable Ns- ]- <> renderArgument arg+ prefix <> [Callable Ns] <> renderArgument arg+ where+ prefix =+ case optionality of+ Required -> [Bare $ esc $ pack $ x <> "="]+ _ ->+ [ Bare $ esc $ pack x+ , Callable Ns+ , Callable Op+ , Callable Cm+ , Bare "="+ ]
src/Mdoc/Gen/CrossRef.hs view
@@ -25,7 +25,8 @@ deriving stock (Eq, Show) instance Ord CrossRef where- a `compare` b = a.section `compare` b.section <> a.name `compare` b.name+ -- SEE ALSO is sorted section then name+ compare = comparing $ \x -> (x.section, x.name) renderCrossRefs :: NonEmpty CrossRef -> NonEmpty MdocLine renderCrossRefs = go . NE.sort
src/Mdoc/Gen/Description.hs view
@@ -58,15 +58,15 @@ $ map switchLines switches <> map optionLines options -switchLines :: Described Flag -> (FlagOrder, [MdocLine])+switchLines :: Described Flag -> (Flag, [MdocLine]) switchLines d =- ( flagOrder d.item+ ( d.item , renderDescribedItem (`renderFlag` Nothing) d ) -optionLines :: Described Option -> (FlagOrder, [MdocLine])+optionLines :: Described Option -> (Flag, [MdocLine]) optionLines d =- ( flagOrder d.item.flag+ ( d.item.flag , renderDescribedItem ((`renderFlag` (Just d.item.argument)) . (.flag)) d )
src/Mdoc/Gen/Environment.hs view
@@ -1,3 +1,11 @@+-- |+--+-- Module : Mdoc.Gen.Environment+-- Copyright : (c) 2026 Patrick Brisbin+-- License : AGPL-3+-- Maintainer : pbrisbin@gmail.com+-- Stability : experimental+-- Portability : POSIX module Mdoc.Gen.Environment ( man1Environment ) where
src/Mdoc/Gen/ExitStatus.hs view
@@ -1,3 +1,11 @@+-- |+--+-- Module : Mdoc.Gen.ExitStatus+-- Copyright : (c) 2026 Patrick Brisbin+-- License : AGPL-3+-- Maintainer : pbrisbin@gmail.com+-- Stability : experimental+-- Portability : POSIX module Mdoc.Gen.ExitStatus ( NonZeroStatus (..) , renderExitStatuses@@ -10,9 +18,9 @@ data NonZeroStatus = NonZeroStatus { status :: Text- -- ^ @>0@+ -- ^ For example, @\">0\"@ , condition :: Text- -- ^ @if an error occurs@+ -- ^ For example, @\"if an error occurs\"@ } deriving stock (Eq, Generic, Show)
src/Mdoc/Gen/Flag.hs view
@@ -8,8 +8,6 @@ -- Portability : POSIX module Mdoc.Gen.Flag ( Flag (..)- , FlagOrder- , flagOrder , renderFlag , renderShort , renderLong@@ -29,18 +27,14 @@ | GNUFlag String [Flag] deriving stock (Eq, Show) -data FlagOrder = FlagOrder- { _byType :: Int- , _byNameLower :: String- , _byUpperThenLower :: Bool- }- deriving stock (Eq, Ord)---- | Shorts before longs, capital then lower together-flagOrder :: Flag -> FlagOrder-flagOrder = \case- Flag c _ -> FlagOrder 1 [toLower c] $ isLower c- GNUFlag s _ -> FlagOrder 2 (map toLower s) False+instance Ord Flag where+ compare = comparing @(Int, String, Bool) $ \case+ -- 1. short before long+ -- | 2. case-insensitive alphabetically+ -- | | 3. upper before lower+ -- | | |+ Flag c _ -> (1, [toLower c], isLower c)+ GNUFlag s _ -> (2, map toLower s, False) renderFlag :: Flag -> Maybe Argument -> [MacroArg] renderFlag flag margument =
src/Mdoc/Gen/Optionality.hs view
@@ -8,12 +8,31 @@ -- Portability : POSIX module Mdoc.Gen.Optionality ( Optionality (..)+ , opLine ) where import Mdoc.Prelude +import Mdoc.MacroArg+import Mdoc.MacroName+import Mdoc.MdocLine+ data Optionality = Optional | Required | Defaulted String deriving stock (Eq, Show)++-- | Render a macro line as @.Op@ or not+--+-- >>> opLine Fl ["A", Ar, "num"] Required+-- .Fl A Ar num+-- -A num+--+-- >>> opLine Fl ["A", Ar, "num"] Optional+-- .Op Fl A Ar num+-- [-A num]+opLine :: MacroName -> [MacroArg] -> Optionality -> MdocLine+opLine n args = \case+ Required -> MacroLine n args+ _ -> MacroLine Op $ Callable n : args
src/Mdoc/Gen/Synopsis.hs view
@@ -28,8 +28,7 @@ MacroLine Nm [Bare $ esc $ pack $ m.name.primary] :| concat [ [MacroLine Bk ["-words"]]- , maybe [] (\cs -> [MacroLine Op [Callable Fl, Bare $ esc $ pack $ toList cs]])- $ shortSwitchChars m.switches+ , maybe [] (pure . shortsLine) $ shortSwitchChars m.switches , mapMaybe shortOptionLine m.options , map snd $ sortOn fst@@ -39,6 +38,9 @@ , [MacroLine Ek []] ] +shortsLine :: NonEmpty Char -> MdocLine+shortsLine cs = MacroLine Op [Callable Fl, Bare $ esc $ pack $ toList cs]+ man5Synopsis :: Man5 -> NonEmpty MdocLine man5Synopsis m = renderFilesCompact m.files @@ -47,37 +49,18 @@ shortOptionLine :: Described Option -> Maybe MdocLine shortOptionLine d = withShort d.item.flag $ \c ->- let margs = renderShort c $ Just d.item.argument- in case d.optionality of- Required -> MacroLine Fl margs- _ -> MacroLine Op $ Callable Fl : margs+ opLine Fl (renderShort c $ Just d.item.argument) d.optionality longSwitchLine :: Described Flag -> Maybe (String, MdocLine) longSwitchLine d = withLong d.item $ \s ->- let- margs = renderLong s Nothing- mline = case d.optionality of- Required -> MacroLine Fl margs- _ -> MacroLine Op $ Callable Fl : margs- in- (s, mline)+ (s, opLine Fl (renderLong s Nothing) d.optionality) longOptionLine :: Described Option -> Maybe (String, MdocLine) longOptionLine d = withLong d.item.flag $ \s ->- let- margs = renderLong s $ Just d.item.argument- mline = case d.optionality of- Required -> MacroLine Fl margs- _ -> MacroLine Op $ Callable Fl : margs- in- (s, mline)+ (s, opLine Fl (renderLong s $ Just d.item.argument) d.optionality) argLine :: Described Argument -> MdocLine-argLine d =- case d.optionality of- Required -> MacroLine Ar margs- Optional -> MacroLine Op $ Callable Ar : margs- Defaulted {} -> MacroLine Op $ Callable Ar : margs+argLine d = opLine Ar margs d.optionality where margs | d.multiple = [Bare $ esc $ pack d.item.schema, "..."]
src/Mdoc/Interpolated.hs view
@@ -52,17 +52,14 @@ InterpolatedLine Name -> toList v.name InterpolatedLine Synopsis -> toList v.synopsis InterpolatedLine Description -> toList v.description- InterpolatedLine Environment -> case v.environment of- Nothing -> []- Just es -> MacroLine Sh ["ENVIRONMENT"] : toList es- InterpolatedLine Files -> case v.files of- Nothing -> []- Just fs -> MacroLine Sh ["FILES"] : toList fs+ InterpolatedLine Environment ->+ maybe [] ((MacroLine Sh ["ENVIRONMENT"] :) . toList) v.environment+ InterpolatedLine Files ->+ maybe [] ((MacroLine Sh ["FILES"] :) . toList) v.files InterpolatedLine ExitStatus -> maybe [MacroLine Ex ["-std"]] toList v.exitStatus- InterpolatedLine SeeAlso -> case v.seeAlso of- Nothing -> []- Just crs -> MacroLine Sh ["SEE", "ALSO"] : toList crs+ InterpolatedLine SeeAlso ->+ maybe [] ((MacroLine Sh ["SEE", "ALSO"] :) . toList) v.seeAlso line -> [line] instance Interpolated Mdoc where
src/Mdoc/Parse.hs view
@@ -35,7 +35,6 @@ import Control.Monad.State.Strict (StateT, evalState) import Data.Char (isSpace) import Data.Monoid (Last (..))-import Data.Semigroup.Generic (GenericSemigroupMonoid (..)) import System.Exit (exitFailure) import System.IO (hPutStrLn, stderr) import Text.Megaparsec hiding (ParseError, parseTest, runParser)@@ -60,7 +59,7 @@ -- ^ If in a macro definition with custom closer, this is it } deriving stock (Generic)- deriving (Monoid, Semigroup) via GenericSemigroupMonoid MacroState+ deriving (Monoid, Semigroup) via Generically MacroState type ParseError = ParseErrorBundle Text Void
src/Mdoc/Pretty.hs view
@@ -50,10 +50,26 @@ | AnnDiffContext data Color- = ColorAuto+ = -- | Behaves like 'ColorAuto' but is an identity under 'Semigroup'+ --+ -- This means you can give parsers this as a default, and still do (e.g.):+ --+ -- @+ -- color :: Color+ -- color = env.color <> opt.color+ -- @+ --+ -- Without finding that the @opt@ default clobbers an explicit @env@.+ ColorDefault+ | ColorAuto | ColorAlways | ColorNever +instance Semigroup Color where+ ColorDefault <> a = a+ a <> ColorDefault = a+ _ <> a = a+ readColor :: String -> Either String Color readColor = \case "auto" -> Right ColorAuto@@ -63,6 +79,7 @@ showColor :: Color -> String showColor = \case+ ColorDefault -> "auto" ColorAuto -> "auto" ColorAlways -> "always" ColorNever -> "never"@@ -70,6 +87,7 @@ putDoc :: MonadIO m => Color -> Handle -> Doc Ann -> m () putDoc c h doc = do useColor <- case c of+ ColorDefault -> liftIO $ hIsTerminalDevice h ColorAuto -> liftIO $ hIsTerminalDevice h ColorAlways -> pure True ColorNever -> pure False
test/Mdoc/Gen/ExitStatusSpec.hs view
@@ -1,3 +1,11 @@+-- |+--+-- Module : Mdoc.Gen.ExitStatusSpec+-- Copyright : (c) 2026 Patrick Brisbin+-- License : AGPL-3+-- Maintainer : pbrisbin@gmail.com+-- Stability : experimental+-- Portability : POSIX module Mdoc.Gen.ExitStatusSpec ( spec ) where
+ test/Mdoc/Gen/FlagSpec.hs view
@@ -0,0 +1,95 @@+-- |+--+-- Module : Mdoc.Gen.FlagSpec+-- Copyright : (c) 2026 Patrick Brisbin+-- License : AGPL-3+-- Maintainer : pbrisbin@gmail.com+-- Stability : experimental+-- Portability : POSIX+module Mdoc.Gen.FlagSpec+ ( spec+ ) where++import Mdoc.Prelude++import Data.List (sort)+import Mdoc.Gen.Flag+import Test.Hspec++spec :: Spec+spec = do+ describe "Ord" $ do+ it "sorts short before long, capital-then-lower together" $ do+ let+ unsorted =+ [ Flag 'q' []+ , Flag 'e' []+ , Flag 'n' []+ , Flag 'G' []+ , Flag 'b' []+ , Flag 'A' []+ , Flag 'I' []+ , Flag 's' []+ , GNUFlag "binary-files" []+ , Flag 'B' []+ , GNUFlag "line-buffered" []+ , Flag 'L' []+ , Flag 'F' []+ , GNUFlag "null" []+ , Flag 'w' []+ , Flag 'o' []+ , Flag 'Z' []+ , Flag 'x' []+ , Flag 'E' []+ , Flag 'v' []+ , GNUFlag "label" []+ , Flag 'h' []+ , Flag 'c' []+ , Flag 'U' []+ , Flag 'H' []+ , Flag 'l' []+ , Flag 'V' []+ , Flag 'f' []+ , Flag 'C' [GNUFlag "context" []]+ , Flag 'i' []+ , Flag 'a' []+ , Flag 'm' []+ , Flag 'R' []+ ]+ sorted =+ [ Flag 'A' []+ , Flag 'a' []+ , Flag 'B' []+ , Flag 'b' []+ , Flag 'C' [GNUFlag "context" []]+ , Flag 'c' []+ , Flag 'E' []+ , Flag 'e' []+ , Flag 'F' []+ , Flag 'f' []+ , Flag 'G' []+ , Flag 'H' []+ , Flag 'h' []+ , Flag 'I' []+ , Flag 'i' []+ , Flag 'L' []+ , Flag 'l' []+ , Flag 'm' []+ , Flag 'n' []+ , Flag 'o' []+ , Flag 'q' []+ , Flag 'R' []+ , Flag 's' []+ , Flag 'U' []+ , Flag 'V' []+ , Flag 'v' []+ , Flag 'w' []+ , Flag 'x' []+ , Flag 'Z' []+ , GNUFlag "binary-files" []+ , GNUFlag "label" []+ , GNUFlag "line-buffered" []+ , GNUFlag "null" []+ ]++ sort unsorted `shouldBe` sorted
test/Mdoc/Test/Fixtures.hs view
@@ -1,5 +1,13 @@ {-# OPTIONS_GHC -Wno-ambiguous-fields #-} +-- |+--+-- Module : Mdoc.Test.Fixtures+-- Copyright : (c) 2026 Patrick Brisbin+-- License : AGPL-3+-- Maintainer : pbrisbin@gmail.com+-- Stability : experimental+-- Portability : POSIX module Mdoc.Test.Fixtures ( grepBase , grepOpt