This repository was archived by the owner on Nov 1, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathTerminal.hs
140 lines (128 loc) · 3.19 KB
/
Terminal.hs
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
module Terminal(setRaw, setCooked, readLine) where
import CCall
import Char
import StdIO
setRaw, setCooked :: IO ()
setRaw = setRC True
setCooked = setRC False
setRC :: Bool -> IO ()
setRC b = ccall set_tty b
-- TODO: TAB-expansion.
-- Read a line from stdin with fancy editing.
-- Args: Prompt, initial line contents, history.
readLine :: String -> String -> [String] -> IO String
readLine prompt start hist =
do
setRaw
hSetBuffering stdout NoBuffering
putStr prompt
s <- readlpr start (reverse hist, "", [])
setCooked
return s
readlpr :: String -> ([String], String, [String]) -> IO String
readlpr start hist =
do
putFmtStr start
readl start (length start) (length start) hist
putFmtStr = putStr . fmtStr
putFmtChar = putStr . fmtChar
lenOf = length . fmtStr
lenOfC = length . fmtChar
fmtStr = concatMap fmtChar
fmtChar c =
if isPrint c then [c] else ['^', toEnum (fromEnum c + fromEnum '@')]
readl :: String -> Int -> Int -> ([String], String, [String]) -> IO String
readl cur len pos hist =
let startOfLine = backwards (lenOf (take pos cur))
eraseLine = do startOfLine; eraseChars (lenOf cur)
endOfLine =
let f n = if n == len then return ()
else do putStr (fmtChar (cur!!n)); f (n+1)
in f pos
eraseChar p =
do
let tl = drop (p+1) cur
let cur' = take p cur ++ tl
putFmtStr tl
putStr (replicate (lenOfC (cur!!p)) ' ')
backwards (lenOf (drop p cur))
readl cur' (len-1) p hist
insChar c =
do
let cur' = insert pos c cur
putFmtStr (drop pos cur')
backwards (lenOf (drop (pos+1) cur'))
readl cur' (len+1) (pos+1) hist
backthis = backwards (lenOfC (cur!!(pos-1)))
in
do
c <- getChar
c' <- arrowCheck c
case c' of
'\n' -> doneit cur
'\r' -> doneit cur
'\b' | pos > 0 ->
do
backthis
eraseChar (pos-1)
'\^D' | pos < len -> eraseChar pos
'\^F' | pos < len ->
do
putFmtChar (cur!!pos)
readl cur len (pos+1) hist
'\^B' | pos > 0 ->
do
backthis
readl cur len (pos-1) hist
'\^A' ->
do
startOfLine
readl cur len 0 hist
'\^E' ->
do
endOfLine
readl cur len len hist
'\^K' ->
do
eraseChars (lenOf (drop pos cur))
readl (take pos cur) pos pos hist
'\^P' ->
case hist of
([], _, _) -> readl cur len pos hist
(p:ps, n, ns) -> do eraseLine; readlpr p (ps, p, n:ns)
'\^N' ->
case hist of
(_, _, []) -> readl cur len pos hist
(ps, p, n:ns) -> do eraseLine; readlpr n (p:ps, n, ns)
'\^Q' ->
do c <- getChar; insChar c
c | isPrint c -> insChar c
_ -> readl cur len pos hist
-- Translate arrow keys to something simple.
arrowCheck :: Char -> IO Char
arrowCheck c@'\ESC' =
do
d <- getChar
if d /= '[' then return c else
do
e <- getChar
case e of
'C' -> return '\^F'
'D' -> return '\^B'
'A' -> return '\^P'
'B' -> return '\^N'
_ -> return c
arrowCheck c = return c
doneit str =
do
putStr "\n"
return str
backwards :: Int -> IO ()
backwards n = sequence (replicate n (putChar '\b'))
eraseChars n =
do
sequence (replicate n (putChar ' '))
backwards n
insert :: Int -> a -> [a] -> [a]
insert 0 x xs = x:xs
insert n x (y:ys) = y : insert (n-1) x ys