@@ -1,83 +1,24 @@
1module Yoga.FillInTheGaps.Component where
1module Yoga.FillInTheGaps.Component where
3import Prelude
3import Prelude
4−import Data.Array (foldMap, foldr, intercalate)
5import Data.Array as A
4import Data.Array as A
6−import Data.Either (Either(..))
7−import Data.Foldable (foldl)
8−import Data.Function.Uncurried (mkFn2)
9−import Data.Lens ((%~))
10−import Data.Lens.Index (ix)
11−import Data.Maybe (Maybe(..), fromMaybe', isJust, isNothing)
12−import Data.Monoid (guard)
13−import Data.String (Pattern(..), split)
14−import Data.String as S
15−import Data.String.Regex (Regex)
16−import Data.String.Regex as Regex
17−import Data.String.Regex.Flags as RegexFlags
18−import Data.String.Regex.Unsafe (unsafeRegex)
19import Data.Tuple.Nested ((/\))
5import Data.Tuple.Nested ((/\))
20−import Debug.Trace (spy)
21import Effect (Effect)
6import Effect (Effect)
22−import Effect.Aff (launchAff_)
7+import Effect.Aff (Milliseconds(..), delay)
23import Effect.Class (liftEffect)
8import Effect.Class (liftEffect)
24import Justifill (justifill)
9import Justifill (justifill)
25−import Partial.Unsafe (unsafeCrashWith)
26−import React.Basic (JSX, ReactComponent, element, fragment)
10+import React.Basic (JSX, ReactComponent, element)
27−import React.Basic.DOM (css)
28import React.Basic.DOM as R
11import React.Basic.DOM as R
29−import React.Basic.Events (handler_)
30−import React.Basic.Helpers (jsx)
31import React.Basic.Hooks (component, useState)
12import React.Basic.Hooks (component, useState)
32import React.Basic.Hooks as React
13import React.Basic.Hooks as React
33−import React.Basic.Hooks.Spring (useTransition)
14+import React.Basic.Hooks.Aff (useAff)
34import React.Basic.SyntaxHighlighter.Component (HighlighterTheme, syntaxHighlighterImpl)
15import React.Basic.SyntaxHighlighter.Component (HighlighterTheme, syntaxHighlighterImpl)
35−import Shared.Models.Body (CompileResult, RunResult)
16+import Yoga.FillInTheGaps.Logic (Segment(..), updateSegments)
36−import Yoga.Button.Component (ButtonType(..), mkButton)
37−import Yoga.ClickAway.Component as ClickAway
38−import Yoga.CloseIcon.Component as CloseIcon
39−import Yoga.Cluster.Component as Cluster
40−import Yoga.Compiler.Types (Compiler)
41import Yoga.Helpers ((?||))
17import Yoga.Helpers ((?||))
42import Yoga.InlineCode.Component as InlineCode
18import Yoga.InlineCode.Component as InlineCode
43−import Yoga.Modal.Component as Modal
44import Yoga.Theme.Styles (useTheme)
19import Yoga.Theme.Styles (useTheme)
45import Yoga.Theme.Syntax (mkHighlighterTheme)
20import Yoga.Theme.Syntax (mkHighlighterTheme)
48− = ExpectedResult String
54−derive instance eqSegment ∷ Eq Segment
55−getResult ∷ Segment -> Maybe String
57− ExpectedResult r -> Just r
60−findResult ∷ Array Segment -> String
61−findResult = fromMaybe' (\_ -> unsafeCrashWith "Even teachers make mistakes") <<< A.findMap getResult
63−toCode ∷ Array (Array Segment) -> String
64−toCode lines = intercalate "\n" mapped
66− mapped = lines <#> (intercalate "" <<< map segmentToCode)
67− segmentToCode = case _ of
72−complete ∷ Array (Array Segment) -> Boolean
73−complete arr = foldl f true (join arr)
81visibleRange ∷ Array (Array Segment) -> { end ∷ Int, start ∷ Int }
22visibleRange ∷ Array (Array Segment) -> { end ∷ Int, start ∷ Int }
82visibleRange arr = { start, end }
23visibleRange arr = { start, end }
@@ -91,7 +32,7 @@ renderSegments highlighterTheme ic update arrs = R.div_ (A.mapWithIndex renderLi
91 renderLine i l = R.div_ (A.mapWithIndex (renderSegment i) l)
32 renderLine i l = R.div_ (A.mapWithIndex (renderSegment i) l)
92 renderSegment i j s =
33 renderSegment i j s =
93 if between start end i then case s of
34 if between start end i then case s of
94− Filler s' -> element syntaxHighlighterImpl { style: highlighterTheme, language: "purescript", children: spy "ch" s' }
35+ Filler s' -> element syntaxHighlighterImpl { style: highlighterTheme, language: "purescript", children: s' }
95 Hole width _ ->
36 Hole width _ ->
96 element ic
37 element ic
97 $ justifill
38 $ justifill
@@ -102,150 +43,13 @@ renderSegments highlighterTheme ic update arrs = R.div_ (A.mapWithIndex renderLi
105−updateSegments ∷ Int -> Int -> String -> Array (Array Segment) -> Array (Array Segment)
106−updateSegments i j v = (ix i <<< ix j) %~ f
109− Hole h _ -> Hole h v
110− _ -> unsafeCrashWith "Updated a non-hole"
113−holeRegex = unsafeRegex "({-.*?-})" RegexFlags.global
116−resultRegex = unsafeRegex "--result" RegexFlags.global
119−startRegex = unsafeRegex "--start here" RegexFlags.global
122−endRegex = unsafeRegex "--end here" RegexFlags.global
124−rawSegments ∷ String -> Array String
125−rawSegments = Regex.split holeRegex
127−toSegment ∷ String -> Segment
128−toSegment = case _ of
130− | Regex.test holeRegex x -> Hole (S.length x - 4) ""
132− | S.indexOf (Pattern "--result ") x == Just 0 -> ExpectedResult ((S.stripPrefix (Pattern "--result ") x) ?|| "")
134− | Regex.test startRegex x -> Start
136− | Regex.test endRegex x -> End
137− other -> Filler other
139−-- Glues together Filler segments
140−-- [[Filler "line1"], [Filler "line2"]] --> [[Filler "line1\nline2"]]
141−-- [[Filler "line1", Filler "line2"]] --> [[Filler "line1\nline2"]]
142−-- [[Filler "line1", Filler "line2"], [Filler "line3"]] --> [[Filler "line1\nline2\line3"]]
143−smooshFillers ∷ Array (Array Segment) -> Array (Array Segment)
144−smooshFillers = foldl smooshOuter []
146− smooshInner segments segment = case segment, A.unsnoc segments of
147− Filler f, Just { init, last: Filler prev } -> A.snoc init (Filler (prev <> "\n" <> f))
148− _, _ -> A.snoc segments segment
149− smooshOuter acc segs = case foldl smooshInner [] segs, A.unsnoc acc of
150− [ Filler f ], Just { init, last: [ Filler prev ] } -> A.snoc init [ Filler (prev <> "\n" <> f) ]
151− _, _ -> A.snoc acc segs
156−makeComponent ∷ { | Ctx () } -> Effect (ReactComponent { code ∷ String })
46+makeComponent ∷ Effect (ReactComponent { initialSegments ∷ Array (Array Segment), update ∷ Array (Array Segment) -> Effect Unit })
157−makeComponent { compileAndRun } = do
47+makeComponent = do
158 ic <- InlineCode.makeComponent
48 ic <- InlineCode.makeComponent
159− modal <- Modal.makeComponent
160− closeIcon <- CloseIcon.makeComponent
161− cluster <- Cluster.makeComponent
162− clickAway <- ClickAway.makeComponent
164− component "FillInTheGaps" \{ code } -> React.do
49+ component "FillInTheGaps" \{ initialSegments, update } -> React.do
166− lines = split (Pattern "\n") code
168− initialSegments = smooshFillers (lines <#> \line -> rawSegments line <#> toSegment)
169 segments /\ modifySegments <- useState initialSegments
50 segments /\ modifySegments <- useState initialSegments
170− result /\ modifyResult <- useState Nothing
51+ useAff segments do
171− cssTheme <- useTheme
52+ delay (200.0 # Milliseconds)
172− let
53+ update segments # liftEffect
173− highlighterTheme = mkHighlighterTheme cssTheme
54+ highlighterTheme <- useTheme <#> mkHighlighterTheme
175− useTransition [ result ] (Just show)
177− { from: { opacity: 0.0, transform: "translate3d(-50%, -50%, 0) scale3d(0.3, 0.3, 1.0)" }
178− , enter: { opacity: 1.0, transform: "translate3d(-50%, -50%, 0) scale3d(1.0, 1.0, 1.0)" }
179− , leave: { opacity: 0.0, transform: "translate3d(-50%, -50%, 0) scale3d(0.3, 0.3, 1.0)" }
181− mkFn2 \_ state -> case state of
182− "leave" -> { mass: 1.0, tension: 140, friction: 15 }
183− _ -> { mass: 1.0, tension: 170, friction: 20 }
185− clickAwayTransitions <-
186− useTransition [ result ] (Just show)
188− { from: { opacity: 0.0 }
189− , enter: { opacity: 1.0 }
190− , leave: { opacity: 0.0 }
192− mkFn2 \_ state -> case state of
193− "leave" -> { mass: 1.0, tension: 140, friction: 15 }
194− _ -> { mass: 1.0, tension: 170, friction: 20 }
197− expectedResult = findResult (join segments)
202− res <- compileAndRun { code: toCode segments }
203− modifyResult (const (Just $ res)) # liftEffect
206− $ [ renderSegments highlighterTheme ic (modifySegments) segments
55+ pure $ renderSegments highlighterTheme ic (modifySegments) segments
210− { onClick: handler_ onClick
212− if complete segments && isNothing result then
217− [ R.text "Incantate" ]
221− $ clickAwayTransitions
222− >>= \{ item, key, props } ->
226− [ element clickAway (justifill { allowEscape: true, style: props, onClick: modifyResult (const Nothing) }) ]
229− >>= \{ item, key, props } ->
237− | S.stripSuffix (S.Pattern "\n") stdout == Just expectedResult -> "Hooray!"
238− (Left l) -> "Does not compile"
239− (Right _) -> "Not " <> (findResult $ join segments)
240− , icon: element closeIcon { onClick: modifyResult (const Nothing), style: Nothing }
243− [ R.pre_ [ R.text $ compileResultToString result ] ]
247−compileResultToString ∷ Maybe (Either CompileResult RunResult) -> String
248−compileResultToString = case _ of
250− Just (Left cr) -> cr.result <#> _.message # intercalate "\n"
251− Just (Right r) -> r.stdout