@@ -11,24 +11,39 @@ module Ace.Halogen.Component
11
11
12
12
import Prelude
13
13
14
+ import Control.Coroutine (($$), consumer , Producer (), Consumer (), runProcess )
15
+ import Control.Coroutine.Aff (produce )
14
16
import Control.Monad (when )
15
- import Control.Monad.Aff (Aff (), runAff )
17
+ import Control.Monad.Aff (Aff (), runAff , later' , forkAff )
16
18
import Control.Monad.Aff.AVar (AVAR ())
17
19
import Control.Monad.Eff (Eff ())
20
+ import Control.Monad.Eff.Class (liftEff )
18
21
import Control.Monad.Eff.Random (random , RANDOM ())
19
22
import Control.Monad.Eff.Ref (Ref (), REF (), readRef , writeRef , modifyRef )
23
+ import Control.Monad.Maybe.Trans (MaybeT (..), runMaybeT )
20
24
25
+ import Data.Array as Arr
26
+ import Data.Either (Either (..))
21
27
import Data.Date (nowEpochMilliseconds , Now ())
22
- import Data.Foldable ( traverse_ )
28
+ import Data.Foldable as F
23
29
import Data.Maybe (Maybe (..), maybe )
30
+ import Data.Nullable (toMaybe )
31
+ import Data.Set as Set
24
32
import Data.StrMap (StrMap ())
25
33
import Data.StrMap as Sm
26
34
import Data.Time (Milliseconds (..))
27
35
28
36
import DOM (DOM ())
29
- import DOM.HTML.Types (HTMLElement ())
37
+ import DOM.HTML (window )
38
+ import DOM.HTML.Types (HTMLElement (), htmlDocumentToParentNode )
39
+ import DOM.HTML.Window (document )
40
+ import DOM.Node.ParentNode (querySelectorAll )
41
+ import DOM.Node.Types (NodeList (), Node ())
42
+ import DOM.Node.NodeList as Nl
30
43
31
- import Halogen
44
+ import Halogen hiding (Prop ())
45
+ import Halogen.HTML.Core (Prop (..), attrName )
46
+ import Halogen.HTML.Properties.Indexed (IProp ())
32
47
import Halogen.HTML.Indexed as H
33
48
import Halogen.HTML.Properties.Indexed as P
34
49
@@ -38,6 +53,14 @@ import Ace.Ext.LanguageTools as LanguageTools
38
53
import Ace.Ext.LanguageTools.Completer as Completer
39
54
import Ace.Types
40
55
56
+ import Unsafe.Coerce (unsafeCoerce )
57
+
58
+ dataAceKey :: forall i r . String -> IProp r i
59
+ dataAceKey = unsafeCoerce nonIndexed
60
+ where
61
+ nonIndexed :: String -> Prop i
62
+ nonIndexed = Attr Nothing (attrName " data-acekey" )
63
+
41
64
-- | Effectful knot of autocomplete functions. It's needed because
42
65
-- | `languageTools.addCompleter` is global and adds completer to
43
66
-- | all editors
@@ -50,9 +73,13 @@ foreign import initialized :: Ref Boolean
50
73
-- | autocomplete function
51
74
foreign import focused :: Ref String
52
75
76
+ -- | Stores `data-acekey` of last checked components
77
+ foreign import keys :: Ref (Array String )
78
+
53
79
-- | Get `dataset` property of element
54
80
foreign import dataset
55
- :: forall eff . HTMLElement -> Eff (dom :: DOM | eff ) (StrMap String )
81
+ :: forall eff . Node -> Eff (dom :: DOM | eff ) (StrMap String )
82
+
56
83
57
84
58
85
-- | Take completion function for currently selected component
@@ -79,19 +106,77 @@ setAutocompleteResume (Just Live) editor = do
79
106
Editor .setEnableBasicAutocompletion true editor
80
107
81
108
-- | Language tools and autocomplete initializer. Runs once.
82
- enableAutocomplete :: forall eff . Eff (AceEffects eff ) Unit
83
- enableAutocomplete = do
84
- languageToolsInitialized <- readRef initialized
85
- when (not languageToolsInitialized) do
86
- completer <- Completer .mkCompleter globalCompleteFn
87
- tools <- LanguageTools .languageTools
88
- LanguageTools .addCompleter completer tools
109
+ globalInitialization :: forall eff . Eff (AceEffects eff ) Unit
110
+ globalInitialization = do
111
+ alreadyInited <- readRef initialized
112
+ when (not alreadyInited) do
113
+ initLanguageTools
114
+ -- This should be removed and altered with finalizer prop
115
+ -- after slamdata/purescript-halogen#272 is resolved
116
+ emulateFinalizer
89
117
writeRef initialized true
118
+
119
+ initLanguageTools :: forall eff . Eff (AceEffects eff ) Unit
120
+ initLanguageTools = do
121
+ completer <- Completer .mkCompleter globalCompleteFn
122
+ tools <- LanguageTools .languageTools
123
+ LanguageTools .addCompleter completer tools
124
+
125
+ emulateFinalizer :: forall eff . Eff (AceEffects eff ) Unit
126
+ emulateFinalizer = do
127
+ runAff (const $ pure unit) pure $ runProcess (tickProducer $$ tickConsumer)
90
128
where
91
- globalCompleteFn editor session position prefix cb = do
92
- fn <- completeFnFocused
93
- runAff (const $ cb Nothing ) (cb <<< Just )
94
- $ fn editor session position prefix
129
+ tickProducer :: Producer Unit (Aff (AceEffects eff )) Unit
130
+ tickProducer =
131
+ produce (runAff (const $ pure unit) pure <<< void <<< forkAff <<< tick)
132
+
133
+ tick emit = do
134
+ liftEff $ emit $ Left unit
135
+ forkAff $ later' 60000 $ tick emit
136
+
137
+ tickConsumer :: Consumer Unit (Aff (AceEffects eff )) Unit
138
+ tickConsumer = consumer \_ -> liftEff do
139
+ storedKeys <- map Set .fromFoldable $ readRef keys
140
+ activeKeysArr <- window
141
+ >>= document
142
+ >>= querySelectorAll " [data-acekey]"
143
+ <<< htmlDocumentToParentNode
144
+ >>= extractKeys [ ] 0
145
+ F .for_ (F .foldl (flip Set .delete) storedKeys activeKeysArr) \key ->
146
+ modifyRef completeFns $ Sm .delete key
147
+ writeRef keys activeKeysArr
148
+ pure Nothing
149
+
150
+ globalCompleteFn
151
+ :: forall eff
152
+ . Editor
153
+ -> EditSession
154
+ -> Position
155
+ -> String
156
+ -> Completer.CompleterCallback (AceEffects eff )
157
+ -> Eff (AceEffects eff ) Unit
158
+ globalCompleteFn editor session position prefix cb = do
159
+ fn <- completeFnFocused
160
+ runAff (const $ cb Nothing ) (cb <<< Just )
161
+ $ fn editor session position prefix
162
+
163
+ extractKeys
164
+ :: forall eff
165
+ . Array String
166
+ -> Int
167
+ -> NodeList
168
+ -> Eff (AceEffects eff ) (Array String )
169
+ extractKeys acc ix nl = do
170
+ count <- Nl .length nl
171
+ if ix >= count
172
+ then pure acc
173
+ else do
174
+ mbKey <- runMaybeT do
175
+ el <- MaybeT $ map toMaybe $ Nl .item ix nl
176
+ ds <- liftEff $ dataset el
177
+ MaybeT $ pure $ Sm .lookup " acekey" ds
178
+ extractKeys (maybe acc (Arr .snoc acc) mbKey) (ix + one) nl
179
+
95
180
96
181
-- | Generate unique key for component
97
182
genKey :: forall eff . Eff (now :: Now , random :: RANDOM | eff ) String
@@ -115,7 +200,6 @@ type AceEffects eff =
115
200
116
201
-- | Ace query algebra
117
202
-- | - `Init` - used internally to handle initialization of component
118
- -- | - `Quit` - used internally to handle finalizing of component.
119
203
-- | - `GetText` - gets the current text value
120
204
-- | - `SetText` - alters the current text value
121
205
-- | - `SetAutocomplete` - sets autocomplete resume:
@@ -129,7 +213,6 @@ type AceEffects eff =
129
213
-- | via the `peek` mechanism.
130
214
data AceQuery a
131
215
= Init HTMLElement a
132
- | Quit a
133
216
| GetText (String -> a )
134
217
| SetText String a
135
218
| SetAutocomplete (Maybe Autocomplete ) a
@@ -173,18 +256,18 @@ aceComponent setup resume = component render eval
173
256
render :: AceState -> ComponentHTML AceQuery
174
257
render state =
175
258
H .div
176
- [ P .initializer \el -> action (Init el)
177
- , P .finalizer \el -> action Quit
178
- ]
259
+ ([ P .initializer \el -> action (Init el) ]
260
+ <> maybe [] (Arr .singleton <<< dataAceKey) state.key)
179
261
[]
180
262
181
263
eval :: Natural AceQuery (ComponentDSL AceState AceQuery (Aff (AceEffects eff )))
182
264
eval (Init el next) = do
183
265
key <- gets _.key >>= maybe (liftEff' genKey) pure
266
+ liftEff' $ modifyRef keys $ Arr .cons key
184
267
editor <- liftEff' $ Ace .editNode el Ace .ace
185
268
modify $ const $ { key: Just key, editor: Just editor }
186
269
liftEff' do
187
- enableAutocomplete
270
+ globalInitialization
188
271
setAutocompleteResume resume editor
189
272
Editor .onFocus editor $ writeRef focused key
190
273
session <- liftEff' $ Editor .getSession editor
@@ -193,12 +276,6 @@ aceComponent setup resume = component render eval
193
276
liftH $ setup editor
194
277
pure next
195
278
196
- eval (Quit next) = do
197
- gets _.key
198
- >>= traverse_ \key ->
199
- liftEff' $ modifyRef completeFns $ Sm .delete key
200
- pure next
201
-
202
279
eval (GetEditor k) =
203
280
map k $ gets _.editor
204
281
@@ -209,20 +286,20 @@ aceComponent setup resume = component render eval
209
286
210
287
eval (SetText text next) = do
211
288
gets _.editor
212
- >>= traverse_ \editor -> do
289
+ >>= F . traverse_ \editor -> do
213
290
current <- liftEff' $ Editor .getValue editor
214
291
when (text /= current) $ void
215
292
$ liftEff' (Editor .setValue text Nothing editor)
216
293
pure next
217
294
218
295
eval (SetAutocomplete mbAc next) = do
219
296
gets _.editor
220
- >>= traverse_ (liftEff' <<< setAutocompleteResume mbAc)
297
+ >>= F . traverse_ (liftEff' <<< setAutocompleteResume mbAc)
221
298
pure next
222
299
223
300
eval (SetCompleteFn fn next) = do
224
301
gets _.key
225
- >>= traverse_ \key ->
302
+ >>= F . traverse_ \key ->
226
303
liftEff' $ modifyRef completeFns $ Sm .insert key fn
227
304
pure next
228
305
0 commit comments