-
Notifications
You must be signed in to change notification settings - Fork 82
Expand file tree
/
Copy pathPrefs.hs
More file actions
144 lines (124 loc) · 5.95 KB
/
Copy pathPrefs.hs
File metadata and controls
144 lines (124 loc) · 5.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
module System.Console.Haskeline.Prefs(
Prefs(..),
defaultPrefs,
readPrefs,
CompletionType(..),
BellStyle(..),
EditMode(..),
HistoryDuplicates(..),
lookupKeyBinding
) where
import Control.Monad.Catch (handle)
import Control.Exception (IOException)
import Data.Char(isSpace,toLower)
import Data.List(foldl')
import qualified Data.Map as Map
import System.Console.Haskeline.Key
{- |
'Prefs' allow the user to customize the terminal-style 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
-}
data Prefs = Prefs { bellStyle :: !BellStyle,
editMode :: !EditMode,
maxHistorySize :: !(Maybe Int),
historyDuplicates :: HistoryDuplicates,
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.
customBindings :: Map.Map Key [Key],
-- (termName, keysequence, key)
customKeySequences :: [(Maybe String, String,Key)],
incAppendHistory :: Bool
-- ^ If 'True' and @historyFile@ is not 'Nothing'
-- flushes command history after every command
}
deriving Show
data CompletionType = ListCompletion | MenuCompletion
deriving (Read,Show)
data BellStyle = NoBell | VisualBell | AudibleBell
deriving (Show, Read)
data EditMode = Vi | Emacs
deriving (Show,Read)
data HistoryDuplicates = AlwaysAdd | IgnoreConsecutive | IgnoreAll
deriving (Show,Read)
-- | The default preferences which may be overwritten in the
-- @.haskeline@ file.
defaultPrefs :: Prefs
defaultPrefs = Prefs {bellStyle = AudibleBell,
maxHistorySize = Just 100,
editMode = Emacs,
completionType = ListCompletion,
completionPaging = True,
completionPromptLimit = Just 100,
listCompletionsImmediately = True,
historyDuplicates = AlwaysAdd,
customBindings = Map.empty,
customKeySequences = [],
incAppendHistory = False
}
mkSettor :: Read a => (a -> Prefs -> Prefs) -> String -> Prefs -> Prefs
mkSettor f str = maybe id f (readMaybe str)
readMaybe :: Read a => String -> Maybe a
readMaybe str = case reads str of
[(x,_)] -> Just x
_ -> Nothing
settors :: [(String, String -> Prefs -> Prefs)]
settors = [("bellstyle", mkSettor $ \x p -> p {bellStyle = x})
,("editmode", mkSettor $ \x p -> p {editMode = x})
,("maxhistorysize", mkSettor $ \x p -> p {maxHistorySize = x})
,("completiontype", mkSettor $ \x p -> p {completionType = x})
,("completionpaging", mkSettor $ \x p -> p {completionPaging = x})
,("completionpromptlimit", mkSettor $ \x p -> p {completionPromptLimit = x})
,("listcompletionsimmediately", mkSettor $ \x p -> p {listCompletionsImmediately = x})
,("historyduplicates", mkSettor $ \x p -> p {historyDuplicates = x})
,("bind", addCustomBinding)
,("keyseq", addCustomKeySequence)
,("incappendhistory", mkSettor $ \x p -> p {incAppendHistory = x})
]
addCustomBinding :: String -> Prefs -> Prefs
addCustomBinding str p = case mapM parseKey (words str) of
Just (k:ks) -> p {customBindings = Map.insert k ks (customBindings p)}
_ -> p
addCustomKeySequence :: String -> Prefs -> Prefs
addCustomKeySequence str = maybe id addKS maybeParse
where
maybeParse :: Maybe (Maybe String, String,Key)
maybeParse = case words str of
[cstr,kstr] -> parseWords Nothing cstr kstr
[term,cstr,kstr] -> parseWords (Just term) cstr kstr
_ -> Nothing
parseWords mterm cstr kstr = do
k <- parseKey kstr
cs <- readMaybe cstr
return (mterm,cs,k)
addKS ks p = p {customKeySequences = ks:customKeySequences p}
lookupKeyBinding :: Key -> Prefs -> [Key]
lookupKeyBinding k = Map.findWithDefault [k] k . customBindings
-- | 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 (\(_::IOException) -> 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