git.iko.soy/typeable/pandoc
on a2ed874 help sign in
src/Text/Pandoc/Parsing.hs+15−10
since splitsnapshots
unifiedsplit
@@ -130,6 +130,7 @@ module Text.Pandoc.Parsing ( anyLine,
130 runParser,
130 runParser,
131 runParserT,
131 runParserT,
132 parse,
132 parse,
133+ tokenPrim,
133 anyToken,
134 anyToken,
134 getInput,
135 getInput,
135 setInput,
136 setInput,
@@ -178,7 +179,9 @@ module Text.Pandoc.Parsing ( anyLine,
178 sourceLine,
179 sourceLine,
179 setSourceColumn,
180 setSourceColumn,
180 setSourceLine,
181 setSourceLine,
181− newPos
182+ newPos,
183+ Line,
184+ Column
182 )
185 )
183where
186where
184
187
@@ -1385,10 +1388,11 @@ extractIdClass (ident, cls, kvs) = (ident', cls', kvs')
1385
1388
1386insertIncludedFile' :: (PandocMonad m, HasIncludeFiles st,
1389insertIncludedFile' :: (PandocMonad m, HasIncludeFiles st,
1387 Functor mf, Applicative mf, Monad mf)
1390 Functor mf, Applicative mf, Monad mf)
1388− => ParserT String st m (mf Blocks)
1391+ => ParserT [a] st m (mf Blocks)
1392+ -> (String -> [a])
1389 -> [FilePath] -> FilePath
1393 -> [FilePath] -> FilePath
1390− -> ParserT String st m (mf Blocks)
1394+ -> ParserT [a] st m (mf Blocks)
1391−insertIncludedFile' blocks dirs f = do
1395+insertIncludedFile' blocks totoks dirs f = do
1392 oldPos <- getPosition
1396 oldPos <- getPosition
1393 oldInput <- getInput
1397 oldInput <- getInput
1394 containers <- getIncludeFiles <$> getState
1398 containers <- getIncludeFiles <$> getState
@@ -1402,7 +1406,7 @@ insertIncludedFile' blocks dirs f = do
1402 report $ CouldNotLoadIncludeFile f oldPos
1406 report $ CouldNotLoadIncludeFile f oldPos
1403 return ""
1407 return ""
1404 setPosition $ newPos f 1 1
1408 setPosition $ newPos f 1 1
1405− setInput contents
1409+ setInput $ totoks contents
1406 bs <- blocks
1410 bs <- blocks
1407 setInput oldInput
1411 setInput oldInput
1408 setPosition oldPos
1412 setPosition oldPos
@@ -1412,11 +1416,12 @@ insertIncludedFile' blocks dirs f = do
1412-- | Parse content of include file as blocks. Circular includes result in an
1416-- | Parse content of include file as blocks. Circular includes result in an
1413-- @PandocParseError@.
1417-- @PandocParseError@.
1414insertIncludedFile :: (PandocMonad m, HasIncludeFiles st)
1418insertIncludedFile :: (PandocMonad m, HasIncludeFiles st)
1415− => ParserT String st m Blocks
1419+ => ParserT [a] st m Blocks
1420+ -> (String -> [a])
1416 -> [FilePath] -> FilePath
1421 -> [FilePath] -> FilePath
1417− -> ParserT String st m Blocks
1422+ -> ParserT [a] st m Blocks
1418−insertIncludedFile blocks dirs f =
1423+insertIncludedFile blocks totoks dirs f =
1419− runIdentity <$> insertIncludedFile' (Identity <$> blocks) dirs f
1424+ runIdentity <$> insertIncludedFile' (Identity <$> blocks) totoks dirs f
1420
1425
1421-- | Parse content of include file as future blocks. Circular includes result in
1426-- | Parse content of include file as future blocks. Circular includes result in
1422-- an @PandocParseError@.
1427-- an @PandocParseError@.
@@ -1424,4 +1429,4 @@ insertIncludedFileF :: (PandocMonad m, HasIncludeFiles st)
1424 => ParserT String st m (Future st Blocks)
1429 => ParserT String st m (Future st Blocks)
1425 -> [FilePath] -> FilePath
1430 -> [FilePath] -> FilePath
1426 -> ParserT String st m (Future st Blocks)
1431 -> ParserT String st m (Future st Blocks)
1427−insertIncludedFileF = insertIncludedFile'
1432+insertIncludedFileF p = insertIncludedFile' p id
src/Text/Pandoc/Readers/LaTeX.hs+1623−1266
@@ -1,4 +1,6 @@
1{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE MultiParamTypeClasses #-}
2{-# LANGUAGE ScopedTypeVariables #-}
4{-# LANGUAGE ScopedTypeVariables #-}
3{-
5{-
4Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu>
6Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu>
@@ -38,24 +40,33 @@ module Text.Pandoc.Readers.LaTeX ( readLaTeX,
38import Control.Applicative (many, optional, (<|>))
40import Control.Applicative (many, optional, (<|>))
39import Control.Monad
41import Control.Monad
40import Control.Monad.Except (throwError)
42import Control.Monad.Except (throwError)
41−import Data.Char (chr, isAlphaNum, isLetter, ord)
43+import Data.Char (chr, isAlphaNum, isLetter, ord, isDigit)
44+import Data.Default
42import Data.Text (Text, unpack)
45import Data.Text (Text, unpack)
46+import qualified Data.Text as T
43import Data.List (intercalate, isPrefixOf)
47import Data.List (intercalate, isPrefixOf)
44import qualified Data.Map as M
48import qualified Data.Map as M
49+import qualified Data.Set as Set
45import Data.Maybe (fromMaybe, maybeToList)
50import Data.Maybe (fromMaybe, maybeToList)
46import Safe (minimumDef)
51import Safe (minimumDef)
47import System.FilePath (addExtension, replaceExtension, takeExtension)
52import System.FilePath (addExtension, replaceExtension, takeExtension)
48import Text.Pandoc.Builder
53import Text.Pandoc.Builder
49import Text.Pandoc.Class (PandocMonad, PandocPure, lookupEnv, readFileFromDirs,
54import Text.Pandoc.Class (PandocMonad, PandocPure, lookupEnv, readFileFromDirs,
50− report, setResourcePath, getResourcePath)
55+ report, setResourcePath, getResourcePath,
56+ runIOorExplode, PandocIO)
51import Text.Pandoc.Highlighting (fromListingsLanguage, languagesByExtension)
57import Text.Pandoc.Highlighting (fromListingsLanguage, languagesByExtension)
52import Text.Pandoc.ImageSize (numUnit, showFl)
58import Text.Pandoc.ImageSize (numUnit, showFl)
53import Text.Pandoc.Logging
59import Text.Pandoc.Logging
54import Text.Pandoc.Options
60import Text.Pandoc.Options
55−import Text.Pandoc.Parsing hiding (many, mathDisplay, mathInline, optional,
61+import Text.Pandoc.Parsing hiding (many, optional, withRaw,
62+ mathInline, mathDisplay,
56− space, (<|>))
63+ space, (<|>), spaces, blankline)
57import Text.Pandoc.Shared
64import Text.Pandoc.Shared
58import Text.Pandoc.Walk
65import Text.Pandoc.Walk
66+import Text.Pandoc.Error (PandocError(PandocParsecError))
67+
68+import Text.Pandoc.Extensions (getDefaultExtensions)
69+import Debug.Trace (traceShowId)
59
70
60-- | Parse LaTeX from string and return 'Pandoc' document.
71-- | Parse LaTeX from string and return 'Pandoc' document.
61readLaTeX :: PandocMonad m
72readLaTeX :: PandocMonad m
@@ -63,18 +74,18 @@ readLaTeX :: PandocMonad m
63 -> Text -- ^ String to parse (assumes @'\n'@ line endings)
74 -> Text -- ^ String to parse (assumes @'\n'@ line endings)
64 -> m Pandoc
75 -> m Pandoc
65readLaTeX opts ltx = do
76readLaTeX opts ltx = do
66− parsed <- readWithM parseLaTeX def{ stateOptions = opts }
77+ parsed <- runParserT parseLaTeX def{ sOptions = opts } "source"
67− (unpack (crFilter ltx))
78+ (tokenize (crFilter ltx))
68 case parsed of
79 case parsed of
69 Right result -> return result
80 Right result -> return result
70− Left e -> throwError e
81+ Left e -> throwError $ PandocParsecError (T.unpack ltx) e
71
82
72parseLaTeX :: PandocMonad m => LP m Pandoc
83parseLaTeX :: PandocMonad m => LP m Pandoc
73parseLaTeX = do
84parseLaTeX = do
74 bs <- blocks
85 bs <- blocks
75 eof
86 eof
76 st <- getState
87 st <- getState
77− let meta = stateMeta st
88+ let meta = sMeta st
78 let doc' = doc bs
89 let doc' = doc bs
79 let headerLevel (Header n _ _) = [n]
90 let headerLevel (Header n _ _) = [n]
80 headerLevel _ = []
91 headerLevel _ = []
@@ -88,177 +99,328 @@ parseLaTeX = do
88 else id) doc'
99 else id) doc'
89 return $ Pandoc meta bs'
100 return $ Pandoc meta bs'
90
101
102+testParser :: LP PandocIO a -> Text -> IO a
103+testParser p t = do
104+ res <- runIOorExplode (runParserT p defaultLaTeXState{
105+ sOptions = def{ readerExtensions =
106+ getDefaultExtensions "latex" }} "name" (tokenize t))
107+ case res of
108+ Left e -> error (show e)
109+ Right r -> return r
110+
111+data Macro = Macro -- TODO
112+ deriving Show
113+
114+data LaTeXState = LaTeXState{ sOptions :: ReaderOptions
115+ , sMeta :: Meta
116+ , sQuoteContext :: QuoteContext
117+ , sMacros :: [Macro]
118+ , sContainers :: [String]
119+ , sHeaders :: M.Map Inlines String
120+ , sLogMessages :: [LogMessage]
121+ , sIdentifiers :: Set.Set String }
122+ deriving Show
123+
124+defaultLaTeXState :: LaTeXState
125+defaultLaTeXState = LaTeXState{ sOptions = def
126+ , sMeta = nullMeta
127+ , sQuoteContext = NoQuote
128+ , sMacros = []
129+ , sContainers = []
130+ , sHeaders = M.empty
131+ , sLogMessages = []
132+ , sIdentifiers = Set.empty }
133+
134+instance PandocMonad m => HasQuoteContext LaTeXState m where
135+ getQuoteContext = sQuoteContext <$> getState
136+ withQuoteContext context parser = do
137+ oldState <- getState
138+ let oldQuoteContext = sQuoteContext oldState
139+ setState oldState { sQuoteContext = context }
140+ result <- parser
141+ newState <- getState
142+ setState newState { sQuoteContext = oldQuoteContext }
143+ return result
144+
145+instance HasLogMessages LaTeXState where
146+ addLogMessage msg st = st{ sLogMessages = msg : sLogMessages st }
147+ getLogMessages st = reverse $ sLogMessages st
148+
149+instance HasIdentifierList LaTeXState where
150+ extractIdentifierList = sIdentifiers
151+ updateIdentifierList f st = st{ sIdentifiers = f $ sIdentifiers st }
152+
153+instance HasIncludeFiles LaTeXState where
154+ getIncludeFiles = sContainers
155+ addIncludeFile f s = s{ sContainers = f : sContainers s }
156+ dropLatestIncludeFile s = s { sContainers = drop 1 $ sContainers s }
157+
158+instance HasHeaderMap LaTeXState where
159+ extractHeaderMap = sHeaders
160+ updateHeaderMap f st = st{ sHeaders = f $ sHeaders st }
161+
162+instance HasReaderOptions LaTeXState where
163+ extractReaderOptions = sOptions
164+
165+instance HasMeta LaTeXState where
166+ setMeta field val st =
167+ st{ sMeta = setMeta field val $ sMeta st }
168+ deleteMeta field st =
169+ st{ sMeta = deleteMeta field $ sMeta st }
170+
171+instance Default LaTeXState where
172+ def = defaultLaTeXState
173+
91−type LP m = ParserT String ParserState m
174+type LP m = ParserT [Tok] LaTeXState m
92−
175+
93−anyControlSeq :: PandocMonad m => LP m String
176+rawLaTeXBlock :: PandocMonad m => ParserT String LaTeXState m String
94−anyControlSeq = do
177+rawLaTeXBlock = mzero
95− char '\\'
178+
96− next <- option '\n' anyChar
179+rawLaTeXInline :: PandocMonad m => ParserT String LaTeXState m Inline
97− case next of
180+rawLaTeXInline = mzero
98− '\n' -> return ""
181+
99− c | isLetter c -> (c:) <$> (many letter <* optional sp)
182+inlineCommand :: PandocMonad m => ParserT String LaTeXState m Inlines
100− | otherwise -> return [c]
183+inlineCommand = mzero
101−
184+
102−controlSeq :: PandocMonad m => String -> LP m String
185+data TokType = CtrlSeq | Spaces | Newline | Symbol | Word | Comment |
103−controlSeq name = try $ do
186+ Esc1 | Esc2
104− char '\\'
187+ deriving (Eq, Ord, Show)
105− case name of
188+
106− "" -> mzero
189+data Tok = Tok (Line, Column) TokType Text
107− [c] | not (isLetter c) -> string [c]
190+ deriving (Eq, Ord, Show)
108− cs -> string cs <* notFollowedBy letter <* optional sp
191+
109− return name
192+tokenize :: Text -> [Tok]
110−
193+tokenize = totoks (1, 1)
111−dimenarg :: PandocMonad m => LP m String
194+
112−dimenarg = try $ do
195+totoks :: (Line, Column) -> Text -> [Tok]
113− ch <- option "" $ string "="
196+totoks (lin,col) t =
114− num <- many1 digit
197+ case T.uncons t of
115− dim <- oneOfStrings ["pt","pc","in","bp","cm","mm","dd","cc","sp"]
198+ Nothing -> []
116− return $ ch ++ num ++ dim
199+ Just (c, rest)
200+ | c == '\n' ->
201+ Tok (lin, col) Newline (T.singleton '\n')
202+ : totoks (lin + 1,1) rest
203+ | isSpaceOrTab c ->
204+ let (sps, rest') = T.span isSpaceOrTab t
205+ in Tok (lin, col) Spaces sps
206+ : totoks (lin, col + T.length sps) rest'
207+ | isAlphaNum c ->
208+ let (ws, rest') = T.span isAlphaNum t
209+ in Tok (lin, col) Word ws
210+ : totoks (lin, col + T.length ws) rest'
211+ | c == '%' ->
212+ let (cs, rest') = T.break (== '\n') rest
213+ in Tok (lin, col) Comment (T.singleton '%' <> cs)
214+ : totoks (lin, col + 1 + T.length cs) rest'
215+ | c == '\\' ->
216+ case T.uncons rest of
217+ Nothing -> [Tok (lin, col) Symbol (T.singleton c)]
218+ Just (d, rest')
219+ | isLetter d ->
220+ let (ws, rest'') = T.span isLetter rest
221+ in Tok (lin, col) CtrlSeq (T.singleton '\\' <> ws)
222+ : totoks (lin, col + 1 + T.length ws) rest''
223+ | d == '\t' || d == '\n' ->
224+ Tok (lin, col) Symbol (T.singleton '\\')
225+ : totoks (lin, col + 1) rest
226+ | otherwise ->
227+ Tok (lin, col) CtrlSeq (T.pack [c,d])
228+ : totoks (lin, col + 2) rest'
229+ | c == '^' ->
230+ case T.uncons rest of
231+ Just ('^', rest') ->
232+ case T.uncons rest' of
233+ Just (d, rest'')
234+ | isLowerHex d ->
235+ case T.uncons rest'' of
236+ Just (e, rest''') | isLowerHex e ->
237+ Tok (lin, col) Esc2 (T.pack ['^','^',d,e])
238+ : totoks (lin, col + 4) rest'''
239+ _ ->
240+ Tok (lin, col) Esc1 (T.pack ['^','^',d])
241+ : totoks (lin, col + 3) rest''
242+ | d < '\128' ->
243+ Tok (lin, col) Esc1 (T.pack ['^','^',d])
244+ : totoks (lin, col + 3) rest''
245+ _ -> [Tok (lin, col) Symbol (T.singleton '^'),
246+ Tok (lin, col + 1) Symbol (T.singleton '^')]
247+ _ -> Tok (lin, col) Symbol (T.singleton '^')
248+ : totoks (lin, col + 1) rest
249+ | otherwise ->
250+ Tok (lin, col) Symbol (T.singleton c) : totoks (lin, col + 1) rest
251+
252+ where isSpaceOrTab ' ' = True
253+ isSpaceOrTab '\t' = True
254+ isSpaceOrTab _ = False
255+
256+isLowerHex :: Char -> Bool
257+isLowerHex x = x >= '0' && x <= '9' || x >= 'a' && x <= 'f'
258+
259+untokenize :: [Tok] -> Text
260+untokenize = mconcat . map untoken
261+
262+untoken :: Tok -> Text
263+untoken (Tok _ _ t) = t
264+
265+-- we put withMacros in front of satisfyTok, so it works on everything
266+satisfyTok :: PandocMonad m => (Tok -> Bool) -> LP m Tok
267+satisfyTok f =
268+ doMacros *> tokenPrim (T.unpack . untoken) updatePos matcher
269+ where matcher t | f t = Just t
270+ | otherwise = Nothing
271+ updatePos :: SourcePos -> Tok -> [Tok] -> SourcePos
272+ updatePos spos _ (Tok (lin,col) _ _ : _) =
273+ setSourceColumn (setSourceLine spos lin) col
274+ updatePos spos _ [] = spos
275+
276+-- TODO use something other than LaTeXState
277+doMacros :: PandocMonad m => LP m ()
278+doMacros = do
279+ inp <- getInput
280+ macros <- sMacros <$> getState
281+ case inp of
282+ t@(Tok spos CtrlSeq txt) : ts
283+ | txt == "\\macro" -> do -- TODO actually check macros
284+ setInput $ map (setpos spos) (tokenize "Hi there") ++ ts
285+ -- TODO loop detection
286+ doMacros
287+ _ -> return ()
288+
289+setpos :: (Line, Column) -> Tok -> Tok
290+setpos spos (Tok _ tt txt) = Tok spos tt txt
291+
292+anyControlSeq :: PandocMonad m => LP m Tok
293+anyControlSeq = satisfyTok isCtrlSeq <* optional sp
294+ where isCtrlSeq (Tok _ CtrlSeq _) = True
295+ isCtrlSeq _ = False
296+
297+anySymbol :: PandocMonad m => LP m Tok
298+anySymbol = satisfyTok isSym
299+ where isSym (Tok _ Symbol _) = True
300+ isSym _ = False
301+
302+spaces :: PandocMonad m => LP m ()
303+spaces = skipMany (satisfyTok (tokTypeIn [Comment, Spaces, Newline]))
304+
305+spaces1 :: PandocMonad m => LP m ()
306+spaces1 = skipMany1 (satisfyTok (tokTypeIn [Comment, Spaces, Newline]))
307+
308+tokTypeIn :: [TokType] -> Tok -> Bool
309+tokTypeIn toktypes (Tok _ tt _) = tt `elem` toktypes
310+
311+controlSeq :: PandocMonad m => Text -> LP m Tok
312+controlSeq name = satisfyTok isNamed
313+ where isNamed (Tok _ CtrlSeq n) = n == T.singleton '\\' <> name
314+ isNamed _ = False
315+
316+symbol :: PandocMonad m => Char -> LP m Tok
317+symbol c = satisfyTok isc
318+ where isc (Tok _ Symbol d) = case T.uncons d of
319+ Just (c',_) -> c == c'
320+ _ -> False
321+ isc _ = False
322+
323+symbolIn :: PandocMonad m => [Char] -> LP m Tok
324+symbolIn cs = satisfyTok isInCs
325+ where isInCs (Tok _ Symbol d) = case T.uncons d of
326+ Just (c,_) -> c `elem` cs
327+ _ -> False
328+ isInCs _ = False
117
329
118sp :: PandocMonad m => LP m ()
330sp :: PandocMonad m => LP m ()
119sp = whitespace <|> endline
331sp = whitespace <|> endline
120
332
121whitespace :: PandocMonad m => LP m ()
333whitespace :: PandocMonad m => LP m ()
122−whitespace = skipMany1 $ satisfy (\c -> c == ' ' || c == '\t')
334+whitespace = () <$ satisfyTok isSpaceTok
123−
335+ where isSpaceTok (Tok _ Spaces _) = True
124−endline :: PandocMonad m => LP m ()
336+ isSpaceTok _ = False
125−endline = try (newline >> lookAhead anyChar >> notFollowedBy blankline)
126
337
127−isLowerHex :: Char -> Bool
338+newlineTok :: PandocMonad m => LP m ()
128−isLowerHex x = x >= '0' && x <= '9' || x >= 'a' && x <= 'f'
339+newlineTok = () <$ satisfyTok isNewlineTok
129
340
130−tildeEscape :: PandocMonad m => LP m Char
341+isNewlineTok :: Tok -> Bool
131−tildeEscape = try $ do
342+isNewlineTok (Tok _ Newline _) = True
132− string "^^"
343+isNewlineTok _ = False
133− c <- satisfy (\x -> x >= '\0' && x <= '\128')
134− d <- if isLowerHex c
135− then option "" $ count 1 (satisfy isLowerHex)
136− else return ""
137− if null d
138− then case ord c of
139− x | x >= 64 && x <= 127 -> return $ chr (x - 64)
140− | otherwise -> return $ chr (x + 64)
141− else return $ chr $ read ('0':'x':c:d)
142
344
143comment :: PandocMonad m => LP m ()
345comment :: PandocMonad m => LP m ()
144−comment = do
346+comment = () <$ satisfyTok isCommentTok
145− char '%'
347+ where isCommentTok (Tok _ Comment _) = True
146− skipMany (satisfy (/='\n'))
348+ isCommentTok _ = False
147− optional newline
349+
350+anyTok :: PandocMonad m => LP m Tok
351+anyTok = satisfyTok (const True)
352+
353+endline :: PandocMonad m => LP m ()
354+endline = try $ do
355+ newlineTok
356+ lookAhead anyTok
357+ notFollowedBy blankline
358+
359+blankline :: PandocMonad m => LP m ()
360+blankline = try $ do
361+ skipMany (satisfyTok (tokTypeIn [Spaces, Newline]))
362+ newlineTok
363+
364+primEscape :: PandocMonad m => LP m Char
365+primEscape = do
366+ Tok _ toktype t <- satisfyTok (tokTypeIn [Esc1, Esc2])
367+ case toktype of
368+ Esc1 -> case T.uncons t of
369+ Just (c, _)
370+ | c >= '\64' && c <= '\127' -> return (chr (ord c - 64))
371+ | otherwise -> return (chr (ord c + 64))
372+ Nothing -> fail "Empty content of Esc1"
373+ Esc2 -> case safeRead ('0':'x':T.unpack (T.drop 2 t)) of
148− return ()
374+ Just x -> return (chr x)
375+ Nothing -> fail $ "Could not read: " ++ T.unpack t
376+ _ -> fail "Expected an Esc1 or Esc2 token" -- should not happen
149
377
150bgroup :: PandocMonad m => LP m ()
378bgroup :: PandocMonad m => LP m ()
151bgroup = try $ do
379bgroup = try $ do
152− skipMany (spaceChar <|> try (newline <* notFollowedBy blankline))
380+ skipMany sp
153− () <$ char '{'
154− <|> () <$ controlSeq "bgroup"
155− <|> () <$ controlSeq "begingroup"
381+ symbol '{' <|> controlSeq "bgroup" <|> controlSeq "begingroup"
382+ return ()
156
383
157egroup :: PandocMonad m => LP m ()
384egroup :: PandocMonad m => LP m ()
158−egroup = () <$ char '}'
159− <|> () <$ controlSeq "egroup"
160− <|> () <$ controlSeq "endgroup"
385+egroup = () <$ (symbol '}' <|> controlSeq "egroup" <|> controlSeq "endgroup")
161
386
162−grouped :: PandocMonad m => Monoid a => LP m a -> LP m a
387+grouped :: (PandocMonad m, Monoid a) => LP m a -> LP m a
163grouped parser = try $ do
388grouped parser = try $ do
164 bgroup
389 bgroup
165 -- first we check for an inner 'grouped', because
390 -- first we check for an inner 'grouped', because
166 -- {{a,b}} should be parsed the same as {a,b}
391 -- {{a,b}} should be parsed the same as {a,b}
167− try (grouped parser <* egroup)
168− <|> (mconcat <$> manyTill parser egroup)
392+ try (grouped parser <* egroup) <|> (mconcat <$> manyTill parser egroup)
169−
393+
170−braced :: PandocMonad m => LP m String
394+braced :: PandocMonad m => LP m [Tok]
171−braced = grouped chunk
395+braced = try $ do
172− where chunk =
396+ bgroup
173− many1 (satisfy (\c -> c /= '\\' && c /= '}' && c /= '{'))
397+ -- {{a,b}} should be parsed the same as {a,b}
174− <|> try (string "\\}")
398+ try (braced <* egroup) <|> manyTill anyTok egroup
175− <|> try (string "\\{")
176− <|> try (string "\\\\")
177− <|> ((\x -> "{" ++ x ++ "}") <$> braced)
178− <|> count 1 anyChar
179
399
180bracketed :: PandocMonad m => Monoid a => LP m a -> LP m a
400bracketed :: PandocMonad m => Monoid a => LP m a -> LP m a
401+bracketed parser = try $ do
402+ symbol '['
181−bracketed parser = try $ char '[' *> (mconcat <$> manyTill parser (char ']'))
403+ mconcat <$> manyTill parser (symbol ']')
182−
183−mathDisplay :: PandocMonad m => LP m String -> LP m Inlines
184−mathDisplay p = displayMath <$> (try p >>= applyMacros' . trim)
185−
186−mathInline :: PandocMonad m => LP m String -> LP m Inlines
187−mathInline p = math <$> (try p >>= applyMacros')
188−
189−mathChars :: PandocMonad m => LP m String
190−mathChars =
191− concat <$> many (escapedChar
192− <|> (snd <$> withRaw braced)
193− <|> many1 (satisfy isOrdChar))
194− where escapedChar = try $ do char '\\'
195− c <- anyChar
196− return ['\\',c]
197− isOrdChar '$' = False
198− isOrdChar '{' = False
199− isOrdChar '}' = False
200− isOrdChar '\\' = False
201− isOrdChar _ = True
202−
203−quoted' :: PandocMonad m => (Inlines -> Inlines) -> LP m String -> LP m () -> LP m Inlines
204−quoted' f starter ender = do
205− startchs <- starter
206− smart <- extensionEnabled Ext_smart <$> getOption readerExtensions
207− if smart
208− then do
209− ils <- many (notFollowedBy ender >> inline)
210− (ender >> return (f (mconcat ils))) <|>
211− (<> mconcat ils) <$>
212− lit (case startchs of
213− "``" -> "“"
214− "`" -> "‘"
215− _ -> startchs)
216− else lit startchs
217
404
218−doubleQuote :: PandocMonad m => LP m Inlines
405+dimenarg :: PandocMonad m => LP m Text
219−doubleQuote = do
406+dimenarg = try $ do
220− quoted' doubleQuoted (try $ string "``") (void $ try $ string "''")
407+ ch <- option False $ True <$ symbol '='
221− <|> quoted' doubleQuoted (string "“") (void $ char '”')
408+ Tok _ _ s <- satisfyTok isWordTok
222− -- the following is used by babel for localized quotes:
409+ guard $ (T.take 2 (T.reverse s)) `elem`
223− <|> quoted' doubleQuoted (try $ string "\"`") (void $ try $ string "\"'")
410+ ["pt","pc","in","bp","cm","mm","dd","cc","sp"]
224− <|> quoted' doubleQuoted (string "\"") (void $ char '"')
411+ let num = T.take (T.length s - 2) s
412+ guard $ T.length num > 0
413+ guard $ T.all isDigit num
414+ return $ T.pack ['=' | ch] <> s
225
415
226−singleQuote :: PandocMonad m => LP m Inlines
416+-- inline elements:
227−singleQuote = do
228− smart <- extensionEnabled Ext_smart <$> getOption readerExtensions
229− if smart
230− then quoted' singleQuoted (string "`") (try $ char '\'' >> notFollowedBy letter)
231− <|> quoted' singleQuoted (string "‘") (try $ char '’' >> notFollowedBy letter)
232− else str <$> many1 (oneOf "`\'‘’")
233
417
234−inline :: PandocMonad m => LP m Inlines
418+inlineText :: PandocMonad m => LP m Inlines
235−inline = (mempty <$ comment)
419+inlineText = (str . T.unpack . untoken) <$> satisfyTok isWordTok
236− <|> (space <$ whitespace)
237− <|> (softbreak <$ endline)
238− <|> inlineText
239− <|> inlineCommand
240− <|> inlineEnvironment
241− <|> inlineGroup
242− <|> (char '-' *> option (str "-")
243− (char '-' *> option (str "–") (str "—" <$ char '-')))
244− <|> doubleQuote
245− <|> singleQuote
246− <|> (str "”" <$ try (string "''"))
247− <|> (str "”" <$ char '”')
248− <|> (str "’" <$ char '\'')
249− <|> (str "’" <$ char '’')
250− <|> (str "\160" <$ char '~')
251− <|> mathDisplay (string "$$" *> mathChars <* string "$$")
252− <|> mathInline (char '$' *> mathChars <* char '$')
253− <|> (guardEnabled Ext_literate_haskell *> char '|' *> doLHSverb)
254− <|> (str . (:[]) <$> tildeEscape)
255− <|> (do res <- oneOf "#&~^'`\"[]"
256− pos <- getPosition
257− report $ ParsingUnescaped [res] pos
258− return $ str [res])
259
420
260−inlines :: PandocMonad m => LP m Inlines
421+isWordTok :: Tok -> Bool
261−inlines = mconcat <$> many (notFollowedBy (char '}') *> inline)
422+isWordTok (Tok _ Word _) = True
423+isWordTok _ = False
262
424
263inlineGroup :: PandocMonad m => LP m Inlines
425inlineGroup :: PandocMonad m => LP m Inlines
264inlineGroup = do
426inlineGroup = do
@@ -269,233 +431,466 @@ inlineGroup = do
269 -- we need the span so we can detitlecase bibtex entries;
431 -- we need the span so we can detitlecase bibtex entries;
270 -- we need to know when something is {C}apitalized
432 -- we need to know when something is {C}apitalized
271
433
272−block :: PandocMonad m => LP m Blocks
434+doLHSverb :: PandocMonad m => LP m Inlines
273−block = (mempty <$ comment)
435+doLHSverb =
274− <|> (mempty <$ ((spaceChar <|> newline) *> spaces))
436+ (codeWith ("",["haskell"],[]) . T.unpack . untokenize)
275− <|> environment
437+ <$> manyTill (satisfyTok (not . isNewlineTok)) (symbol '|')
276− <|> include
277− <|> macro
278− <|> blockCommand
279− <|> paragraph
280− <|> grouped block
281−
282−blocks :: PandocMonad m => LP m Blocks
283−blocks = mconcat <$> many block
284
438
285−getRawCommand :: PandocMonad m => String -> LP m String
439+lit :: String -> LP m Inlines
286−getRawCommand name' = do
440+lit = pure . str
287− rawargs <- withRaw (many (try (optional sp *> opt)) *>
288− option "" (try (optional sp *> dimenarg)) *>
289− many braced)
290− return $ '\\' : name' ++ snd rawargs
291
441
292−lookupListDefault :: (Ord k) => v -> [k] -> M.Map k v -> v
442+doubleQuote :: PandocMonad m => LP m Inlines
293−lookupListDefault d = (fromMaybe d .) . lookupList
443+doubleQuote = do
294− where
444+ quoted' doubleQuoted (try $ count 2 $ symbol '`')
295− lookupList l m = msum $ map (`M.lookup` m) l
445+ (void $ try $ count 2 $ symbol '\'')
446+ <|> quoted' doubleQuoted ((:[]) <$> symbol '“') (void $ symbol '”')
447+ -- the following is used by babel for localized quotes:
448+ <|> quoted' doubleQuoted (try $ sequence [symbol '"', symbol '`'])
449+ (void $ try $ sequence [symbol '"', symbol '\''])
450+ <|> quoted' doubleQuoted ((:[]) <$> symbol '"')
451+ (void $ symbol '"')
296
452
297−blockCommand :: PandocMonad m => LP m Blocks
453+singleQuote :: PandocMonad m => LP m Inlines
298−blockCommand = try $ do
454+singleQuote = do
299− name <- anyControlSeq
455+ quoted' singleQuoted ((:[]) <$> symbol '`')
300− guard $ name /= "begin" && name /= "end"
456+ (try $ symbol '\'' >>
301− star <- option "" (string "*" <* optional sp)
457+ notFollowedBy (satisfyTok startsWithLetter))
302− let name' = name ++ star
458+ <|> quoted' singleQuoted ((:[]) <$> symbol '‘')
303− let raw = do
459+ (try $ symbol '’' >>
304− rawcommand <- getRawCommand name'
460+ notFollowedBy (satisfyTok startsWithLetter))
305− transformed <- applyMacros' rawcommand
461+ where startsWithLetter (Tok _ Word t) =
306− guard $ transformed /= rawcommand
462+ case T.uncons t of
307− notFollowedBy $ parseFromString' inlines transformed
463+ Just (c, _) | isLetter c -> True
308− parseFromString' blocks transformed
464+ _ -> False
309− lookupListDefault raw [name',name] blockCommands
465+ startsWithLetter _ = False
466+
467+quoted' :: PandocMonad m
468+ => (Inlines -> Inlines)
469+ -> LP m [Tok]
470+ -> LP m ()
471+ -> LP m Inlines
472+quoted' f starter ender = do
473+ startchs <- (T.unpack . untokenize) <$> starter
474+ smart <- extensionEnabled Ext_smart <$> getOption readerExtensions
475+ if smart
476+ then do
477+ ils <- many (notFollowedBy ender >> inline)
478+ (ender >> return (f (mconcat ils))) <|>
479+ (<> mconcat ils) <$>
480+ lit (case startchs of
481+ "``" -> "“"
482+ "`" -> "‘"
483+ cs -> cs)
484+ else lit startchs
310
485
311−inBrackets :: Inlines -> Inlines
486+enquote :: PandocMonad m => LP m Inlines
312−inBrackets x = str "[" <> x <> str "]"
487+enquote = do
488+ skipopts
489+ quoteContext <- sQuoteContext <$> getState
490+ if quoteContext == InDoubleQuote
491+ then singleQuoted <$> withQuoteContext InSingleQuote tok
492+ else doubleQuoted <$> withQuoteContext InDoubleQuote tok
313
493
314−-- eat an optional argument and one or more arguments in braces
315−ignoreInlines :: PandocMonad m => String -> (String, LP m Inlines)
494+doverb :: PandocMonad m => LP m Inlines
316−ignoreInlines name = (name, p)
495+doverb = do
317− where
496+ Tok _ Symbol t <- anySymbol
318− p = do oa <- optargs
497+ marker <- case T.uncons t of
319− let rawCommand = '\\':name ++ oa
498+ Just (c, ts) | T.null ts -> return c
320− let doraw = guardRaw >> return (rawInline "latex" rawCommand)
499+ _ -> mzero
321− doraw <|> ignore rawCommand
500+ (code . T.unpack . untokenize) <$> manyTill (verbTok marker) (symbol marker)
501+
502+verbTok :: PandocMonad m => Char -> LP m Tok
503+verbTok stopchar = do
504+ t@(Tok (lin, col) toktype txt) <- satisfyTok (not . isNewlineTok)
505+ case T.findIndex (== stopchar) txt of
506+ Nothing -> return t
507+ Just i -> do
508+ let (t1, t2) = T.splitAt i txt
509+ inp <- getInput
510+ setInput $ Tok (lin, col + i) Symbol (T.singleton stopchar)
511+ : (totoks (lin, col + i + 1) (T.drop 1 t2)) ++ inp
512+ return $ Tok (lin, col) toktype t1
322
513
323−guardRaw :: PandocMonad m => LP m ()
514+dolstinline :: PandocMonad m => LP m Inlines
324−guardRaw = getOption readerExtensions >>= guard . extensionEnabled Ext_raw_tex
515+dolstinline = do
516+ options <- option [] keyvals
517+ let classes = maybeToList $ lookup "language" options >>= fromListingsLanguage
518+ Tok _ Symbol t <- anySymbol
519+ marker <- case T.uncons t of
520+ Just (c, ts) | T.null ts -> return c
521+ _ -> mzero
522+ let stopchar = if marker == '{' then '}' else marker
523+ (codeWith ("",classes,[]) . T.unpack . untokenize) <$>
524+ manyTill (verbTok stopchar) (symbol stopchar)
325
525
326−optargs :: PandocMonad m => LP m String
526+keyval :: PandocMonad m => LP m (String, String)
327−optargs = snd <$> withRaw (skipopts *> skipMany (try $ optional sp *> braced))
527+keyval = try $ do
528+ Tok _ Word key <- satisfyTok isWordTok
529+ let isSpecSym (Tok _ Symbol t) = t `elem` [".",":","-","|","\\"]
530+ isSpecSym _ = False
531+ val <- option [] $ do
532+ symbol '='
533+ braced <|> (many1 (satisfyTok isWordTok <|> satisfyTok isSpecSym))
534+ optional sp
535+ optional (symbol ',')
536+ optional sp
537+ return (T.unpack key, T.unpack . untokenize $ val)
328
538
329−ignore :: (Monoid a, PandocMonad m) => String -> ParserT s u m a
539+keyvals :: PandocMonad m => LP m [(String, String)]
330−ignore raw = do
540+keyvals = try $ symbol '[' >> manyTill keyval (symbol ']')
331− pos <- getPosition
332− report $ SkippedContent raw pos
333− return mempty
334
541
335−ignoreBlocks :: PandocMonad m => String -> (String, LP m Blocks)
542+accent :: (Char -> String) -> Inlines -> LP m Inlines
336−ignoreBlocks name = (name, p)
543+accent f ils =
337− where
544+ case toList ils of
338− p = do oa <- optargs
545+ (Str (x:xs) : ys) -> return $ fromList (Str (f x ++ xs) : ys)
339− let rawCommand = '\\':name ++ oa
546+ [] -> mzero
340− let doraw = guardRaw >> return (rawBlock "latex" rawCommand)
547+ _ -> return ils
341− doraw <|> ignore rawCommand
342
548
343−blockCommands :: PandocMonad m => M.Map String (LP m Blocks)
549+grave :: Char -> String
344−blockCommands = M.fromList $
550+grave 'A' = "À"
345− [ ("par", mempty <$ skipopts)
551+grave 'E' = "È"
346− , ("parbox", braced >> grouped blocks)
552+grave 'I' = "Ì"
347− , ("title", mempty <$ (skipopts *>
553+grave 'O' = "Ò"
348− (grouped inline >>= addMeta "title")
554+grave 'U' = "Ù"
349− <|> (grouped block >>= addMeta "title")))
555+grave 'a' = "à"
350− , ("subtitle", mempty <$ (skipopts *> tok >>= addMeta "subtitle"))
556+grave 'e' = "è"
351− , ("author", mempty <$ (skipopts *> authors))
557+grave 'i' = "ì"
352− -- -- in letter class, temp. store address & sig as title, author
558+grave 'o' = "ò"
353− , ("address", mempty <$ (skipopts *> tok >>= addMeta "address"))
559+grave 'u' = "ù"
354− , ("signature", mempty <$ (skipopts *> authors))
560+grave c = [c]
355− , ("date", mempty <$ (skipopts *> tok >>= addMeta "date"))
356− -- Koma-script metadata commands
357− , ("dedication", mempty <$ (skipopts *> tok >>= addMeta "dedication"))
358− -- sectioning
359− , ("part", section nullAttr (-1))
360− , ("part*", section nullAttr (-1))
361− , ("chapter", section nullAttr 0)
362− , ("chapter*", section ("",["unnumbered"],[]) 0)
363− , ("section", section nullAttr 1)
364− , ("section*", section ("",["unnumbered"],[]) 1)
365− , ("subsection", section nullAttr 2)
366− , ("subsection*", section ("",["unnumbered"],[]) 2)
367− , ("subsubsection", section nullAttr 3)
368− , ("subsubsection*", section ("",["unnumbered"],[]) 3)
369− , ("paragraph", section nullAttr 4)
370− , ("paragraph*", section ("",["unnumbered"],[]) 4)
371− , ("subparagraph", section nullAttr 5)
372− , ("subparagraph*", section ("",["unnumbered"],[]) 5)
373− -- beamer slides
374− , ("frametitle", section nullAttr 3)
375− , ("framesubtitle", section nullAttr 4)
376− -- letters
377− , ("opening", (para . trimInlines) <$> (skipopts *> tok))
378− , ("closing", skipopts *> closing)
379− --
380− , ("hrule", pure horizontalRule)
381− , ("strut", pure mempty)
382− , ("rule", skipopts *> tok *> tok *> pure horizontalRule)
383− , ("item", skipopts *> looseItem)
384− , ("documentclass", skipopts *> braced *> preamble)
385− , ("centerline", (para . trimInlines) <$> (skipopts *> tok))
386− , ("caption", skipopts *> setCaption)
387− , ("bibliography", mempty <$ (skipopts *> braced >>=
388− addMeta "bibliography" . splitBibs))
389− , ("addbibresource", mempty <$ (skipopts *> braced >>=
390− addMeta "bibliography" . splitBibs))
391− -- includes
392− , ("lstinputlisting", inputListing)
393− , ("graphicspath", graphicsPath)
394− -- hyperlink
395− , ("hypertarget", braced >> grouped block)
396− ] ++ map ignoreBlocks
397− -- these commands will be ignored unless --parse-raw is specified,
398− -- in which case they will appear as raw latex blocks
399− [ "newcommand", "renewcommand", "newenvironment", "renewenvironment"
400− -- newcommand, etc. should be parsed by macro, but we need this
401− -- here so these aren't parsed as inline commands to ignore
402− , "special", "pdfannot", "pdfstringdef"
403− , "bibliographystyle"
404− , "maketitle", "makeindex", "makeglossary"
405− , "addcontentsline", "addtocontents", "addtocounter"
406− -- \ignore{} is used conventionally in literate haskell for definitions
407− -- that are to be processed by the compiler but not printed.
408− , "ignore"
409− , "hyperdef"
410− , "markboth", "markright", "markleft"
411− , "hspace", "vspace"
412− , "newpage"
413− , "clearpage"
414− , "pagebreak"
415− ]
416
561
417−graphicsPath :: PandocMonad m => LP m Blocks
562+acute :: Char -> String
418−graphicsPath = do
563+acute 'A' = "Á"
419− ps <- bgroup *> (manyTill braced egroup)
564+acute 'E' = "É"
420− getResourcePath >>= setResourcePath . (++ ps)
565+acute 'I' = "Í"
421− return mempty
566+acute 'O' = "Ó"
567+acute 'U' = "Ú"
568+acute 'Y' = "Ý"
569+acute 'a' = "á"
570+acute 'e' = "é"
571+acute 'i' = "í"
572+acute 'o' = "ó"
573+acute 'u' = "ú"
574+acute 'y' = "ý"
575+acute 'C' = "Ć"
576+acute 'c' = "ć"
577+acute 'L' = "Ĺ"
578+acute 'l' = "ĺ"
579+acute 'N' = "Ń"
580+acute 'n' = "ń"
581+acute 'R' = "Ŕ"
582+acute 'r' = "ŕ"
583+acute 'S' = "Ś"
584+acute 's' = "ś"
585+acute 'Z' = "Ź"
586+acute 'z' = "ź"
587+acute c = [c]
422
588
423−addMeta :: PandocMonad m => ToMetaValue a => String -> a -> LP m ()
589+circ :: Char -> String
424−addMeta field val = updateState $ \st ->
590+circ 'A' = "Â"
425− st{ stateMeta = addMetaField field val $ stateMeta st }
591+circ 'E' = "Ê"
426−
592+circ 'I' = "Î"
427−splitBibs :: String -> [Inlines]
593+circ 'O' = "Ô"
428−splitBibs = map (str . flip replaceExtension "bib" . trim) . splitBy (==',')
594+circ 'U' = "Û"
429−
595+circ 'a' = "â"
430−setCaption :: PandocMonad m => LP m Blocks
596+circ 'e' = "ê"
431−setCaption = do
597+circ 'i' = "î"
432− ils <- tok
598+circ 'o' = "ô"
433− mblabel <- option Nothing $
599+circ 'u' = "û"
434− try $ spaces' >> controlSeq "label" >> (Just <$> tok)
600+circ 'C' = "Ĉ"
435− let ils' = case mblabel of
601+circ 'c' = "ĉ"
436− Just lab -> ils <> spanWith
602+circ 'G' = "Ĝ"
437− ("",[],[("data-label", stringify lab)]) mempty
603+circ 'g' = "ĝ"
438− Nothing -> ils
604+circ 'H' = "Ĥ"
439− updateState $ \st -> st{ stateCaption = Just ils' }
605+circ 'h' = "ĥ"
440− return mempty
606+circ 'J' = "Ĵ"
607+circ 'j' = "ĵ"
608+circ 'S' = "Ŝ"
609+circ 's' = "ŝ"
610+circ 'W' = "Ŵ"
611+circ 'w' = "ŵ"
612+circ 'Y' = "Ŷ"
613+circ 'y' = "ŷ"
614+circ c = [c]
441
615
442−resetCaption :: PandocMonad m => LP m ()
616+tilde :: Char -> String
443−resetCaption = updateState $ \st -> st{ stateCaption = Nothing }
617+tilde 'A' = "Ã"
618+tilde 'a' = "ã"
619+tilde 'O' = "Õ"
620+tilde 'o' = "õ"
621+tilde 'I' = "Ĩ"
622+tilde 'i' = "ĩ"
623+tilde 'U' = "Ũ"
624+tilde 'u' = "ũ"
625+tilde 'N' = "Ñ"
626+tilde 'n' = "ñ"
627+tilde c = [c]
444
628
445−authors :: PandocMonad m => LP m ()
629+umlaut :: Char -> String
446−authors = try $ do
630+umlaut 'A' = "Ä"
447− bgroup
631+umlaut 'E' = "Ë"
448− let oneAuthor = mconcat <$>
632+umlaut 'I' = "Ï"
449− many1 (notFollowedBy' (controlSeq "and") >>
633+umlaut 'O' = "Ö"
450− (inline <|> mempty <$ blockCommand))
634+umlaut 'U' = "Ü"
451− -- skip e.g. \vspace{10pt}
635+umlaut 'a' = "ä"
452− auths <- sepBy oneAuthor (controlSeq "and")
636+umlaut 'e' = "ë"
453− egroup
637+umlaut 'i' = "ï"
454− addMeta "author" (map trimInlines auths)
638+umlaut 'o' = "ö"
639+umlaut 'u' = "ü"
640+umlaut c = [c]
455
641
456−section :: PandocMonad m => Attr -> Int -> LP m Blocks
642+hungarumlaut :: Char -> String
457−section (ident, classes, kvs) lvl = do
643+hungarumlaut 'A' = "A̋"
458− skipopts
644+hungarumlaut 'E' = "E̋"
459− contents <- grouped inline
645+hungarumlaut 'I' = "I̋"
460− lab <- option ident $ try (spaces' >> controlSeq "label" >> spaces' >> braced)
646+hungarumlaut 'O' = "Ő"
461− attr' <- registerHeader (lab, classes, kvs) contents
647+hungarumlaut 'U' = "Ű"
462− return $ headerWith attr' lvl contents
648+hungarumlaut 'Y' = "ӳ"
649+hungarumlaut 'a' = "a̋"
650+hungarumlaut 'e' = "e̋"
651+hungarumlaut 'i' = "i̋"
652+hungarumlaut 'o' = "ő"
653+hungarumlaut 'u' = "ű"
654+hungarumlaut 'y' = "ӳ"
655+hungarumlaut c = [c]
463
656
657+dot :: Char -> String
658+dot 'C' = "Ċ"
659+dot 'c' = "ċ"
660+dot 'E' = "Ė"
661+dot 'e' = "ė"
662+dot 'G' = "Ġ"
663+dot 'g' = "ġ"
664+dot 'I' = "İ"
665+dot 'Z' = "Ż"
666+dot 'z' = "ż"
667+dot c = [c]
668+
669+macron :: Char -> String
670+macron 'A' = "Ā"
671+macron 'E' = "Ē"
672+macron 'I' = "Ī"
673+macron 'O' = "Ō"
674+macron 'U' = "Ū"
675+macron 'a' = "ā"
676+macron 'e' = "ē"
677+macron 'i' = "ī"
678+macron 'o' = "ō"
679+macron 'u' = "ū"
680+macron c = [c]
681+
682+cedilla :: Char -> String
683+cedilla 'c' = "ç"
684+cedilla 'C' = "Ç"
685+cedilla 's' = "ş"
686+cedilla 'S' = "Ş"
687+cedilla 't' = "ţ"
688+cedilla 'T' = "Ţ"
689+cedilla 'e' = "ȩ"
690+cedilla 'E' = "Ȩ"
691+cedilla 'h' = "ḩ"
692+cedilla 'H' = "Ḩ"
693+cedilla 'o' = "o̧"
694+cedilla 'O' = "O̧"
695+cedilla c = [c]
696+
697+hacek :: Char -> String
698+hacek 'A' = "Ǎ"
699+hacek 'a' = "ǎ"
700+hacek 'C' = "Č"
701+hacek 'c' = "č"
702+hacek 'D' = "Ď"
703+hacek 'd' = "ď"
704+hacek 'E' = "Ě"
705+hacek 'e' = "ě"
706+hacek 'G' = "Ǧ"
707+hacek 'g' = "ǧ"
708+hacek 'H' = "Ȟ"
709+hacek 'h' = "ȟ"
710+hacek 'I' = "Ǐ"
711+hacek 'i' = "ǐ"
712+hacek 'j' = "ǰ"
713+hacek 'K' = "Ǩ"
714+hacek 'k' = "ǩ"
715+hacek 'L' = "Ľ"
716+hacek 'l' = "ľ"
717+hacek 'N' = "Ň"
718+hacek 'n' = "ň"
719+hacek 'O' = "Ǒ"
720+hacek 'o' = "ǒ"
721+hacek 'R' = "Ř"
722+hacek 'r' = "ř"
723+hacek 'S' = "Š"
724+hacek 's' = "š"
725+hacek 'T' = "Ť"
726+hacek 't' = "ť"
727+hacek 'U' = "Ǔ"
728+hacek 'u' = "ǔ"
729+hacek 'Z' = "Ž"
730+hacek 'z' = "ž"
731+hacek c = [c]
732+
733+breve :: Char -> String
734+breve 'A' = "Ă"
735+breve 'a' = "ă"
736+breve 'E' = "Ĕ"
737+breve 'e' = "ĕ"
738+breve 'G' = "Ğ"
739+breve 'g' = "ğ"
740+breve 'I' = "Ĭ"
741+breve 'i' = "ĭ"
742+breve 'O' = "Ŏ"
743+breve 'o' = "ŏ"
744+breve 'U' = "Ŭ"
745+breve 'u' = "ŭ"
746+breve c = [c]
747+
748+toksToString :: [Tok] -> String
749+toksToString = T.unpack . untokenize
750+
751+mathDisplay :: String -> Inlines
752+mathDisplay = displayMath . trim
753+-- TODO we shouldn't need applyMacros
754+-- mathDisplay p = displayMath <$> (try p >>= applyMacros' . trim)
755+
756+mathInline :: String -> Inlines
757+mathInline = math . trim
758+-- TODO we shouldn't need applyMacros
759+-- mathInline p = math <$> (try p >>= applyMacros')
760+
761+dollarsMath :: PandocMonad m => LP m Inlines
762+dollarsMath = do
763+ symbol '$'
764+ display <- option False (True <$ symbol '$')
765+ contents <- many1Till anyTok (symbol '$')
766+ when display (() <$ symbol '$')
767+ let constructor = if display then mathDisplay else mathInline
768+ return $ constructor $ toksToString contents
769+
464−inlineCommand :: PandocMonad m => LP m Inlines
770+inlineCommand' :: PandocMonad m => LP m Inlines
465−inlineCommand = try $ do
771+inlineCommand' = try $ do
466− (name, raw') <- withRaw anyControlSeq
772+ cs@(Tok _ CtrlSeq t) <- anyControlSeq
773+ let name = T.drop 1 t
467− guard $ name /= "begin" && name /= "end"
774+ guard $ name /= "begin" && name /= "end"
468− star <- option "" (string "*")
775+ mbstar <- option Nothing (Just <$> symbol '*')
469− let name' = name ++ star
776+ let names = name : maybe [] (const [name <> "*"]) mbstar
777+ optional $ try $ symbol '{' >> symbol '}'
470 let raw = do
778 let raw = do
471− guard $ not (isBlockCommand name)
779+ guard $ not (isBlockCommand name)
472− rawargs <- withRaw
780+ (_, rawargs) <- withRaw
473− (skipangles *> skipopts *> option "" dimenarg *> many braced)
781+ (skipangles *> skipopts *> option "" dimenarg *> many braced)
474− let rawcommand = raw' ++ star ++ snd rawargs
782+ let rawcommand = T.unpack $ untokenize $
783+ cs : maybe [] (:[]) mbstar ++ rawargs
784+ (guardEnabled Ext_raw_tex >> return (rawInline "latex" rawcommand))
785+ <|> ignore rawcommand
475− transformed <- applyMacros' rawcommand
786+ -- transformed <- applyMacros' rawcommand -- TODO or get rid of
476− exts <- getOption readerExtensions
787+ -- exts <- getOption readerExtensions
477− if transformed /= rawcommand
788+ -- if transformed /= rawcommand
478− then parseFromString' inlines transformed
789+ -- then parseFromString' inlines transformed
479− else if extensionEnabled Ext_raw_tex exts
790+ -- else if extensionEnabled Ext_raw_tex exts
480− then return $ rawInline "latex" rawcommand
791+ -- then return $ rawInline "latex" rawcommand
481− else ignore rawcommand
792+ -- else ignore rawcommand
482− (lookupListDefault raw [name',name] inlineCommands <*
793+ lookupListDefault raw names inlineCommands
794+
795+tok :: PandocMonad m => LP m Inlines
796+tok = try $ grouped inline <|> inlineCommand' <|> singleChar
797+ where singleChar = do
798+ Tok (lin,col) toktype t <- satisfyTok (tokTypeIn [Word, Symbol])
799+ if T.length t > 1
800+ then do
801+ let (t1, t2) = (T.take 1 t, T.drop 1 t)
802+ inp <- getInput
803+ setInput $ (Tok (lin, col + 1) toktype t2) : inp
804+ return $ str (T.unpack t1)
805+ else return $ str (T.unpack t)
806+
807+opt :: PandocMonad m => LP m Inlines
808+opt = bracketed inline
809+
810+rawopt :: PandocMonad m => LP m Text
811+rawopt = do
812+ symbol '['
813+ inner <- untokenize <$> manyTill anyTok (symbol ']')
483− optional (try (string "{}")))
814+ optional sp
484−
815+ return $ "[" <> inner <> "]"
485−rawInlineOr :: PandocMonad m => String -> LP m Inlines -> LP m Inlines
816+
486−rawInlineOr name' fallback = do
817+skipopts :: PandocMonad m => LP m ()
487− parseRaw <- extensionEnabled Ext_raw_tex <$> getOption readerExtensions
818+skipopts = skipMany rawopt
488− if parseRaw
819+
489− then rawInline "latex" <$> getRawCommand name'
820+-- eat an optional argument and one or more arguments in braces
490− else fallback
821+ignoreInlines :: PandocMonad m => Text -> (Text, LP m Inlines)
822+ignoreInlines name = (name, p)
823+ where p = do oa <- optargs
824+ let rawCommand = '\\' : T.unpack (name <> oa)
825+ let doraw = guardRaw >> return (rawInline "latex" rawCommand)
826+ doraw <|> ignore rawCommand
827+
828+guardRaw :: PandocMonad m => LP m ()
829+guardRaw = getOption readerExtensions >>= guard . extensionEnabled Ext_raw_tex
830+
831+optargs :: PandocMonad m => LP m Text
832+optargs = do
833+ (_, raw) <- withRaw (skipopts *> skipMany (try $ optional sp *> braced))
834+ return $ untokenize raw
835+
836+-- opts in angle brackets are used in beamer
837+rawangle :: PandocMonad m => LP m ()
838+rawangle = try $ do
839+ symbol '<'
840+ () <$ manyTill anyTok (symbol '>')
841+
842+skipangles :: PandocMonad m => LP m ()
843+skipangles = skipMany rawangle
844+
845+ignore :: (Monoid a, PandocMonad m) => String -> ParserT s u m a
846+ignore raw = do
847+ pos <- getPosition
848+ report $ SkippedContent raw pos
849+ return mempty
491
850
492−isBlockCommand :: String -> Bool
851+withRaw :: PandocMonad m => LP m a -> LP m (a, [Tok])
493−isBlockCommand s = s `M.member` (blockCommands :: M.Map String (LP PandocPure Blocks))
852+withRaw parser = do
853+ inp <- getInput
854+ result <- parser
855+ nxt <- option (Tok (0,0) Word "") (lookAhead anyTok)
856+ let raw = takeWhile (/= nxt) inp
857+ return (result, raw)
494
858
859+inBrackets :: Inlines -> Inlines
860+inBrackets x = str "[" <> x <> str "]"
861+
862+unescapeURL :: String -> String
863+unescapeURL ('\\':x:xs) | isEscapable x = x:unescapeURL xs
864+ where isEscapable c = c `elem` ("#$%&~_^\\{}" :: String)
865+unescapeURL (x:xs) = x:unescapeURL xs
866+unescapeURL [] = ""
867+
868+mathEnvWith :: PandocMonad m
869+ => (Inlines -> a) -> Maybe String -> String -> LP m a
870+mathEnvWith f innerEnv name = f . mathDisplay . inner <$> mathEnv name
871+ where inner x = case innerEnv of
872+ Nothing -> x
873+ Just y -> "\\begin{" ++ y ++ "}\n" ++ x ++
874+ "\\end{" ++ y ++ "}"
875+
876+mathEnv :: PandocMonad m => String -> LP m String
877+mathEnv name = do
878+ skipopts
879+ optional blankline
880+ let endEnv = try $ controlSeq "end" *>
881+ (T.unpack . untokenize <$> braced) >>= guard . (== name)
882+ res <- manyTill anyTok endEnv
883+ return $ stripTrailingNewlines $ T.unpack $ untokenize res
884+
885+inlineEnvironment :: PandocMonad m => LP m Inlines
886+inlineEnvironment = try $ do
887+ controlSeq "begin"
888+ name <- untokenize <$> braced
889+ M.findWithDefault mzero name inlineEnvironments
495
890
496−inlineEnvironments :: PandocMonad m => M.Map String (LP m Inlines)
891+inlineEnvironments :: PandocMonad m => M.Map Text (LP m Inlines)
497−inlineEnvironments = M.fromList
892+inlineEnvironments = M.fromList [
498− [ ("displaymath", mathEnvWith id Nothing "displaymath")
893+ ("displaymath", mathEnvWith id Nothing "displaymath")
499 , ("math", math <$> mathEnv "math")
894 , ("math", math <$> mathEnv "math")
500 , ("equation", mathEnvWith id Nothing "equation")
895 , ("equation", mathEnvWith id Nothing "equation")
501 , ("equation*", mathEnvWith id Nothing "equation*")
896 , ("equation*", mathEnvWith id Nothing "equation*")
@@ -511,7 +906,7 @@ inlineEnvironments = M.fromList
511 , ("alignat*", mathEnvWith id (Just "aligned") "alignat*")
906 , ("alignat*", mathEnvWith id (Just "aligned") "alignat*")
512 ]
907 ]
513
908
514−inlineCommands :: PandocMonad m => M.Map String (LP m Inlines)
909+inlineCommands :: PandocMonad m => M.Map Text (LP m Inlines)
515inlineCommands = M.fromList $
910inlineCommands = M.fromList $
516 [ ("emph", extractSpaces emph <$> tok)
911 [ ("emph", extractSpaces emph <$> tok)
517 , ("textit", extractSpaces emph <$> tok)
912 , ("textit", extractSpaces emph <$> tok)
@@ -540,9 +935,9 @@ inlineCommands = M.fromList $
540 , ("textgreek", tok)
935 , ("textgreek", tok)
541 , ("sep", lit ",")
936 , ("sep", lit ",")
542 , ("cref", rawInlineOr "cref" (inBrackets <$> tok)) -- from cleveref.sty
937 , ("cref", rawInlineOr "cref" (inBrackets <$> tok)) -- from cleveref.sty
543− , ("(", mathInline $ manyTill anyChar (try $ string "\\)"))
938+ , ("(", mathInline . toksToString <$> manyTill anyTok (controlSeq ")"))
544− , ("[", mathDisplay $ manyTill anyChar (try $ string "\\]"))
939+ , ("[", mathDisplay . toksToString <$> manyTill anyTok (controlSeq "]"))
545− , ("ensuremath", mathInline braced)
940+ , ("ensuremath", mathInline . toksToString <$> braced)
546 , ("texorpdfstring", (\_ x -> x) <$> tok <*> tok)
941 , ("texorpdfstring", (\_ x -> x) <$> tok <*> tok)
547 , ("P", lit "¶")
942 , ("P", lit "¶")
548 , ("S", lit "§")
943 , ("S", lit "§")
@@ -592,7 +987,7 @@ inlineCommands = M.fromList $
592 , ("v", option (str "v") $ try $ tok >>= accent hacek)
987 , ("v", option (str "v") $ try $ tok >>= accent hacek)
593 , ("u", option (str "u") $ try $ tok >>= accent breve)
988 , ("u", option (str "u") $ try $ tok >>= accent breve)
594 , ("i", lit "i")
989 , ("i", lit "i")
595− , ("\\", linebreak <$ (optional (bracketed inline) *> spaces'))
990+ , ("\\", linebreak <$ (optional (bracketed inline) *> spaces))
596 , (",", lit "\8198")
991 , (",", lit "\8198")
597 , ("@", pure mempty)
992 , ("@", pure mempty)
598 , (" ", lit "\160")
993 , (" ", lit "\160")
@@ -607,73 +1002,73 @@ inlineCommands = M.fromList $
607 , ("verb", doverb)
1002 , ("verb", doverb)
608 , ("lstinline", dolstinline)
1003 , ("lstinline", dolstinline)
609 , ("Verb", doverb)
1004 , ("Verb", doverb)
610− , ("url", (unescapeURL <$> braced) >>= \url ->
1005+ , ("url", ((unescapeURL . T.unpack . untokenize) <$> braced) >>= \url ->
611− pure (link url "" (str url)))
1006+ pure (link url "" (str url)))
612− , ("href", (unescapeURL <$> braced <* optional sp) >>= \url ->
1007+-- , ("href", (unescapeURL <$> braced <* optional sp) >>= \url ->
613− tok >>= \lab ->
1008+-- tok >>= \lab ->
614− pure (link url "" lab))
1009+-- pure (link url "" lab))
615− , ("includegraphics", do options <- option [] keyvals
1010+-- , ("includegraphics", do options <- option [] keyvals
616− src <- unescapeURL . removeDoubleQuotes <$> braced
1011+-- src <- unescapeURL . removeDoubleQuotes <$> braced
617− mkImage options src)
1012+-- mkImage options src)
618 , ("enquote", enquote)
1013 , ("enquote", enquote)
619− , ("cite", citation "cite" NormalCitation False)
1014+-- , ("cite", citation "cite" NormalCitation False)
620− , ("Cite", citation "Cite" NormalCitation False)
1015+-- , ("Cite", citation "Cite" NormalCitation False)
621− , ("citep", citation "citep" NormalCitation False)
1016+-- , ("citep", citation "citep" NormalCitation False)
622− , ("citep*", citation "citep*" NormalCitation False)
1017+-- , ("citep*", citation "citep*" NormalCitation False)
623− , ("citeal", citation "citeal" NormalCitation False)
1018+-- , ("citeal", citation "citeal" NormalCitation False)
624− , ("citealp", citation "citealp" NormalCitation False)
1019+-- , ("citealp", citation "citealp" NormalCitation False)
625− , ("citealp*", citation "citealp*" NormalCitation False)
1020+-- , ("citealp*", citation "citealp*" NormalCitation False)
626− , ("autocite", citation "autocite" NormalCitation False)
1021+-- , ("autocite", citation "autocite" NormalCitation False)
627− , ("smartcite", citation "smartcite" NormalCitation False)
1022+-- , ("smartcite", citation "smartcite" NormalCitation False)
628− , ("footcite", inNote <$> citation "footcite" NormalCitation False)
1023+-- , ("footcite", inNote <$> citation "footcite" NormalCitation False)
629− , ("parencite", citation "parencite" NormalCitation False)
1024+-- , ("parencite", citation "parencite" NormalCitation False)
630− , ("supercite", citation "supercite" NormalCitation False)
1025+-- , ("supercite", citation "supercite" NormalCitation False)
631− , ("footcitetext", inNote <$> citation "footcitetext" NormalCitation False)
1026+-- , ("footcitetext", inNote <$> citation "footcitetext" NormalCitation False)
632− , ("citeyearpar", citation "citeyearpar" SuppressAuthor False)
1027+-- , ("citeyearpar", citation "citeyearpar" SuppressAuthor False)
633− , ("citeyear", citation "citeyear" SuppressAuthor False)
1028+-- , ("citeyear", citation "citeyear" SuppressAuthor False)
634− , ("autocite*", citation "autocite*" SuppressAuthor False)
1029+-- , ("autocite*", citation "autocite*" SuppressAuthor False)
635− , ("cite*", citation "cite*" SuppressAuthor False)
1030+-- , ("cite*", citation "cite*" SuppressAuthor False)
636− , ("parencite*", citation "parencite*" SuppressAuthor False)
1031+-- , ("parencite*", citation "parencite*" SuppressAuthor False)
637− , ("textcite", citation "textcite" AuthorInText False)
1032+-- , ("textcite", citation "textcite" AuthorInText False)
638− , ("citet", citation "citet" AuthorInText False)
1033+-- , ("citet", citation "citet" AuthorInText False)
639− , ("citet*", citation "citet*" AuthorInText False)
1034+-- , ("citet*", citation "citet*" AuthorInText False)
640− , ("citealt", citation "citealt" AuthorInText False)
1035+-- , ("citealt", citation "citealt" AuthorInText False)
641− , ("citealt*", citation "citealt*" AuthorInText False)
1036+-- , ("citealt*", citation "citealt*" AuthorInText False)
642− , ("textcites", citation "textcites" AuthorInText True)
1037+-- , ("textcites", citation "textcites" AuthorInText True)
643− , ("cites", citation "cites" NormalCitation True)
1038+-- , ("cites", citation "cites" NormalCitation True)
644− , ("autocites", citation "autocites" NormalCitation True)
1039+-- , ("autocites", citation "autocites" NormalCitation True)
645− , ("footcites", inNote <$> citation "footcites" NormalCitation True)
1040+-- , ("footcites", inNote <$> citation "footcites" NormalCitation True)
646− , ("parencites", citation "parencites" NormalCitation True)
1041+-- , ("parencites", citation "parencites" NormalCitation True)
647− , ("supercites", citation "supercites" NormalCitation True)
1042+-- , ("supercites", citation "supercites" NormalCitation True)
648− , ("footcitetexts", inNote <$> citation "footcitetexts" NormalCitation True)
1043+-- , ("footcitetexts", inNote <$> citation "footcitetexts" NormalCitation True)
649− , ("Autocite", citation "Autocite" NormalCitation False)
1044+-- , ("Autocite", citation "Autocite" NormalCitation False)
650− , ("Smartcite", citation "Smartcite" NormalCitation False)
1045+-- , ("Smartcite", citation "Smartcite" NormalCitation False)
651− , ("Footcite", citation "Footcite" NormalCitation False)
1046+-- , ("Footcite", citation "Footcite" NormalCitation False)
652− , ("Parencite", citation "Parencite" NormalCitation False)
1047+-- , ("Parencite", citation "Parencite" NormalCitation False)
653− , ("Supercite", citation "Supercite" NormalCitation False)
1048+-- , ("Supercite", citation "Supercite" NormalCitation False)
654− , ("Footcitetext", inNote <$> citation "Footcitetext" NormalCitation False)
1049+-- , ("Footcitetext", inNote <$> citation "Footcitetext" NormalCitation False)
655− , ("Citeyearpar", citation "Citeyearpar" SuppressAuthor False)
1050+-- , ("Citeyearpar", citation "Citeyearpar" SuppressAuthor False)
656− , ("Citeyear", citation "Citeyear" SuppressAuthor False)
1051+-- , ("Citeyear", citation "Citeyear" SuppressAuthor False)
657− , ("Autocite*", citation "Autocite*" SuppressAuthor False)
1052+-- , ("Autocite*", citation "Autocite*" SuppressAuthor False)
658− , ("Cite*", citation "Cite*" SuppressAuthor False)
1053+-- , ("Cite*", citation "Cite*" SuppressAuthor False)
659− , ("Parencite*", citation "Parencite*" SuppressAuthor False)
1054+-- , ("Parencite*", citation "Parencite*" SuppressAuthor False)
660− , ("Textcite", citation "Textcite" AuthorInText False)
1055+-- , ("Textcite", citation "Textcite" AuthorInText False)
661− , ("Textcites", citation "Textcites" AuthorInText True)
1056+-- , ("Textcites", citation "Textcites" AuthorInText True)
662− , ("Cites", citation "Cites" NormalCitation True)
1057+-- , ("Cites", citation "Cites" NormalCitation True)
663− , ("Autocites", citation "Autocites" NormalCitation True)
1058+-- , ("Autocites", citation "Autocites" NormalCitation True)
664− , ("Footcites", citation "Footcites" NormalCitation True)
1059+-- , ("Footcites", citation "Footcites" NormalCitation True)
665− , ("Parencites", citation "Parencites" NormalCitation True)
1060+-- , ("Parencites", citation "Parencites" NormalCitation True)
666− , ("Supercites", citation "Supercites" NormalCitation True)
1061+-- , ("Supercites", citation "Supercites" NormalCitation True)
667− , ("Footcitetexts", inNote <$> citation "Footcitetexts" NormalCitation True)
1062+-- , ("Footcitetexts", inNote <$> citation "Footcitetexts" NormalCitation True)
668− , ("citetext", complexNatbibCitation NormalCitation)
1063+-- , ("citetext", complexNatbibCitation NormalCitation)
669− , ("citeauthor", (try (tok *> optional sp *> controlSeq "citetext") *>
1064+-- , ("citeauthor", (try (tok *> optional sp *> controlSeq "citetext") *>
670− complexNatbibCitation AuthorInText)
1065+-- complexNatbibCitation AuthorInText)
671− <|> citation "citeauthor" AuthorInText False)
1066+-- <|> citation "citeauthor" AuthorInText False)
672− , ("nocite", mempty <$ (citation "nocite" NormalCitation False >>=
1067+-- , ("nocite", mempty <$ (citation "nocite" NormalCitation False >>=
673− addMeta "nocite"))
1068+-- addMeta "nocite"))
674 , ("hypertarget", braced >> tok)
1069 , ("hypertarget", braced >> tok)
675− -- siuntix
1070+-- -- siuntix
676− , ("SI", dosiunitx)
1071+-- , ("SI", dosiunitx)
677 -- hyphenat
1072 -- hyphenat
678 , ("bshyp", lit "\\\173")
1073 , ("bshyp", lit "\\\173")
679 , ("fshyp", lit "/\173")
1074 , ("fshyp", lit "/\173")
@@ -700,543 +1095,135 @@ inlineCommands = M.fromList $
700ttfamily :: PandocMonad m => LP m Inlines
1095ttfamily :: PandocMonad m => LP m Inlines
701ttfamily = (code . stringify . toList) <$> tok
1096ttfamily = (code . stringify . toList) <$> tok
702
1097
703−mkImage :: PandocMonad m => [(String, String)] -> String -> LP m Inlines
1098+rawInlineOr :: PandocMonad m => Text -> LP m Inlines -> LP m Inlines
704−mkImage options src = do
1099+rawInlineOr name' fallback = do
705− let replaceTextwidth (k,v) = case numUnit v of
1100+ parseRaw <- extensionEnabled Ext_raw_tex <$> getOption readerExtensions
706− Just (num, "\\textwidth") -> (k, showFl (num * 100) ++ "%")
1101+ if parseRaw
707− _ -> (k, v)
1102+ then rawInline "latex" <$> getRawCommand name'
708− let kvs = map replaceTextwidth $ filter (\(k,_) -> k `elem` ["width", "height"]) options
1103+ else fallback
709− let attr = ("",[], kvs)
710− let alt = str "image"
711− case takeExtension src of
712− "" -> do
713− defaultExt <- getOption readerDefaultImageExtension
714− return $ imageWith attr (addExtension src defaultExt) "" alt
715− _ -> return $ imageWith attr src "" alt
716−
717−inNote :: Inlines -> Inlines
718−inNote ils =
719− note $ para $ ils <> str "."
720−
721−unescapeURL :: String -> String
722−unescapeURL ('\\':x:xs) | isEscapable x = x:unescapeURL xs
723− where isEscapable c = c `elem` ("#$%&~_^\\{}" :: String)
724−unescapeURL (x:xs) = x:unescapeURL xs
725−unescapeURL [] = ""
726
1104
727−enquote :: PandocMonad m => LP m Inlines
1105+getRawCommand :: PandocMonad m => Text -> LP m String
728−enquote = do
1106+getRawCommand name' = do
729− skipopts
1107+ (_, rawargs) <- withRaw
730− context <- stateQuoteContext <$> getState
1108+ (many (try (optional sp *> opt)) *>
731− if context == InDoubleQuote
1109+ option "" (try (optional sp *> dimenarg)) *>
732− then singleQuoted <$> withQuoteContext InSingleQuote tok
1110+ many braced)
733− else doubleQuoted <$> withQuoteContext InDoubleQuote tok
1111+ return $ '\\' : T.unpack (name' <> untokenize rawargs)
734
1112
735−doverb :: PandocMonad m => LP m Inlines
1113+isBlockCommand :: Text -> Bool
736−doverb = do
1114+isBlockCommand s =
737− marker <- anyChar
1115+ s `M.member` (blockCommands :: M.Map Text (LP PandocPure Blocks))
738− code <$> manyTill (satisfy (/='\n')) (char marker)
739
1116
740−dolstinline :: PandocMonad m => LP m Inlines
1117+lookupListDefault :: (Ord k) => v -> [k] -> M.Map k v -> v
741−dolstinline = do
1118+lookupListDefault d = (fromMaybe d .) . lookupList
742− options <- option [] keyvals
1119+ where
743− let classes = maybeToList $ lookup "language" options >>= fromListingsLanguage
1120+ lookupList l m = msum $ map (`M.lookup` m) l
744− marker <- char '{' <|> anyChar
745− codeWith ("",classes,[]) <$> manyTill (satisfy (/='\n')) (char '}' <|> char marker)
746
1121
747−doLHSverb :: PandocMonad m => LP m Inlines
1122+inline :: PandocMonad m => LP m Inlines
748−doLHSverb = codeWith ("",["haskell"],[]) <$> manyTill (satisfy (/='\n')) (char '|')
1123+inline = (mempty <$ comment)
1124+ <|> (space <$ whitespace)
1125+ <|> (softbreak <$ endline)
1126+ <|> inlineText
1127+ <|> inlineCommand'
1128+ <|> inlineEnvironment
1129+ <|> inlineGroup
1130+ <|> (symbol '-' *>
1131+ option (str "-") (symbol '-' *>
1132+ option (str "–") (str "—" <$ symbol '-')))
1133+ <|> doubleQuote
1134+ <|> singleQuote
1135+ <|> (str "”" <$ try (symbol '\'' >> symbol '\''))
1136+ <|> (str "”" <$ symbol '”')
1137+ <|> (str "’" <$ symbol '\'')
1138+ <|> (str "’" <$ symbol '’')
1139+ <|> (str "\160" <$ symbol '~')
1140+ <|> dollarsMath
1141+ <|> (guardEnabled Ext_literate_haskell *> symbol '|' *> doLHSverb)
1142+ <|> (str . (:[]) <$> primEscape)
1143+ <|> (do res <- symbolIn "#&~^'`\"[]"
1144+ pos <- getPosition
1145+ let s = T.unpack (untoken res)
1146+ report $ ParsingUnescaped s pos
1147+ return $ str s)
749
1148
750−-- converts e.g. \SI{1}[\$]{} to "$ 1" or \SI{1}{\euro} to "1 €"
751−dosiunitx :: PandocMonad m => LP m Inlines
1149+inlines :: PandocMonad m => LP m Inlines
752−dosiunitx = do
1150+inlines = mconcat <$> many inline
753− skipopts
754− value <- tok
755− valueprefix <- option "" $ char '[' >> (mconcat <$> manyTill tok (char ']'))
756− unit <- tok
757− let emptyOr160 "" = ""
758− emptyOr160 _ = "\160"
759− return . mconcat $ [valueprefix,
760− emptyOr160 valueprefix,
761− value,
762− emptyOr160 unit,
763− unit]
764
1151
765−lit :: String -> LP m Inlines
1152+-- block elements:
766−lit = pure . str
767
1153
768−accent :: (Char -> String) -> Inlines -> LP m Inlines
1154+paragraph :: PandocMonad m => LP m Blocks
769−accent f ils =
1155+paragraph = do
770− case toList ils of
1156+ x <- trimInlines . mconcat <$> many1 inline
771− (Str (x:xs) : ys) -> return $ fromList (Str (f x ++ xs) : ys)
1157+ if x == mempty
772− [] -> mzero
1158+ then return mempty
773− _ -> return ils
1159+ else return $ para x
774
1160
775−grave :: Char -> String
1161+include :: PandocMonad m => LP m Blocks
776−grave 'A' = "À"
1162+include = do
777−grave 'E' = "È"
1163+ (Tok _ _ name) <- controlSeq "include" <|> controlSeq "input" <|>
778−grave 'I' = "Ì"
1164+ controlSeq "subfile" <|> controlSeq "usepackage"
779−grave 'O' = "Ò"
1165+ skipMany $ bracketed inline -- skip options
780−grave 'U' = "Ù"
1166+ fs <- (map trim . splitBy (==',') . T.unpack . untokenize) <$> braced
781−grave 'a' = "à"
1167+ let fs' = if name == "\\usepackage"
782−grave 'e' = "è"
1168+ then map (maybeAddExtension ".sty") fs
783−grave 'i' = "ì"
1169+ else map (maybeAddExtension ".tex") fs
784−grave 'o' = "ò"
1170+ dirs <- (splitBy (==':') . fromMaybe ".") <$> lookupEnv "TEXINPUTS"
785−grave 'u' = "ù"
1171+ mconcat <$> mapM (insertIncludedFile blocks (tokenize . T.pack) dirs) fs'
786−grave c = [c]
787
1172
788−acute :: Char -> String
1173+maybeAddExtension :: String -> FilePath -> FilePath
789−acute 'A' = "Á"
1174+maybeAddExtension ext fp =
790−acute 'E' = "É"
1175+ if null (takeExtension fp)
791−acute 'I' = "Í"
1176+ then addExtension fp ext
792−acute 'O' = "Ó"
1177+ else fp
793−acute 'U' = "Ú"
794−acute 'Y' = "Ý"
795−acute 'a' = "á"
796−acute 'e' = "é"
797−acute 'i' = "í"
798−acute 'o' = "ó"
799−acute 'u' = "ú"
800−acute 'y' = "ý"
801−acute 'C' = "Ć"
802−acute 'c' = "ć"
803−acute 'L' = "Ĺ"
804−acute 'l' = "ĺ"
805−acute 'N' = "Ń"
806−acute 'n' = "ń"
807−acute 'R' = "Ŕ"
808−acute 'r' = "ŕ"
809−acute 'S' = "Ś"
810−acute 's' = "ś"
811−acute 'Z' = "Ź"
812−acute 'z' = "ź"
813−acute c = [c]
814
1178
815−circ :: Char -> String
1179+ignoreBlocks :: PandocMonad m => Text -> (Text, LP m Blocks)
816−circ 'A' = "Â"
1180+ignoreBlocks name = (name, p)
817−circ 'E' = "Ê"
1181+ where
818−circ 'I' = "Î"
1182+ p = do oa <- optargs
819−circ 'O' = "Ô"
1183+ let rawCommand = '\\' : T.unpack (name <> oa)
820−circ 'U' = "Û"
1184+ let doraw = guardRaw >> return (rawBlock "latex" rawCommand)
821−circ 'a' = "â"
1185+ doraw <|> ignore rawCommand
822−circ 'e' = "ê"
823−circ 'i' = "î"
824−circ 'o' = "ô"
825−circ 'u' = "û"
826−circ 'C' = "Ĉ"
827−circ 'c' = "ĉ"
828−circ 'G' = "Ĝ"
829−circ 'g' = "ĝ"
830−circ 'H' = "Ĥ"
831−circ 'h' = "ĥ"
832−circ 'J' = "Ĵ"
833−circ 'j' = "ĵ"
834−circ 'S' = "Ŝ"
835−circ 's' = "ŝ"
836−circ 'W' = "Ŵ"
837−circ 'w' = "ŵ"
838−circ 'Y' = "Ŷ"
839−circ 'y' = "ŷ"
840−circ c = [c]
841−
842−tilde :: Char -> String
843−tilde 'A' = "Ã"
844−tilde 'a' = "ã"
845−tilde 'O' = "Õ"
846−tilde 'o' = "õ"
847−tilde 'I' = "Ĩ"
848−tilde 'i' = "ĩ"
849−tilde 'U' = "Ũ"
850−tilde 'u' = "ũ"
851−tilde 'N' = "Ñ"
852−tilde 'n' = "ñ"
853−tilde c = [c]
854−
855−umlaut :: Char -> String
856−umlaut 'A' = "Ä"
857−umlaut 'E' = "Ë"
858−umlaut 'I' = "Ï"
859−umlaut 'O' = "Ö"
860−umlaut 'U' = "Ü"
861−umlaut 'a' = "ä"
862−umlaut 'e' = "ë"
863−umlaut 'i' = "ï"
864−umlaut 'o' = "ö"
865−umlaut 'u' = "ü"
866−umlaut c = [c]
867−
868−hungarumlaut :: Char -> String
869−hungarumlaut 'A' = "A̋"
870−hungarumlaut 'E' = "E̋"
871−hungarumlaut 'I' = "I̋"
872−hungarumlaut 'O' = "Ő"
873−hungarumlaut 'U' = "Ű"
874−hungarumlaut 'Y' = "ӳ"
875−hungarumlaut 'a' = "a̋"
876−hungarumlaut 'e' = "e̋"
877−hungarumlaut 'i' = "i̋"
878−hungarumlaut 'o' = "ő"
879−hungarumlaut 'u' = "ű"
880−hungarumlaut 'y' = "ӳ"
881−hungarumlaut c = [c]
882−
883−dot :: Char -> String
884−dot 'C' = "Ċ"
885−dot 'c' = "ċ"
886−dot 'E' = "Ė"
887−dot 'e' = "ė"
888−dot 'G' = "Ġ"
889−dot 'g' = "ġ"
890−dot 'I' = "İ"
891−dot 'Z' = "Ż"
892−dot 'z' = "ż"
893−dot c = [c]
894−
895−macron :: Char -> String
896−macron 'A' = "Ā"
897−macron 'E' = "Ē"
898−macron 'I' = "Ī"
899−macron 'O' = "Ō"
900−macron 'U' = "Ū"
901−macron 'a' = "ā"
902−macron 'e' = "ē"
903−macron 'i' = "ī"
904−macron 'o' = "ō"
905−macron 'u' = "ū"
906−macron c = [c]
907−
908−cedilla :: Char -> String
909−cedilla 'c' = "ç"
910−cedilla 'C' = "Ç"
911−cedilla 's' = "ş"
912−cedilla 'S' = "Ş"
913−cedilla 't' = "ţ"
914−cedilla 'T' = "Ţ"
915−cedilla 'e' = "ȩ"
916−cedilla 'E' = "Ȩ"
917−cedilla 'h' = "ḩ"
918−cedilla 'H' = "Ḩ"
919−cedilla 'o' = "o̧"
920−cedilla 'O' = "O̧"
921−cedilla c = [c]
922−
923−hacek :: Char -> String
924−hacek 'A' = "Ǎ"
925−hacek 'a' = "ǎ"
926−hacek 'C' = "Č"
927−hacek 'c' = "č"
928−hacek 'D' = "Ď"
929−hacek 'd' = "ď"
930−hacek 'E' = "Ě"
931−hacek 'e' = "ě"
932−hacek 'G' = "Ǧ"
933−hacek 'g' = "ǧ"
934−hacek 'H' = "Ȟ"
935−hacek 'h' = "ȟ"
936−hacek 'I' = "Ǐ"
937−hacek 'i' = "ǐ"
938−hacek 'j' = "ǰ"
939−hacek 'K' = "Ǩ"
940−hacek 'k' = "ǩ"
941−hacek 'L' = "Ľ"
942−hacek 'l' = "ľ"
943−hacek 'N' = "Ň"
944−hacek 'n' = "ň"
945−hacek 'O' = "Ǒ"
946−hacek 'o' = "ǒ"
947−hacek 'R' = "Ř"
948−hacek 'r' = "ř"
949−hacek 'S' = "Š"
950−hacek 's' = "š"
951−hacek 'T' = "Ť"
952−hacek 't' = "ť"
953−hacek 'U' = "Ǔ"
954−hacek 'u' = "ǔ"
955−hacek 'Z' = "Ž"
956−hacek 'z' = "ž"
957−hacek c = [c]
958−
959−breve :: Char -> String
960−breve 'A' = "Ă"
961−breve 'a' = "ă"
962−breve 'E' = "Ĕ"
963−breve 'e' = "ĕ"
964−breve 'G' = "Ğ"
965−breve 'g' = "ğ"
966−breve 'I' = "Ĭ"
967−breve 'i' = "ĭ"
968−breve 'O' = "Ŏ"
969−breve 'o' = "ŏ"
970−breve 'U' = "Ŭ"
971−breve 'u' = "ŭ"
972−breve c = [c]
973−
974−tok :: PandocMonad m => LP m Inlines
975−tok = try $ grouped inline <|> inlineCommand <|> str <$> count 1 inlineChar
976−
977−opt :: PandocMonad m => LP m Inlines
978−opt = bracketed inline
979−
980−rawopt :: PandocMonad m => LP m String
981−rawopt = do
982− contents <- bracketed (many1 (noneOf "[]") <|> try (string "\\]") <|>
983− try (string "\\[") <|> rawopt)
984− optional sp
985− return $ "[" ++ contents ++ "]"
986−
987−skipopts :: PandocMonad m => LP m ()
988−skipopts = skipMany rawopt
989−
990−-- opts in angle brackets are used in beamer
991−rawangle :: PandocMonad m => LP m ()
992−rawangle = try $ do
993− char '<'
994− skipMany (noneOf ">")
995− char '>'
996− return ()
997−
998−skipangles :: PandocMonad m => LP m ()
999−skipangles = skipMany rawangle
1000−
1001−inlineText :: PandocMonad m => LP m Inlines
1002−inlineText = str <$> many1 inlineChar
1003−
1004−inlineChar :: PandocMonad m => LP m Char
1005−inlineChar = noneOf "\\$%&~#{}^'`\"‘’“”-[] \t\n"
1006−
1007−environment :: PandocMonad m => LP m Blocks
1008−environment = do
1009− controlSeq "begin"
1010− name <- braced
1011− M.findWithDefault mzero name environments
1012− <|> rawEnv name
1013−
1014−inlineEnvironment :: PandocMonad m => LP m Inlines
1015−inlineEnvironment = try $ do
1016− controlSeq "begin"
1017− name <- braced
1018− M.findWithDefault mzero name inlineEnvironments
1019−
1020−rawEnv :: PandocMonad m => String -> LP m Blocks
1021−rawEnv name = do
1022− exts <- getOption readerExtensions
1023− let parseRaw = extensionEnabled Ext_raw_tex exts
1024− rawOptions <- mconcat <$> many rawopt
1025− let beginCommand = "\\begin{" ++ name ++ "}" ++ rawOptions
1026− pos1 <- getPosition
1027− (bs, raw) <- withRaw $ env name blocks
1028− raw' <- applyMacros' $ beginCommand ++ raw
1029− if raw' /= beginCommand ++ raw
1030− then parseFromString' blocks raw'
1031− else if parseRaw
1032− then return $ rawBlock "latex" $ beginCommand ++ raw'
1033− else do
1034− unless parseRaw $ do
1035− report $ SkippedContent beginCommand pos1
1036− pos2 <- getPosition
1037− report $ SkippedContent ("\\end{" ++ name ++ "}") pos2
1038− return bs
1039−
1040−rawVerbEnv :: PandocMonad m => String -> LP m Blocks
1041−rawVerbEnv name = do
1042− pos <- getPosition
1043− (_, raw) <- withRaw $ verbEnv name
1044− let raw' = "\\begin{tikzpicture}" ++ raw
1045− exts <- getOption readerExtensions
1046− let parseRaw = extensionEnabled Ext_raw_tex exts
1047− if parseRaw
1048− then return $ rawBlock "latex" raw'
1049− else do
1050− report $ SkippedContent raw' pos
1051− return mempty
1052−
1053−----
1054−
1055−maybeAddExtension :: String -> FilePath -> FilePath
1056−maybeAddExtension ext fp =
1057− if null (takeExtension fp)
1058− then addExtension fp ext
1059− else fp
1060−
1061−include :: PandocMonad m => LP m Blocks
1062−include = do
1063− fs' <- try $ do
1064− char '\\'
1065− name <- try (string "include")
1066− <|> try (string "input")
1067− <|> try (string "subfile")
1068− <|> string "usepackage"
1069− -- skip options
1070− skipMany $ try $ char '[' *> manyTill anyChar (char ']')
1071− fs <- (map trim . splitBy (==',')) <$> braced
1072− return $ if name == "usepackage"
1073− then map (maybeAddExtension ".sty") fs
1074− else map (maybeAddExtension ".tex") fs
1075− dirs <- (splitBy (==':') . fromMaybe ".") <$> lookupEnv "TEXINPUTS"
1076− mconcat <$> mapM (insertIncludedFile blocks dirs) fs'
1077−
1078−inputListing :: PandocMonad m => LP m Blocks
1079−inputListing = do
1080− pos <- getPosition
1081− options <- option [] keyvals
1082− f <- filter (/='"') <$> braced
1083− dirs <- (splitBy (==':') . fromMaybe ".") <$> lookupEnv "TEXINPUTS"
1084− mbCode <- readFileFromDirs dirs f
1085− codeLines <- case mbCode of
1086− Just s -> return $ lines s
1087− Nothing -> do
1088− report $ CouldNotLoadIncludeFile f pos
1089− return []
1090− let (ident,classes,kvs) = parseListingsOptions options
1091− let language = case lookup "language" options >>= fromListingsLanguage of
1092− Just l -> [l]
1093− Nothing -> take 1 $ languagesByExtension (takeExtension f)
1094− let firstline = fromMaybe 1 $ lookup "firstline" options >>= safeRead
1095− let lastline = fromMaybe (length codeLines) $
1096− lookup "lastline" options >>= safeRead
1097− let codeContents = intercalate "\n" $ take (1 + lastline - firstline) $
1098− drop (firstline - 1) codeLines
1099− return $ codeBlockWith (ident,ordNub (classes ++ language),kvs) codeContents
1100−
1101−parseListingsOptions :: [(String, String)] -> Attr
1102−parseListingsOptions options =
1103− let kvs = [ (if k == "firstnumber"
1104− then "startFrom"
1105− else k, v) | (k,v) <- options ]
1106− classes = [ "numberLines" |
1107− lookup "numbers" options == Just "left" ]
1108− ++ maybeToList (lookup "language" options
1109− >>= fromListingsLanguage)
1110− in (fromMaybe "" (lookup "label" options), classes, kvs)
1111−
1112−----
1113−
1114−keyval :: PandocMonad m => LP m (String, String)
1115−keyval = try $ do
1116− key <- many1 alphaNum
1117− val <- option "" $ char '=' >> braced <|> (many1 (alphaNum <|> oneOf ".:-|\\"))
1118− skipMany spaceChar
1119− optional (char ',')
1120− skipMany spaceChar
1121− return (key, val)
1122
1186
1187+addMeta :: PandocMonad m => ToMetaValue a => String -> a -> LP m ()
1188+addMeta field val = updateState $ \st ->
1189+ st{ sMeta = addMetaField field val $ sMeta st }
1123
1190
1124−keyvals :: PandocMonad m => LP m [(String, String)]
1191+authors :: PandocMonad m => LP m ()
1125−keyvals = try $ char '[' *> manyTill keyval (char ']')
1192+authors = try $ do
1126−
1193+ bgroup
1127−alltt :: PandocMonad m => String -> LP m Blocks
1194+ let oneAuthor = mconcat <$>
1128−alltt t = walk strToCode <$> parseFromString' blocks
1195+ many1 (notFollowedBy' (controlSeq "and") >>
1129− (substitute " " "\\ " $ substitute "%" "\\%" $
1196+ (inline <|> mempty <$ blockCommand))
1130− intercalate "\\\\\n" $ lines t)
1197+ -- skip e.g. \vspace{10pt}
1131− where strToCode (Str s) = Code nullAttr s
1198+ auths <- sepBy oneAuthor (controlSeq "and")
1132− strToCode x = x
1199+ egroup
1133−
1200+ addMeta "author" (map trimInlines auths)
1134−rawLaTeXBlock :: PandocMonad m => LP m String
1135−rawLaTeXBlock = snd <$> try (withRaw (environment <|> blockCommand))
1136−
1137−rawLaTeXInline :: PandocMonad m => LP m Inline
1138−rawLaTeXInline = do
1139− raw <- (snd <$> withRaw inlineCommand)
1140− <|> (snd <$> withRaw inlineEnvironment)
1141− <|> (snd <$> withRaw blockCommand)
1142− RawInline "latex" <$> applyMacros' raw
1143−
1144−addImageCaption :: PandocMonad m => Blocks -> LP m Blocks
1145−addImageCaption = walkM go
1146− where go (Image attr alt (src,tit))
1147− | not ("fig:" `isPrefixOf` tit) = do
1148− mbcapt <- stateCaption <$> getState
1149− return $ case mbcapt of
1150− Just ils -> Image attr (toList ils) (src, "fig:" ++ tit)
1151− Nothing -> Image attr alt (src,tit)
1152− go x = return x
1153−
1154−addTableCaption :: PandocMonad m => Blocks -> LP m Blocks
1155−addTableCaption = walkM go
1156− where go (Table c als ws hs rs) = do
1157− mbcapt <- stateCaption <$> getState
1158− return $ case mbcapt of
1159− Just ils -> Table (toList ils) als ws hs rs
1160− Nothing -> Table c als ws hs rs
1161− go x = return x
1162−
1163−environments :: PandocMonad m => M.Map String (LP m Blocks)
1164−environments = M.fromList
1165− [ ("document", env "document" blocks <* skipMany anyChar)
1166− , ("abstract", mempty <$ (env "abstract" blocks >>= addMeta "abstract"))
1167− , ("letter", env "letter" letterContents)
1168− , ("minipage", env "minipage" $
1169− skipopts *> spaces' *> optional braced *> spaces' *> blocks)
1170− , ("figure", env "figure" $ skipopts *> figure)
1171− , ("subfigure", env "subfigure" $ skipopts *> tok *> figure)
1172− , ("center", env "center" blocks)
1173− , ("longtable", env "longtable" $
1174− resetCaption *> simpTable "longtable" False >>= addTableCaption)
1175− , ("table", env "table" $
1176− resetCaption *> skipopts *> blocks >>= addTableCaption)
1177− , ("tabular*", env "tabular" $ simpTable "tabular*" True)
1178− , ("tabularx", env "tabularx" $ simpTable "tabularx" True)
1179− , ("tabular", env "tabular" $ simpTable "tabular" False)
1180− , ("quote", blockQuote <$> env "quote" blocks)
1181− , ("quotation", blockQuote <$> env "quotation" blocks)
1182− , ("verse", blockQuote <$> env "verse" blocks)
1183− , ("itemize", bulletList <$> listenv "itemize" (many item))
1184− , ("description", definitionList <$> listenv "description" (many descItem))
1185− , ("enumerate", orderedList')
1186− , ("alltt", alltt =<< verbEnv "alltt")
1187− , ("code", guardEnabled Ext_literate_haskell *>
1188− (codeBlockWith ("",["sourceCode","literate","haskell"],[]) <$>
1189− verbEnv "code"))
1190− , ("comment", mempty <$ verbEnv "comment")
1191− , ("verbatim", codeBlock <$> verbEnv "verbatim")
1192− , ("Verbatim", fancyverbEnv "Verbatim")
1193− , ("BVerbatim", fancyverbEnv "BVerbatim")
1194− , ("lstlisting", do attr <- parseListingsOptions <$> option [] keyvals
1195− codeBlockWith attr <$> verbEnv "lstlisting")
1196− , ("minted", do options <- option [] keyvals
1197− lang <- grouped (many1 $ satisfy (/='}'))
1198− let kvs = [ (if k == "firstnumber"
1199− then "startFrom"
1200− else k, v) | (k,v) <- options ]
1201− let classes = [ lang | not (null lang) ] ++
1202− [ "numberLines" |
1203− lookup "linenos" options == Just "true" ]
1204− let attr = ("",classes,kvs)
1205− codeBlockWith attr <$> verbEnv "minted")
1206− , ("obeylines", parseFromString
1207− (para . trimInlines . mconcat <$> many inline) =<<
1208− intercalate "\\\\\n" . lines <$> verbEnv "obeylines")
1209− , ("displaymath", mathEnvWith para Nothing "displaymath")
1210− , ("equation", mathEnvWith para Nothing "equation")
1211− , ("equation*", mathEnvWith para Nothing "equation*")
1212− , ("gather", mathEnvWith para (Just "gathered") "gather")
1213− , ("gather*", mathEnvWith para (Just "gathered") "gather*")
1214− , ("multline", mathEnvWith para (Just "gathered") "multline")
1215− , ("multline*", mathEnvWith para (Just "gathered") "multline*")
1216− , ("eqnarray", mathEnvWith para (Just "aligned") "eqnarray")
1217− , ("eqnarray*", mathEnvWith para (Just "aligned") "eqnarray*")
1218− , ("align", mathEnvWith para (Just "aligned") "align")
1219− , ("align*", mathEnvWith para (Just "aligned") "align*")
1220− , ("alignat", mathEnvWith para (Just "aligned") "alignat")
1221− , ("alignat*", mathEnvWith para (Just "aligned") "alignat*")
1222− , ("tikzpicture", rawVerbEnv "tikzpicture")
1223− ]
1224
1201
1225−figure :: PandocMonad m => LP m Blocks
1202+section :: PandocMonad m => Attr -> Int -> LP m Blocks
1226−figure = try $ do
1203+section (ident, classes, kvs) lvl = do
1227− resetCaption
1204+ skipopts
1228− blocks >>= addImageCaption
1205+ contents <- grouped inline
1206+ lab <- option ident $
1207+ try (spaces >> controlSeq "label"
1208+ >> spaces >> toksToString <$> braced)
1209+ attr' <- registerHeader (lab, classes, kvs) contents
1210+ return $ headerWith attr' lvl contents
1229
1211
1230−letterContents :: PandocMonad m => LP m Blocks
1212+blockCommand :: PandocMonad m => LP m Blocks
1231−letterContents = do
1213+blockCommand = try $ do
1232− bs <- blocks
1214+ Tok _ CtrlSeq name <- anyControlSeq
1233− st <- getState
1215+ guard $ name /= "\\begin" && name /= "\\end"
1234− -- add signature (author) and address (title)
1216+ star <- option "" ("*" <$ symbol '*' <* optional sp)
1235− let addr = case lookupMeta "address" (stateMeta st) of
1217+ let name' = name <> star
1236− Just (MetaBlocks [Plain xs]) ->
1218+ let names = name : [name' | name' /= name]
1237− para $ trimInlines $ fromList xs
1219+ let raw = rawBlock "latex" <$> getRawCommand name'
1238− _ -> mempty
1220+ -- TODO do we still need this?
1239− return $ addr <> bs -- sig added by \closing
1221+ -- rawcommand <- getRawCommand name'
1222+ -- transformed <- applyMacros' rawcommand
1223+ -- guard $ transformed /= rawcommand
1224+ -- notFollowedBy $ parseFromString' inlines transformed
1225+ -- parseFromString' blocks transformed
1226+ lookupListDefault raw names blockCommands
1240
1227
1241closing :: PandocMonad m => LP m Blocks
1228closing :: PandocMonad m => LP m Blocks
1242closing = do
1229closing = do
@@ -1245,328 +1232,698 @@ closing = do
1245 let extractInlines (MetaBlocks [Plain ys]) = ys
1232 let extractInlines (MetaBlocks [Plain ys]) = ys
1246 extractInlines (MetaBlocks [Para ys ]) = ys
1233 extractInlines (MetaBlocks [Para ys ]) = ys
1247 extractInlines _ = []
1234 extractInlines _ = []
1248− let sigs = case lookupMeta "author" (stateMeta st) of
1235+ let sigs = case lookupMeta "author" (sMeta st) of
1249 Just (MetaList xs) ->
1236 Just (MetaList xs) ->
1250 para $ trimInlines $ fromList $
1237 para $ trimInlines $ fromList $
1251 intercalate [LineBreak] $ map extractInlines xs
1238 intercalate [LineBreak] $ map extractInlines xs
1252 _ -> mempty
1239 _ -> mempty
1253 return $ para (trimInlines contents) <> sigs
1240 return $ para (trimInlines contents) <> sigs
1254
1241
1255−item :: PandocMonad m => LP m Blocks
1242+blockCommands :: PandocMonad m => M.Map Text (LP m Blocks)
1256−item = blocks *> controlSeq "item" *> skipopts *> blocks
1243+blockCommands = M.fromList $
1257−
1244+ [ ("par", mempty <$ skipopts)
1258−looseItem :: PandocMonad m => LP m Blocks
1245+ , ("parbox", braced >> grouped blocks)
1259−looseItem = do
1246+ , ("title", mempty <$ (skipopts *>
1260− ctx <- stateParserContext `fmap` getState
1247+ (grouped inline >>= addMeta "title")
1261− if ctx == ListItemState
1248+ <|> (grouped block >>= addMeta "title")))
1262− then mzero
1249+ , ("subtitle", mempty <$ (skipopts *> tok >>= addMeta "subtitle"))
1263− else return mempty
1250+ , ("author", mempty <$ (skipopts *> authors))
1251+ -- -- in letter class, temp. store address & sig as title, author
1252+ , ("address", mempty <$ (skipopts *> tok >>= addMeta "address"))
1253+ , ("signature", mempty <$ (skipopts *> authors))
1254+ , ("date", mempty <$ (skipopts *> tok >>= addMeta "date"))
1255+ -- Koma-script metadata commands
1256+ , ("dedication", mempty <$ (skipopts *> tok >>= addMeta "dedication"))
1257+ -- sectioning
1258+ , ("part", section nullAttr (-1))
1259+ , ("part*", section nullAttr (-1))
1260+ , ("chapter", section nullAttr 0)
1261+ , ("chapter*", section ("",["unnumbered"],[]) 0)
1262+ , ("section", section nullAttr 1)
1263+ , ("section*", section ("",["unnumbered"],[]) 1)
1264+ , ("subsection", section nullAttr 2)
1265+ , ("subsection*", section ("",["unnumbered"],[]) 2)
1266+ , ("subsubsection", section nullAttr 3)
1267+ , ("subsubsection*", section ("",["unnumbered"],[]) 3)
1268+ , ("paragraph", section nullAttr 4)
1269+ , ("paragraph*", section ("",["unnumbered"],[]) 4)
1270+ , ("subparagraph", section nullAttr 5)
1271+ , ("subparagraph*", section ("",["unnumbered"],[]) 5)
1272+ -- beamer slides
1273+ , ("frametitle", section nullAttr 3)
1274+ , ("framesubtitle", section nullAttr 4)
1275+ -- letters
1276+ , ("opening", (para . trimInlines) <$> (skipopts *> tok))
1277+ , ("closing", skipopts *> closing)
1278+ --
1279+ , ("hrule", pure horizontalRule)
1280+ , ("strut", pure mempty)
1281+ , ("rule", skipopts *> tok *> tok *> pure horizontalRule)
1282+-- , ("item", skipopts *> looseItem)
1283+-- , ("documentclass", skipopts *> braced *> preamble)
1284+ , ("centerline", (para . trimInlines) <$> (skipopts *> tok))
1285+-- , ("caption", skipopts *> setCaption)
1286+-- , ("bibliography", mempty <$ (skipopts *> braced >>=
1287+-- addMeta "bibliography" . splitBibs))
1288+-- , ("addbibresource", mempty <$ (skipopts *> braced >>=
1289+-- addMeta "bibliography" . splitBibs))
1290+-- -- includes
1291+-- , ("lstinputlisting", inputListing)
1292+-- , ("graphicspath", graphicsPath)
1293+-- -- hyperlink
1294+ , ("hypertarget", braced >> grouped block)
1295+ ] ++ map ignoreBlocks
1296+ -- these commands will be ignored unless --parse-raw is specified,
1297+ -- in which case they will appear as raw latex blocks
1298+ [ "newcommand", "renewcommand", "newenvironment", "renewenvironment"
1299+ -- newcommand, etc. should be parsed by macro, but we need this
1300+ -- here so these aren't parsed as inline commands to ignore
1301+ , "special", "pdfannot", "pdfstringdef"
1302+ , "bibliographystyle"
1303+ , "maketitle", "makeindex", "makeglossary"
1304+ , "addcontentsline", "addtocontents", "addtocounter"
1305+ -- \ignore{} is used conventionally in literate haskell for definitions
1306+ -- that are to be processed by the compiler but not printed.
1307+ , "ignore"
1308+ , "hyperdef"
1309+ , "markboth", "markright", "markleft"
1310+ , "hspace", "vspace"
1311+ , "newpage"
1312+ , "clearpage"
1313+ , "pagebreak"
1314+ ]
1315+
1316+environments :: PandocMonad m => M.Map Text (LP m Blocks)
1317+environments = M.fromList
1318+ [ ("document", env "document" blocks)
1319+-- , ("abstract", mempty <$ (env "abstract" blocks >>= addMeta "abstract"))
1320+-- , ("letter", env "letter" letterContents)
1321+-- , ("minipage", env "minipage" $
1322+-- skipopts *> spaces' *> optional braced *> spaces' *> blocks)
1323+-- , ("figure", env "figure" $ skipopts *> figure)
1324+-- , ("subfigure", env "subfigure" $ skipopts *> tok *> figure)
1325+-- , ("center", env "center" blocks)
1326+-- , ("longtable", env "longtable" $
1327+-- resetCaption *> simpTable "longtable" False >>= addTableCaption)
1328+-- , ("table", env "table" $
1329+-- resetCaption *> skipopts *> blocks >>= addTableCaption)
1330+-- , ("tabular*", env "tabular" $ simpTable "tabular*" True)
1331+-- , ("tabularx", env "tabularx" $ simpTable "tabularx" True)
1332+-- , ("tabular", env "tabular" $ simpTable "tabular" False)
1333+-- , ("quote", blockQuote <$> env "quote" blocks)
1334+-- , ("quotation", blockQuote <$> env "quotation" blocks)
1335+-- , ("verse", blockQuote <$> env "verse" blocks)
1336+-- , ("itemize", bulletList <$> listenv "itemize" (many item))
1337+-- , ("description", definitionList <$> listenv "description" (many descItem))
1338+-- , ("enumerate", orderedList')
1339+-- , ("alltt", alltt =<< verbEnv "alltt")
1340+-- , ("code", guardEnabled Ext_literate_haskell *>
1341+-- (codeBlockWith ("",["sourceCode","literate","haskell"],[]) <$>
1342+-- verbEnv "code"))
1343+-- , ("comment", mempty <$ verbEnv "comment")
1344+-- , ("verbatim", codeBlock <$> verbEnv "verbatim")
1345+-- , ("Verbatim", fancyverbEnv "Verbatim")
1346+-- , ("BVerbatim", fancyverbEnv "BVerbatim")
1347+-- , ("lstlisting", do attr <- parseListingsOptions <$> option [] keyvals
1348+-- codeBlockWith attr <$> verbEnv "lstlisting")
1349+-- , ("minted", do options <- option [] keyvals
1350+-- lang <- grouped (many1 $ satisfy (/='}'))
1351+-- let kvs = [ (if k == "firstnumber"
1352+-- then "startFrom"
1353+-- else k, v) | (k,v) <- options ]
1354+-- let classes = [ lang | not (null lang) ] ++
1355+-- [ "numberLines" |
1356+-- lookup "linenos" options == Just "true" ]
1357+-- let attr = ("",classes,kvs)
1358+-- codeBlockWith attr <$> verbEnv "minted")
1359+-- , ("obeylines", parseFromString
1360+-- (para . trimInlines . mconcat <$> many inline) =<<
1361+-- intercalate "\\\\\n" . lines <$> verbEnv "obeylines")
1362+-- , ("displaymath", mathEnvWith para Nothing "displaymath")
1363+-- , ("equation", mathEnvWith para Nothing "equation")
1364+-- , ("equation*", mathEnvWith para Nothing "equation*")
1365+-- , ("gather", mathEnvWith para (Just "gathered") "gather")
1366+-- , ("gather*", mathEnvWith para (Just "gathered") "gather*")
1367+-- , ("multline", mathEnvWith para (Just "gathered") "multline")
1368+-- , ("multline*", mathEnvWith para (Just "gathered") "multline*")
1369+-- , ("eqnarray", mathEnvWith para (Just "aligned") "eqnarray")
1370+-- , ("eqnarray*", mathEnvWith para (Just "aligned") "eqnarray*")
1371+-- , ("align", mathEnvWith para (Just "aligned") "align")
1372+-- , ("align*", mathEnvWith para (Just "aligned") "align*")
1373+-- , ("alignat", mathEnvWith para (Just "aligned") "alignat")
1374+-- , ("alignat*", mathEnvWith para (Just "aligned") "alignat*")
1375+-- , ("tikzpicture", rawVerbEnv "tikzpicture")
1376+ ]
1264
1377
1265−descItem :: PandocMonad m => LP m (Inlines, [Blocks])
1378+environment :: PandocMonad m => LP m Blocks
1266−descItem = do
1379+environment = do
1267− blocks -- skip blocks before item
1268− controlSeq "item"
1380+ controlSeq "begin"
1269− optional sp
1381+ name <- untokenize <$> braced
1270− ils <- opt
1382+ M.findWithDefault mzero name environments
1271− bs <- blocks
1383+ <|> rawEnv name
1272− return (ils, [bs])
1273
1384
1274−env :: PandocMonad m => String -> LP m a -> LP m a
1385+env :: PandocMonad m => Text -> LP m a -> LP m a
1275env name p = p <*
1386env name p = p <*
1276− (try (controlSeq "end" *> braced >>= guard . (== name))
1387+ (try (controlSeq "end" *> braced >>= guard . (== name) . untokenize)
1277− <?> ("\\end{" ++ name ++ "}"))
1388+ <?> ("\\end{" ++ T.unpack name ++ "}"))
1278−
1279−listenv :: PandocMonad m => String -> LP m a -> LP m a
1280−listenv name p = try $ do
1281− oldCtx <- stateParserContext `fmap` getState
1282− updateState $ \st -> st{ stateParserContext = ListItemState }
1283− res <- env name p
1284− updateState $ \st -> st{ stateParserContext = oldCtx }
1285− return res
1286−
1287−mathEnvWith :: PandocMonad m
1288− => (Inlines -> a) -> Maybe String -> String -> LP m a
1289−mathEnvWith f innerEnv name = f <$> mathDisplay (inner <$> mathEnv name)
1290− where inner x = case innerEnv of
1291− Nothing -> x
1292− Just y -> "\\begin{" ++ y ++ "}\n" ++ x ++
1293− "\\end{" ++ y ++ "}"
1294−
1295−mathEnv :: PandocMonad m => String -> LP m String
1296−mathEnv name = do
1297− skipopts
1298− optional blankline
1299− let endEnv = try $ controlSeq "end" *> braced >>= guard . (== name)
1300− charMuncher = skipMany comment *>
1301− (many1 (noneOf "\\%") <|> try (string "\\%")
1302− <|> try (string "\\\\") <|> count 1 anyChar)
1303− res <- concat <$> manyTill charMuncher endEnv
1304− return $ stripTrailingNewlines res
1305−
1306−verbEnv :: PandocMonad m => String -> LP m String
1307−verbEnv name = do
1308− skipopts
1309− optional blankline
1310− let endEnv = try $ controlSeq "end" *> braced >>= guard . (== name)
1311− charMuncher = anyChar
1312− res <- manyTill charMuncher endEnv
1313− return $ stripTrailingNewlines res
1314
1389
1315−fancyverbEnv :: PandocMonad m => String -> LP m Blocks
1390+rawEnv :: PandocMonad m => Text -> LP m Blocks
1316−fancyverbEnv name = do
1391+rawEnv name = do
1317− options <- option [] keyvals
1392+ exts <- getOption readerExtensions
1318− let kvs = [ (if k == "firstnumber"
1393+ let parseRaw = extensionEnabled Ext_raw_tex exts
1319− then "startFrom"
1394+ rawOptions <- mconcat <$> many rawopt
1320− else k, v) | (k,v) <- options ]
1395+ let beginCommand = "\\begin{" <> name <> "}" <> rawOptions
1321− let classes = [ "numberLines" |
1396+ pos1 <- getPosition
1322− lookup "numbers" options == Just "left" ]
1397+ (bs, raw) <- withRaw $ env name blocks
1323− let attr = ("",classes,kvs)
1398+ if parseRaw
1324− codeBlockWith attr <$> verbEnv name
1399+ then return $ rawBlock "latex"
1325−
1400+ $ T.unpack $ beginCommand <> untokenize raw
1326−orderedList' :: PandocMonad m => LP m Blocks
1401+ else do
1327−orderedList' = try $ do
1402+ unless parseRaw $ do
1328− optional sp
1403+ report $ SkippedContent (T.unpack beginCommand) pos1
1329− (_, style, delim) <- option (1, DefaultStyle, DefaultDelim) $
1404+ pos2 <- getPosition
1330− try $ char '[' *> anyOrderedListMarker <* char ']'
1405+ report $ SkippedContent ("\\end{" ++ T.unpack name ++ "}") pos2
1331− spaces
1406+ return bs
1332− optional $ try $ controlSeq "setlength" *> grouped (controlSeq "itemindent") *> braced
1407+
1333− spaces
1408+ -- raw' <- applyMacros' $ beginCommand <> untokenize raw
1334− start <- option 1 $ try $ do controlSeq "setcounter"
1409+ -- if raw' /= beginCommand ++ raw
1335− grouped (string "enum" *> many1 (oneOf "iv"))
1410+ -- then parseFromString' blocks raw'
1336− optional sp
1411+ -- else if parseRaw
1337− num <- grouped (many1 digit)
1412+ -- then return $ rawBlock "latex" $ beginCommand ++ raw'
1338− spaces
1413+ -- else do
1339− return (read num + 1 :: Int)
1414+ -- unless parseRaw $ do
1340− bs <- listenv "enumerate" (many item)
1415+ -- report $ SkippedContent beginCommand pos1
1341− return $ orderedListWith (start, style, delim) bs
1416+ -- pos2 <- getPosition
1417+ -- report $ SkippedContent ("\\end{" ++ name ++ "}") pos2
1418+ -- return bs
1342
1419
1343−paragraph :: PandocMonad m => LP m Blocks
1420+block :: PandocMonad m => LP m Blocks
1344−paragraph = do
1421+block = (mempty <$ spaces1)
1345− x <- trimInlines . mconcat <$> many1 inline
1422+ <|> environment
1346− if x == mempty
1423+ <|> include
1347− then return mempty
1424+ -- <|> macro
1348− else return $ para x
1425+ <|> blockCommand
1426+ <|> paragraph
1427+ <|> grouped block
1349
1428
1350−preamble :: PandocMonad m => LP m Blocks
1429+blocks :: PandocMonad m => LP m Blocks
1351−preamble = mempty <$> manyTill preambleBlock beginDoc
1430+blocks = mconcat <$> many block
1352− where beginDoc = lookAhead $ try $ controlSeq "begin" *> string "{document}"
1353− preambleBlock = void comment
1354− <|> void sp
1355− <|> void blanklines
1356− <|> void include
1357− <|> void macro
1358− <|> void blockCommand
1359− <|> void anyControlSeq
1360− <|> void braced
1361− <|> void anyChar
1362−
1363−-------
1364−
1365−-- citations
1366−
1367−addPrefix :: [Inline] -> [Citation] -> [Citation]
1368−addPrefix p (k:ks) = k {citationPrefix = p ++ citationPrefix k} : ks
1369−addPrefix _ _ = []
1370−
1371−addSuffix :: [Inline] -> [Citation] -> [Citation]
1372−addSuffix s ks@(_:_) =
1373− let k = last ks
1374− in init ks ++ [k {citationSuffix = citationSuffix k ++ s}]
1375−addSuffix _ _ = []
1376−
1377−simpleCiteArgs :: PandocMonad m => LP m [Citation]
1378−simpleCiteArgs = try $ do
1379− first <- optionMaybe $ toList <$> opt
1380− second <- optionMaybe $ toList <$> opt
1381− keys <- try $ bgroup *> (manyTill citationLabel egroup)
1382− let (pre, suf) = case (first , second ) of
1383− (Just s , Nothing) -> (mempty, s )
1384− (Just s , Just t ) -> (s , t )
1385− _ -> (mempty, mempty)
1386− conv k = Citation { citationId = k
1387− , citationPrefix = []
1388− , citationSuffix = []
1389− , citationMode = NormalCitation
1390− , citationHash = 0
1391− , citationNoteNum = 0
1392− }
1393− return $ addPrefix pre $ addSuffix suf $ map conv keys
1394−
1395−citationLabel :: PandocMonad m => LP m String
1396−citationLabel = optional sp *>
1397− (many1 (satisfy isBibtexKeyChar)
1398− <* optional sp
1399− <* optional (char ',')
1400− <* optional sp)
1401− where isBibtexKeyChar c = isAlphaNum c || c `elem` (".:;?!`'()/*@_+=-[]" :: String)
1402−
1403−cites :: PandocMonad m => CitationMode -> Bool -> LP m [Citation]
1404−cites mode multi = try $ do
1405− cits <- if multi
1406− then many1 simpleCiteArgs
1407− else count 1 simpleCiteArgs
1408− let cs = concat cits
1409− return $ case mode of
1410− AuthorInText -> case cs of
1411− (c:rest) -> c {citationMode = mode} : rest
1412− [] -> []
1413− _ -> map (\a -> a {citationMode = mode}) cs
1414−
1415−citation :: PandocMonad m => String -> CitationMode -> Bool -> LP m Inlines
1416−citation name mode multi = do
1417− (c,raw) <- withRaw $ cites mode multi
1418− return $ cite c (rawInline "latex" $ "\\" ++ name ++ raw)
1419−
1420−complexNatbibCitation :: PandocMonad m => CitationMode -> LP m Inlines
1421−complexNatbibCitation mode = try $ do
1422− let ils = (toList . trimInlines . mconcat) <$>
1423− many (notFollowedBy (oneOf "\\};") >> inline)
1424− let parseOne = try $ do
1425− skipSpaces
1426− pref <- ils
1427− cit' <- inline -- expect a citation
1428− let citlist = toList cit'
1429− cits' <- case citlist of
1430− [Cite cs _] -> return cs
1431− _ -> mzero
1432− suff <- ils
1433− skipSpaces
1434− optional $ char ';'
1435− return $ addPrefix pref $ addSuffix suff cits'
1436− (c:cits, raw) <- withRaw $ grouped parseOne
1437− return $ cite (c{ citationMode = mode }:cits)
1438− (rawInline "latex" $ "\\citetext" ++ raw)
1439−
1440−-- tables
1441−
1442−parseAligns :: PandocMonad m => LP m [(Alignment, Double, (String, String))]
1443−parseAligns = try $ do
1444− bgroup
1445− let maybeBar = skipMany $ sp <|> () <$ char '|' <|> () <$ (char '@' >> braced)
1446− maybeBar
1447− let cAlign = AlignCenter <$ char 'c'
1448− let lAlign = AlignLeft <$ char 'l'
1449− let rAlign = AlignRight <$ char 'r'
1450− let parAlign = AlignLeft <$ char 'p'
1451− -- algins from tabularx
1452− let xAlign = AlignLeft <$ char 'X'
1453− let mAlign = AlignLeft <$ char 'm'
1454− let bAlign = AlignLeft <$ char 'b'
1455− let alignChar = cAlign <|> lAlign <|> rAlign <|> parAlign
1456− <|> xAlign <|> mAlign <|> bAlign
1457− let alignPrefix = char '>' >> braced
1458− let alignSuffix = char '<' >> braced
1459− let colWidth = try $ do
1460− char '{'
1461− ds <- many1 (oneOf "0123456789.")
1462− spaces
1463− string "\\linewidth"
1464− char '}'
1465− case safeRead ds of
1466− Just w -> return w
1467− Nothing -> return 0.0
1468− let alignSpec = do
1469− spaces
1470− pref <- option "" alignPrefix
1471− spaces
1472− al <- alignChar
1473− width <- colWidth <|> option 0.0 (do s <- braced
1474− pos <- getPosition
1475− report $ SkippedContent s pos
1476− return 0.0)
1477− spaces
1478− suff <- option "" alignSuffix
1479− return (al, width, (pref, suff))
1480− aligns' <- sepEndBy alignSpec maybeBar
1481− spaces
1482− egroup
1483− spaces
1484− return $ aligns'
1485−
1486−hline :: PandocMonad m => LP m ()
1487−hline = try $ do
1488− spaces'
1489− controlSeq "hline" <|>
1490− -- booktabs rules:
1491− controlSeq "toprule" <|>
1492− controlSeq "bottomrule" <|>
1493− controlSeq "midrule" <|>
1494− controlSeq "endhead" <|>
1495− controlSeq "endfirsthead"
1496− spaces'
1497− optional $ bracketed (many1 (satisfy (/=']')))
1498− return ()
1499
1431
1500−lbreak :: PandocMonad m => LP m ()
1432+{-
1501−lbreak = () <$ try (spaces' *>
1433+-- graphicsPath :: PandocMonad m => LP m Blocks
1502− (controlSeq "\\" <|> controlSeq "tabularnewline") <*
1434+-- graphicsPath = do
1503− spaces')
1435+-- ps <- bgroup *> (manyTill braced egroup)
1504−
1436+-- getResourcePath >>= setResourcePath . (++ ps)
1505−amp :: PandocMonad m => LP m ()
1437+-- return mempty
1506−amp = () <$ try (spaces' *> char '&' <* spaces')
1438+--
1507−
1439+-- addMeta :: PandocMonad m => ToMetaValue a => String -> a -> LP m ()
1508−parseTableRow :: PandocMonad m
1440+-- addMeta field val = updateState $ \st ->
1509− => String -- ^ table environment name
1441+-- st{ sMeta = addMetaField field val $ sMeta st }
1510− -> [(String, String)] -- ^ pref/suffixes
1442+--
1511− -> LP m [Blocks]
1443+-- splitBibs :: String -> [Inlines]
1512−parseTableRow envname prefsufs = try $ do
1444+-- splitBibs = map (str . flip replaceExtension "bib" . trim) . splitBy (==',')
1513− let cols = length prefsufs
1445+--
1514− let tableCellRaw = concat <$> many
1446+-- setCaption :: PandocMonad m => LP m Blocks
1515− (do notFollowedBy amp
1447+-- setCaption = do
1516− notFollowedBy lbreak
1448+-- ils <- tok
1517− notFollowedBy $ () <$ try (string ("\\end{" ++ envname ++ "}"))
1449+-- mblabel <- option Nothing $
1518− many1 (noneOf "&%\n\r\\")
1450+-- try $ spaces' >> controlSeq "label" >> (Just <$> tok)
1519− <|> try (string "\\&")
1451+-- let ils' = case mblabel of
1520− <|> count 1 anyChar)
1452+-- Just lab -> ils <> spanWith
1521− let plainify bs = case toList bs of
1453+-- ("",[],[("data-label", stringify lab)]) mempty
1522− [Para ils] -> plain (fromList ils)
1454+-- Nothing -> ils
1523− _ -> bs
1455+-- updateState $ \st -> st{ stateCaption = Just ils' }
1524− rawcells <- sepBy1 tableCellRaw amp
1456+-- return mempty
1525− guard $ length rawcells == cols
1457+--
1526− let rawcells' = zipWith (\c (p, s) -> p ++ trim c ++ s) rawcells prefsufs
1458+-- resetCaption :: PandocMonad m => LP m ()
1527− let tableCell = plainify <$> blocks
1459+-- resetCaption = updateState $ \st -> st{ stateCaption = Nothing }
1528− cells' <- mapM (parseFromString' tableCell) rawcells'
1460+--
1529− let numcells = length cells'
1461+-- mkImage :: PandocMonad m => [(String, String)] -> String -> LP m Inlines
1530− guard $ numcells <= cols && numcells >= 1
1462+-- mkImage options src = do
1531− guard $ cells' /= [mempty]
1463+-- let replaceTextwidth (k,v) = case numUnit v of
1532− -- note: a & b in a three-column table leaves an empty 3rd cell:
1464+-- Just (num, "\\textwidth") -> (k, showFl (num * 100) ++ "%")
1533− let cells'' = cells' ++ replicate (cols - numcells) mempty
1465+-- _ -> (k, v)
1534− spaces'
1466+-- let kvs = map replaceTextwidth $ filter (\(k,_) -> k `elem` ["width", "height"]) options
1535− return cells''
1467+-- let attr = ("",[], kvs)
1536−
1468+-- let alt = str "image"
1537−spaces' :: PandocMonad m => LP m ()
1469+-- case takeExtension src of
1538−spaces' = spaces *> skipMany (comment *> spaces)
1470+-- "" -> do
1539−
1471+-- defaultExt <- getOption readerDefaultImageExtension
1540−simpTable :: PandocMonad m => String -> Bool -> LP m Blocks
1472+-- return $ imageWith attr (addExtension src defaultExt) "" alt
1541−simpTable envname hasWidthParameter = try $ do
1473+-- _ -> return $ imageWith attr src "" alt
1542− when hasWidthParameter $ () <$ (spaces' >> tok)
1474+--
1543− skipopts
1475+-- inNote :: Inlines -> Inlines
1544− colspecs <- parseAligns
1476+-- inNote ils =
1545− let (aligns, widths, prefsufs) = unzip3 colspecs
1477+-- note $ para $ ils <> str "."
1546− let cols = length colspecs
1478+--
1547− optional $ controlSeq "caption" *> skipopts *> setCaption
1479+-
1548− optional lbreak
1480+-- -- converts e.g. \SI{1}[\$]{} to "$ 1" or \SI{1}{\euro} to "1 €"
1549− spaces'
1481+-- dosiunitx :: PandocMonad m => LP m Inlines
1550− skipMany hline
1482+-- dosiunitx = do
1551− spaces'
1483+-- skipopts
1552− header' <- option [] $ try (parseTableRow envname prefsufs <*
1484+-- value <- tok
1553− lbreak <* many1 hline)
1485+-- valueprefix <- option "" $ char '[' >> (mconcat <$> manyTill tok (char ']'))
1554− spaces'
1486+-- unit <- tok
1555− rows <- sepEndBy (parseTableRow envname prefsufs)
1487+-- let emptyOr160 "" = ""
1556− (lbreak <* optional (skipMany hline))
1488+-- emptyOr160 _ = "\160"
1557− spaces'
1489+-- return . mconcat $ [valueprefix,
1558− optional $ controlSeq "caption" *> skipopts *> setCaption
1490+-- emptyOr160 valueprefix,
1559− optional lbreak
1491+-- value,
1560− spaces'
1492+-- emptyOr160 unit,
1561− let header'' = if null header'
1493+-- unit]
1562− then replicate cols mempty
1494+--
1563− else header'
1495+-- skipangles :: PandocMonad m => LP m ()
1564− lookAhead $ controlSeq "end" -- make sure we're at end
1496+-- skipangles = skipMany rawangle
1565− return $ table mempty (zip aligns widths) header'' rows
1497+--
1566−
1498+-- inlineText :: PandocMonad m => LP m Inlines
1567−removeDoubleQuotes :: String -> String
1499+-- inlineText = str <$> many1 inlineChar
1568−removeDoubleQuotes ('"':xs) =
1500+--
1569− case reverse xs of
1501+-- inlineChar :: PandocMonad m => LP m Char
1570− '"':ys -> reverse ys
1502+-- inlineChar = noneOf "\\$%&~#{}^'`\"‘’“”-[] \t\n"
1571− _ -> '"':xs
1503+--
1572−removeDoubleQuotes xs = xs
1504+--
1505+-- inlineEnvironment :: PandocMonad m => LP m Inlines
1506+-- inlineEnvironment = try $ do
1507+-- controlSeq "begin"
1508+-- name <- braced
1509+-- M.findWithDefault mzero name inlineEnvironments
1510+--
1511+--
1512+-- rawVerbEnv :: PandocMonad m => String -> LP m Blocks
1513+-- rawVerbEnv name = do
1514+-- pos <- getPosition
1515+-- (_, raw) <- withRaw $ verbEnv name
1516+-- let raw' = "\\begin{tikzpicture}" ++ raw
1517+-- exts <- getOption readerExtensions
1518+-- let parseRaw = extensionEnabled Ext_raw_tex exts
1519+-- if parseRaw
1520+-- then return $ rawBlock "latex" raw'
1521+-- else do
1522+-- report $ SkippedContent raw' pos
1523+-- return mempty
1524+--
1525+-- ----
1526+--
1527+-- alltt :: PandocMonad m => String -> LP m Blocks
1528+-- alltt t = walk strToCode <$> parseFromString' blocks
1529+-- (substitute " " "\\ " $ substitute "%" "\\%" $
1530+-- intercalate "\\\\\n" $ lines t)
1531+-- where strToCode (Str s) = Code nullAttr s
1532+-- strToCode x = x
1533+--
1534+-- rawLaTeXBlock :: PandocMonad m => ParserT String LaTeXState String
1535+-- rawLaTeXBlock = snd <$> try (withRaw (environment <|> blockCommand))
1536+--
1537+-- rawLaTeXInline :: PandocMonad m => ParserT String LaTeXState Inline
1538+-- rawLaTeXInline = do
1539+-- raw <- (snd <$> withRaw inlineCommand)
1540+-- <|> (snd <$> withRaw inlineEnvironment)
1541+-- <|> (snd <$> withRaw blockCommand)
1542+-- RawInline "latex" <$> applyMacros' raw
1543+--
1544+-- addImageCaption :: PandocMonad m => Blocks -> LP m Blocks
1545+-- addImageCaption = walkM go
1546+-- where go (Image attr alt (src,tit))
1547+-- | not ("fig:" `isPrefixOf` tit) = do
1548+-- mbcapt <- stateCaption <$> getState
1549+-- return $ case mbcapt of
1550+-- Just ils -> Image attr (toList ils) (src, "fig:" ++ tit)
1551+-- Nothing -> Image attr alt (src,tit)
1552+-- go x = return x
1553+--
1554+-- addTableCaption :: PandocMonad m => Blocks -> LP m Blocks
1555+-- addTableCaption = walkM go
1556+-- where go (Table c als ws hs rs) = do
1557+-- mbcapt <- stateCaption <$> getState
1558+-- return $ case mbcapt of
1559+-- Just ils -> Table (toList ils) als ws hs rs
1560+-- Nothing -> Table c als ws hs rs
1561+-- go x = return x
1562+--
1563+--
1564+-- figure :: PandocMonad m => LP m Blocks
1565+-- figure = try $ do
1566+-- resetCaption
1567+-- blocks >>= addImageCaption
1568+--
1569+-- letterContents :: PandocMonad m => LP m Blocks
1570+-- letterContents = do
1571+-- bs <- blocks
1572+-- st <- getState
1573+-- -- add signature (author) and address (title)
1574+-- let addr = case lookupMeta "address" (sMeta st) of
1575+-- Just (MetaBlocks [Plain xs]) ->
1576+-- para $ trimInlines $ fromList xs
1577+-- _ -> mempty
1578+-- return $ addr <> bs -- sig added by \closing
1579+--
1580+--
1581+-- item :: PandocMonad m => LP m Blocks
1582+-- item = blocks *> controlSeq "item" *> skipopts *> blocks
1583+--
1584+-- looseItem :: PandocMonad m => LP m Blocks
1585+-- looseItem = do
1586+-- ctx <- stateParserContext `fmap` getState
1587+-- if ctx == ListItemState
1588+-- then mzero
1589+-- else return mempty
1590+--
1591+-- descItem :: PandocMonad m => LP m (Inlines, [Blocks])
1592+-- descItem = do
1593+-- blocks -- skip blocks before item
1594+-- controlSeq "item"
1595+-- optional sp
1596+-- ils <- opt
1597+-- bs <- blocks
1598+-- return (ils, [bs])
1599+--
1600+--
1601+-- listenv :: PandocMonad m => String -> LP m a -> LP m a
1602+-- listenv name p = try $ do
1603+-- oldCtx <- stateParserContext `fmap` getState
1604+-- updateState $ \st -> st{ stateParserContext = ListItemState }
1605+-- res <- env name p
1606+-- updateState $ \st -> st{ stateParserContext = oldCtx }
1607+-- return res
1608+--
1609+-- mathEnvWith :: PandocMonad m
1610+-- => (Inlines -> a) -> Maybe String -> String -> LP m a
1611+-- mathEnvWith f innerEnv name = f <$> mathDisplay (inner <$> mathEnv name)
1612+-- where inner x = case innerEnv of
1613+-- Nothing -> x
1614+-- Just y -> "\\begin{" ++ y ++ "}\n" ++ x ++
1615+-- "\\end{" ++ y ++ "}"
1616+--
1617+-- mathEnv :: PandocMonad m => String -> LP m String
1618+-- mathEnv name = do
1619+-- skipopts
1620+-- optional blankline
1621+-- let endEnv = try $ controlSeq "end" *> braced >>= guard . (== name)
1622+-- charMuncher = skipMany comment *>
1623+-- (many1 (noneOf "\\%") <|> try (string "\\%")
1624+-- <|> try (string "\\\\") <|> count 1 anyChar)
1625+-- res <- concat <$> manyTill charMuncher endEnv
1626+-- return $ stripTrailingNewlines res
1627+--
1628+-- verbEnv :: PandocMonad m => String -> LP m String
1629+-- verbEnv name = do
1630+-- skipopts
1631+-- optional blankline
1632+-- let endEnv = try $ controlSeq "end" *> braced >>= guard . (== name)
1633+-- charMuncher = anyChar
1634+-- res <- manyTill charMuncher endEnv
1635+-- return $ stripTrailingNewlines res
1636+--
1637+-- fancyverbEnv :: PandocMonad m => String -> LP m Blocks
1638+-- fancyverbEnv name = do
1639+-- options <- option [] keyvals
1640+-- let kvs = [ (if k == "firstnumber"
1641+-- then "startFrom"
1642+-- else k, v) | (k,v) <- options ]
1643+-- let classes = [ "numberLines" |
1644+-- lookup "numbers" options == Just "left" ]
1645+-- let attr = ("",classes,kvs)
1646+-- codeBlockWith attr <$> verbEnv name
1647+--
1648+-- orderedList' :: PandocMonad m => LP m Blocks
1649+-- orderedList' = try $ do
1650+-- optional sp
1651+-- (_, style, delim) <- option (1, DefaultStyle, DefaultDelim) $
1652+-- try $ char '[' *> anyOrderedListMarker <* char ']'
1653+-- spaces
1654+-- optional $ try $ controlSeq "setlength" *> grouped (controlSeq "itemindent") *> braced
1655+-- spaces
1656+-- start <- option 1 $ try $ do controlSeq "setcounter"
1657+-- grouped (string "enum" *> many1 (oneOf "iv"))
1658+-- optional sp
1659+-- num <- grouped (many1 digit)
1660+-- spaces
1661+-- return (read num + 1 :: Int)
1662+-- bs <- listenv "enumerate" (many item)
1663+-- return $ orderedListWith (start, style, delim) bs
1664+--
1665+-- paragraph :: PandocMonad m => LP m Blocks
1666+-- paragraph = do
1667+-- x <- trimInlines . mconcat <$> many1 inline
1668+-- if x == mempty
1669+-- then return mempty
1670+-- else return $ para x
1671+--
1672+-- preamble :: PandocMonad m => LP m Blocks
1673+-- preamble = mempty <$> manyTill preambleBlock beginDoc
1674+-- where beginDoc = lookAhead $ try $ controlSeq "begin" *> string "{document}"
1675+-- preambleBlock = void comment
1676+-- <|> void sp
1677+-- <|> void blanklines
1678+-- <|> void include
1679+-- <|> void macro
1680+-- <|> void blockCommand
1681+-- <|> void anyControlSeq
1682+-- <|> void braced
1683+-- <|> void anyChar
1684+--
1685+-- -------
1686+--
1687+-- -- citations
1688+--
1689+-- addPrefix :: [Inline] -> [Citation] -> [Citation]
1690+-- addPrefix p (k:ks) = k {citationPrefix = p ++ citationPrefix k} : ks
1691+-- addPrefix _ _ = []
1692+--
1693+-- addSuffix :: [Inline] -> [Citation] -> [Citation]
1694+-- addSuffix s ks@(_:_) =
1695+-- let k = last ks
1696+-- in init ks ++ [k {citationSuffix = citationSuffix k ++ s}]
1697+-- addSuffix _ _ = []
1698+--
1699+-- simpleCiteArgs :: PandocMonad m => LP m [Citation]
1700+-- simpleCiteArgs = try $ do
1701+-- first <- optionMaybe $ toList <$> opt
1702+-- second <- optionMaybe $ toList <$> opt
1703+-- keys <- try $ bgroup *> (manyTill citationLabel egroup)
1704+-- let (pre, suf) = case (first , second ) of
1705+-- (Just s , Nothing) -> (mempty, s )
1706+-- (Just s , Just t ) -> (s , t )
1707+-- _ -> (mempty, mempty)
1708+-- conv k = Citation { citationId = k
1709+-- , citationPrefix = []
1710+-- , citationSuffix = []
1711+-- , citationMode = NormalCitation
1712+-- , citationHash = 0
1713+-- , citationNoteNum = 0
1714+-- }
1715+-- return $ addPrefix pre $ addSuffix suf $ map conv keys
1716+--
1717+-- citationLabel :: PandocMonad m => LP m String
1718+-- citationLabel = optional sp *>
1719+-- (many1 (satisfy isBibtexKeyChar)
1720+-- <* optional sp
1721+-- <* optional (char ',')
1722+-- <* optional sp)
1723+-- where isBibtexKeyChar c = isAlphaNum c || c `elem` (".:;?!`'()/*@_+=-[]" :: String)
1724+--
1725+-- cites :: PandocMonad m => CitationMode -> Bool -> LP m [Citation]
1726+-- cites mode multi = try $ do
1727+-- cits <- if multi
1728+-- then many1 simpleCiteArgs
1729+-- else count 1 simpleCiteArgs
1730+-- let cs = concat cits
1731+-- return $ case mode of
1732+-- AuthorInText -> case cs of
1733+-- (c:rest) -> c {citationMode = mode} : rest
1734+-- [] -> []
1735+-- _ -> map (\a -> a {citationMode = mode}) cs
1736+--
1737+-- citation :: PandocMonad m => String -> CitationMode -> Bool -> LP m Inlines
1738+-- citation name mode multi = do
1739+-- (c,raw) <- withRaw $ cites mode multi
1740+-- return $ cite c (rawInline "latex" $ "\\" ++ name ++ raw)
1741+--
1742+-- complexNatbibCitation :: PandocMonad m => CitationMode -> LP m Inlines
1743+-- complexNatbibCitation mode = try $ do
1744+-- let ils = (toList . trimInlines . mconcat) <$>
1745+-- many (notFollowedBy (oneOf "\\};") >> inline)
1746+-- let parseOne = try $ do
1747+-- skipSpaces
1748+-- pref <- ils
1749+-- cit' <- inline -- expect a citation
1750+-- let citlist = toList cit'
1751+-- cits' <- case citlist of
1752+-- [Cite cs _] -> return cs
1753+-- _ -> mzero
1754+-- suff <- ils
1755+-- skipSpaces
1756+-- optional $ char ';'
1757+-- return $ addPrefix pref $ addSuffix suff cits'
1758+-- (c:cits, raw) <- withRaw $ grouped parseOne
1759+-- return $ cite (c{ citationMode = mode }:cits)
1760+-- (rawInline "latex" $ "\\citetext" ++ raw)
1761+--
1762+-- -- tables
1763+--
1764+-- parseAligns :: PandocMonad m => LP m [(Alignment, Double, (String, String))]
1765+-- parseAligns = try $ do
1766+-- bgroup
1767+-- let maybeBar = skipMany $ sp <|> () <$ char '|' <|> () <$ (char '@' >> braced)
1768+-- maybeBar
1769+-- let cAlign = AlignCenter <$ char 'c'
1770+-- let lAlign = AlignLeft <$ char 'l'
1771+-- let rAlign = AlignRight <$ char 'r'
1772+-- let parAlign = AlignLeft <$ char 'p'
1773+-- -- algins from tabularx
1774+-- let xAlign = AlignLeft <$ char 'X'
1775+-- let mAlign = AlignLeft <$ char 'm'
1776+-- let bAlign = AlignLeft <$ char 'b'
1777+-- let alignChar = cAlign <|> lAlign <|> rAlign <|> parAlign
1778+-- <|> xAlign <|> mAlign <|> bAlign
1779+-- let alignPrefix = char '>' >> braced
1780+-- let alignSuffix = char '<' >> braced
1781+-- let colWidth = try $ do
1782+-- char '{'
1783+-- ds <- many1 (oneOf "0123456789.")
1784+-- spaces
1785+-- string "\\linewidth"
1786+-- char '}'
1787+-- case safeRead ds of
1788+-- Just w -> return w
1789+-- Nothing -> return 0.0
1790+-- let alignSpec = do
1791+-- spaces
1792+-- pref <- option "" alignPrefix
1793+-- spaces
1794+-- al <- alignChar
1795+-- width <- colWidth <|> option 0.0 (do s <- braced
1796+-- pos <- getPosition
1797+-- report $ SkippedContent s pos
1798+-- return 0.0)
1799+-- spaces
1800+-- suff <- option "" alignSuffix
1801+-- return (al, width, (pref, suff))
1802+-- aligns' <- sepEndBy alignSpec maybeBar
1803+-- spaces
1804+-- egroup
1805+-- spaces
1806+-- return $ aligns'
1807+--
1808+-- hline :: PandocMonad m => LP m ()
1809+-- hline = try $ do
1810+-- spaces'
1811+-- controlSeq "hline" <|>
1812+-- -- booktabs rules:
1813+-- controlSeq "toprule" <|>
1814+-- controlSeq "bottomrule" <|>
1815+-- controlSeq "midrule" <|>
1816+-- controlSeq "endhead" <|>
1817+-- controlSeq "endfirsthead"
1818+-- spaces'
1819+-- optional $ bracketed (many1 (satisfy (/=']')))
1820+-- return ()
1821+--
1822+-- lbreak :: PandocMonad m => LP m ()
1823+-- lbreak = () <$ try (spaces' *>
1824+-- (controlSeq "\\" <|> controlSeq "tabularnewline") <*
1825+-- spaces')
1826+--
1827+-- amp :: PandocMonad m => LP m ()
1828+-- amp = () <$ try (spaces' *> char '&' <* spaces')
1829+--
1830+-- parseTableRow :: PandocMonad m
1831+-- => String -- ^ table environment name
1832+-- -> [(String, String)] -- ^ pref/suffixes
1833+-- -> LP m [Blocks]
1834+-- parseTableRow envname prefsufs = try $ do
1835+-- let cols = length prefsufs
1836+-- let tableCellRaw = concat <$> many
1837+-- (do notFollowedBy amp
1838+-- notFollowedBy lbreak
1839+-- notFollowedBy $ () <$ try (string ("\\end{" ++ envname ++ "}"))
1840+-- many1 (noneOf "&%\n\r\\")
1841+-- <|> try (string "\\&")
1842+-- <|> count 1 anyChar)
1843+-- let plainify bs = case toList bs of
1844+-- [Para ils] -> plain (fromList ils)
1845+-- _ -> bs
1846+-- rawcells <- sepBy1 tableCellRaw amp
1847+-- guard $ length rawcells == cols
1848+-- let rawcells' = zipWith (\c (p, s) -> p ++ trim c ++ s) rawcells prefsufs
1849+-- let tableCell = plainify <$> blocks
1850+-- cells' <- mapM (parseFromString' tableCell) rawcells'
1851+-- let numcells = length cells'
1852+-- guard $ numcells <= cols && numcells >= 1
1853+-- guard $ cells' /= [mempty]
1854+-- -- note: a & b in a three-column table leaves an empty 3rd cell:
1855+-- let cells'' = cells' ++ replicate (cols - numcells) mempty
1856+-- spaces'
1857+-- return cells''
1858+--
1859+-- spaces' :: PandocMonad m => LP m ()
1860+-- spaces' = spaces *> skipMany (comment *> spaces)
1861+--
1862+-- simpTable :: PandocMonad m => String -> Bool -> LP m Blocks
1863+-- simpTable envname hasWidthParameter = try $ do
1864+-- when hasWidthParameter $ () <$ (spaces' >> tok)
1865+-- skipopts
1866+-- colspecs <- parseAligns
1867+-- let (aligns, widths, prefsufs) = unzip3 colspecs
1868+-- let cols = length colspecs
1869+-- optional $ controlSeq "caption" *> skipopts *> setCaption
1870+-- optional lbreak
1871+-- spaces'
1872+-- skipMany hline
1873+-- spaces'
1874+-- header' <- option [] $ try (parseTableRow envname prefsufs <*
1875+-- lbreak <* many1 hline)
1876+-- spaces'
1877+-- rows <- sepEndBy (parseTableRow envname prefsufs)
1878+-- (lbreak <* optional (skipMany hline))
1879+-- spaces'
1880+-- optional $ controlSeq "caption" *> skipopts *> setCaption
1881+-- optional lbreak
1882+-- spaces'
1883+-- let header'' = if null header'
1884+-- then replicate cols mempty
1885+-- else header'
1886+-- lookAhead $ controlSeq "end" -- make sure we're at end
1887+-- return $ table mempty (zip aligns widths) header'' rows
1888+--
1889+-- removeDoubleQuotes :: String -> String
1890+-- removeDoubleQuotes ('"':xs) =
1891+-- case reverse xs of
1892+-- '"':ys -> reverse ys
1893+-- _ -> '"':xs
1894+-- removeDoubleQuotes xs = xs
1895+--
1896+-- inputListing :: PandocMonad m => LP m Blocks
1897+-- inputListing = do
1898+-- pos <- getPosition
1899+-- options <- option [] keyvals
1900+-- f <- filter (/='"') <$> braced
1901+-- dirs <- (splitBy (==':') . fromMaybe ".") <$> lookupEnv "TEXINPUTS"
1902+-- mbCode <- readFileFromDirs dirs f
1903+-- codeLines <- case mbCode of
1904+-- Just s -> return $ lines s
1905+-- Nothing -> do
1906+-- report $ CouldNotLoadIncludeFile f pos
1907+-- return []
1908+-- let (ident,classes,kvs) = parseListingsOptions options
1909+-- let language = case lookup "language" options >>= fromListingsLanguage of
1910+-- Just l -> [l]
1911+-- Nothing -> take 1 $ languagesByExtension (takeExtension f)
1912+-- let firstline = fromMaybe 1 $ lookup "firstline" options >>= safeRead
1913+-- let lastline = fromMaybe (length codeLines) $
1914+-- lookup "lastline" options >>= safeRead
1915+-- let codeContents = intercalate "\n" $ take (1 + lastline - firstline) $
1916+-- drop (firstline - 1) codeLines
1917+-- return $ codeBlockWith (ident,ordNub (classes ++ language),kvs) codeContents
1918+--
1919+-- parseListingsOptions :: [(String, String)] -> Attr
1920+-- parseListingsOptions options =
1921+-- let kvs = [ (if k == "firstnumber"
1922+-- then "startFrom"
1923+-- else k, v) | (k,v) <- options ]
1924+-- classes = [ "numberLines" |
1925+-- lookup "numbers" options == Just "left" ]
1926+-- ++ maybeToList (lookup "language" options
1927+-- >>= fromListingsLanguage)
1928+-- in (fromMaybe "" (lookup "label" options), classes, kvs)
1929+-}