From c6ff79abd5c2992d74357b6b88b5484376f5fe34 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Thu, 12 Feb 2015 23:44:20 -0500 Subject: Weave: Escape references to LaTeX properly --- src/Weave.hs | 62 ++++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/Weave.hs b/src/Weave.hs index f2d7836..4a61032 100644 --- a/src/Weave.hs +++ b/src/Weave.hs @@ -14,28 +14,62 @@ this program. If not, see . -} module Weave (weave) where +import Control.Monad (liftM) +import Data.Functor ((<$>)) + import Fragment weave :: [Fragment] -> Either String String -weave = Right . concatMap weaveFragment +weave = concatMapM weaveFragment -weaveFragment :: Fragment -> String -weaveFragment (Documentation text) = text -weaveFragment (BlockCode name body) = - "\\begin{LytBlockCode}{" ++ name ++ "}\n" - ++ weaveBlockBody body - ++ "\\end{LytBlockCode}\n" +weaveFragment :: Fragment -> Either String String +weaveFragment (Documentation text) = Right text +weaveFragment (BlockCode name body) = do + escapeCharacter <- texEscape <$> pickTexEscapeCharacter body + return $ "\\begin{LytBlockCode}{" ++ name ++ "}{" ++ escapeCharacter ++ "}\n" + ++ weaveBlockBody escapeCharacter body + ++ "\\end{LytBlockCode}\n" -weaveBlockBody :: [CodeOrReference] -> String -weaveBlockBody ((Code first):rest) = +weaveBlockBody :: String -> [CodeOrReference] -> String +weaveBlockBody escapeCharacter ((Code first):rest) = {- The first block is code. To make sure it gets typeset correctly, drop everything up to the first newline or non-space character. -} (case dropWhile (==' ') first of '\n' : first' -> first' first' -> first') - ++ concatMap weaveCodeOrReference rest -weaveBlockBody blocks = concatMap weaveCodeOrReference blocks + ++ concatMap (weaveCodeOrReference escapeCharacter) rest +weaveBlockBody escapeCharacter blocks = + concatMap (weaveCodeOrReference escapeCharacter) blocks + +weaveCodeOrReference :: String -> CodeOrReference -> String +weaveCodeOrReference _ (Code text) = text +weaveCodeOrReference escapeCharacter (Reference name) = + escapeCharacter ++ "\\LytFragmentReference{" ++ name ++ "}" ++ escapeCharacter + +pickTexEscapeCharacter :: [CodeOrReference] -> Either String Char +pickTexEscapeCharacter blocks = + case firstNotIn (consolidate blocks) texPotentialEscapeCharacters of + Just c -> Right c + Nothing -> Left "could not find a suitable LaTeX escape character" + where consolidate = foldl (\current block -> case block of + Code text -> current ++ text + Reference _ -> current) + "" + +firstNotIn :: Eq a => [a] -> [a] -> Maybe a +firstNotIn _ [] = Nothing +firstNotIn haystack (x:xs) + | x `elem` haystack = firstNotIn haystack xs + | otherwise = Just x + +texPotentialEscapeCharacters :: [Char] +texPotentialEscapeCharacters = + "!@#$%^&*()-_+={}[]|\"':;/?,.~`" ++ ['A' .. 'Z' ] ++ ['a' .. 'z'] + +texEscape :: Char -> String +texEscape char + | char `elem` "#$%^&_{}~" = ['\\', char] + | otherwise = [char] -weaveCodeOrReference :: CodeOrReference -> String -weaveCodeOrReference (Code text) = text -weaveCodeOrReference (Reference name) = "\\LytFragmentReference{" ++ name ++ "}" +concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b] +concatMapM f lists = liftM concat $ mapM f lists -- cgit v1.2.3