1
1
module Ace.Halogen.Component
2
2
( aceComponent
3
- , aceConstructor
4
3
, AceQuery (..)
5
- , AceState (..)
6
- , initialAceState
4
+ , AceMessage (..)
7
5
, AceEffects
8
6
, Autocomplete (..)
9
7
, CompleteFn
@@ -13,14 +11,14 @@ import Prelude
13
11
14
12
import Control.Monad.Aff (Aff , runAff )
15
13
import Control.Monad.Aff.AVar (AVAR )
16
- import Control.Monad.Aff.Free (class Affable )
17
14
import Control.Monad.Eff (Eff )
15
+ import Control.Monad.Aff.Class (class MonadAff )
18
16
import Control.Monad.Eff.Now (NOW , now )
19
17
import Control.Monad.Eff.Random (random , RANDOM )
20
18
import Control.Monad.Eff.Ref (Ref , REF , readRef , writeRef , modifyRef )
21
19
22
20
import Data.DateTime.Instant (unInstant )
23
- import Data.Foldable (traverse_ , for_ )
21
+ import Data.Foldable (traverse_ )
24
22
import Data.Maybe (Maybe (..), maybe )
25
23
import Data.Newtype (unwrap )
26
24
import Data.StrMap (StrMap )
@@ -37,8 +35,8 @@ import DOM (DOM)
37
35
import DOM.HTML.Types (HTMLElement )
38
36
39
37
import Halogen as H
40
- import Halogen.HTML.Indexed as HH
41
- import Halogen.HTML.Properties.Indexed as HP
38
+ import Halogen.HTML as HH
39
+ import Halogen.HTML.Properties as HP
42
40
43
41
-- | Effectful knot of autocomplete functions. It's needed because
44
42
-- | `languageTools.addCompleter` is global and adds completer to
@@ -129,19 +127,19 @@ type AceEffects eff =
129
127
-- | - `Just Live` - enables live autocomplete
130
128
-- | - `SetCompleteFn` - sets function providing autocomplete variants.
131
129
-- | - `GetEditor` - returns ace editor instance handled by this component.
132
- -- | - `TextChanged` - raised internally when the value in the editor is
133
- -- | changed. Allows for parent component to observe when the value changes
134
- -- | via the `peek` mechanism.
135
130
data AceQuery a
136
- = SetElement (Maybe HTMLElement ) a
137
- | Init a
131
+ = Init a
138
132
| Quit a
139
133
| GetText (String → a )
140
134
| SetText String a
141
135
| SetAutocomplete (Maybe Autocomplete ) a
142
136
| SetCompleteFn (∀ eff . CompleteFn eff ) a
143
137
| GetEditor (Maybe Editor → a )
144
- | TextChanged a
138
+ | HandleChange (H.SubscribeStatus -> a )
139
+
140
+ -- | Ace output messages
141
+ -- | - `AceValueChanged` - raised when the value in the editor is changed.
142
+ data AceMessage = TextChanged String
145
143
146
144
-- | The type for autocomplete function s. Takes editor, session, text position,
147
145
-- | prefix, and returns array of possible completions in the `Aff` monad.
@@ -158,108 +156,96 @@ type CompleteFn eff
158
156
type AceState =
159
157
{ key ∷ Maybe String
160
158
, editor ∷ Maybe Editor
161
- , element ∷ Maybe HTMLElement
162
159
}
163
160
161
+ type DSL m = H.ComponentDSL AceState AceQuery AceMessage m
162
+
164
163
-- | An initial empty state value.
165
- initialAceState ∷ AceState
166
- initialAceState =
164
+ initialState ∷ AceState
165
+ initialState =
167
166
{ key: Nothing
168
167
, editor: Nothing
169
- , element: Nothing
170
168
}
171
169
172
170
-- | The Ace component.
173
171
aceComponent
174
- ∷ ∀ eff g
175
- . ( Monad g , Affable (AceEffects eff ) g )
176
- ⇒ (Editor → g Unit )
172
+ ∷ ∀ eff m
173
+ . MonadAff (AceEffects eff ) m
174
+ ⇒ (Editor → m Unit )
177
175
→ Maybe Autocomplete
178
- → H.Component AceState AceQuery g
179
- aceComponent setup resume = H .lifecycleComponent
180
- { render
176
+ → H.Component HH.HTML AceQuery Unit AceMessage m
177
+ aceComponent setup resume =
178
+ H .lifecycleComponent
179
+ { initialState: const initialState
180
+ , render
181
181
, eval: eval setup resume
182
182
, initializer: Just (H .action Init )
183
183
, finalizer: Just (H .action Quit )
184
+ , receiver: const Nothing
184
185
}
185
186
186
187
render ∷ AceState → H.ComponentHTML AceQuery
187
- render = const $ HH .div [ HP .ref (H .action <<< SetElement ) ] []
188
+ render = const $ HH .div [ HP .ref (H.RefLabel " container " ) ] []
188
189
189
- eval ∷ ∀ eff g
190
- . ( Monad g , Affable (AceEffects eff ) g )
191
- ⇒ (Editor → g Unit )
190
+ eval ∷ ∀ eff m
191
+ . MonadAff (AceEffects eff ) m
192
+ ⇒ (Editor → m Unit )
192
193
→ Maybe Autocomplete
193
194
→ AceQuery
194
- ~> H.ComponentDSL AceState AceQuery g
195
+ ~> DSL m
195
196
eval setup resume = case _ of
196
- SetElement el next → do
197
- state ← H .get
198
- for_ state.editor $ H .fromEff <<< Editor .destroy
199
- H .modify _{ element = el, editor = Nothing }
200
- pure next
201
197
202
198
Init next → do
203
- el ← H .gets _.element
204
- for_ el \el' → do
205
- key ← H .gets _.key >>= maybe (H .fromEff genKey) pure
206
- editor ← H .fromEff $ Ace .editNode el' Ace .ace
207
- H .set { key: Just key, editor: Just editor, element: Just el' }
208
- H .fromEff do
199
+ H .getHTMLElementRef (H.RefLabel " container" ) >>= traverse_ \el → do
200
+ key ← H .gets _.key >>= maybe (H .liftEff genKey) pure
201
+ editor ← H .liftEff $ Ace .editNode el Ace .ace
202
+ H .put { key: Just key, editor: Just editor }
203
+ H .liftEff do
209
204
enableAutocomplete
210
205
setAutocompleteResume resume editor
211
206
Editor .onFocus editor $ writeRef focused key
212
- session ← H .fromEff $ Editor .getSession editor
213
- H .subscribe $ H .eventSource_ (Session .onChange session) do
214
- pure $ H .action TextChanged
215
- H .liftH $ setup editor
207
+ session ← H .liftEff $ Editor .getSession editor
208
+ H .subscribe $ H .eventSource_ (Session .onChange session) (H .request HandleChange )
209
+ H .lift $ setup editor
216
210
pure next
217
211
218
212
Quit next → do
219
213
H .gets _.key
220
214
>>= traverse_ \key →
221
- H .fromEff $ modifyRef completeFns $ Sm .delete key
215
+ H .liftEff $ modifyRef completeFns $ Sm .delete key
222
216
pure next
223
217
224
218
GetEditor k →
225
219
map k $ H .gets _.editor
226
220
227
221
GetText k →
228
- H .gets _.editor
229
- >>= maybe (pure " " ) (H .fromEff <<< Editor .getValue)
230
- >>= k >>> pure
222
+ pure <<< k =<< getText
231
223
232
224
SetText text next → do
233
225
H .gets _.editor
234
226
>>= traverse_ \editor → do
235
- current ← H .fromEff $ Editor .getValue editor
227
+ current ← H .liftEff $ Editor .getValue editor
236
228
when (text /= current) $ void
237
- $ H .fromEff (Editor .setValue text Nothing editor)
229
+ $ H .liftEff (Editor .setValue text Nothing editor)
238
230
pure next
239
231
240
232
SetAutocomplete mbAc next → do
241
233
H .gets _.editor
242
- >>= traverse_ (H .fromEff <<< setAutocompleteResume mbAc)
234
+ >>= traverse_ (H .liftEff <<< setAutocompleteResume mbAc)
243
235
pure next
244
236
245
237
SetCompleteFn fn next → do
246
238
H .gets _.key
247
239
>>= traverse_ \key →
248
- H .fromEff $ modifyRef completeFns $ Sm .insert key fn
240
+ H .liftEff $ modifyRef completeFns $ Sm .insert key fn
249
241
pure next
250
242
251
- TextChanged next → pure next
243
+ HandleChange k → do
244
+ H .raise <<< TextChanged =<< getText
245
+ pure $ k H.Listening
252
246
253
- -- | A convenience function for creating a `SlotConstructor` for an Ace
254
- -- | component.
255
- aceConstructor
256
- ∷ ∀ p eff
257
- . p
258
- → (Editor → Aff (AceEffects eff ) Unit )
259
- → Maybe Autocomplete
260
- → H.SlotConstructor AceState AceQuery (Aff (AceEffects eff )) p
261
- aceConstructor p setup mbAc =
262
- H.SlotConstructor p \_ →
263
- { component: aceComponent setup mbAc
264
- , initialState: initialAceState
265
- }
247
+ where
248
+
249
+ getText :: DSL m String
250
+ getText =
251
+ maybe (pure " " ) (H .liftEff <<< Editor .getValue) =<< H .gets _.editor
0 commit comments