lambdatex 0.1.1.0 → 0.2.0.0
raw patch · 21 files changed
+439/−371 lines, 21 filesdep +filepathsetup-changedPVP ok
version bump matches the API change (PVP)
Dependencies added: filepath
API changes (from Hackage documentation)
- Text.LaTeX.LambdaTeX: data GenerationConfig
- Text.LaTeX.LambdaTeX.Types: data ΛConfig
- Text.LaTeX.LambdaTeX.Types: data ΛState
+ Text.LaTeX.LambdaTeX: [projectBuildDir] :: ProjectConfig -> FilePath
+ Text.LaTeX.LambdaTeX: instance GHC.Classes.Eq Text.LaTeX.LambdaTeX.GenerationConfig
+ Text.LaTeX.LambdaTeX: instance GHC.Classes.Eq Text.LaTeX.LambdaTeX.ProjectConfig
+ Text.LaTeX.LambdaTeX: instance GHC.Show.Show Text.LaTeX.LambdaTeX.GenerationConfig
+ Text.LaTeX.LambdaTeX: instance GHC.Show.Show Text.LaTeX.LambdaTeX.ProjectConfig
+ Text.LaTeX.LambdaTeX: newtype GenerationConfig
+ Text.LaTeX.LambdaTeX.Types: newtype ΛConfig
+ Text.LaTeX.LambdaTeX.Types: newtype ΛState
- Text.LaTeX.LambdaTeX: ProjectConfig :: GenerationConfig -> String -> String -> ProjectConfig
+ Text.LaTeX.LambdaTeX: ProjectConfig :: GenerationConfig -> String -> String -> FilePath -> ProjectConfig
- Text.LaTeX.LambdaTeX: execLambdaTeXT :: Monad m => ΛTeXT m a -> GenerationConfig -> m ([ΛError], LaTeX, [Reference], [(String, IO ())])
+ Text.LaTeX.LambdaTeX: execLambdaTeXT :: Monad m => ΛTeXT m a -> GenerationConfig -> m ([ΛError], LaTeX, [Reference], [(String, FilePath -> IO ())])
- Text.LaTeX.LambdaTeX.Action: registerAction :: Monad m => String -> IO () -> ΛTeXT m ()
+ Text.LaTeX.LambdaTeX.Action: registerAction :: Monad m => String -> (FilePath -> IO ()) -> ΛTeXT m ()
- Text.LaTeX.LambdaTeX.Types: [outputActions] :: ΛOutput -> [(String, IO ())]
+ Text.LaTeX.LambdaTeX.Types: [outputActions] :: ΛOutput -> [(String, FilePath -> IO ())]
- Text.LaTeX.LambdaTeX.Types: runΛTeX :: ΛTeXT m a -> ΛConfig -> ΛState -> m ((a, LaTeX), ΛState, ΛOutput)
+ Text.LaTeX.LambdaTeX.Types: runΛTeX :: Monad m => ΛTeXT m a -> ΛConfig -> ΛState -> m ((a, LaTeX), ΛState, ΛOutput)
- Text.LaTeX.LambdaTeX.Types: ΛOutput :: Set PackageDep -> Set Reference -> Set Text -> Set Text -> [(String, IO ())] -> ΛOutput
+ Text.LaTeX.LambdaTeX.Types: ΛOutput :: Set PackageDep -> Set Reference -> Set Text -> Set Text -> [(String, FilePath -> IO ())] -> ΛOutput
Files
- Setup.hs +1/−0
- lambdatex.cabal +64/−63
- src/Text/LaTeX/LambdaTeX.hs +62/−68
- src/Text/LaTeX/LambdaTeX/Action.hs +8/−5
- src/Text/LaTeX/LambdaTeX/Error.hs +1/−1
- src/Text/LaTeX/LambdaTeX/Package.hs +13/−9
- src/Text/LaTeX/LambdaTeX/Package/Internal.hs +31/−26
- src/Text/LaTeX/LambdaTeX/Package/Types.hs +6/−6
- src/Text/LaTeX/LambdaTeX/Part.hs +7/−7
- src/Text/LaTeX/LambdaTeX/Reference.hs +14/−19
- src/Text/LaTeX/LambdaTeX/Reference/Internal.hs +22/−23
- src/Text/LaTeX/LambdaTeX/Reference/Types.hs +7/−8
- src/Text/LaTeX/LambdaTeX/Selection.hs +9/−11
- src/Text/LaTeX/LambdaTeX/Selection/Internal.hs +26/−25
- src/Text/LaTeX/LambdaTeX/Selection/Types.hs +6/−5
- src/Text/LaTeX/LambdaTeX/Types.hs +72/−86
- src/Text/LaTeX/LambdaTeX/Utils.hs +7/−7
- test/MainTest.hs +3/−2
- test/Text/LaTeX/LambdaTeX/Part/Arbitrary.hs +11/−0
- test/Text/LaTeX/LambdaTeX/Selection/Arbitrary.hs +11/−0
- test/Text/LaTeX/LambdaTeX/Selection/InternalSpec.hs +58/−0
Setup.hs view
@@ -1,2 +1,3 @@ import Distribution.Simple+ main = defaultMain
lambdatex.cabal view
@@ -1,68 +1,69 @@-name: lambdatex-version: 0.1.1.0-synopsis: Type-Safe LaTeX EDSL-description: - ΛTeX, pronounced 'LambdaTeX' is a Haskell EDSL that adds type-safety to LaTeX.- ΛTeX-generated LaTeX code should compile without errors.+name: lambdatex+version: 0.2.0.0+cabal-version: >=1.10+build-type: Simple+license: GPL+license-file: LICENSE+copyright: 2015 Tom Sydney Kerckhove+maintainer: syd.kerckhove@gmail.com+homepage: http://github.com/NorfairKing/lambdatex+synopsis: Type-Safe LaTeX EDSL+description:+ ΛTeX, pronounced 'LambdaTeX' is a Haskell EDSL that adds type-safety to LaTeX.+ ΛTeX-generated LaTeX code should compile without errors.+ TODO(kerckhove) big fancy example+category: Text+author: Tom Sydney Kerckhove - TODO(kerckhove) big fancy example-homepage: http://github.com/NorfairKing/lambdatex-license: GPL-license-file: LICENSE-author: Tom Sydney Kerckhove-maintainer: syd.kerckhove@gmail.com-copyright: 2015 Tom Sydney Kerckhove-category: Text-build-type: Simple--- extra-source-files:-cabal-version: >=1.10+source-repository head+ type: git+ location: https://github.com/NorfairKing/lambdatex library- hs-source-dirs: src- exposed-modules: Text.LaTeX.LambdaTeX- Text.LaTeX.LambdaTeX.Action- Text.LaTeX.LambdaTeX.Error- Text.LaTeX.LambdaTeX.Package- Text.LaTeX.LambdaTeX.Package.Internal- Text.LaTeX.LambdaTeX.Package.Types- Text.LaTeX.LambdaTeX.Part- Text.LaTeX.LambdaTeX.Reference- Text.LaTeX.LambdaTeX.Reference.Internal- Text.LaTeX.LambdaTeX.Reference.Types- Text.LaTeX.LambdaTeX.Selection- Text.LaTeX.LambdaTeX.Selection.Internal- Text.LaTeX.LambdaTeX.Selection.Types- Text.LaTeX.LambdaTeX.Types- Text.LaTeX.LambdaTeX.Utils- build-depends: base >= 4.8 && < 5.0- , HaTeX >= 3.16 && < 3.18- , mtl >= 2.2 && < 2.3- , text >= 1.2 && < 1.3- , containers >= 0.5 && < 0.6- , transformers >= 0.4 && < 0.6- , directory >= 1.2 && < 1.3- , async >= 2.0 && < 2.2- ghc-options: -Wall- -fwarn-unused-imports- -fwarn-incomplete-patterns- -fno-warn-unused-do-bind- -fno-warn-name-shadowing- -fno-warn-orphans- default-language: Haskell2010+ exposed-modules:+ Text.LaTeX.LambdaTeX+ Text.LaTeX.LambdaTeX.Action+ Text.LaTeX.LambdaTeX.Error+ Text.LaTeX.LambdaTeX.Package+ Text.LaTeX.LambdaTeX.Package.Internal+ Text.LaTeX.LambdaTeX.Package.Types+ Text.LaTeX.LambdaTeX.Part+ Text.LaTeX.LambdaTeX.Reference+ Text.LaTeX.LambdaTeX.Reference.Internal+ Text.LaTeX.LambdaTeX.Reference.Types+ Text.LaTeX.LambdaTeX.Selection+ Text.LaTeX.LambdaTeX.Selection.Internal+ Text.LaTeX.LambdaTeX.Selection.Types+ Text.LaTeX.LambdaTeX.Types+ Text.LaTeX.LambdaTeX.Utils+ build-depends:+ base >=4.8 && <5.0,+ HaTeX >=3.16 && <3.18,+ mtl >=2.2 && <2.3,+ text >=1.2 && <1.3,+ containers >=0.5 && <0.6,+ transformers >=0.4 && <0.6,+ directory >=1.2 && <1.4,+ async >=2.0 && <2.2,+ filepath >=1.4 && <1.5+ default-language: Haskell2010+ hs-source-dirs: src+ ghc-options: -Wall -fwarn-unused-imports -fwarn-incomplete-patterns -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-orphans test-suite lambdatex-test- type: exitcode-stdio-1.0- hs-source-dirs: test- main-is: MainTest.hs- build-depends: base- , lambdatex- , hspec- , QuickCheck- , text >= 1.2 && < 1.3- , quickcheck-text >= 0.1 && < 0.2- ghc-options: -threaded -rtsopts -with-rtsopts=-N- default-language: Haskell2010--source-repository head- type: git- location: https://github.com/NorfairKing/lambdatex+ type: exitcode-stdio-1.0+ main-is: MainTest.hs+ build-depends:+ base -any,+ lambdatex -any,+ hspec -any,+ QuickCheck -any,+ text >=1.2 && <1.3,+ quickcheck-text >=0.1 && <0.2+ default-language: Haskell2010+ hs-source-dirs: test+ other-modules:+ Text.LaTeX.LambdaTeX.Part.Arbitrary+ Text.LaTeX.LambdaTeX.Selection.Arbitrary+ Text.LaTeX.LambdaTeX.Selection.InternalSpec+ ghc-options: -threaded -rtsopts -with-rtsopts=-N
src/Text/LaTeX/LambdaTeX.hs view
@@ -2,47 +2,41 @@ -- TODO(kerckhove) big example here! -- {-# LANGUAGE OverloadedStrings #-}-module Text.LaTeX.LambdaTeX (- -- * Building ΛTeX- module Text.LaTeX.LambdaTeX +module Text.LaTeX.LambdaTeX+ ( module Text.LaTeX.LambdaTeX -- ** Selections , module Text.LaTeX.LambdaTeX.Selection- -- ** References , module Text.LaTeX.LambdaTeX.Reference- -- ** Packages dependencies , module Text.LaTeX.LambdaTeX.Package- -- ** IO dependencies , module Text.LaTeX.LambdaTeX.Action- -- ** Re-exports , module Text.LaTeX.LambdaTeX.Types- ) where -import Control.Monad (forM_, void)-import Control.Monad.IO.Class (MonadIO (..))+import Control.Monad (forM_, void)+import Control.Monad.IO.Class (MonadIO(..)) -import Control.Concurrent.Async (async, wait)+import Control.Concurrent.Async (async, wait)+import System.FilePath -import qualified Data.Set as S-import qualified Data.Text.IO as T+import qualified Data.Set as S+import qualified Data.Text.IO as T -import Text.LaTeX.Base (LaTeX, renderFile)+import Text.LaTeX.Base (LaTeX, renderFile) -import Text.LaTeX.LambdaTeX.Action-import Text.LaTeX.LambdaTeX.Package-import Text.LaTeX.LambdaTeX.Package.Internal-import Text.LaTeX.LambdaTeX.Reference-import Text.LaTeX.LambdaTeX.Reference.Internal-import Text.LaTeX.LambdaTeX.Reference.Types-import Text.LaTeX.LambdaTeX.Selection-import Text.LaTeX.LambdaTeX.Selection.Types-import Text.LaTeX.LambdaTeX.Types-import Text.LaTeX.LambdaTeX.Utils+import Text.LaTeX.LambdaTeX.Action+import Text.LaTeX.LambdaTeX.Package+import Text.LaTeX.LambdaTeX.Package.Internal+import Text.LaTeX.LambdaTeX.Reference+import Text.LaTeX.LambdaTeX.Reference.Internal+import Text.LaTeX.LambdaTeX.Reference.Types+import Text.LaTeX.LambdaTeX.Selection+import Text.LaTeX.LambdaTeX.Selection.Types+import Text.LaTeX.LambdaTeX.Types -- | Build all the files for a LaTeX project given by a ΛTeXT generator -- This either returns Left with an error or Right () to signify success.@@ -53,33 +47,31 @@ -- * Automatic bibtex file generation -- * All safety provided by 'execLambdaTeXT' (in the form of textual errors) -- * Automatic asynchronic resolution of IO dependencies for graphviz or tikz figures-buildLaTeXProject :: MonadIO m => ΛTeXT m a -> ProjectConfig -> m (Either [ΛError] ())+buildLaTeXProject ::+ MonadIO m => ΛTeXT m a -> ProjectConfig -> m (Either [ΛError] ()) buildLaTeXProject func conf = do- (errs, latex, refs, actions) <- execLambdaTeXT func $ projectGenerationConfig conf--+ (errs, latex, refs, actions) <-+ execLambdaTeXT func $ projectGenerationConfig conf -- Render tex file let renderTex = do let mainTexFile = projectTexFileName conf ++ ".tex"- renderFile mainTexFile latex-+ renderFile (projectBuildDir conf </> mainTexFile) latex -- Render bib file let renderMain = do let mainBibFile = projectBibFileName conf ++ ".bib"- removeIfExists mainBibFile- T.appendFile mainBibFile $ renderReferences refs-+ T.writeFile (projectBuildDir conf </> mainBibFile) $+ renderReferences refs let performAction (name, action) = do- void action+ void $ action $ projectBuildDir conf putStrLn $ "Job " ++ name ++ " done."- -- Perform all the IO actions asynchronously- as <- liftIO $ mapM async $ renderTex : renderMain : map performAction actions+ as <-+ liftIO $ mapM async $ renderTex : renderMain : map performAction actions liftIO $ forM_ as wait-- return $ if null errs- then Right ()- else Left errs+ return $+ if null errs+ then Right ()+ else Left errs -- | Execute a ΛTeXT generation -- This either returns a tuple of the errors and a tuple of the resulting LaTeX value and a list of external references that need to be put into a bibtex file.@@ -91,60 +83,62 @@ -- * Internal dependency safety. No more '??' for external references in the internal pdf. -- * Package dependency resolution, TODO(kerckhove) with packages in the right order -- * Dependency selection of figure dependencies on graphviz or tikz figures-execLambdaTeXT :: Monad m => ΛTeXT m a -> GenerationConfig -> m ([ΛError], LaTeX, [Reference], [(String, IO ())])+execLambdaTeXT ::+ Monad m+ => ΛTeXT m a+ -> GenerationConfig+ -> m ([ΛError], LaTeX, [Reference], [(String, FilePath -> IO ())]) execLambdaTeXT func conf = do- ((_,latex), _, output) <- runΛTeX func (ΛConfig $ generationSelection conf) initState-- let mresult = injectPackageDependencies (S.toList $ outputPackageDependencies output) latex- let (extraErrs, result) = case mresult of- Nothing -> ([IncompatibleDependencies], latex)- Just res -> ([], res)-+ ((_, latex), _, output) <-+ runΛTeX func (ΛConfig $ generationSelection conf) initState+ let mresult =+ injectPackageDependencies+ (S.toList $ outputPackageDependencies output)+ latex+ let (extraErrs, result) =+ case mresult of+ Nothing -> ([IncompatibleDependencies], latex)+ Just res -> ([], res) let refs = S.toList $ outputExternalReferences output let actions = outputActions output- -- Check reference errors let made = outputLabelsMade output needed = outputLabelsNeeded output diff = S.difference needed made- let referss = map ReferenceMissing $ S.toList diff- return (extraErrs ++ referss, result, refs, actions)- where initState :: ΛState- initState = ΛState { stateCurrentPart = emptyPart }-+ initState = ΛState {stateCurrentPart = emptyPart} -- * Configuration- -- | Configuration of a ΛTeX project-data ProjectConfig = ProjectConfig {- projectGenerationConfig :: GenerationConfig- , projectBibFileName :: String- , projectTexFileName :: String- }+data ProjectConfig = ProjectConfig+ { projectGenerationConfig :: GenerationConfig+ , projectBibFileName :: String+ , projectTexFileName :: String+ , projectBuildDir :: FilePath+ } deriving (Show, Eq) -- | Default project configuration. -- -- Modify this instead of building your own 'ProjectConfig' defaultProjectConfig :: ProjectConfig-defaultProjectConfig = ProjectConfig {- projectGenerationConfig = defaultGenerationConfig+defaultProjectConfig =+ ProjectConfig+ { projectGenerationConfig = defaultGenerationConfig , projectBibFileName = "main" , projectTexFileName = "main"+ , projectBuildDir = "." } -- | Configuration of ΛTeX generation-data GenerationConfig = GenerationConfig {- generationSelection :: Selection- }+newtype GenerationConfig = GenerationConfig+ { generationSelection :: Selection+ } deriving (Show, Eq) -- | Default generation config. -- -- Modify this instead of building your own 'GenerationConfig' defaultGenerationConfig :: GenerationConfig-defaultGenerationConfig = GenerationConfig {- generationSelection = [All]- }+defaultGenerationConfig = GenerationConfig {generationSelection = [All]}
src/Text/LaTeX/LambdaTeX/Action.hs view
@@ -1,12 +1,15 @@ {-# LANGUAGE OverloadedStrings #-}+ module Text.LaTeX.LambdaTeX.Action where -import Text.LaTeX.LambdaTeX.Types+import Text.LaTeX.LambdaTeX.Types -- | Register an IO action (with a given name) that needs to be completed before the pdf can be built. -- -- Use this to generate and build external resources that are included with @includegraphics@ for example.-registerAction :: Monad m => String -- ^ Name of the job- -> IO () -- ^ Job- -> ΛTeXT m ()-registerAction name func = λtell $ mempty { outputActions = [(name, func)] }+registerAction ::+ Monad m+ => String -- ^ Name of the job+ -> (FilePath -> IO ()) -- ^ Job, it takes the project directory as an argument+ -> ΛTeXT m ()+registerAction name func = λtell $ mempty {outputActions = [(name, func)]}
src/Text/LaTeX/LambdaTeX/Error.hs view
@@ -1,6 +1,6 @@ module Text.LaTeX.LambdaTeX.Error where -import Data.Text (Text)+import Data.Text (Text) data ΛError = ReferenceMissing Text
src/Text/LaTeX/LambdaTeX/Package.hs view
@@ -3,19 +3,23 @@ -- Note: If you want different arguments to a package, you have to specify this by making a 'manual' 'packageDep' declaration before your document. module Text.LaTeX.LambdaTeX.Package where -import Text.LaTeX.Base.Syntax+import Text.LaTeX.Base.Syntax -import qualified Data.Set as S+import qualified Data.Set as S -import Text.LaTeX.LambdaTeX.Package.Types-import Text.LaTeX.LambdaTeX.Types+import Text.LaTeX.LambdaTeX.Package.Types+import Text.LaTeX.LambdaTeX.Types -- | Declare a dependency to a package, with given arguments-packageDep :: Monad m => [LaTeX] -- ^ Arguments- -> Text -- ^ Name of the LaTeX package- -> ΛTeXT m ()-packageDep args name = λtell $ mempty { outputPackageDependencies = S.singleton (PackageDep name args) }+packageDep ::+ Monad m+ => [LaTeX] -- ^ Arguments+ -> Text -- ^ Name of the LaTeX package+ -> ΛTeXT m ()+packageDep args name =+ λtell $+ mempty {outputPackageDependencies = S.singleton (PackageDep name args)} -- | Declare a dependency to a package without any arguments packageDep_ :: Monad m => Text -> ΛTeXT m ()-packageDep_ name = packageDep [] name+packageDep_ = packageDep []
src/Text/LaTeX/LambdaTeX/Package/Internal.hs view
@@ -1,67 +1,73 @@ {-# LANGUAGE OverloadedStrings #-}+ module Text.LaTeX.LambdaTeX.Package.Internal where -import Control.Monad-import Data.List+import Control.Monad+import Data.List -import Text.LaTeX.Base.Syntax+import Text.LaTeX.Base.Syntax -import Text.LaTeX.LambdaTeX.Package.Types-import Text.LaTeX.LambdaTeX.Types+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+ 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 :(+ 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+ then Just bestEffort+ else Nothing -data PackageRule- = PackageOrder Text Text+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 $+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+ 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+ruleIsSatisfied (PackageOrder n1 n2) ps =+ case (<) <$> elemIndex n1 names <*> elemIndex n2 names of Just False -> False _ -> True- where names = map packageDepName ps+ where+ names = map packageDepName ps allPackageCombinationRules :: [PackageRule]-allPackageCombinationRules =- [ mintedLibertineRule- ]+allPackageCombinationRules = [mintedLibertineRule] mintedLibertineRule :: PackageRule mintedLibertineRule = PackageOrder "minted" "libertine"@@ -70,4 +76,3 @@ -- Don't use this directly, use the packageDep instead! usepackage :: [LaTeX] -> Text -> LaTeX usepackage ls pn = TeXComm "usepackage" [MOptArg ls, FixArg $ TeXRaw pn]-
src/Text/LaTeX/LambdaTeX/Package/Types.hs view
@@ -1,14 +1,14 @@ module Text.LaTeX.LambdaTeX.Package.Types where -import Data.Ord (comparing)-import Data.Text (Text)+import Data.Ord (comparing)+import Data.Text (Text) -import Text.LaTeX.Base.Syntax (LaTeX)+import Text.LaTeX.Base.Syntax (LaTeX) -data PackageDep = PackageDep {- packageDepName :: Text+data PackageDep = PackageDep+ { packageDepName :: Text , packageDepOptions :: [LaTeX]- } deriving (Show, Eq)+ } deriving (Show, Eq) instance Ord PackageDep where compare = comparing packageDepName
src/Text/LaTeX/LambdaTeX/Part.hs view
@@ -1,16 +1,16 @@ module Text.LaTeX.LambdaTeX.Part where -import Data.Text (Text)+import Data.Text (Text) -newtype Part = Part { unPart :: [Text] }- deriving Show+newtype Part = Part+ { unPart :: [Text]+ } deriving (Show) emptyPart :: Part-emptyPart = Part { unPart = [] }+emptyPart = Part {unPart = []} pushPart :: Part -> Text -> Part-pushPart p t = Part { unPart = unPart p ++ [t] }+pushPart p t = Part {unPart = unPart p ++ [t]} popPart :: Part -> Part-popPart p = Part { unPart = init $ unPart p }-+popPart p = Part {unPart = init $ unPart p}
src/Text/LaTeX/LambdaTeX/Reference.hs view
@@ -1,15 +1,16 @@ {-# LANGUAGE OverloadedStrings #-}+ module Text.LaTeX.LambdaTeX.Reference where -import Text.LaTeX.Base.Class-import Text.LaTeX.Base.Syntax+import Text.LaTeX.Base.Class+import Text.LaTeX.Base.Syntax -import qualified Text.LaTeX.Base.Commands as H (label, pageref,- ref)+import qualified Text.LaTeX.Base.Commands as H+ (label, pageref, ref) -import Text.LaTeX.LambdaTeX.Reference.Internal-import Text.LaTeX.LambdaTeX.Reference.Types-import Text.LaTeX.LambdaTeX.Types+import Text.LaTeX.LambdaTeX.Reference.Internal+import Text.LaTeX.LambdaTeX.Reference.Types+import Text.LaTeX.LambdaTeX.Types -- * Internal references -- An internal references point is defined by a label@@ -17,9 +18,7 @@ -- -- ΛTeX automatically ensures that those references are always internally consistent -- BUT you have to make sure that you use these functions instead of the regular ones.- -- ** Labels- -- | Label a part label :: Monad m => Text -> ΛTeXT m () label l = do@@ -40,27 +39,24 @@ -- * External references -- ** Defining references- -- TODO(kerckhove) Check for duplicate fields?- -- | Define an external reference. -- Use 'cite' or 'nocite' to use this in your document. makeReference :: ReferenceType -> Text -> [(Text, Text)] -> Reference makeReference = Reference -- ** Using references- -- | Refer to an external reference. cite :: Monad m => Reference -> ΛTeXT m ()-cite ref = do- fromLaTeX $ comm1 "cite" $ TeXRaw $ referenceName ref- addReference ref+cite rf = do+ fromLaTeX $ comm1 "cite" $ TeXRaw $ referenceName rf+ addReference rf -- | Put an external reference in your list of references without using it. nocite :: Monad m => Reference -> ΛTeXT m ()-nocite ref = do- fromLaTeX $ comm1 "nocite" $ TeXRaw $ referenceName ref- addReference ref+nocite rf = do+ fromLaTeX $ comm1 "nocite" $ TeXRaw $ referenceName rf+ addReference rf -- ** Reference Types --@@ -78,4 +74,3 @@ article :: ReferenceType article = "article"-
src/Text/LaTeX/LambdaTeX/Reference/Internal.hs view
@@ -1,43 +1,42 @@ {-# LANGUAGE OverloadedStrings #-}+ module Text.LaTeX.LambdaTeX.Reference.Internal where -import qualified Data.Set as S-import qualified Data.Text as T+import qualified Data.Set as S+import qualified Data.Text as T -import Text.LaTeX.LambdaTeX.Reference.Types-import Text.LaTeX.LambdaTeX.Types+import Text.LaTeX.LambdaTeX.Reference.Types+import Text.LaTeX.LambdaTeX.Types -- * Internal references- -- | Declare that a label has been made-addLabelMade :: Monad m => Text -> ΛTeXT m ()-addLabelMade label = λtell $ mempty { outputLabelsMade = S.singleton label }+addLabelMade :: Monad m => Text -> ΛTeXT m ()+addLabelMade label = λtell $ mempty {outputLabelsMade = S.singleton label} -- | Declare that a label is needed-addLabelNeeded :: Monad m => Text -> ΛTeXT m ()-addLabelNeeded label = λtell $ mempty { outputLabelsNeeded = S.singleton label }+addLabelNeeded :: Monad m => Text -> ΛTeXT m ()+addLabelNeeded label = λtell $ mempty {outputLabelsNeeded = S.singleton label} -- * External references- -- | Render references to bib(La)TeX format renderReferences :: [Reference] -> Text-renderReferences rs = (`mappend` "\n\n") . T.intercalate ",\n\n" $ map showRef rs+renderReferences rs =+ (`mappend` "\n\n") . T.intercalate ",\n\n" $ map showRef rs where showRef :: Reference -> Text- showRef r = mconcat- [- "@"- , referenceType r- , "{"- , referenceName r- , ",\n"- , T.intercalate ",\n" (map showField $ referenceFields r)- , "\n}"- ]-+ showRef r =+ mconcat+ [ "@"+ , referenceType r+ , "{"+ , referenceName r+ , ",\n"+ , T.intercalate ",\n" (map showField $ referenceFields r)+ , "\n}"+ ] showField :: (Text, Text) -> Text showField (a, b) = mconcat [" ", a, " = {", b, "}"] -- | Add a reference to a ΛTeX generator addReference :: Monad m => Reference -> ΛTeXT m ()-addReference ref = λtell $ mempty { outputExternalReferences = S.singleton ref }+addReference ref = λtell $ mempty {outputExternalReferences = S.singleton ref}
src/Text/LaTeX/LambdaTeX/Reference/Types.hs view
@@ -1,16 +1,15 @@ module Text.LaTeX.LambdaTeX.Reference.Types where -import Data.Ord (comparing)-import Data.Text (Text)+import Data.Ord (comparing)+import Data.Text (Text) type ReferenceType = Text-data Reference = Reference {- referenceType :: ReferenceType- , referenceName :: Text++data Reference = Reference+ { referenceType :: ReferenceType+ , referenceName :: Text , referenceFields :: [(Text, Text)]- } deriving (Show, Eq)+ } deriving (Show, Eq) instance Ord Reference where compare = comparing referenceName--
src/Text/LaTeX/LambdaTeX/Selection.hs view
@@ -1,15 +1,14 @@ module Text.LaTeX.LambdaTeX.Selection where -import Control.Monad (when)+import Control.Monad (when) -import qualified Data.Text as T+import qualified Data.Text as T -import Text.LaTeX.LambdaTeX.Selection.Internal-import Text.LaTeX.LambdaTeX.Selection.Types-import Text.LaTeX.LambdaTeX.Types+import Text.LaTeX.LambdaTeX.Selection.Internal+import Text.LaTeX.LambdaTeX.Selection.Types+import Text.LaTeX.LambdaTeX.Types -- * Making selections- -- | Construct a selection from a space-separated list of strings representing selectors -- -- > constructSelection = map constructSelector . words@@ -36,11 +35,10 @@ constructSelector s = Match $ map T.pack $ split s -- * Using selections- -- | Declare a sub-part of the document with a name. -- This allows you to use the subset-selection feature later. note :: Monad m => Text -> ΛTeXT m () -> ΛTeXT m ()-note partname func = inPart partname $ do- s <- isSelected- when s func-+note partname func =+ inPart partname $ do+ s <- isSelected+ when s func
src/Text/LaTeX/LambdaTeX/Selection/Internal.hs view
@@ -1,9 +1,9 @@ module Text.LaTeX.LambdaTeX.Selection.Internal where -import Data.List (isPrefixOf)+import Data.List (isPrefixOf) -import Text.LaTeX.LambdaTeX.Selection.Types-import Text.LaTeX.LambdaTeX.Types+import Text.LaTeX.LambdaTeX.Selection.Types+import Text.LaTeX.LambdaTeX.Types -- | Whether a part is selected by a given list of selectors. --@@ -12,18 +12,20 @@ -- All parts are selected or -- It it is matched and not ignored. selects :: Part -> [Selector] -> Bool-selects (Part ps) ss = go ps ss False- where- go :: [Text] -> [Selector] -> Bool -> Bool- go _ [] b = b- go ps (All:ss) _ = go ps ss True- go ps ((Match s):ss) b = if (ps `isPrefixOf` s || s `isPrefixOf` ps)- then go ps ss True- else go ps ss b- go ps ((Ignore s):ss) True = if s `isPrefixOf` ps- then go ps ss False- else go ps ss True- go ps ((Ignore _):ss) False = go ps ss False+selects (Part parts) selectors = go parts selectors False+ where+ go :: [Text] -> [Selector] -> Bool -> Bool+ go _ [] b = b+ go ps (All:ss) _ = go ps ss True+ go ps (Match s:ss) b =+ if ps `isPrefixOf` s || s `isPrefixOf` ps+ then go ps ss True+ else go ps ss b+ go ps (Ignore s:ss) True =+ if s `isPrefixOf` ps+ then go ps ss False+ else go ps ss True+ go ps (Ignore _:ss) False = go ps ss False -- | Whether the current part is selected to be generated isSelected :: Monad m => ΛTeXT m Bool@@ -33,7 +35,6 @@ return $ selects part sels -- * Current part Helpers- -- | Returns the current part from the ΛTeX state currentPart :: Monad m => ΛTeXT m Part currentPart = λgets stateCurrentPart@@ -49,17 +50,18 @@ -- -- WARNING: This should never be used outside of 'inPart' pushCurrentPart :: Monad m => Text -> ΛTeXT m ()-pushCurrentPart partname = λmodify (\s -> s { stateCurrentPart = pushPart (stateCurrentPart s) partname})+pushCurrentPart partname =+ λmodify+ (\s -> s {stateCurrentPart = pushPart (stateCurrentPart s) partname}) -- | Pop a part name off the current part stack -- -- WARNING: This should never be used outside of 'inPart' popCurrentPart :: Monad m => ΛTeXT m ()-popCurrentPart = λmodify (\s -> s { stateCurrentPart = popPart $ stateCurrentPart s })-+popCurrentPart =+ λmodify (\s -> s {stateCurrentPart = popPart $ stateCurrentPart s}) -- * Text Helpers- -- | Split a string on a dot. -- This can be useful to convert from a 'Selector's textual representation back to a selector --@@ -69,11 +71,10 @@ -- | Split a string on a specific character splitOn :: Char -> String -> [String]-splitOn c s = go s []+splitOn c str = go str [] where go :: String -> String -> [String] go [] s = [s]- go (sc:ss) acc | sc == c = acc : go ss []- | otherwise = go ss (acc ++ [sc])--+ go (sc:ss) acc+ | sc == c = acc : go ss []+ | otherwise = go ss (acc ++ [sc])
src/Text/LaTeX/LambdaTeX/Selection/Types.hs view
@@ -1,10 +1,11 @@ module Text.LaTeX.LambdaTeX.Selection.Types where -import Data.Text (Text)+import Data.Text (Text) type Selection = [Selector]-data Selector = All- | Match [Text]- | Ignore [Text]- deriving (Show, Eq) +data Selector+ = All+ | Match [Text]+ | Ignore [Text]+ deriving (Show, Eq)
src/Text/LaTeX/LambdaTeX/Types.hs view
@@ -1,65 +1,54 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE FlexibleInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-} -module Text.LaTeX.LambdaTeX.Types (- module Text.LaTeX.LambdaTeX.Types+module Text.LaTeX.LambdaTeX.Types+ ( module Text.LaTeX.LambdaTeX.Types , module Text.LaTeX.LambdaTeX.Part , module Text.LaTeX.LambdaTeX.Error , module Text.LaTeX.LambdaTeX.Package.Types , module Text.LaTeX.LambdaTeX.Reference.Types , module Text.LaTeX.LambdaTeX.Selection.Types--#if !MIN_VERSION_base(4,8,0)- , module Control.Applicative- , module Data.Monoid-#endif , Text ) where -#if !MIN_VERSION_base(4,8,0)-import Control.Applicative-import Data.Monoid hiding (All)-#endif--import Control.Monad (liftM)-import Control.Monad.IO.Class (MonadIO, liftIO)-import Control.Monad.Reader (MonadReader (..), asks)-import Control.Monad.RWS (RWST (..))-import Control.Monad.State (MonadState (..), gets,- modify)-import Control.Monad.Trans (MonadTrans (..), lift)-import Control.Monad.Writer (MonadWriter (..))+import Control.Monad.IO.Class (MonadIO, liftIO)+import Control.Monad.RWS (RWST(..))+import Control.Monad.Reader (MonadReader(..), asks)+import Control.Monad.State (MonadState(..), gets, modify)+import Control.Monad.Trans (MonadTrans(..), lift)+import Control.Monad.Writer (MonadWriter(..)) -import Data.Set (Set)-import qualified Data.Set as S-import Data.String (IsString (..))-import Data.Text (Text)+import Data.Set (Set)+import qualified Data.Set as S+import Data.String (IsString(..))+import Data.Text (Text) -import Text.LaTeX.Base (LaTeX, LaTeXT, runLaTeXT)-import Text.LaTeX.Base.Class (LaTeXC (..), fromLaTeX)-import Text.LaTeX.Base.Writer (extractLaTeX, textell)+import Text.LaTeX.Base (LaTeX, LaTeXT, runLaTeXT)+import Text.LaTeX.Base.Class (LaTeXC(..), fromLaTeX)+import Text.LaTeX.Base.Writer (extractLaTeX, textell) -import Text.LaTeX.Packages.AMSMath ()+import Text.LaTeX.Packages.AMSMath () -import Text.LaTeX.LambdaTeX.Error-import Text.LaTeX.LambdaTeX.Package.Types-import Text.LaTeX.LambdaTeX.Part-import Text.LaTeX.LambdaTeX.Reference.Types-import Text.LaTeX.LambdaTeX.Selection.Types+import Text.LaTeX.LambdaTeX.Error+import Text.LaTeX.LambdaTeX.Package.Types+import Text.LaTeX.LambdaTeX.Part+import Text.LaTeX.LambdaTeX.Reference.Types+import Text.LaTeX.LambdaTeX.Selection.Types -- * The ΛTeX Monad type LambdaTeXT = ΛTeXT+ type LambdaTeXT_ m = ΛTeXT_ m type ΛTeXT_ m = ΛTeXT m () -newtype ΛTeXT m a =- ΛTeXT { unwrapΛTeXT :: LaTeXT (RWST ΛConfig ΛOutput ΛState m) a }- deriving (Functor, Applicative, Monad)+newtype ΛTeXT m a = ΛTeXT+ { unwrapΛTeXT :: LaTeXT (RWST ΛConfig ΛOutput ΛState m) a+ } deriving (Functor, Applicative, Monad) -- TODO(kerckhove) Move and add amsmath dependency instance (Monad m, a ~ ()) => Num (ΛTeXT m a) where@@ -72,16 +61,19 @@ -- Non-defined methods signum = ΛTeXT . signum . unwrapΛTeXT - -- TODO(kerckhove) Move and add amsmath dependency instance (Monad m, a ~ ()) => Fractional (ΛTeXT m a) where (/) a b = ΛTeXT $ unwrapΛTeXT a / unwrapΛTeXT b fromRational = fromLaTeX . fromRational -- TODO(kerckhove) Also instantiate floating?--runΛTeX :: ΛTeXT m a -> ΛConfig -> ΛState -> m ((a, LaTeX), ΛState, ΛOutput)-runΛTeX func conf state = runRWST (runLaTeXT $ unwrapΛTeXT func) conf state+runΛTeX ::+ Monad m+ => ΛTeXT m a+ -> ΛConfig+ -> ΛState+ -> m ((a, LaTeX), ΛState, ΛOutput)+runΛTeX func = runRWST (runLaTeXT $ unwrapΛTeXT func) instance MonadTrans ΛTeXT where lift = ΛTeXT . lift . lift@@ -122,64 +114,60 @@ -- | This function run a 'LaTeXT' computation, -- lifting the result again in the monad.-extractΛLaTeX :: Monad m => ΛTeXT m a -> ΛTeXT m (a,LaTeX)+extractΛLaTeX :: Monad m => ΛTeXT m a -> ΛTeXT m (a, LaTeX) extractΛLaTeX = ΛTeXT . extractLaTeX . unwrapΛTeXT -- | Like 'extractΛLaTeX' but without the result extractΛLaTeX_ :: Monad m => ΛTeXT m a -> ΛTeXT m LaTeX-extractΛLaTeX_ = liftM snd . extractΛLaTeX+extractΛLaTeX_ = fmap snd . extractΛLaTeX -- | Internal ΛTeXT configration-data ΛConfig = ΛConfig {- configSelection :: Selection+newtype ΛConfig = ΛConfig+ { configSelection :: Selection } -- | Internal ΛTeXT configration output-data ΛOutput = ΛOutput {- outputPackageDependencies :: Set PackageDep- , outputExternalReferences :: Set Reference- , outputLabelsMade :: Set Text- , outputLabelsNeeded :: Set Text- , outputActions :: [(String, IO ())]+data ΛOutput = ΛOutput+ { outputPackageDependencies :: Set PackageDep+ , outputExternalReferences :: Set Reference+ , outputLabelsMade :: Set Text+ , outputLabelsNeeded :: Set Text+ , outputActions :: [(String, FilePath -> IO ())] } instance Monoid ΛOutput where- mempty = ΛOutput {- outputPackageDependencies = S.empty- , outputExternalReferences = S.empty- , outputLabelsMade = S.empty- , outputLabelsNeeded = S.empty- , outputActions = []+ mempty =+ ΛOutput+ { outputPackageDependencies = S.empty+ , outputExternalReferences = S.empty+ , outputLabelsMade = S.empty+ , outputLabelsNeeded = S.empty+ , outputActions = [] }-- mappend o1 o2 = ΛOutput {- outputPackageDependencies =- S.union- (outputPackageDependencies o1)- (outputPackageDependencies o2)- , outputExternalReferences =- S.union- (outputExternalReferences o1)- (outputExternalReferences o2)- , outputLabelsMade =- S.union- (outputLabelsMade o1)- (outputLabelsMade o2)- , outputLabelsNeeded =- S.union- (outputLabelsNeeded o1)- (outputLabelsNeeded o2)- , outputActions = outputActions o1 ++ outputActions o2+ mappend o1 o2 =+ ΛOutput+ { outputPackageDependencies =+ S.union+ (outputPackageDependencies o1)+ (outputPackageDependencies o2)+ , outputExternalReferences =+ S.union+ (outputExternalReferences o1)+ (outputExternalReferences o2)+ , outputLabelsMade = S.union (outputLabelsMade o1) (outputLabelsMade o2)+ , outputLabelsNeeded =+ S.union (outputLabelsNeeded o1) (outputLabelsNeeded o2)+ , outputActions = outputActions o1 ++ outputActions o2 } -- | Internal ΛTeXT configration state-data ΛState = ΛState {- stateCurrentPart :: Part+newtype ΛState = ΛState+ { stateCurrentPart :: Part } -- Orphan Monad Transformer instances for LaTeXT instance MonadReader r m => MonadReader r (LaTeXT m) where- ask = lift ask+ ask = lift ask local = local reader = lift . reader @@ -193,10 +181,9 @@ listen = listen pass = pass - -- Monad Transformer instances for ΛTeXT instance MonadReader r m => MonadReader r (ΛTeXT m) where- ask = lift ask+ ask = lift ask local = local reader = lift . reader @@ -209,4 +196,3 @@ tell = lift . tell listen = listen pass = pass-
src/Text/LaTeX/LambdaTeX/Utils.hs view
@@ -1,14 +1,14 @@ -- | ΛTeX utilities module Text.LaTeX.LambdaTeX.Utils where -import Control.Exception-import System.Directory (removeFile)-import System.IO.Error (isDoesNotExistError)+import Control.Exception+import System.Directory (removeFile)+import System.IO.Error (isDoesNotExistError) -- | Remove the given file if it exists removeIfExists :: FilePath -> IO () removeIfExists fileName = removeFile fileName `catch` handleExists- where handleExists e- | isDoesNotExistError e = return ()- | otherwise = throwIO e-+ where+ handleExists e+ | isDoesNotExistError e = return ()+ | otherwise = throwIO e
test/MainTest.hs view
@@ -1,9 +1,10 @@ {-# OPTIONS_GHC -F -pgmF hspec-discover #-}+ module MainTest where import qualified Spec-import Test.Hspec.Formatters-import Test.Hspec.Runner+import Test.Hspec.Formatters+import Test.Hspec.Runner main :: IO () main = hspecWith defaultConfig {configFormatter = Just progress} Spec.spec
+ test/Text/LaTeX/LambdaTeX/Part/Arbitrary.hs view
@@ -0,0 +1,11 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Text.LaTeX.LambdaTeX.Part.Arbitrary where++import Test.QuickCheck++import Data.Text.Arbitrary ()+import Text.LaTeX.LambdaTeX.Part++instance Arbitrary Part where+ arbitrary = Part <$> arbitrary
+ test/Text/LaTeX/LambdaTeX/Selection/Arbitrary.hs view
@@ -0,0 +1,11 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Text.LaTeX.LambdaTeX.Selection.Arbitrary where++import Test.QuickCheck++import Data.Text.Arbitrary ()+import Text.LaTeX.LambdaTeX.Selection.Types++instance Arbitrary Selector where+ arbitrary = oneof [pure All, Match <$> arbitrary, Ignore <$> arbitrary]
+ test/Text/LaTeX/LambdaTeX/Selection/InternalSpec.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE OverloadedStrings #-}++module Text.LaTeX.LambdaTeX.Selection.InternalSpec where++import Test.Hspec+import Test.QuickCheck++import Text.LaTeX.LambdaTeX.Part+import Text.LaTeX.LambdaTeX.Part.Arbitrary ()+import Text.LaTeX.LambdaTeX.Selection+import Text.LaTeX.LambdaTeX.Selection.Arbitrary ()+import Text.LaTeX.LambdaTeX.Selection.Internal+import Text.LaTeX.LambdaTeX.Selection.Types++spec :: Spec+spec =+ describe "selects" $ do+ it "is false for the empty selection" $+ property $ \p -> selects p [] `shouldBe` False+ it "is true for the 'match all' selection" $+ property $ \p -> selects p [All] `shouldBe` True+ it "is true for the empty part and this nonempty selection" $+ selects emptyPart [Match ["part"]] `shouldBe` True+ it "matches explicitly matched parts" $+ property $ \p -> selects (Part p) [Match p] `shouldBe` True+ it "ignore expicitly ignored parts" $+ property $ \p -> selects (Part p) [All, Ignore p] `shouldBe` False+ it "matches the right part for these simple test cases" $ do+ let p = Part ["this", "part"]+ selects p [Match ["this"]] `shouldBe` True+ selects p [Match ["this", "part"]] `shouldBe` True+ selects p [Match ["this", "part", "should"]] `shouldBe` True+ selects p [Match ["this", "part", "should", "be"]] `shouldBe` True+ selects p [Match ["this", "part", "should", "be", "matched"]] `shouldBe`+ True+ selects p [Match ["something"]] `shouldBe` False+ selects p [Match ["something", "else"]] `shouldBe` False+ it "ignores the right part for these simple test cases" $ do+ let p = Part ["this", "part"]+ selects p [All, Ignore ["this"]] `shouldBe` False+ selects p [All, Ignore ["this", "part"]] `shouldBe` False+ selects p [All, Ignore ["this", "part", "but", "it", "should"]] `shouldBe`+ True+ selects+ p+ [All, Ignore ["this", "part", "but", "it", "should", "be"]] `shouldBe`+ True+ selects+ p+ [ All+ , Ignore+ ["this", "part", "but", "it", "should", "be", "matched"]+ ] `shouldBe`+ True+ it+ "constructs this selection that's more than three subparts long correctly" $+ constructSelector "something.in.four.subparts" `shouldBe`+ Match ["something", "in", "four", "subparts"]