purview-0.1.0.0: src/Diffing.hs
{-# LANGUAGE DeriveGeneric #-}
module Diffing where
import GHC.Generics
import Data.Typeable
import Data.Aeson
import Component
import Unsafe.Coerce (unsafeCoerce)
{-
Since actions target specific locations, we can't stop going the tree early
because changes may have happened beneath the top level. kind of the
downside not having a single, passed down, state.
We still need render, but render needs to be targeted to specific locations.
I dunno how it should work lol.
Let's start at the basics, with dumb tests. If there's a div in the new
tree, and not one in the old tree, it should produce something saying
to add that div.
To know where to make a change, I guess you need a location and a command.
-}
type Location = [Int]
data Change a = Update Location a | Delete Location a | Add Location a
deriving (Show, Eq, Generic)
instance ToJSON a => ToJSON (Change a) where
toEncoding = genericToEncoding defaultOptions
diff
:: Maybe Location
-> Location
-> Purview parentAction action m
-> Purview parentAction action m
-> [Change (Purview parentAction action m)]
diff target location oldGraph newGraph = case (oldGraph, newGraph) of
(Html kind children, Html kind' children') ->
concatMap
(\(index, oldChild, newChild) -> diff target (index:location) oldChild newChild)
(zip3 [0..] children children')
(Text str, Text str') ->
[Update location (Text str') | str /= str']
(Html kind children, unknown) ->
[Update location newGraph]
(unknown, Html kind children) ->
[Update location newGraph]
(Hide (EffectHandler _ loc state _ cont), Hide (EffectHandler _ loc' newState _ newCont)) ->
case cast state of
Just state' ->
[Update location newGraph | state' /= newState && loc == loc']
-- TODO: this is weak, instead of walking the whole tree it should be targetted
-- to specific effect handlers
-- if we hit the target, we're already saying update the whole tree
<> if Just location == target
then []
else diff target (0:location) (unsafeCoerce cont state) (unsafeCoerce newCont newState)
-- different kinds of state
Nothing ->
[Update location newGraph]
((Attribute attr a), (Attribute attr' b)) ->
[Update location newGraph | attr /= attr']
((Value _), _) ->
[Update location newGraph]
((EffectHandler _ _ _ _ _), _) ->
[Update location newGraph]
(_, _) -> [Update location newGraph]
-- (a, b) -> error (show a <> "\n" <> show b)