{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -fsimpl-tick-factor=140 #-} module Typst.Show (applyShowRules) where import Control.Monad (foldM) import Data.Array ((!)) import qualified Data.Map as M import Data.Sequence (Seq) import qualified Data.Sequence as Seq import Data.Text (Text) import qualified Data.Text as T import Text.Parsec (getState, updateState, (<|>)) import qualified Text.Regex.TDFA as TDFA import Typst.Regex (RE (..), makeLiteralRE) import Typst.Syntax import Typst.Types -- import Debug.Trace applyShowRules :: Monad m => Seq Content -> MP m (Seq Content) applyShowRules cs = do rules <- evalShowRules <$> getState foldM (tryShowRules rules) mempty cs withoutShowRule :: Monad m => Selector -> MP m a -> MP m a withoutShowRule selector pa = do oldShowRules <- evalShowRules <$> getState updateState $ \st -> st { evalShowRules = [ ShowRule sel f | ShowRule sel f <- evalShowRules st, sel /= selector ] } res <- pa updateState $ \st -> st {evalShowRules = oldShowRules} pure res -- By experiment, it seems that show rules work this way: -- the first (i.e. most recently defined) one to match a given element -- are applied first. tryShowRules :: Monad m => [ShowRule] -> Seq Content -> Content -> MP m (Seq Content) tryShowRules [] cs c = pure $ cs Seq.|> c tryShowRules (ShowRule sel f : rs) cs c = do c' <- case c of Elt name pos fields -> do let applyToVal (VContent cs') = VContent <$> foldMap (tryShowRules [ShowRule sel f] mempty) cs' applyToVal (VArray as) = VArray <$> mapM applyToVal as applyToVal x = pure x fields' <- mapM applyToVal fields pure $ Elt name pos fields' _ -> pure c case (sel, c') of (SelectString s, Txt t) -> ( do re <- makeLiteralRE s withoutShowRule sel ((cs <>) <$> (replaceRegexContent re t f >>= applyShowRules)) ) <|> tryShowRules rs cs c' (SelectRegex re, Txt t) -> ( withoutShowRule sel ((cs <>) <$> (replaceRegexContent re t f >>= applyShowRules)) ) <|> tryShowRules rs cs c' (SelectLabel s, elt@(Elt _ _ fields)) | Just (VLabel s') <- M.lookup "label" fields, s' == s -> withoutShowRule sel ((cs <>) <$> (f elt >>= applyShowRules)) (SelectElement name fields, elt@(Elt name' _ fields')) | name == name', fieldsMatch fields fields' -> withoutShowRule sel $ (cs <>) <$> (f elt >>= applyShowRules) (SelectOr _sel1 _sel2, _elt) -> fail "or is not yet implemented for select" (SelectAnd _sel1 _sel2, _elt) -> fail "and is not yet implemented for select" (SelectBefore _sel1 _sel2, _elt) -> fail "before is not yet implemented for select" (SelectAfter _sel1 _sel2, _elt) -> fail "after is not yet implemented for select" _ -> tryShowRules rs cs c' fieldsMatch :: [(Identifier, Val)] -> (M.Map Identifier Val) -> Bool fieldsMatch [] _ = True fieldsMatch ((k, v) : rest) m = ( case M.lookup k m of Just v' -> v == v' Nothing -> False ) && fieldsMatch rest m replaceRegexContent :: Monad m => RE -> Text -> (forall m'. Monad m' => Content -> MP m' (Seq Content)) -> MP m (Seq Content) replaceRegexContent (RE _ re) strIn f = let matches = map (! 0) (TDFA.matchAll re strIn) go _i str [] = pure $ Seq.singleton (Txt str) go i str ((off, len) : rest) = let i' = off + len before = T.take (off - i) str matched = T.take len (T.drop (off - i) str) after = T.drop (i' - i) str in seq i' $ (\x y -> Seq.singleton (Txt before) <> x <> y) <$> f (Txt matched) <*> go i' after rest in go 0 strIn matches