@@ -1,232 +0,0 @@
1−module FillInTheGaps where
5−import Data.Array (foldMap, intercalate)
7−import Data.Either (Either(..))
8−import Data.Foldable (foldl)
9−import Data.Function.Uncurried (mkFn2)
10−import Data.Lens ((%~))
11−import Data.Lens.Index (ix)
12−import Data.Maybe (Maybe(..), fromMaybe', isJust, isNothing)
13−import Data.Monoid (guard)
14−import Data.String (Pattern(..), split)
15−import Data.String as S
16−import Data.String.Regex (Regex)
17−import Data.String.Regex as Regex
18−import Data.String.Regex.Flags as RegexFlags
19−import Data.String.Regex.Unsafe (unsafeRegex)
20−import Data.Tuple.Nested ((/\))
21−import Debug.Trace (spy)
22−import Effect (Effect)
23−import Effect.Aff (launchAff_)
24−import Effect.Class (liftEffect)
25−import Justifill (justifill)
27−import Milkis.Impl (FetchImpl)
28−import Partial.Unsafe (unsafeCrashWith)
29−import React.Basic (JSX, ReactComponent, element, fragment)
30−import React.Basic.DOM (css)
31−import React.Basic.DOM as R
32−import React.Basic.DOM.Events (preventDefault)
33−import React.Basic.Events (handler, handler_)
34−import React.Basic.Helpers (jsx)
35−import React.Basic.Hooks (component, useState)
36−import React.Basic.Hooks as React
37−import React.Basic.Hooks.Spring (animatedDiv, useTransition)
38−import Shared.Models.Body (CompileResult, RunResult)
39−import Yoga.Button.Component (ButtonType(..), mkButton)
40−import Yoga.ClickAway.Component as ClickAway
41−import Yoga.CloseIcon.Component as CloseIcon
42−import Yoga.Cluster.Component as Cluster
43−import Yoga.CompileEditor.Component (compileAndRun)
44−import Yoga.Helpers ((?||))
45−import Yoga.InlineCode.Component as InlineCode
46−import Yoga.Modal.Component as Modal
49− = ExpectedResult String
55−derive instance eqSegment ∷ Eq Segment
56−getResult ∷ Segment -> Maybe String
58− ExpectedResult r -> Just r
61−findResult ∷ Array Segment -> String
62−findResult = fromMaybe' (\_ -> unsafeCrashWith "Even teachers make mistakes") <<< A.findMap getResult
64−toCode ∷ (Array (Array Segment)) -> String
65−toCode lines = intercalate "\n" mapped
67− mapped = lines <#> (intercalate "" <<< map segmentToCode)
68− segmentToCode = case _ of
73−complete ∷ (Array (Array Segment)) -> Boolean
74−complete arr = foldl f true (join arr)
82−visibleRange ∷ Array (Array Segment) -> { end ∷ Int, start ∷ Int }
83−visibleRange arr = { start, end }
85− start = A.findIndex (_ == [ Start ]) arr ?|| 0
86− end = A.findIndex (_ == [ End ]) arr ?|| A.length arr
88−renderSegments ∷ ReactComponent InlineCode.Props -> ((Array (Array Segment) -> Array (Array Segment)) -> Effect Unit) -> Array (Array Segment) -> JSX
89−renderSegments ic update arrs = R.div_ (A.mapWithIndex renderLine arrs)
91− { start, end } = visibleRange arrs
92− renderLine i l = R.div_ (A.mapWithIndex (renderSegment i) l)
93− renderSegment i j s =
94− if between start end i then case s of
95− Filler s' -> R.code_ [ R.text s' ]
100− , onSubmit: update <<< updateSegments i j
106−updateSegments ∷ Int -> Int -> String -> Array (Array Segment) -> Array (Array Segment)
107−updateSegments i j v = (ix i <<< ix j) %~ f
110− Hole h _ -> Hole h v
111− _ -> unsafeCrashWith "Updated a non-hole"
114−holeRegex = unsafeRegex "({-.*?-})" RegexFlags.global
117−resultRegex = unsafeRegex "--result" RegexFlags.global
120−startRegex = unsafeRegex "--start here" RegexFlags.global
123−endRegex = unsafeRegex "--end here" RegexFlags.global
125−rawSegments ∷ String -> Array String
126−rawSegments = Regex.split holeRegex
128−toSegment ∷ String -> Segment
129−toSegment = case _ of
131− | Regex.test holeRegex x -> Hole (S.length x - 4) ""
133− | S.indexOf (Pattern "--result ") x == Just 0 -> ExpectedResult ((S.stripPrefix (Pattern "--result ") x) ?|| "")
135− | Regex.test startRegex x -> Start
137− | Regex.test endRegex x -> End
138− other -> Filler other
140−mkFillInTheGaps ∷ FetchImpl -> Effect (ReactComponent { code ∷ String })
141−mkFillInTheGaps fetch = do
142− ic <- InlineCode.makeComponent
143− modal <- Modal.makeComponent
144− closeIcon <- CloseIcon.makeComponent
145− cluster <- Cluster.makeComponent
146− clickAway <- ClickAway.makeComponent
148− component "FillInTheGaps" \{ code } -> React.do
150− lines = split (Pattern "\n") code
152− initialSegments = lines <#> \line -> rawSegments line <#> toSegment
153− segments /\ modifySegments <- useState initialSegments
154− result /\ modifyResult <- useState Nothing
156− useTransition [ result ] (Just show)
158− { from: { opacity: 0.0, transform: "translate3d(-50%, -50%, 0) scale3d(0.3, 0.3, 1.0)" }
159− , enter: { opacity: 1.0, transform: "translate3d(-50%, -50%, 0) scale3d(1.0, 1.0, 1.0)" }
160− , leave: { opacity: 0.0, transform: "translate3d(-50%, -50%, 0) scale3d(0.3, 0.3, 1.0)" }
161− , config: mkFn2 \_ state ->
162− case spy "state" state of
163− "leave" -> { mass: 1.0, tension: 140, friction: 15 }
164− _ -> { mass: 1.0, tension: 170, friction: 20 }
166− clickAwayTransitions <-
167− useTransition [ result ] (Just show)
169− { from: { opacity: 0.0 }
170− , enter: { opacity: 1.0 }
171− , leave: { opacity: 0.0 }
172− , config: mkFn2 \_ state ->
173− case spy "state" state of
174− "leave" -> { mass: 1.0, tension: 140, friction: 15 }
175− _ -> { mass: 1.0, tension: 170, friction: 20 }
178− expectedResult = findResult (join segments)
183− res <- compileAndRun (M.fetch fetch) { code: toCode segments }
184− modifyResult (const (Just $ res)) # liftEffect
187− $ [ renderSegments ic (modifySegments) segments
191− { onClick: handler_ onClick
193− if complete segments && isNothing result then
202− $ clickAwayTransitions
203− >>= \{ item, key, props } ->
207− [element clickAway (justifill { allowEscape: true, style: props, onClick: modifyResult (const Nothing) })]
210− >>= \{ item, key, props } ->
218− | S.stripSuffix (S.Pattern "\n") stdout == Just expectedResult -> "Hooray!"
219− (Left l) -> "Does not compile"
220− (Right _) -> "Not " <> (findResult $ join segments)
221− , icon: element closeIcon { onClick: modifyResult (const Nothing), style: Nothing }
224− [ R.text $ compileResultToString result ]
228−compileResultToString ∷ Maybe (Either CompileResult RunResult) -> String
229−compileResultToString = case _ of
231− Just (Left cr) -> cr.result <#> _.message # intercalate "/n"
232− Just (Right r) -> r.stdout