1module FillInTheGaps where
4import Data.Array (foldMap, intercalate)
6import Data.Either (Either(..))
7import Data.Foldable (foldl, for_)
8import Data.Maybe (Maybe(..), fromMaybe, fromMaybe')
9import Data.Monoid (guard)
10import Data.String (Pattern(..), split)
11import Data.String as S
12import Data.String.Regex (Regex)
13import Data.String.Regex as Regex
14import Data.String.Regex.Flags as RegexFlags
15import Data.String.Regex.Unsafe (unsafeRegex)
16import Data.Tuple.Nested ((/\))
18import Effect.Aff (launchAff_)
19import Effect.Class (liftEffect)
20import Justifill (justifill)
22import Milkis.Impl (FetchImpl)
23import Milkis.Impl.Window (windowFetch)
24import Partial.Unsafe (unsafeCrashWith)
25import React.Basic (JSX, ReactComponent, element, fragment)
26import React.Basic.DOM (form_)
27import React.Basic.DOM as R
28import React.Basic.Events (handler_)
29import React.Basic.Helpers (jsx)
30import React.Basic.Hooks (component, useEffect, useState)
31import React.Basic.Hooks as React
32import React.Basic.Hooks.Aff (useAff)
33import Yoga.Button.Component (ButtonType(..), mkButton)
34import Yoga.CompileEditor.Component (compileAndRun)
35import Yoga.Helpers ((?||))
36import Yoga.InlineCode.Component as InlineCode
37import Yoga.Modal.Component as Modal
40 = ExpectedResult String
46derive instance eqSegment ∷ Eq Segment
47getResult ∷ Segment -> Maybe String
49 ExpectedResult r -> Just r
52findResult ∷ Array Segment -> String
53findResult = fromMaybe' (\_ -> unsafeCrashWith "Even teachers make mistakes") <<< A.findMap getResult
55toCode ∷ (Array (Array Segment)) -> String
56toCode lines = intercalate "\n" mapped
58 mapped = lines <#> (intercalate "" <<< map segmentToCode)
59 segmentToCode = case _ of
64complete ∷ (Array (Array Segment)) -> Boolean
65complete arr = foldl f true (join arr)
73renderSegments ∷ ReactComponent InlineCode.Props -> ((Array (Array Segment) -> Array (Array Segment)) -> Effect Unit) -> Array (Array Segment) -> JSX
74renderSegments ic update arrs = R.div_ (A.mapWithIndex renderLine (onlyVisible arrs))
76 onlyVisible arr = A.slice start end arr
78 start = A.findIndex (_ == [ Start ]) arr ?|| 0
79 end = A.findIndex (_ == [ End ]) arr ?|| A.length arr
80 renderLine i l = R.div_ (A.mapWithIndex (renderSegment i) l)
81 renderSegment i j = case _ of
82 Filler s -> R.code_ [ R.text s ]
83 Hole width _ -> element ic $ justifill { width, onSubmit: \v -> update (updateSegments i j (S.trim v)) }
86updateSegments ∷ Int -> Int -> String -> Array (Array Segment) -> Array (Array Segment)
87updateSegments i j v arrs = fromMaybe [] (A.modifyAt i (updateLine j v) arrs)
89updateLine ∷ Int -> String -> Array Segment -> Array Segment
90updateLine idx v arr = fromMaybe [] (A.modifyAt idx f arr)
94 _ -> unsafeCrashWith "Updated a non-hole"
97holeRegex = unsafeRegex "({-.*?-})" RegexFlags.global
100resultRegex = unsafeRegex "--result" RegexFlags.global
103startRegex = unsafeRegex "--start here" RegexFlags.global
106endRegex = unsafeRegex "--end here" RegexFlags.global
108rawSegments ∷ String -> Array String
109rawSegments = Regex.split holeRegex
111toSegment ∷ String -> Segment
114 | Regex.test holeRegex x -> Hole (S.length x - 4) ""
116 | S.indexOf (Pattern "--result ") x == Just 0 -> ExpectedResult ((S.stripPrefix (Pattern "--result ") x) ?|| "")
118 | Regex.test startRegex x -> Start
120 | Regex.test endRegex x -> End
121 other -> Filler other
123mkFillInTheGaps ∷ FetchImpl -> Effect (ReactComponent { code ∷ String })
124mkFillInTheGaps fetch = do
125 ic <- InlineCode.makeComponent
126 modal <- Modal.makeComponent
128 component "FillInTheGaps" \{ code } -> React.do
130 lines = split (Pattern "\n") code
132 initialSegments = lines <#> \line -> rawSegments line <#> toSegment
133 segments /\ modifySegments <- useState initialSegments
134 result /\ modifyResult <- useState Nothing
136 expectedResult = findResult (join segments)
141 res <- compileAndRun (M.fetch fetch) { code: toCode segments }
142 modifyResult (const (Just $ res)) # liftEffect
145 [ renderSegments ic (modifySegments) segments
147 { onClick: handler_ onClick
149 -- if complete segments
152 -- else DisabledButton
161 | stdout == expectedResult -> "Hooray!"
162 Left l -> "Does not compile"
163 Right _ -> "Not " <> (findResult (join segments))
165 [ R.text $ compileResultToString result ]
168compileResultToString = case _ of
170 Just (Left cr) -> cr.result <#> _.message # intercalate "/n"
171 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