{-# LANGUAGE OverloadedStrings #-} module Text.LaTeX.LambdaTeX.Package.Internal where import Control.Monad import Data.List import Text.LaTeX.Base.Syntax import Text.LaTeX.LambdaTeX.Package.Types import Text.LaTeX.LambdaTeX.Types -- | Inject package dependencies into a given LaTeX document. -- This is done by top-level functions in @Text.LaTeX.LambdaTeX@ automatically injectPackageDependencies :: [PackageDep] -> LaTeX -> Maybe LaTeX injectPackageDependencies packages latex = do ps <- reorderPackages packages let packageDecs = mconcat $ map (\(PackageDep name args) -> usepackage args name) ps pure (inject packageDecs latex) where inject ps = go -- We're looking for this: TeXComm "documentclass" ... -- We have to go looking through the LaTeX :( where go t@(TeXComm "documentclass" _) = TeXSeq t ps go (TeXSeq t1 t2) = TeXSeq (go t1) (go t2) go c = c reorderPackages :: [PackageDep] -> Maybe [PackageDep] reorderPackages deps = do bestEffort <- foldM (flip reorderToSatisfy) deps allPackageCombinationRules if all (`ruleIsSatisfied` bestEffort) allPackageCombinationRules then Just bestEffort else Nothing data PackageRule = PackageOrder Text Text deriving (Show, Eq) -- Yes it's dumb, but it works for now. reorderToSatisfy :: PackageRule -> [PackageDep] -> Maybe [PackageDep] reorderToSatisfy (PackageOrder n1 n2) ps = Just $ case (,) <$> elemIndex n1 names <*> elemIndex n2 names of Nothing -> ps Just (ix1, ix2) -> if ix1 < ix2 then ps else (ps !! ix1) : (ps !! ix2) : map snd (filter (\(ix, _) -> ix /= ix1 && ix /= ix2) (zip [0 ..] ps)) where names = map packageDepName ps ruleIsSatisfied :: PackageRule -> [PackageDep] -> Bool ruleIsSatisfied (PackageOrder n1 n2) ps = case (<) <$> elemIndex n1 names <*> elemIndex n2 names of Just False -> False _ -> True where names = map packageDepName ps allPackageCombinationRules :: [PackageRule] allPackageCombinationRules = [mintedLibertineRule] mintedLibertineRule :: PackageRule mintedLibertineRule = PackageOrder "minted" "libertine" -- | Redefinition of @usepackage@ to use Text -- Don't use this directly, use the packageDep instead! usepackage :: [LaTeX] -> Text -> LaTeX usepackage ls pn = TeXComm "usepackage" [MOptArg ls, FixArg $ TeXRaw pn]