@@ -11,24 +11,39 @@ module Ace.Halogen.Component
1111
1212import Prelude
1313
14+ import Control.Coroutine (($$), consumer , Producer (), Consumer (), runProcess )
15+ import Control.Coroutine.Aff (produce )
1416import Control.Monad (when )
15- import Control.Monad.Aff (Aff (), runAff )
17+ import Control.Monad.Aff (Aff (), runAff , later' , forkAff )
1618import Control.Monad.Aff.AVar (AVAR ())
1719import Control.Monad.Eff (Eff ())
20+ import Control.Monad.Eff.Class (liftEff )
1821import Control.Monad.Eff.Random (random , RANDOM ())
1922import 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 (..))
2127import Data.Date (nowEpochMilliseconds , Now ())
22- import Data.Foldable ( traverse_ )
28+ import Data.Foldable as F
2329import Data.Maybe (Maybe (..), maybe )
30+ import Data.Nullable (toMaybe )
31+ import Data.Set as Set
2432import Data.StrMap (StrMap ())
2533import Data.StrMap as Sm
2634import Data.Time (Milliseconds (..))
2735
2836import 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 ())
3247import Halogen.HTML.Indexed as H
3348import Halogen.HTML.Properties.Indexed as P
3449
@@ -38,6 +53,14 @@ import Ace.Ext.LanguageTools as LanguageTools
3853import Ace.Ext.LanguageTools.Completer as Completer
3954import 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
5174foreign 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
5480foreign 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
97182genKey :: 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.
130214data 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