{- | 'Prefs' allow the user to customize the line-editing interface. They are read by default from @~/.haskeline@; to override that behavior, use 'readPrefs' and 'runInputTWithPrefs'. Each line of a @.haskeline@ file defines one field of the 'Prefs' datatype; field names are case-insensitive and unparseable lines are ignored. For example: > editMode: Vi > completionType: MenuCompletion > maxhistorysize: Just 40 -} module System.Console.Haskeline.Prefs where import Language.Haskell.TH import Data.Char(isSpace,toLower) import Data.List(foldl') import Control.Exception(handle) data Prefs = Prefs { bellStyle :: !BellStyle, editMode :: !EditMode, maxHistorySize :: !(Maybe Int), completionType :: !CompletionType, completionPaging :: !Bool, -- ^ When listing completion alternatives, only display -- one screen of possibilities at a time. completionPromptLimit :: !(Maybe Int), -- ^ If more than this number of completion -- possibilities are found, then ask before listing -- them. listCompletionsImmediately :: !Bool -- ^ If 'False', completions with multiple possibilities -- will ring the bell and only display them if the user -- presses @TAB@ again. } deriving (Read,Show) data CompletionType = ListCompletion | MenuCompletion deriving (Read,Show) data BellStyle = NoBell | VisualBell | AudibleBell deriving (Show, Read) data EditMode = Vi | Emacs deriving (Show,Read) {- | The default preferences which may be overwritten in the @.haskeline@ file: > defaultPrefs = Prefs {bellStyle = AudibleBell, > maxHistorySize = Nothing, > editMode = Emacs, > completionType = ListCompletion, > completionPaging = True, > completionPromptLimit = Just 100, > listCompletionsImmediately = True > } -} defaultPrefs :: Prefs defaultPrefs = Prefs {bellStyle = AudibleBell, maxHistorySize = Nothing, editMode = Emacs, completionType = ListCompletion, completionPaging = True, completionPromptLimit = Just 100, listCompletionsImmediately = True } mkSettor :: Read a => (a -> Prefs -> Prefs) -> String -> Prefs -> Prefs mkSettor f str = case reads str of [(x,_)] -> f x _ -> id settors :: [(String,String -> Prefs -> Prefs)] settors = $(do DataConI _ _ prefsType _ <- reify 'Prefs TyConI (DataD _ _ _ [RecC _ fields] _) <- reify prefsType x <- newName "x" p <- newName "p" -- settor f => ("f", mkSettor (\x p -> p {f=x})) let settor (f,_,_) = TupE [LitE (StringL (map toLower $ nameBase f)), AppE (VarE 'mkSettor) $ LamE [VarP x,VarP p] $ RecUpdE (VarE p) [(f,VarE x)]] return $ ListE $ map settor fields) -- | Read 'Prefs' from a given file. If there is an error reading the file, -- the 'defaultPrefs' will be returned. readPrefs :: FilePath -> IO Prefs readPrefs file = handle (\_ -> return defaultPrefs) $ do ls <- fmap lines $ readFile file return $ foldl' applyField defaultPrefs ls where applyField p l = case break (==':') l of (name,val) -> case lookup (map toLower $ trimSpaces name) settors of Nothing -> p Just set -> set (drop 1 val) p -- drop initial ":", don't crash if val=="" trimSpaces = dropWhile isSpace . reverse . dropWhile isSpace . reverse