-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathModel.hs
242 lines (217 loc) · 7.92 KB
/
Model.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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- |
module Model where
import SharedTypes
import Types
import Control.Applicative
import Control.Concurrent.STM
import Control.Exception
import qualified Control.Lens as Lens
import Control.Monad
import Control.Monad.Reader
import Data.Function
import Data.List
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import Data.Maybe
import Data.Monoid
import Data.Ord
import Data.Text (Text)
import qualified Data.Text as T
import GHCJS.Yesod
import Prelude hiding (pi)
import React
import React.Ace (Ace)
import qualified React.Ace as Ace
import React.Internal
-- | Make the application.
getApp :: IO (App State IO)
getApp =
makeApp StartingState id
-- | Run the loading process.
loadingProcess :: App State m -> IO ()
loadingProcess (App var _ _ _) =
fix (\loop first ->
do result <-
try (call (PollLoading first))
case result of
Left ex ->
case ex of
BadStatusCode status ->
badStatusCode status var
DecodingError msg payload ->
decodingError msg payload var
NoData -> noData var
Right status ->
loadResult loop status var)
True
-- | When a load command came back with a result, handle the status.
loadResult :: (Bool -> IO ()) -> LoadingStatus -> TVar State -> IO ()
loadResult loop status var =
case status of
AmbiguousTargets targets ->
do setTVarIO (ChooseTargetsState (M.fromList (map defaultChosenTarget targets))) var
loop False
LoadOK targets ms ->
do ace <- Ace.getDef
setTVarIO (LoadedState
(defaultLoaded (M.fromList targets)
ms
ace))
var
loop False
LoadFailed errs ->
do setTVarIO (FailedState errs) var
loop False
_ ->
do setTVarIO (LoadingProjectState status) var
loop False
-- | No data from the server.
noData :: TVar State -> IO ()
noData =
setTVarIO (FailedState
[Left (mconcat ["No data from server when calling command. "
,"Please report this as a bug."])])
-- | A JSON decoding error.
decodingError :: String -> Text -> TVar State -> IO ()
decodingError msg payload =
setTVarIO (FailedState
[Left "Unable to decode JSON from server. Consider this a bug."
,Left ("Decoding error was: " <> T.pack msg)
,Left ("Payload was: " <> payload)])
-- | HTTP server probably stopped.
badStatusCode :: Int -> TVar State -> IO ()
badStatusCode status =
setTVarIO (FailedState (concat errors))
where errors =
[[Left "Bad HTTP status code from server."
,Left ("Status code was: " <>
T.pack (show status))]
,[Left "This probably means you stopped the server." | status == 0]]
-- | Jump to place definition on double click.
doubleClicked :: TVar State -> IO ()
doubleClicked var =
do mloaded <- previewTVarIO _LoadedState var
case do l <- mloaded
(fp,_,_,_,_,_) <- _loadedCurrent l
(_,sp,_) <- _loadedTypeInfo l
return (fp,sp) of
Nothing -> return ()
Just (fp,sp) ->
do mloc <- call (GetIdentLocation fp sp)
maybe (return ())
(viewModule var)
mloc
-- | Select the given span and trigger a state update.
select :: (Int,Int) -> Span -> TVar State -> IO ()
select xy sp@(Span sl sc el ec) var =
do mloaded <- previewTVarIO _LoadedState var
case mloaded of
Just l@(Loaded{_loadedCurrent = Just (fp,text',_,_,_,_)}) ->
do unless (noChange (_loadedTypeInfo l))
(do spans <-
call (GetExpTypes fp
(Span sl sc el ec))
setTVarAt _LoadedState
((case sortBy (on thinner fst) spans of
((child,typ):parents) ->
l {_loadedCurrent =
Just (fp,text',sl,sc,el,ec)
,_loadedTypeInfo =
Just (listToMaybe (filter (/= child) (map fst parents))
,child
,typ)
,_loadedMouseXY =
Just xy}
_ ->
let new =
defaultLoaded (_loadedTargets l)
(_loadedModules l)
(_loadedAce l)
in new {_loadedCurrent =
Just (fp,text',sl,sc,el,ec)
,_loadedMouseXY =
Just xy}))
var)
_ -> return ()
where noChange Nothing = False
noChange (Just (_,cur,_)) = cur == sp
-- | Is x thinner than y?
thinner :: Span -> Span -> Ordering
thinner x y =
comparing (if on (==) spanSL x y &&
on (==) spanEL x y
then \(Span _ s _ e) -> e - s
else \(Span s _ e _) -> e - s)
x
y
-- | View a module.
viewModule :: TVar State -> Loc -> IO ()
viewModule var (Loc fp line col) =
do contents <- call (GetModule fp)
modifyTVarIO
_LoadedState
(\l ->
l {_loadedCurrent =
(Just (fp,contents,line,col,line,col))
,_loadedTypeInfo = Nothing
,_loadedMouseXY = Nothing})
var
-- | Expand the current selection.
expandSelection :: TVar State -> IO ()
expandSelection var =
do mloaded <-
previewTVarIO _LoadedState var
case mloaded of
Just (l::Loaded) ->
case (,) <$> _loadedTypeInfo l <*> _loadedMouseXY l of
Just ((mparent,_,_),xy') ->
case mparent of
Nothing -> return ()
Just parent ->
select xy' parent var
Nothing -> return ()
_ -> return ()
--------------------------------------------------------------------------------
-- Defaults
-- | Default loaded state.
defaultLoaded :: Map TargetIdent Bool -> [Text] -> Ace -> Loaded
defaultLoaded targets ms ace =
Loaded {_loadedModules = ms
,_loadedCurrent = Nothing
,_loadedAce = ace
,_loadedTypeInfo = Nothing
,_loadedMouseXY = Nothing
,_loadedTargets = targets}
-- | Determine whether the target should be chosen by default or not.
defaultChosenTarget :: TargetIdent -> (TargetIdent, Bool)
defaultChosenTarget t =
(t
,case t of
LibraryIdent{} -> True
_ -> False)
--------------------------------------------------------------------------------
-- Tvar/lens helpers
setTVarIO :: a -> TVar a -> IO ()
setTVarIO a v = atomically (writeTVar v a)
setTVarAt :: Lens.ASetter' s a -> a -> TVar s -> IO ()
setTVarAt l a v =
atomically
(modifyTVar v
(Lens.set l a))
modifyTVarIO :: Lens.ASetter' s a -> (a -> a) -> TVar s -> IO ()
modifyTVarIO l f v =
atomically
(modifyTVar v
(Lens.over l f))
viewTVarIO :: Lens.Getting a s a -> TVar s -> IO a
viewTVarIO g v =
atomically
(fmap (Lens.view g)
(readTVar v))
previewTVarIO :: Lens.Getting (First a) s a -> TVar s -> IO (Maybe a)
previewTVarIO g v =
atomically
(fmap (Lens.preview g)
(readTVar v))