Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix #14: Implement maximum info width setting #21

Merged
merged 1 commit into from
Jun 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions example/CustomInfoWidth.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-}
-- | Custom info width example.
module Main (main) where

import Env
import System.Environment (getArgs)
import Text.Read (readMaybe)


main :: IO ()
main = do
(readMaybe -> Just widthMax) : _ <- getArgs
_ <- hello widthMax
error "impossible"

hello :: Int -> IO ()
hello n = Env.parse (header "envparse example" . Env.widthMax n) $ do
_ <- var (str @String) "NAME" (help loremIpsum)
_ <- var (str @String) "A_NAME_THAT_DOESN'T_FIT_IN_THE_COMPACT_VIEW" (help loremIpsum)
pure ()

loremIpsum :: String
loremIpsum =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, \
\sed do eiusmod tempor incididunt ut labore et dolore magna \
\aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco \
\laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure \
\dolor in reprehenderit in voluptate velit esse cillum dolore eu \
\fugiat nulla pariatur. Excepteur sint occaecat cupidatat non \
\proident, sunt in culpa qui officia deserunt mollit anim id est \
\laborum."
1 change: 1 addition & 0 deletions src/Env.hs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ module Env
, Help.header
, Help.desc
, Help.footer
, Help.widthMax
, Help.handleError
, Help.ErrorHandler
, Help.defaultErrorHandler
Expand Down
54 changes: 35 additions & 19 deletions src/Env/Internal/Help.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module Env.Internal.Help
, header
, desc
, footer
, widthMax
, handleError
) where

Expand All @@ -25,35 +26,42 @@ import Env.Internal.Parser hiding (Mod)


helpInfo :: Info e -> Parser e b -> [(String, e)] -> String
helpInfo Info {infoHeader, infoDesc, infoFooter, infoHandleError} p errors =
helpInfo Info {infoHeader, infoDesc, infoFooter, infoHandleError, infoWidthMax} p errors =
List.intercalate "\n\n" $ catMaybes
[ infoHeader
, fmap (List.intercalate "\n" . splitWords 50) infoDesc
, Just (helpDoc p)
, fmap (List.intercalate "\n" . splitWords 50) infoFooter
, fmap (List.intercalate "\n" . splitWords infoWidthMax) infoDesc
, Just (helpDoc infoWidthMax p)
, fmap (List.intercalate "\n" . splitWords infoWidthMax) infoFooter
] ++ helpErrors infoHandleError errors

-- | A pretty-printed list of recognized environment variables suitable for usage messages
helpDoc :: Parser e a -> String
helpDoc p =
List.intercalate "\n" ("Available environment variables:\n" : helpParserDoc p)
helpDoc :: Int -> Parser e a -> String
helpDoc widthMax p =
List.intercalate "\n" ("Available environment variables:\n" : helpParserDoc widthMax p)

helpParserDoc :: Parser e a -> [String]
helpParserDoc =
concat . Map.elems . foldAlt (\v -> Map.singleton (varfName v) (helpVarfDoc v)) . unParser
helpParserDoc :: Int -> Parser e a -> [String]
helpParserDoc widthMax =
concat . Map.elems . foldAlt (\v -> Map.singleton (varfName v) (helpVarfDoc widthMax v)) . unParser

helpVarfDoc :: VarF e a -> [String]
helpVarfDoc VarF {varfName, varfHelp, varfHelpDef} =
helpVarfDoc :: Int -> VarF e a -> [String]
helpVarfDoc widthMax VarF {varfName, varfHelp, varfHelpDef} =
case varfHelp of
Nothing -> [indent 2 varfName]
Nothing -> [indent vo varfName]
Just h
| k > 15 -> indent 2 varfName : map (indent 25) (splitWords 30 t)
| k > nameWidthMax ->
indent vo varfName : map (indent ho) (splitWords (widthMax - ho) t)
| otherwise ->
case zipWith indent (23 - k : repeat 25) (splitWords 30 t) of
(x : xs) -> (indent 2 varfName ++ x) : xs
[] -> [indent 2 varfName]
where k = length varfName
t = maybe h (\s -> h ++ " (default: " ++ s ++")") varfHelpDef
case zipWith indent (ho - vo - k : repeat ho) (splitWords (widthMax - ho) t) of
(x : xs) -> (indent vo varfName ++ x) : xs
[] -> [indent vo varfName]
where
k = length varfName
t = maybe h (\s -> h ++ " (default: " ++ s ++")") varfHelpDef
where
-- The longest variable name that fits the compact view.
nameWidthMax = ho - vo - 1 {- the space between the variable name and the help text -}
vo = 2 -- variable name offset
ho = 25 -- help text offset

splitWords :: Int -> String -> [String]
splitWords n =
Expand Down Expand Up @@ -87,6 +95,7 @@ data Info e = Info
, infoDesc :: Maybe String
, infoFooter :: Maybe String
, infoHandleError :: ErrorHandler e
, infoWidthMax :: Int
}

-- | Given a variable name and an error value, try to produce a useful error message
Expand All @@ -98,6 +107,7 @@ defaultInfo = Info
, infoDesc = Nothing
, infoFooter = Nothing
, infoHandleError = defaultErrorHandler
, infoWidthMax = 80
}

-- | Set the help text header (it usually includes the application's name and version)
Expand All @@ -112,6 +122,12 @@ desc h i = i {infoDesc=Just h}
footer :: String -> Info e -> Info e
footer h i = i {infoFooter=Just h}

-- | Set the max info width.
--
-- /Note:/ It will be set to 26 columns if a smaller value is passed.
widthMax :: Int -> Info e -> Info e
widthMax n i = i {infoWidthMax=max 26 n}

-- | An error handler
handleError :: ErrorHandler e -> Info x -> Info e
handleError handler i = i {infoHandleError=handler}
Expand Down