2{-# LANGUAGE DeriveDataTypeable #-}
3{-# LANGUAGE FlexibleContexts, FlexibleInstances #-}
4{-# LANGUAGE TypeOperators #-}
5{-# LANGUAGE ScopedTypeVariables #-}
6-- | A 'Config' can be provided to mkVty to customize the applications use of vty. A config file can
7-- be used to customize vty for a user's system.
9-- The 'Config' provided is mappend'd to 'Config's loaded from @'getAppUserDataDirectory'/config@
10-- and @$VTY_CONFIG_FILE@. The @$VTY_CONFIG_FILE@ takes precedence over the @config@ file or the
11-- application provided 'Config'.
13-- Lines in config files that fail to parse are ignored. Later entries take precedence over
19-- string := \"\\\"\" chars+ \"\\\"\"
32-- The value of the environment variable @VTY_DEBUG_LOG@ is equivalent to a debugLog entry at the
33-- end of the last config file.
42-- \"map\" term string key modifier_list
44-- key := KEsc | KChar Char | KBS ... (same as 'Key')
45-- modifier_list := \"[\" modifier+ \"]\"
46-- modifier := MShift | MCtrl | MMeta | MAlt
47-- term := "_" | string
50-- EG: If the contents are
53-- map _ \"\\ESC[B\" KUp []
54-- map _ \"\\ESC[1;3B\" KDown [MAlt]
55-- map \"xterm\" \"\\ESC[D\" KLeft []
58-- Then the bytes @\"\\ESC[B\"@ will result in the KUp event on all terminals. The bytes
59-- @\"\\ESC[1;3B\"@ will result in the event KDown with the MAlt modifier on all terminals.
60-- The bytes @\"\\ESC[D\"@ will result in the KLeft event when @TERM@ is @xterm@.
62-- If a debug log is requested then vty will output the current input table to the log in the above
65-- Set VTY_DEBUG_LOG. Run vty. Check debug log for incorrect mappings. Add corrected mappings to
68module Graphics.Vty.Config
71 , VtyConfigurationError(..)
79#if __GLASGOW_HASKELL__ > 704
82import Prelude hiding (catch)
85import Control.Applicative hiding (many)
87import Control.Exception (catch, IOException, Exception(..), throwIO)
88import Control.Monad (liftM, guard, void)
90import qualified Data.ByteString as BS
93import Data.Typeable (Typeable)
95import Graphics.Vty.Input.Events
99import System.Directory (getAppUserDataDirectory)
100import System.Posix.Env (getEnv)
101import System.Posix.IO (stdInput, stdOutput)
102import System.Posix.Types (Fd(..))
104import Text.Parsec hiding ((<|>))
105import Text.Parsec.Token ( GenLanguageDef(..) )
106import qualified Text.Parsec.Token as P
108-- | Type of errors that can be thrown when configuring VTY
109data VtyConfigurationError
110 = VtyMissingTermEnvVar -- ^ TERM environment variable not set
111 deriving (Show, Eq, Typeable)
113instance Exception VtyConfigurationError where
114#if MIN_VERSION_base(4,8,0)
115 displayException VtyMissingTermEnvVar = "TERM environment variable not set"
118-- | Mappings from input bytes to event in the order specified. Later entries take precedence over
119-- earlier in the case multiple entries have the same byte string.
120type InputMap = [(Maybe String, String, Event)]
124 -- | The default is 1 character.
126 -- | The default is 100 milliseconds, 0.1 seconds.
128 -- | The default is False.
129 , mouseMode :: Maybe Bool
130 -- | The default is False.
131 , bracketedPasteMode :: Maybe Bool
132 -- | Debug information is appended to this file if not Nothing.
133 , debugLog :: Maybe FilePath
134 -- | The (input byte, output event) pairs extend the internal input table of VTY and the table
137 -- See "Graphics.Vty.Config" module documentation for documentation of the @map@ directive.
138 , inputMap :: InputMap
139 -- | The input file descriptor to use. The default is 'System.Posix.IO.stdInput'
140 , inputFd :: Maybe Fd
141 -- | The output file descriptor to use. The default is 'System.Posix.IO.stdOutput'
142 , outputFd :: Maybe Fd
143 -- | The terminal name used to look up terminfo capabilities.
144 -- The default is the value of the TERM environment variable.
145 , termName :: Maybe String
146 } deriving (Show, Eq)
148instance Default Config where
151instance Monoid Config where
155 , mouseMode = Nothing
156 , bracketedPasteMode = Nothing
163 mappend c0 c1 = Config
164 -- latter config takes priority for everything but inputMap
165 { vmin = vmin c1 <|> vmin c0
166 , vtime = vtime c1 <|> vtime c0
167 , mouseMode = mouseMode c1
168 , bracketedPasteMode = bracketedPasteMode c1
169 , debugLog = debugLog c1 <|> debugLog c0
170 , inputMap = inputMap c0 <> inputMap c1
171 , inputFd = inputFd c1 <|> inputFd c0
172 , outputFd = outputFd c1 <|> outputFd c0
173 , termName = termName c1 <|> termName c0
176-- | Config from @'getAppUserDataDirectory'/config@ and @$VTY_CONFIG_FILE@
177userConfig :: IO Config
179 configFile <- (mappend <$> getAppUserDataDirectory "vty" <*> pure "/config") >>= parseConfigFile
180 overrideConfig <- maybe (return def) parseConfigFile =<< getEnv "VTY_CONFIG_FILE"
181 let base = configFile <> overrideConfig
182 mappend base <$> overrideEnvConfig
184overrideEnvConfig :: IO Config
185overrideEnvConfig = do
186 d <- getEnv "VTY_DEBUG_LOG"
187 return $ def { debugLog = d }
189-- | Configures VTY using defaults suitable for terminals. This action
190-- can raise 'VtyConfigurationError'.
191standardIOConfig :: IO Config
195 Nothing -> throwIO VtyMissingTermEnvVar
199 , mouseMode = Just False
200 , bracketedPasteMode = Just False
202 , inputFd = Just stdInput
203 , outputFd = Just stdOutput
207parseConfigFile :: FilePath -> IO Config
208parseConfigFile path = do
209 catch (runParseConfig path <$> BS.readFile path)
210 (\(_ :: IOException) -> return def)
212runParseConfig :: String -> BS.ByteString -> Config
213runParseConfig name cfgTxt =
214 case runParser parseConfig () name cfgTxt of
218------------------------------------------------------------------------
220type Parser = Parsec BS.ByteString ()
222-- I tried to use the haskellStyle here but that was specialized (without requirement?) to the
223-- String stream type.
224configLanguage :: Monad m => P.GenLanguageDef BS.ByteString () m
225configLanguage = LanguageDef
226 { commentStart = "{-"
229 , nestedComments = True
230 , identStart = letter <|> char '_'
231 , identLetter = alphaNum <|> oneOf "_'"
232 , opStart = opLetter configLanguage
233 , opLetter = oneOf ":!#$%&*+./<=>?@\\^|-~"
234 , reservedOpNames = []
236 , caseSensitive = True
239configLexer :: Monad m => P.GenTokenParser BS.ByteString () m
240configLexer = P.makeTokenParser configLanguage
242mapDecl :: Parser Config
244 "map" <- P.identifier configLexer
245 termIdent <- (char '_' >> P.whiteSpace configLexer >> return Nothing)
246 <|> (Just <$> P.stringLiteral configLexer)
247 bytes <- P.stringLiteral configLexer
249 modifiers <- parseValue
250 return def { inputMap = [(termIdent, bytes, EvKey key modifiers)] }
252debugLogDecl :: Parser Config
254 "debugLog" <- P.identifier configLexer
255 path <- P.stringLiteral configLexer
256 return def { debugLog = Just path }
258ignoreLine :: Parser ()
259ignoreLine = void $ manyTill anyChar newline
261parseConfig :: Parser Config
262parseConfig = liftM mconcat $ many $ do
263 P.whiteSpace configLexer
264 let directives = [try mapDecl, try debugLogDecl]
265 choice directives <|> (ignoreLine >> return def)
267class Parse a where parseValue :: Parser a
268instance Parse Char where parseValue = P.charLiteral configLexer
269instance Parse Int where parseValue = fromInteger <$> P.natural configLexer
270instance Parse Key where parseValue = genericParse
271instance Parse Modifier where parseValue = genericParse
272instance Parse a => Parse [a] where
273 parseValue = P.brackets configLexer
274 (parseValue `sepBy` P.symbol configLexer ",")
276------------------------------------------------------------------------
277-- Derived parser for ADTs via generics
278------------------------------------------------------------------------
280genericParse :: (Generic a, GParse (Rep a)) => Parser a
281genericParse = to <$> gparse
283class GParse f where gparse :: Parser (f a)
284instance GParse f => GParse (M1 S i f) where gparse = M1 <$> gparse
285instance GParse U1 where gparse = return U1
286instance Parse a => GParse (K1 i a) where gparse = K1 <$> parseValue
288instance (GParse f, GParse g) => GParse (f :*: g) where
289 gparse = (:*:) <$> gparse <*> gparse
291instance GParseAlts f => GParse (M1 D i f) where
293 do con <- P.identifier configLexer
294 M1 <$> gparseAlts con
296------------------------------------------------------------------------
298class GParseAlts f where
299 gparseAlts :: String -> Parser (f a)
301instance (Constructor i, GParse f) => GParseAlts (M1 C i f) where
303 do guard (con == conName (M1 Nothing :: C1 i Maybe a))
306instance (GParseAlts f, GParseAlts g) => GParseAlts (f :+: g) where
307 gparseAlts con = L1 <$> gparseAlts con <|> R1 <$> gparseAlts con
309instance GParseAlts V1 where gparseAlts _ = fail "GParse: V1"
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list