1 {-# LANGUAGE AllowAmbiguousTypes #-}
2 {-# LANGUAGE OverloadedStrings #-}
3 {-# LANGUAGE Rank2Types #-}
4 {-# LANGUAGE StrictData #-}
5 {-# OPTIONS_GHC -Wno-missing-signatures #-}
6 {-# OPTIONS_GHC -Wno-orphans #-}
7 module Textphile.CLI.Utils where
9 import Control.Applicative (Applicative(..), Alternative(..))
10 import Control.Arrow (left)
11 import Control.Monad (Monad(..), forM_, when)
12 import Control.Monad.Trans.Maybe (MaybeT(..))
13 import Control.Monad.Trans.Except (runExceptT)
14 import Control.Monad.Trans.Reader (Reader, runReader)
16 import Data.Either (Either(..))
17 import Data.Eq (Eq(..))
18 import Data.Foldable (Foldable)
19 import Data.Function (($), (.), id)
20 import Data.Functor ((<$>))
21 import Data.Maybe (Maybe(..), maybe)
22 import Data.Monoid (Monoid(..))
23 import Data.Ord (Ord(..))
24 import Data.Semigroup (Semigroup(..))
25 import Data.Text (Text)
26 import Data.Typeable (Typeable)
27 import Prelude (min, max, (-))
28 import Symantic.CLI as CLI
30 import Text.Show (Show(..))
31 import qualified Control.Monad.Classes as MC
32 import qualified Data.ByteString.Char8 as BS8
33 import qualified Data.ByteString.Lazy.Char8 as BSL8
34 import qualified Data.Char as Char
35 import qualified Data.List as List
36 import qualified Data.Map.Strict as Map
37 import qualified Data.Text as Text
38 import qualified Data.Text.Encoding as T
39 import qualified Symantic.Document as Doc
40 import qualified System.Console.Terminal.Size as Console
41 import qualified System.Directory as IO
42 import qualified System.FilePath as FP
43 import qualified System.IO as IO
44 import qualified System.Posix as Posix
45 import qualified Data.Version as Version
46 import qualified Paths_textphile as Textphile
49 import Textphile.CLI.Lang
52 progname = "textphile"
57 Text.pack (Version.showVersion Textphile.version)
62 api_compact <.> response @Doc <!>
63 api_full <.> response @Doc
65 (api_compact <!> api_full) <.> response @Doc
68 (if full then helps l10n_help_opt_help_compact else id) $
71 (if full then helps l10n_help_opt_help_full else id) $
72 tag "help" (just True)
74 run_help lay = route :!: route
76 route helpInh_full = do
77 width <- Just . maybe 80 (min 80 . max 0 . (\x -> x - 2) . Console.width)
81 runLayout helpInh_full lay
83 -- * Type 'Cfg_Global'
86 { cfg_global_stderr_prepend_newline :: Bool
87 , cfg_global_stderr_prepend_carriage :: Bool
88 , cfg_global_stderr_append_newline :: Bool
89 , cfg_global_verbosity :: Verbosity
90 , cfg_global_lang :: Lang
95 Cfg_Global False False True
96 <$> api_param_verbosity
99 (\v -> Map.findWithDefault
100 (LocaleIn @Langs en_US)
101 (Text.pack $ List.takeWhile (\c -> Char.isAlphaNum c || c == '_') v)
102 (locales @Langs)) <$>
103 toPermDefault "en_US" (env "LANG")
105 -- * Type 'Verbosity'
113 instance IOType Verbosity
114 instance FromSegment Verbosity where
116 "error" -> return $ Right Verbosity_Error
117 "warning" -> return $ Right Verbosity_Warning
118 "info" -> return $ Right Verbosity_Info
119 "debug" -> return $ Right Verbosity_Debug
120 _ -> return $ Left "invalid verbosity"
122 api_param_verbosity =
123 helps l10n_help_opt_verbosity $
124 toPermDefault Verbosity_Info $
126 constant "error" Verbosity_Error `alt`
127 constant "warning" Verbosity_Warning `alt`
128 constant "info" Verbosity_Info `alt`
129 constant "debug" Verbosity_Debug
131 env "TEXTPHILE_VERBOSITY"
133 outputMessage :: MC.MonadExec IO m => Cfg_Global -> Doc -> Doc -> m ()
134 outputMessage Cfg_Global{..} hdr msg =
135 MC.exec @IO $ output $ OnHandle IO.stderr $ (`runReader` loqualize cfg_global_lang) $
136 (if cfg_global_stderr_prepend_newline then Doc.newline else mempty) <>
137 (if cfg_global_stderr_prepend_carriage then "\r" else mempty) <>
139 (if cfg_global_stderr_append_newline then Doc.newline else mempty)
141 outputError :: MC.MonadExec IO m => Cfg_Global -> Doc -> MaybeT m a
142 outputError cfg_global@Cfg_Global{..} msg = do
143 when (Verbosity_Error <= cfg_global_verbosity) $ do
144 outputMessage cfg_global (Doc.redder "ERROR") msg
147 outputWarning :: MC.MonadExec IO m => Cfg_Global -> Doc -> m ()
148 outputWarning cfg_global@Cfg_Global{..} msg = do
149 when (Verbosity_Warning <= cfg_global_verbosity) $ do
150 outputMessage cfg_global (Doc.yellower "WARNING") msg
152 outputInfo :: MC.MonadExec IO m => Cfg_Global -> Doc -> m ()
153 outputInfo cfg_global@Cfg_Global{..} msg = do
154 when (Verbosity_Info <= cfg_global_verbosity) $ do
155 outputMessage cfg_global (Doc.greener "info") msg
157 outputDebug :: MC.MonadExec IO m => Cfg_Global -> Doc -> m ()
158 outputDebug cfg_global@Cfg_Global{..} msg = do
159 when (Verbosity_Debug <= cfg_global_verbosity) $ do
160 outputMessage cfg_global (Doc.magentaer "debug") msg