Skip to content

Commit d644f4f

Browse files
committed
Merge pull request #11 from cryogenian/emulate-finalizer
removed Quit, made emulateFinalizer
2 parents 4d669b8 + fbe06f8 commit d644f4f

File tree

3 files changed

+112
-32
lines changed

3 files changed

+112
-32
lines changed

bower.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"purescript-refs": "^0.2.0",
2828
"purescript-datetime": "^0.9.1",
2929
"purescript-random": "^0.2.3",
30-
"purescript-ace": "~0.11.0"
30+
"purescript-ace": "~0.11.0",
31+
"purescript-sets": "^0.5.7"
3132
}
3233
}

src/Ace/Halogen/Component.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ exports.initialized = {value: false};
66

77
exports.focused = {value: ""};
88

9+
exports.keys = {value: []};
10+
911
exports.dataset = function(node) {
1012
return function() {
1113
return node.dataset;

src/Ace/Halogen/Component.purs

Lines changed: 108 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,39 @@ module Ace.Halogen.Component
1111

1212
import Prelude
1313

14+
import Control.Coroutine (($$), consumer, Producer(), Consumer(), runProcess)
15+
import Control.Coroutine.Aff (produce)
1416
import Control.Monad (when)
15-
import Control.Monad.Aff (Aff(), runAff)
17+
import Control.Monad.Aff (Aff(), runAff, later', forkAff)
1618
import Control.Monad.Aff.AVar (AVAR())
1719
import Control.Monad.Eff (Eff())
20+
import Control.Monad.Eff.Class (liftEff)
1821
import Control.Monad.Eff.Random (random, RANDOM())
1922
import Control.Monad.Eff.Ref (Ref(), REF(), readRef, writeRef, modifyRef)
23+
import Control.Monad.Maybe.Trans (MaybeT(..), runMaybeT)
2024

25+
import Data.Array as Arr
26+
import Data.Either (Either(..))
2127
import Data.Date (nowEpochMilliseconds, Now())
22-
import Data.Foldable (traverse_)
28+
import Data.Foldable as F
2329
import Data.Maybe (Maybe(..), maybe)
30+
import Data.Nullable (toMaybe)
31+
import Data.Set as Set
2432
import Data.StrMap (StrMap())
2533
import Data.StrMap as Sm
2634
import Data.Time (Milliseconds(..))
2735

2836
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
3043

31-
import Halogen
44+
import Halogen hiding (Prop())
45+
import Halogen.HTML.Core (Prop(..), attrName)
46+
import Halogen.HTML.Properties.Indexed (IProp())
3247
import Halogen.HTML.Indexed as H
3348
import Halogen.HTML.Properties.Indexed as P
3449

@@ -38,6 +53,14 @@ import Ace.Ext.LanguageTools as LanguageTools
3853
import Ace.Ext.LanguageTools.Completer as Completer
3954
import Ace.Types
4055

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+
4164
-- | Effectful knot of autocomplete functions. It's needed because
4265
-- | `languageTools.addCompleter` is global and adds completer to
4366
-- | all editors
@@ -50,9 +73,13 @@ foreign import initialized :: Ref Boolean
5073
-- | autocomplete function
5174
foreign import focused :: Ref String
5275

76+
-- | Stores `data-acekey` of last checked components
77+
foreign import keys :: Ref (Array String)
78+
5379
-- | Get `dataset` property of element
5480
foreign import dataset
55-
:: forall eff. HTMLElement -> Eff (dom :: DOM | eff) (StrMap String)
81+
:: forall eff. Node -> Eff (dom :: DOM | eff) (StrMap String)
82+
5683

5784

5885
-- | Take completion function for currently selected component
@@ -79,19 +106,77 @@ setAutocompleteResume (Just Live) editor = do
79106
Editor.setEnableBasicAutocompletion true editor
80107

81108
-- | 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
89117
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)
90128
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+
95180

96181
-- | Generate unique key for component
97182
genKey :: forall eff. Eff (now :: Now, random :: RANDOM | eff) String
@@ -115,7 +200,6 @@ type AceEffects eff =
115200

116201
-- | Ace query algebra
117202
-- | - `Init` - used internally to handle initialization of component
118-
-- | - `Quit` - used internally to handle finalizing of component.
119203
-- | - `GetText` - gets the current text value
120204
-- | - `SetText` - alters the current text value
121205
-- | - `SetAutocomplete` - sets autocomplete resume:
@@ -129,7 +213,6 @@ type AceEffects eff =
129213
-- | via the `peek` mechanism.
130214
data AceQuery a
131215
= Init HTMLElement a
132-
| Quit a
133216
| GetText (String -> a)
134217
| SetText String a
135218
| SetAutocomplete (Maybe Autocomplete) a
@@ -173,18 +256,18 @@ aceComponent setup resume = component render eval
173256
render :: AceState -> ComponentHTML AceQuery
174257
render state =
175258
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)
179261
[]
180262

181263
eval :: Natural AceQuery (ComponentDSL AceState AceQuery (Aff (AceEffects eff)))
182264
eval (Init el next) = do
183265
key <- gets _.key >>= maybe (liftEff' genKey) pure
266+
liftEff' $ modifyRef keys $ Arr.cons key
184267
editor <- liftEff' $ Ace.editNode el Ace.ace
185268
modify $ const $ { key: Just key, editor: Just editor }
186269
liftEff' do
187-
enableAutocomplete
270+
globalInitialization
188271
setAutocompleteResume resume editor
189272
Editor.onFocus editor $ writeRef focused key
190273
session <- liftEff' $ Editor.getSession editor
@@ -193,12 +276,6 @@ aceComponent setup resume = component render eval
193276
liftH $ setup editor
194277
pure next
195278

196-
eval (Quit next) = do
197-
gets _.key
198-
>>= traverse_ \key ->
199-
liftEff' $ modifyRef completeFns $ Sm.delete key
200-
pure next
201-
202279
eval (GetEditor k) =
203280
map k $ gets _.editor
204281

@@ -209,20 +286,20 @@ aceComponent setup resume = component render eval
209286

210287
eval (SetText text next) = do
211288
gets _.editor
212-
>>= traverse_ \editor -> do
289+
>>= F.traverse_ \editor -> do
213290
current <- liftEff' $ Editor.getValue editor
214291
when (text /= current) $ void
215292
$ liftEff' (Editor.setValue text Nothing editor)
216293
pure next
217294

218295
eval (SetAutocomplete mbAc next) = do
219296
gets _.editor
220-
>>= traverse_ (liftEff' <<< setAutocompleteResume mbAc)
297+
>>= F.traverse_ (liftEff' <<< setAutocompleteResume mbAc)
221298
pure next
222299

223300
eval (SetCompleteFn fn next) = do
224301
gets _.key
225-
>>= traverse_ \key ->
302+
>>= F.traverse_ \key ->
226303
liftEff' $ modifyRef completeFns $ Sm.insert key fn
227304
pure next
228305

0 commit comments

Comments
 (0)