1module FillInTheGaps where
4import Data.Array (foldMap, intercalate)
6import Data.Either (Either(..))
7import Data.Foldable (foldl, for_)
8import Data.Lens (over, (%~))
9import Data.Lens.Index (ix)
10import Data.Maybe (Maybe(..), fromMaybe, fromMaybe')
11import Data.Monoid (guard)
12import Data.String (Pattern(..), split)
13import Data.String as S
14import Data.String.Regex (Regex)
15import Data.String.Regex as Regex
16import Data.String.Regex.Flags as RegexFlags
17import Data.String.Regex.Unsafe (unsafeRegex)
18import Data.Tuple.Nested ((/\))
19import Debug.Trace (spy)
21import Effect.Aff (launchAff_)
22import Effect.Class (liftEffect)
23import Justifill (justifill)
25import Milkis.Impl (FetchImpl)
26import Milkis.Impl.Window (windowFetch)
27import Partial.Unsafe (unsafeCrashWith)
28import React.Basic (JSX, ReactComponent, element, fragment)
29import React.Basic.DOM (form_)
30import React.Basic.DOM as R
31import React.Basic.Events (handler_)
32import React.Basic.Helpers (jsx)
33import React.Basic.Hooks (component, useEffect, useState)
34import React.Basic.Hooks as React
35import React.Basic.Hooks.Aff (useAff)
36import Yoga.Button.Component (ButtonType(..), mkButton)
37import Yoga.CompileEditor.Component (compileAndRun)
38import Yoga.Helpers ((?||))
39import Yoga.InlineCode.Component as InlineCode
40import Yoga.Modal.Component as Modal
43 = ExpectedResult String
49derive instance eqSegment ∷ Eq Segment
50getResult ∷ Segment -> Maybe String
52 ExpectedResult r -> Just r
55findResult ∷ Array Segment -> String
56findResult = fromMaybe' (\_ -> unsafeCrashWith "Even teachers make mistakes") <<< A.findMap getResult
58toCode ∷ (Array (Array Segment)) -> String
59toCode lines = intercalate "\n" mapped
61 mapped = lines <#> (intercalate "" <<< map segmentToCode)
62 segmentToCode = case _ of
67complete ∷ (Array (Array Segment)) -> Boolean
68complete arr = foldl f true (join arr)
76visibleRange ∷ Array (Array Segment) -> { end ∷ Int, start ∷ Int }
77visibleRange arr = { start, end }
79 start = A.findIndex (_ == [ Start ]) arr ?|| 0
80 end = A.findIndex (_ == [ End ]) arr ?|| A.length arr
82renderSegments ∷ ReactComponent InlineCode.Props -> ((Array (Array Segment) -> Array (Array Segment)) -> Effect Unit) -> Array (Array Segment) -> JSX
83renderSegments ic update arrs = R.div_ (A.mapWithIndex renderLine arrs)
85 { start, end } = visibleRange arrs
86 renderLine i l = R.div_ (A.mapWithIndex (renderSegment i) l)
88 if between start end i then case s of
89 Filler s' -> R.code_ [ R.text s' ]
94 , onSubmit: update <<< updateSegments i j
100updateSegments ∷ Int -> Int -> String -> Array (Array Segment) -> Array (Array Segment)
101updateSegments i j v = (ix (spy "i" i) <<< ix (spy "j" j)) %~ f
105 _ -> unsafeCrashWith "Updated a non-hole"
108holeRegex = unsafeRegex "({-.*?-})" RegexFlags.global
111resultRegex = unsafeRegex "--result" RegexFlags.global
114startRegex = unsafeRegex "--start here" RegexFlags.global
117endRegex = unsafeRegex "--end here" RegexFlags.global
119rawSegments ∷ String -> Array String
120rawSegments = Regex.split holeRegex
122toSegment ∷ String -> Segment
125 | Regex.test holeRegex x -> Hole (S.length x - 4) ""
127 | S.indexOf (Pattern "--result ") x == Just 0 -> ExpectedResult ((S.stripPrefix (Pattern "--result ") x) ?|| "")
129 | Regex.test startRegex x -> Start
131 | Regex.test endRegex x -> End
132 other -> Filler other
134mkFillInTheGaps ∷ FetchImpl -> Effect (ReactComponent { code ∷ String })
135mkFillInTheGaps fetch = do
136 ic <- InlineCode.makeComponent
137 modal <- Modal.makeComponent
139 component "FillInTheGaps" \{ code } -> React.do
141 lines = split (Pattern "\n") code
143 initialSegments = lines <#> \line -> rawSegments line <#> toSegment
144 segments /\ modifySegments <- useState initialSegments
145 result /\ modifyResult <- useState Nothing
147 expectedResult = findResult (join segments)
152 res <- compileAndRun (M.fetch fetch) { code: toCode segments }
153 modifyResult (const (Just $ res)) # liftEffect
156 [ renderSegments ic (modifySegments) segments
158 { onClick: handler_ onClick
160 if complete (spy "segsm" segments) then
172 | stdout == expectedResult -> "Hooray!"
173 Left l -> "Does not compile"
174 Right _ -> "Not " <> (findResult (spy "segs" $ join segments))
176 [ R.text $ compileResultToString result ]
179compileResultToString = case _ of
181 Just (Left cr) -> cr.result <#> _.message # intercalate "/n"
182 Just (Right r) -> r.stdout
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list