summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Weave.hs62
1 files changed, 48 insertions, 14 deletions
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 <http://www.gnu.org/licenses/>. -}
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