--------------------------------------------------------------------------------
-- | Module for trimming whitespace from tempaltes.
module Hakyll.Web.Template.Internal.Trim
    ( trim
    ) where


--------------------------------------------------------------------------------
import           Data.Char                            (isSpace)
import           Data.List                            (dropWhileEnd)


--------------------------------------------------------------------------------
import           Hakyll.Web.Template.Internal.Element


--------------------------------------------------------------------------------
trim :: [TemplateElement] -> [TemplateElement]
trim :: [TemplateElement] -> [TemplateElement]
trim = [TemplateElement] -> [TemplateElement]
cleanse ([TemplateElement] -> [TemplateElement])
-> ([TemplateElement] -> [TemplateElement])
-> [TemplateElement]
-> [TemplateElement]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [TemplateElement] -> [TemplateElement]
canonicalize


--------------------------------------------------------------------------------
-- | Apply the Trim nodes to the Chunks.
cleanse :: [TemplateElement] -> [TemplateElement]
cleanse :: [TemplateElement] -> [TemplateElement]
cleanse = ([TemplateElement] -> [TemplateElement])
-> [TemplateElement] -> [TemplateElement]
recurse [TemplateElement] -> [TemplateElement]
cleanse ([TemplateElement] -> [TemplateElement])
-> ([TemplateElement] -> [TemplateElement])
-> [TemplateElement]
-> [TemplateElement]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [TemplateElement] -> [TemplateElement]
process
    where process :: [TemplateElement] -> [TemplateElement]
process [] = []
          process (TemplateElement
TrimR:Chunk String
str:[TemplateElement]
ts) = let str' :: String
str' = (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
dropWhile Char -> Bool
isSpace String
str
                                         in if String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
str'
                                                then [TemplateElement] -> [TemplateElement]
process [TemplateElement]
ts
                                                -- Might need to TrimL.
                                                else [TemplateElement] -> [TemplateElement]
process ([TemplateElement] -> [TemplateElement])
-> [TemplateElement] -> [TemplateElement]
forall a b. (a -> b) -> a -> b
$ String -> TemplateElement
Chunk String
str'TemplateElement -> [TemplateElement] -> [TemplateElement]
forall a. a -> [a] -> [a]
:[TemplateElement]
ts

          process (Chunk String
str:TemplateElement
TrimL:[TemplateElement]
ts) = let str' :: String
str' = (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
dropWhileEnd Char -> Bool
isSpace String
str
                                         in if String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
str'
                                                then [TemplateElement] -> [TemplateElement]
process [TemplateElement]
ts
                                                else String -> TemplateElement
Chunk String
str'TemplateElement -> [TemplateElement] -> [TemplateElement]
forall a. a -> [a] -> [a]
:[TemplateElement] -> [TemplateElement]
process [TemplateElement]
ts

          process (TemplateElement
t:[TemplateElement]
ts) = TemplateElement
tTemplateElement -> [TemplateElement] -> [TemplateElement]
forall a. a -> [a] -> [a]
:[TemplateElement] -> [TemplateElement]
process [TemplateElement]
ts

--------------------------------------------------------------------------------
-- | Enforce the invariant that:
--
--     * Every 'TrimL' has a 'Chunk' to its left.
--     * Every 'TrimR' has a 'Chunk' to its right.
--
canonicalize :: [TemplateElement] -> [TemplateElement]
canonicalize :: [TemplateElement] -> [TemplateElement]
canonicalize = [TemplateElement] -> [TemplateElement]
go
    where go :: [TemplateElement] -> [TemplateElement]
go [TemplateElement]
t = let t' :: [TemplateElement]
t' = [TemplateElement] -> [TemplateElement]
redundant ([TemplateElement] -> [TemplateElement])
-> ([TemplateElement] -> [TemplateElement])
-> [TemplateElement]
-> [TemplateElement]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [TemplateElement] -> [TemplateElement]
swap ([TemplateElement] -> [TemplateElement])
-> [TemplateElement] -> [TemplateElement]
forall a b. (a -> b) -> a -> b
$ [TemplateElement] -> [TemplateElement]
dedupe [TemplateElement]
t
                 in if [TemplateElement]
t [TemplateElement] -> [TemplateElement] -> Bool
forall a. Eq a => a -> a -> Bool
== [TemplateElement]
t' then [TemplateElement]
t else [TemplateElement] -> [TemplateElement]
go [TemplateElement]
t'


--------------------------------------------------------------------------------
-- | Remove the 'TrimR' and 'TrimL's that are no-ops.
redundant :: [TemplateElement] -> [TemplateElement]
redundant :: [TemplateElement] -> [TemplateElement]
redundant = ([TemplateElement] -> [TemplateElement])
-> [TemplateElement] -> [TemplateElement]
recurse [TemplateElement] -> [TemplateElement]
redundant ([TemplateElement] -> [TemplateElement])
-> ([TemplateElement] -> [TemplateElement])
-> [TemplateElement]
-> [TemplateElement]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [TemplateElement] -> [TemplateElement]
process
    where -- Remove the leading 'TrimL's.
          process :: [TemplateElement] -> [TemplateElement]
process (TemplateElement
TrimL:[TemplateElement]
ts) = [TemplateElement] -> [TemplateElement]
process [TemplateElement]
ts
          -- Remove trailing 'TrimR's.
          process [TemplateElement]
ts = (TemplateElement -> [TemplateElement] -> [TemplateElement])
-> [TemplateElement] -> [TemplateElement] -> [TemplateElement]
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr TemplateElement -> [TemplateElement] -> [TemplateElement]
trailing [] [TemplateElement]
ts
              where trailing :: TemplateElement -> [TemplateElement] -> [TemplateElement]
trailing TemplateElement
TrimR [] = []
                    trailing TemplateElement
x [TemplateElement]
xs     = TemplateElement
xTemplateElement -> [TemplateElement] -> [TemplateElement]
forall a. a -> [a] -> [a]
:[TemplateElement]
xs


--------------------------------------------------------------------------------
-- >>> swap $ [TrimR, TrimL]
-- [TrimL, TrimR]
swap :: [TemplateElement] -> [TemplateElement]
swap :: [TemplateElement] -> [TemplateElement]
swap = ([TemplateElement] -> [TemplateElement])
-> [TemplateElement] -> [TemplateElement]
recurse [TemplateElement] -> [TemplateElement]
swap ([TemplateElement] -> [TemplateElement])
-> ([TemplateElement] -> [TemplateElement])
-> [TemplateElement]
-> [TemplateElement]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [TemplateElement] -> [TemplateElement]
process
    where process :: [TemplateElement] -> [TemplateElement]
process []               = []
          process (TemplateElement
TrimR:TemplateElement
TrimL:[TemplateElement]
ts) = TemplateElement
TrimLTemplateElement -> [TemplateElement] -> [TemplateElement]
forall a. a -> [a] -> [a]
:[TemplateElement] -> [TemplateElement]
process (TemplateElement
TrimRTemplateElement -> [TemplateElement] -> [TemplateElement]
forall a. a -> [a] -> [a]
:[TemplateElement]
ts)
          process (TemplateElement
t:[TemplateElement]
ts)           = TemplateElement
tTemplateElement -> [TemplateElement] -> [TemplateElement]
forall a. a -> [a] -> [a]
:[TemplateElement] -> [TemplateElement]
process [TemplateElement]
ts


--------------------------------------------------------------------------------
-- | Remove 'TrimR' and 'TrimL' duplication.
dedupe :: [TemplateElement] -> [TemplateElement]
dedupe :: [TemplateElement] -> [TemplateElement]
dedupe = ([TemplateElement] -> [TemplateElement])
-> [TemplateElement] -> [TemplateElement]
recurse [TemplateElement] -> [TemplateElement]
dedupe ([TemplateElement] -> [TemplateElement])
-> ([TemplateElement] -> [TemplateElement])
-> [TemplateElement]
-> [TemplateElement]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [TemplateElement] -> [TemplateElement]
process
    where process :: [TemplateElement] -> [TemplateElement]
process []               = []
          process (TemplateElement
TrimR:TemplateElement
TrimR:[TemplateElement]
ts) = [TemplateElement] -> [TemplateElement]
process (TemplateElement
TrimRTemplateElement -> [TemplateElement] -> [TemplateElement]
forall a. a -> [a] -> [a]
:[TemplateElement]
ts)
          process (TemplateElement
TrimL:TemplateElement
TrimL:[TemplateElement]
ts) = [TemplateElement] -> [TemplateElement]
process (TemplateElement
TrimLTemplateElement -> [TemplateElement] -> [TemplateElement]
forall a. a -> [a] -> [a]
:[TemplateElement]
ts)
          process (TemplateElement
t:[TemplateElement]
ts)           = TemplateElement
tTemplateElement -> [TemplateElement] -> [TemplateElement]
forall a. a -> [a] -> [a]
:[TemplateElement] -> [TemplateElement]
process [TemplateElement]
ts


--------------------------------------------------------------------------------
-- | @'recurse' f t@ applies f to every '[TemplateElement]' in t.
recurse :: ([TemplateElement] -> [TemplateElement])
        -> [TemplateElement]
        -> [TemplateElement]
recurse :: ([TemplateElement] -> [TemplateElement])
-> [TemplateElement] -> [TemplateElement]
recurse [TemplateElement] -> [TemplateElement]
_ []     = []
recurse [TemplateElement] -> [TemplateElement]
f (TemplateElement
x:[TemplateElement]
xs) = TemplateElement -> TemplateElement
process TemplateElement
xTemplateElement -> [TemplateElement] -> [TemplateElement]
forall a. a -> [a] -> [a]
:([TemplateElement] -> [TemplateElement])
-> [TemplateElement] -> [TemplateElement]
recurse [TemplateElement] -> [TemplateElement]
f [TemplateElement]
xs
    where process :: TemplateElement -> TemplateElement
process TemplateElement
y = case TemplateElement
y of
                          If TemplateExpr
e [TemplateElement]
tb Maybe [TemplateElement]
eb -> TemplateExpr
-> [TemplateElement] -> Maybe [TemplateElement] -> TemplateElement
If TemplateExpr
e ([TemplateElement] -> [TemplateElement]
f [TemplateElement]
tb) ([TemplateElement] -> [TemplateElement]
f ([TemplateElement] -> [TemplateElement])
-> Maybe [TemplateElement] -> Maybe [TemplateElement]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe [TemplateElement]
eb)
                          For TemplateExpr
e [TemplateElement]
t Maybe [TemplateElement]
s  -> TemplateExpr
-> [TemplateElement] -> Maybe [TemplateElement] -> TemplateElement
For TemplateExpr
e ([TemplateElement] -> [TemplateElement]
f [TemplateElement]
t) ([TemplateElement] -> [TemplateElement]
f ([TemplateElement] -> [TemplateElement])
-> Maybe [TemplateElement] -> Maybe [TemplateElement]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe [TemplateElement]
s)
                          TemplateElement
_          -> TemplateElement
y