diff --git a/hledger-lib/Hledger/Utils/IO.hs b/hledger-lib/Hledger/Utils/IO.hs index 4a03c388e..6240eacfa 100644 --- a/hledger-lib/Hledger/Utils/IO.hs +++ b/hledger-lib/Hledger/Utils/IO.hs @@ -22,6 +22,11 @@ module Hledger.Utils.IO ( -- * Viewing with pager pager, + -- * Terminal size + getTerminalHeightWidth, + getTerminalHeight, + getTerminalWidth, + -- * Command line arguments progArgs, outputFileOption, @@ -31,10 +36,31 @@ module Hledger.Utils.IO ( colorOption, useColorOnStdout, useColorOnStderr, + Color(..), + ColorIntensity(..), color, bgColor, colorB, bgColorB, + bold', + faint', + black', + red', + green', + yellow', + blue', + magenta', + cyan', + white', + brightBlack', + brightRed', + brightGreen', + brightYellow', + brightBlue', + brightMagenta', + brightCyan', + brightWhite', + rgb', terminalIsLight, terminalLightness, terminalFgColor, @@ -73,10 +99,12 @@ import qualified Data.Text.Lazy.Builder as TB import Data.Time.Clock (getCurrentTime) import Data.Time.LocalTime (LocalTime, ZonedTime, getCurrentTimeZone, utcToLocalTime, utcToZonedTime) -import Data.Word (Word16) +import Data.Word (Word8, Word16) import Language.Haskell.TH.Syntax (Q, Exp) -import System.Console.ANSI - (Color,ColorIntensity,ConsoleLayer(..), SGR(..), hSupportsANSIColor, setSGRCode, getLayerColor) +import String.ANSI +import System.Console.ANSI (Color(..),ColorIntensity(..), + ConsoleLayer(..), SGR(..), hSupportsANSIColor, setSGRCode, getLayerColor) +import System.Console.Terminal.Size (Window (Window), size) import System.Directory (getHomeDirectory) import System.Environment (getArgs, lookupEnv) import System.FilePath (isRelative, ()) @@ -138,6 +166,19 @@ pager s = do (if dumbterm then putStrLn else printOrPage . T.pack) s #endif +-- | An alternative to ansi-terminal's getTerminalSize, based on +-- the more robust-looking terminal-size package. +-- Tries to get stdout's terminal's current height and width. +getTerminalHeightWidth :: IO (Maybe (Int,Int)) +getTerminalHeightWidth = fmap (fmap unwindow) size + where unwindow (Window h w) = (h,w) + +getTerminalHeight :: IO (Maybe Int) +getTerminalHeight = fmap fst <$> getTerminalHeightWidth + +getTerminalWidth :: IO (Maybe Int) +getTerminalWidth = fmap snd <$> getTerminalHeightWidth + -- Command line arguments -- | The command line arguments that were used at program startup. @@ -175,6 +216,67 @@ hasOutputFile = outputFileOption `notElem` [Nothing, Just "-"] -- ANSI colour +ifAnsi f = if useColorOnStdout then f else id + +-- | Versions of some of text-ansi's string colors/styles which are more careful +-- to not print junk onscreen. These use our useColorOnStdout. +bold' :: String -> String +bold' = ifAnsi bold + +faint' :: String -> String +faint' = ifAnsi faint + +black' :: String -> String +black' = ifAnsi black + +red' :: String -> String +red' = ifAnsi red + +green' :: String -> String +green' = ifAnsi green + +yellow' :: String -> String +yellow' = ifAnsi yellow + +blue' :: String -> String +blue' = ifAnsi blue + +magenta' :: String -> String +magenta' = ifAnsi magenta + +cyan' :: String -> String +cyan' = ifAnsi cyan + +white' :: String -> String +white' = ifAnsi white + +brightBlack' :: String -> String +brightBlack' = ifAnsi brightBlack + +brightRed' :: String -> String +brightRed' = ifAnsi brightRed + +brightGreen' :: String -> String +brightGreen' = ifAnsi brightGreen + +brightYellow' :: String -> String +brightYellow' = ifAnsi brightYellow + +brightBlue' :: String -> String +brightBlue' = ifAnsi brightBlue + +brightMagenta' :: String -> String +brightMagenta' = ifAnsi brightMagenta + +brightCyan' :: String -> String +brightCyan' = ifAnsi brightCyan + +brightWhite' :: String -> String +brightWhite' = ifAnsi brightWhite + +rgb' :: Word8 -> Word8 -> Word8 -> String -> String +rgb' r g b = ifAnsi (rgb r g b) + -- | Read the value of the --color or --colour command line option provided at program startup -- using unsafePerformIO. If this option was not provided, returns the empty string. colorOption :: String @@ -227,6 +329,9 @@ useColorOnHandle h = unsafePerformIO $ do || (coloroption `notElem` ["never","no"] && not no_color && supports_color) -- | Wrap a string in ANSI codes to set and reset foreground colour. +-- ColorIntensity is @Dull@ or @Vivid@ (ie normal and bold). +-- Color is one of @Black@, @Red@, @Green@, @Yellow@, @Blue@, @Magenta@, @Cyan@, @White@. +-- Eg: @color Dull Red "text"@. color :: ColorIntensity -> Color -> String -> String color int col s = setSGRCode [SetColor Foreground int col] ++ s ++ setSGRCode [] diff --git a/hledger-lib/package.yaml b/hledger-lib/package.yaml index 690fe2ce7..ce9817b4d 100644 --- a/hledger-lib/package.yaml +++ b/hledger-lib/package.yaml @@ -67,6 +67,7 @@ dependencies: - tasty >=1.2.3 - tasty-hunit >=0.10.0.2 - template-haskell +- terminal-size >=0.3.3 - text >=1.2 - text-ansi >=0.2.1 - time >=1.5 diff --git a/hledger/Hledger/Cli/Commands.hs b/hledger/Hledger/Cli/Commands.hs index 9517b7e49..00355761d 100644 --- a/hledger/Hledger/Cli/Commands.hs +++ b/hledger/Hledger/Cli/Commands.hs @@ -163,9 +163,6 @@ accent highlightAddon = id --- More careful version of bold -bold' = if useColorOnStdout then bold else id - -- | The commands list, showing command names, standard aliases, -- and short descriptions. This is modified at runtime, as follows: -- diff --git a/stack8.10.yaml b/stack8.10.yaml index 664554e0e..491901bbd 100644 --- a/stack8.10.yaml +++ b/stack8.10.yaml @@ -16,6 +16,7 @@ extra-deps: # for hledger-lib: - doctest-0.20.0 - ansi-terminal-0.11.4 +- terminal-size-0.3.4 - text-ansi-0.2.1 - text-builder-0.6.7 - text-builder-dev-0.3.3.2