packages feed

lambdatex-0.1.1.0: src/Text/LaTeX/LambdaTeX/Package/Internal.hs

{-# 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
      where
        -- We're looking for this: TeXComm "documentclass" ...
        -- We have to go looking through the LaTeX :(
        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]