dev: Hledger.Utils.IO colour helpers: cleanup
This commit is contained in:
parent
75ff6c8218
commit
9c81bb2a06
@ -40,20 +40,13 @@ module Hledger.Utils.IO (
|
|||||||
parseYNA,
|
parseYNA,
|
||||||
YNA(..),
|
YNA(..),
|
||||||
|
|
||||||
-- * ANSI color
|
-- * ANSI color/styles
|
||||||
|
-- ** hledger-specific
|
||||||
|
colorOption,
|
||||||
useColorOnStdout,
|
useColorOnStdout,
|
||||||
useColorOnStderr,
|
useColorOnStderr,
|
||||||
colorOption,
|
|
||||||
useColorOnStdoutUnsafe,
|
useColorOnStdoutUnsafe,
|
||||||
useColorOnStderrUnsafe,
|
useColorOnStderrUnsafe,
|
||||||
-- XXX needed for using color/bgColor/colorB/bgColorB, but clashing with UIUtils:
|
|
||||||
-- Color(..),
|
|
||||||
-- ColorIntensity(..),
|
|
||||||
color,
|
|
||||||
bgColor,
|
|
||||||
colorB,
|
|
||||||
bgColorB,
|
|
||||||
--
|
|
||||||
bold',
|
bold',
|
||||||
faint',
|
faint',
|
||||||
black',
|
black',
|
||||||
@ -73,6 +66,16 @@ module Hledger.Utils.IO (
|
|||||||
brightCyan',
|
brightCyan',
|
||||||
brightWhite',
|
brightWhite',
|
||||||
rgb',
|
rgb',
|
||||||
|
|
||||||
|
-- ** Generic
|
||||||
|
-- XXX Types used with color/bgColor/colorB/bgColorB,
|
||||||
|
-- not re-exported because clashing with UIUtils:
|
||||||
|
-- Color(..),
|
||||||
|
-- ColorIntensity(..),
|
||||||
|
color,
|
||||||
|
bgColor,
|
||||||
|
colorB,
|
||||||
|
bgColorB,
|
||||||
terminalIsLight,
|
terminalIsLight,
|
||||||
terminalLightness,
|
terminalLightness,
|
||||||
terminalFgColor,
|
terminalFgColor,
|
||||||
@ -358,8 +361,6 @@ parseYNA s
|
|||||||
| otherwise = error' $ "value should be one of " <> (intercalate ", " ["y","yes","n","no","a","auto"])
|
| otherwise = error' $ "value should be one of " <> (intercalate ", " ["y","yes","n","no","a","auto"])
|
||||||
where l = map toLower s
|
where l = map toLower s
|
||||||
|
|
||||||
-- Command line arguments
|
|
||||||
|
|
||||||
-- | The command line arguments that were used at program startup.
|
-- | The command line arguments that were used at program startup.
|
||||||
-- Uses unsafePerformIO.
|
-- Uses unsafePerformIO.
|
||||||
{-# NOINLINE progArgs #-}
|
{-# NOINLINE progArgs #-}
|
||||||
@ -385,16 +386,55 @@ hasOutputFile = do
|
|||||||
Just "-" -> False
|
Just "-" -> False
|
||||||
_ -> True
|
_ -> True
|
||||||
|
|
||||||
-- ANSI colour
|
|
||||||
|
|
||||||
|
-- ANSI colour/style helpers. Some of these use unsafePerformIO to read info.
|
||||||
|
|
||||||
|
-- hledger-specific:
|
||||||
|
|
||||||
|
-- | Get the value of the rightmost --color or --colour option from the program's command line arguments.
|
||||||
|
colorOption :: IO YNA
|
||||||
|
colorOption = maybe Auto parseYNA <$> getOpt ["color","colour"]
|
||||||
|
|
||||||
|
-- | Should ANSI color and styles be used with this output handle ?
|
||||||
|
-- Considers colorOption, the NO_COLOR environment variable, and hSupportsANSIColor.
|
||||||
|
useColorOnHandle :: Handle -> IO Bool
|
||||||
|
useColorOnHandle h = do
|
||||||
|
no_color <- isJust <$> lookupEnv "NO_COLOR"
|
||||||
|
supports_color <- hSupportsANSIColor h
|
||||||
|
yna <- colorOption
|
||||||
|
return $ yna==Yes || (yna==Auto && not no_color && supports_color)
|
||||||
|
|
||||||
|
-- | Should ANSI color and styles be used for standard output ?
|
||||||
|
-- Considers useColorOnHandle stdout and whether there's an --output-file option.
|
||||||
|
useColorOnStdout :: IO Bool
|
||||||
|
useColorOnStdout = do
|
||||||
|
nooutputfile <- not <$> hasOutputFile
|
||||||
|
usecolor <- useColorOnHandle stdout
|
||||||
|
return $ nooutputfile && usecolor
|
||||||
|
|
||||||
|
-- | Should ANSI color and styles be used for standard error output ?
|
||||||
|
-- Considers useColorOnHandle stderr; is not affected by an --output-file option.
|
||||||
|
useColorOnStderr :: IO Bool
|
||||||
|
useColorOnStderr = useColorOnHandle stderr
|
||||||
|
|
||||||
|
-- | Like useColorOnStdout, but using unsafePerformIO. Useful eg in low-level debug code.
|
||||||
|
-- Sticky in GHCI, may not be affected by --color in a config file, etc.
|
||||||
|
useColorOnStdoutUnsafe :: Bool
|
||||||
|
useColorOnStdoutUnsafe = unsafePerformIO useColorOnStdout
|
||||||
|
|
||||||
|
-- | Like useColorOnStdoutUnsafe, but for stderr.
|
||||||
|
useColorOnStderrUnsafe :: Bool
|
||||||
|
useColorOnStderrUnsafe = unsafePerformIO useColorOnStderr
|
||||||
|
|
||||||
-- Detect whether ANSI should be used on stdout using useColorOnStdoutUnsafe,
|
-- Detect whether ANSI should be used on stdout using useColorOnStdoutUnsafe,
|
||||||
-- and if so prepend and append the given SGR codes to a string.
|
-- and if so prepend and append the given SGR codes to a string.
|
||||||
-- Currently used in a few places (eg: the commands list, the demo command, the recentassertions error message.)
|
-- Currently used in a few places (the commands list, the demo command, the recentassertions error message).
|
||||||
-- This tends to get stuck on or off in GHCI,
|
-- This tends to get stuck on or off in GHCI until reloaded,
|
||||||
-- respects the command line --color if compiled,
|
-- respects --color on the command line if the program is compiled,
|
||||||
-- and ignores the config file.
|
-- and ignores --color in the config file.
|
||||||
ansiWrap :: SGRString -> SGRString -> String -> String
|
ansiWrapUnsafe :: SGRString -> SGRString -> String -> String
|
||||||
ansiWrap pre post s = if useColorOnStdoutUnsafe then pre<>s<>post else s
|
ansiWrapUnsafe pre post s = if useColorOnStdoutUnsafe then pre<>s<>post else s
|
||||||
|
|
||||||
type SGRString = String
|
type SGRString = String
|
||||||
|
|
||||||
@ -422,106 +462,63 @@ sgrrgb r g b = setSGRCode [SetRGBColor Foreground $ sRGB r g b]
|
|||||||
|
|
||||||
-- | Set various ANSI styles/colours in a string, only if useColorOnStdoutUnsafe says we should.
|
-- | Set various ANSI styles/colours in a string, only if useColorOnStdoutUnsafe says we should.
|
||||||
bold' :: String -> String
|
bold' :: String -> String
|
||||||
bold' = ansiWrap sgrbold sgrnormal
|
bold' = ansiWrapUnsafe sgrbold sgrnormal
|
||||||
|
|
||||||
faint' :: String -> String
|
faint' :: String -> String
|
||||||
faint' = ansiWrap sgrfaint sgrnormal
|
faint' = ansiWrapUnsafe sgrfaint sgrnormal
|
||||||
|
|
||||||
black' :: String -> String
|
black' :: String -> String
|
||||||
black' = ansiWrap sgrblack sgrresetfg
|
black' = ansiWrapUnsafe sgrblack sgrresetfg
|
||||||
|
|
||||||
red' :: String -> String
|
red' :: String -> String
|
||||||
red' = ansiWrap sgrred sgrresetfg
|
red' = ansiWrapUnsafe sgrred sgrresetfg
|
||||||
|
|
||||||
green' :: String -> String
|
green' :: String -> String
|
||||||
green' = ansiWrap sgrgreen sgrresetfg
|
green' = ansiWrapUnsafe sgrgreen sgrresetfg
|
||||||
|
|
||||||
yellow' :: String -> String
|
yellow' :: String -> String
|
||||||
yellow' = ansiWrap sgryellow sgrresetfg
|
yellow' = ansiWrapUnsafe sgryellow sgrresetfg
|
||||||
|
|
||||||
blue' :: String -> String
|
blue' :: String -> String
|
||||||
blue' = ansiWrap sgrblue sgrresetfg
|
blue' = ansiWrapUnsafe sgrblue sgrresetfg
|
||||||
|
|
||||||
magenta' :: String -> String
|
magenta' :: String -> String
|
||||||
magenta' = ansiWrap sgrmagenta sgrresetfg
|
magenta' = ansiWrapUnsafe sgrmagenta sgrresetfg
|
||||||
|
|
||||||
cyan' :: String -> String
|
cyan' :: String -> String
|
||||||
cyan' = ansiWrap sgrcyan sgrresetfg
|
cyan' = ansiWrapUnsafe sgrcyan sgrresetfg
|
||||||
|
|
||||||
white' :: String -> String
|
white' :: String -> String
|
||||||
white' = ansiWrap sgrwhite sgrresetfg
|
white' = ansiWrapUnsafe sgrwhite sgrresetfg
|
||||||
|
|
||||||
brightBlack' :: String -> String
|
brightBlack' :: String -> String
|
||||||
brightBlack' = ansiWrap sgrbrightblack sgrresetfg
|
brightBlack' = ansiWrapUnsafe sgrbrightblack sgrresetfg
|
||||||
|
|
||||||
brightRed' :: String -> String
|
brightRed' :: String -> String
|
||||||
brightRed' = ansiWrap sgrbrightred sgrresetfg
|
brightRed' = ansiWrapUnsafe sgrbrightred sgrresetfg
|
||||||
|
|
||||||
brightGreen' :: String -> String
|
brightGreen' :: String -> String
|
||||||
brightGreen' = ansiWrap sgrbrightgreen sgrresetfg
|
brightGreen' = ansiWrapUnsafe sgrbrightgreen sgrresetfg
|
||||||
|
|
||||||
brightYellow' :: String -> String
|
brightYellow' :: String -> String
|
||||||
brightYellow' = ansiWrap sgrbrightyellow sgrresetfg
|
brightYellow' = ansiWrapUnsafe sgrbrightyellow sgrresetfg
|
||||||
|
|
||||||
brightBlue' :: String -> String
|
brightBlue' :: String -> String
|
||||||
brightBlue' = ansiWrap sgrbrightblue sgrresetfg
|
brightBlue' = ansiWrapUnsafe sgrbrightblue sgrresetfg
|
||||||
|
|
||||||
brightMagenta' :: String -> String
|
brightMagenta' :: String -> String
|
||||||
brightMagenta' = ansiWrap sgrbrightmagenta sgrresetfg
|
brightMagenta' = ansiWrapUnsafe sgrbrightmagenta sgrresetfg
|
||||||
|
|
||||||
brightCyan' :: String -> String
|
brightCyan' :: String -> String
|
||||||
brightCyan' = ansiWrap sgrbrightcyan sgrresetfg
|
brightCyan' = ansiWrapUnsafe sgrbrightcyan sgrresetfg
|
||||||
|
|
||||||
brightWhite' :: String -> String
|
brightWhite' :: String -> String
|
||||||
brightWhite' = ansiWrap sgrbrightwhite sgrresetfg
|
brightWhite' = ansiWrapUnsafe sgrbrightwhite sgrresetfg
|
||||||
|
|
||||||
rgb' :: Float -> Float -> Float -> String -> String
|
rgb' :: Float -> Float -> Float -> String -> String
|
||||||
rgb' r g b = ansiWrap (sgrrgb r g b) sgrresetfg
|
rgb' r g b = ansiWrapUnsafe (sgrrgb r g b) sgrresetfg
|
||||||
|
|
||||||
-- | Should ANSI color & styling be used for standard output ?
|
-- Generic:
|
||||||
-- Considers useColorOnHandle stdout and whether there's an --output-file.
|
|
||||||
useColorOnStdout :: IO Bool
|
|
||||||
useColorOnStdout = do
|
|
||||||
nooutputfile <- not <$> hasOutputFile
|
|
||||||
usecolor <- useColorOnHandle stdout
|
|
||||||
return $ nooutputfile && usecolor
|
|
||||||
|
|
||||||
-- traceWith (("USE COLOR ON STDOUT: "<>).show) <$>
|
|
||||||
|
|
||||||
useColorOnStderr :: IO Bool
|
|
||||||
useColorOnStderr = useColorOnHandle stderr
|
|
||||||
|
|
||||||
-- | Should ANSI color & styling be used with this output handle ?
|
|
||||||
-- Considers hSupportsANSIColor stdout, whether NO_COLOR is defined,
|
|
||||||
-- and the rightmost --color option.
|
|
||||||
useColorOnHandle :: Handle -> IO Bool
|
|
||||||
useColorOnHandle h = do
|
|
||||||
no_color <- isJust <$> lookupEnv "NO_COLOR"
|
|
||||||
supports_color <- hSupportsANSIColor h
|
|
||||||
yna <- colorOption
|
|
||||||
return $ yna==Yes || (yna==Auto && not no_color && supports_color)
|
|
||||||
|
|
||||||
colorOption :: IO YNA
|
|
||||||
colorOption = maybe Auto parseYNA <$> getOpt ["color","colour"]
|
|
||||||
|
|
||||||
-- | Check the IO environment to see if ANSI colour codes should be used on stdout.
|
|
||||||
-- This is done using unsafePerformIO so it can be used anywhere, eg in
|
|
||||||
-- low-level debug utilities, which should be ok since we are just reading.
|
|
||||||
-- The logic is: use color if
|
|
||||||
-- the program was started with --color=yes|always
|
|
||||||
-- or (
|
|
||||||
-- the program was not started with --color=no|never
|
|
||||||
-- and a NO_COLOR environment variable is not defined
|
|
||||||
-- and stdout supports ANSI color
|
|
||||||
-- and -o/--output-file was not used, or its value is "-"
|
|
||||||
-- ).
|
|
||||||
useColorOnStdoutUnsafe :: Bool
|
|
||||||
useColorOnStdoutUnsafe = unsafePerformIO useColorOnStdout
|
|
||||||
|
|
||||||
-- | Like useColorOnStdoutUnsafe, but checks for ANSI color support on stderr,
|
|
||||||
-- and is not affected by -o/--output-file.
|
|
||||||
useColorOnStderrUnsafe :: Bool
|
|
||||||
useColorOnStderrUnsafe = unsafePerformIO useColorOnStderr
|
|
||||||
|
|
||||||
-- | Wrap a string in ANSI codes to set and reset foreground colour.
|
-- | Wrap a string in ANSI codes to set and reset foreground colour.
|
||||||
-- ColorIntensity is @Dull@ or @Vivid@ (ie normal and bold).
|
-- ColorIntensity is @Dull@ or @Vivid@ (ie normal and bold).
|
||||||
@ -568,7 +565,7 @@ terminalFgColor = terminalColor Foreground
|
|||||||
terminalColor :: ConsoleLayer -> Maybe (RGB Float)
|
terminalColor :: ConsoleLayer -> Maybe (RGB Float)
|
||||||
terminalColor = unsafePerformIO . getLayerColor'
|
terminalColor = unsafePerformIO . getLayerColor'
|
||||||
|
|
||||||
-- A version of getLayerColor that is less likely to leak escape sequences to output,
|
-- A version of ansi-terminal's getLayerColor that is less likely to leak escape sequences to output,
|
||||||
-- and that returns a RGB of Floats (0..1) that is more compatible with the colour package.
|
-- and that returns a RGB of Floats (0..1) that is more compatible with the colour package.
|
||||||
-- This does nothing in a non-interactive context (eg when piping stdout to another command),
|
-- This does nothing in a non-interactive context (eg when piping stdout to another command),
|
||||||
-- inside emacs (emacs shell buffers show the escape sequence for some reason),
|
-- inside emacs (emacs shell buffers show the escape sequence for some reason),
|
||||||
@ -584,6 +581,8 @@ getLayerColor' l = do
|
|||||||
fractionalRGB :: (Fractional a) => RGB Word16 -> RGB a
|
fractionalRGB :: (Fractional a) => RGB Word16 -> RGB a
|
||||||
fractionalRGB (RGB r g b) = RGB (fromIntegral r / 65535) (fromIntegral g / 65535) (fromIntegral b / 65535) -- chatgpt
|
fractionalRGB (RGB r g b) = RGB (fromIntegral r / 65535) (fromIntegral g / 65535) (fromIntegral b / 65535) -- chatgpt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- Errors
|
-- Errors
|
||||||
|
|
||||||
-- | Simpler alias for errorWithoutStackTrace
|
-- | Simpler alias for errorWithoutStackTrace
|
||||||
@ -594,6 +593,8 @@ error' = errorWithoutStackTrace . ("Error: " <>)
|
|||||||
usageError :: String -> a
|
usageError :: String -> a
|
||||||
usageError = error' . (++ " (use -h to see usage)")
|
usageError = error' . (++ " (use -h to see usage)")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- Files
|
-- Files
|
||||||
|
|
||||||
-- | Expand a tilde (representing home directory) at the start of a file path.
|
-- | Expand a tilde (representing home directory) at the start of a file path.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user