git.iko.soy/typeable/pandoc
on master help sign in
Rewrote LaTeX reader with proper tokenization.

This rewrite is primarily motivated by the need to get macros working properly (#982, #934, #3779, #3236, #1390, #2888, #2118). We now tokenize the input text, then parse the token stream. Macros modify the token stream, so they should now be effective in any context, including math. (Thus, we no longer need the clunky macro processing capacities of texmath.) A custom state LaTeXState is used instead of ParserState. This, plus the tokenization, will require some rewriting of the exported functions rawLaTeXInline, inlineCommand, rawLaTeXBlock.

commit0c100a2e6f8a856bdb0a629f8f5f2caceefccbe6 authorJohn MacFarlane <jgm@berkeley.edu> dateMon 3 Jul 2017, 17:55 (9 years ago) parent3449f8b
5 files changed+961−780
src/Text/Pandoc/Error.hs+3 src/Text/Pandoc/Readers/LaTeX.hs+955−777 test/Tests/Readers/LaTeX.hs+2−1 test/latex-reader.latex−1 test/latex-reader.native+1−1
src/Text/Pandoc/Error.hs+3
@@ -64,6 +64,7 @@ data PandocError = PandocIOError String IOError
6464 | PandocTemplateError String
6565 | PandocAppError String
6666 | PandocEpubSubdirectoryError String
67+ | PandocMacroLoop String
6768 deriving (Show, Typeable, Generic)
6869
6970instance Exception PandocError
@@ -107,6 +108,8 @@ handleError (Left e) =
107108 PandocAppError s -> err 1 s
108109 PandocEpubSubdirectoryError s -> err 31 $
109110 "EPUB subdirectory name '" ++ s ++ "' contains illegal characters"
111+ PandocMacroLoop s -> err 91 $
112+ "Loop encountered in expanding macro " ++ s
110113
111114err :: Int -> String -> IO a
112115err exitCode msg = do
src/Text/Pandoc/Readers/LaTeX.hs+955−777
@@ -1,4 +1,5 @@
11{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE PatternGuards #-}
23{-# LANGUAGE FlexibleInstances #-}
34{-# LANGUAGE MultiParamTypeClasses #-}
45{-# LANGUAGE ScopedTypeVariables #-}
@@ -30,6 +31,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
3031 Portability : portable
3132
3233Conversion of LaTeX to 'Pandoc' document.
34+
35+TODO:
36+[ ] exported rawLaTeXInline, rawLaTeXBlock, inlineCommand
3337-}
3438module Text.Pandoc.Readers.LaTeX ( readLaTeX,
3539 rawLaTeXInline,
@@ -42,7 +46,7 @@ import Control.Monad
4246import Control.Monad.Except (throwError)
4347import Data.Char (chr, isAlphaNum, isLetter, ord, isDigit)
4448import Data.Default
45−import Data.Text (Text, unpack)
49+import Data.Text (Text)
4650import qualified Data.Text as T
4751import Data.List (intercalate, isPrefixOf)
4852import qualified Data.Map as M
@@ -59,11 +63,11 @@ import Text.Pandoc.ImageSize (numUnit, showFl)
5963import Text.Pandoc.Logging
6064import Text.Pandoc.Options
6165import Text.Pandoc.Parsing hiding (many, optional, withRaw,
62− mathInline, mathDisplay,
66+ mathInline, mathDisplay, macro,
6367 space, (<|>), spaces, blankline)
6468import Text.Pandoc.Shared
6569import Text.Pandoc.Walk
66−import Text.Pandoc.Error (PandocError(PandocParsecError))
70+import Text.Pandoc.Error (PandocError(PandocParsecError, PandocMacroLoop))
6771
6872import Text.Pandoc.Extensions (getDefaultExtensions)
6973import Debug.Trace (traceShowId)
@@ -103,32 +107,44 @@ testParser :: LP PandocIO a -> Text -> IO a
103107testParser p t = do
104108 res <- runIOorExplode (runParserT p defaultLaTeXState{
105109 sOptions = def{ readerExtensions =
106− getDefaultExtensions "latex" }} "name" (tokenize t))
110+ enableExtension Ext_raw_tex $
111+ getDefaultExtensions "latex" }} "source" (tokenize t))
107112 case res of
108113 Left e -> error (show e)
109114 Right r -> return r
110115
111−data Macro = CommandMacro Text ([Tok] -> [Tok])
112− | EnvironmentMacro Text ([Tok] -> [Tok])
116+data Macro = Macro Int (Maybe [Tok]) [Tok]
117+ deriving Show
113118
114119data LaTeXState = LaTeXState{ sOptions :: ReaderOptions
115120 , sMeta :: Meta
116121 , sQuoteContext :: QuoteContext
117− , sMacros :: [Macro]
122+ , sMacros :: M.Map Text Macro
118123 , sContainers :: [String]
119124 , sHeaders :: M.Map Inlines String
120125 , sLogMessages :: [LogMessage]
121− , sIdentifiers :: Set.Set String }
126+ , sIdentifiers :: Set.Set String
127+ , sVerbatimMode :: Bool
128+ , sCaption :: Maybe Inlines
129+ , sInListItem :: Bool
130+ , sInTableCell :: Bool
131+ }
132+ deriving Show
122133
123134defaultLaTeXState :: LaTeXState
124135defaultLaTeXState = LaTeXState{ sOptions = def
125136 , sMeta = nullMeta
126137 , sQuoteContext = NoQuote
127− , sMacros = []
138+ , sMacros = M.empty
128139 , sContainers = []
129140 , sHeaders = M.empty
130141 , sLogMessages = []
131− , sIdentifiers = Set.empty }
142+ , sIdentifiers = Set.empty
143+ , sVerbatimMode = False
144+ , sCaption = Nothing
145+ , sInListItem = False
146+ , sInTableCell = False
147+ }
132148
133149instance PandocMonad m => HasQuoteContext LaTeXState m where
134150 getQuoteContext = sQuoteContext <$> getState
@@ -172,17 +188,48 @@ instance Default LaTeXState where
172188
173189type LP m = ParserT [Tok] LaTeXState m
174190
175−rawLaTeXBlock :: PandocMonad m => ParserT String LaTeXState m String
191+withVerbatimMode :: PandocMonad m => LP m a -> LP m a
192+withVerbatimMode parser = do
193+ updateState $ \st -> st{ sVerbatimMode = True }
194+ result <- parser
195+ updateState $ \st -> st{ sVerbatimMode = False }
196+ return result
197+
198+-- TODO: export our local macro parsing code
199+-- use that in markdown reader and change ParserState
200+-- TODO: remove use of texmath's applyMacros in Markdown reader
201+-- (and elsewhere?). Instead, use an exported
202+-- applyMacros that uses our Macro type. or perhaps
203+-- just run the latex reader on the math (adding delims
204+-- $$).
205+
206+-- TODO: replace ParserState with variable and constraints.
207+-- can we use 'tokens' from Parsec?
208+-- feed ParserState -> LaTeXState and then back again
209+rawLaTeXBlock :: PandocMonad m => ParserT String ParserState m String
176210rawLaTeXBlock = mzero
211+-- rawLaTeXBlock :: PandocMonad m => ParserT String LaTeXState String
212+-- rawLaTeXBlock = snd <$> try (withRaw (environment <|> blockCommand))
177213
178−rawLaTeXInline :: PandocMonad m => ParserT String LaTeXState m Inline
214+rawLaTeXInline :: PandocMonad m => ParserT String ParserState m Inline
179215rawLaTeXInline = mzero
216+-- rawLaTeXInline :: PandocMonad m => ParserT String LaTeXState Inline
217+-- rawLaTeXInline = do
218+-- raw <- (snd <$> withRaw inlineCommand)
219+-- <|> (snd <$> withRaw inlineEnvironment)
220+-- <|> (snd <$> withRaw blockCommand)
221+-- RawInline "latex" <$> applyMacros' raw
180222
181−inlineCommand :: PandocMonad m => ParserT String LaTeXState m Inlines
223+-- TODO this is only used in org reader.
224+-- I think the reason is that tarleb wanted to avoid
225+-- the blockCommand alternative in rawLaTeXInline.
226+-- I don't know whyt that's there and I think it should be
227+-- removed.
228+inlineCommand :: PandocMonad m => ParserT String ParserState m Inlines
182229inlineCommand = mzero
183230
184−data TokType = CtrlSeq | Spaces | Newline | Symbol | Word | Comment |
185− Esc1 | Esc2
231+data TokType = CtrlSeq Text | Spaces | Newline | Symbol | Word | Comment |
232+ Esc1 | Esc2 | Arg Int
186233 deriving (Eq, Ord, Show)
187234
188235data Tok = Tok (Line, Column) TokType Text
@@ -197,7 +244,7 @@ totoks (lin,col) t =
197244 Nothing -> []
198245 Just (c, rest)
199246 | c == '\n' ->
200− Tok (lin, col) Newline (T.singleton '\n')
247+ Tok (lin, col) Newline "\n"
201248 : totoks (lin + 1,1) rest
202249 | isSpaceOrTab c ->
203250 let (sps, rest') = T.span isSpaceOrTab t
@@ -209,7 +256,7 @@ totoks (lin,col) t =
209256 : totoks (lin, col + T.length ws) rest'
210257 | c == '%' ->
211258 let (cs, rest') = T.break (== '\n') rest
212− in Tok (lin, col) Comment (T.singleton '%' <> cs)
259+ in Tok (lin, col) Comment ("%" <> cs)
213260 : totoks (lin, col + 1 + T.length cs) rest'
214261 | c == '\\' ->
215262 case T.uncons rest of
@@ -217,14 +264,25 @@ totoks (lin,col) t =
217264 Just (d, rest')
218265 | isLetter d ->
219266 let (ws, rest'') = T.span isLetter rest
220− in Tok (lin, col) CtrlSeq (T.singleton '\\' <> ws)
221− : totoks (lin, col + 1 + T.length ws) rest''
267+ (ss, rest''') = T.span isSpaceOrTab rest''
268+ in Tok (lin, col) (CtrlSeq ws) ("\\" <> ws <> ss)
269+ : totoks (lin,
270+ col + 1 + T.length ws + T.length ss) rest'''
222271 | d == '\t' || d == '\n' ->
223− Tok (lin, col) Symbol (T.singleton '\\')
272+ Tok (lin, col) Symbol ("\\")
224273 : totoks (lin, col + 1) rest
225274 | otherwise ->
226− Tok (lin, col) CtrlSeq (T.pack [c,d])
275+ Tok (lin, col) (CtrlSeq (T.singleton d)) (T.pack [c,d])
227276 : totoks (lin, col + 2) rest'
277+ | c == '#' ->
278+ let (t1, t2) = T.span (\d -> d >= '0' && d <= '9') rest
279+ in case safeRead (T.unpack t1) of
280+ Just i ->
281+ Tok (lin, col) (Arg i) ("#" <> t1)
282+ : totoks (lin, col + 1 + T.length t1) t2
283+ Nothing ->
284+ Tok (lin, col) Symbol ("#")
285+ : totoks (lin, col + 1) t2
228286 | c == '^' ->
229287 case T.uncons rest of
230288 Just ('^', rest') ->
@@ -241,9 +299,9 @@ totoks (lin,col) t =
241299 | d < '\128' ->
242300 Tok (lin, col) Esc1 (T.pack ['^','^',d])
243301 : totoks (lin, col + 3) rest''
244− _ -> [Tok (lin, col) Symbol (T.singleton '^'),
245− Tok (lin, col + 1) Symbol (T.singleton '^')]
246− _ -> Tok (lin, col) Symbol (T.singleton '^')
302+ _ -> [Tok (lin, col) Symbol ("^"),
303+ Tok (lin, col + 1) Symbol ("^")]
304+ _ -> Tok (lin, col) Symbol ("^")
247305 : totoks (lin, col + 1) rest
248306 | otherwise ->
249307 Tok (lin, col) Symbol (T.singleton c) : totoks (lin, col + 1) rest
@@ -261,10 +319,12 @@ untokenize = mconcat . map untoken
261319untoken :: Tok -> Text
262320untoken (Tok _ _ t) = t
263321
264−-- we put withMacros in front of satisfyTok, so it works on everything
265322satisfyTok :: PandocMonad m => (Tok -> Bool) -> LP m Tok
266323satisfyTok f =
267− doMacros *> tokenPrim (T.unpack . untoken) updatePos matcher
324+ try $ do
325+ res <- tokenPrim (T.unpack . untoken) updatePos matcher
326+ doMacros 0 -- apply macros on remaining input stream
327+ return res
268328 where matcher t | f t = Just t
269329 | otherwise = Nothing
270330 updatePos :: SourcePos -> Tok -> [Tok] -> SourcePos
@@ -272,26 +332,51 @@ satisfyTok f =
272332 setSourceColumn (setSourceLine spos lin) col
273333 updatePos spos _ [] = spos
274334
275−-- TODO use something other than LaTeXState
276−doMacros :: PandocMonad m => LP m ()
277−doMacros = do
278− inp <- getInput
279− macros <- sMacros <$> getState
280− case inp of
281− t@(Tok spos CtrlSeq txt) : ts
282− | txt == "\\macro" -> do -- TODO actually check macros
283− setInput $ map (setpos spos) (tokenize "Hi there") ++ ts
284− -- TODO loop detection
285− doMacros
286− _ -> return ()
335+doMacros :: PandocMonad m => Int -> LP m ()
336+doMacros n = do
337+ applyMacros <- readerApplyMacros . sOptions <$> getState
338+ verbatimMode <- sVerbatimMode <$> getState
339+ when (applyMacros && not verbatimMode) $ do
340+ inp <- getInput
341+ case inp of
342+ Tok spos (CtrlSeq "begin") _ : Tok _ Symbol "{" :
343+ Tok _ Word name : Tok _ Symbol "}" : ts
344+ -> handleMacros spos name ts
345+ Tok spos (CtrlSeq "end") _ : Tok _ Symbol "{" :
346+ Tok _ Word name : Tok _ Symbol "}" : ts
347+ -> handleMacros spos ("end" <> name) ts
348+ Tok spos (CtrlSeq name) _ : ts
349+ -> handleMacros spos name ts
350+ _ -> return ()
351+ where handleMacros spos name ts = do
352+ macros <- sMacros <$> getState
353+ case M.lookup name macros of
354+ Nothing -> return ()
355+ Just (Macro numargs optarg newtoks) -> do
356+ setInput ts
357+ let getarg = spaces >> braced
358+ args <- case optarg of
359+ Nothing -> count numargs getarg
360+ Just o ->
361+ (:) <$> option o bracketedToks
362+ <*> count (numargs - 1) getarg
363+ let addTok (Tok _ (Arg i) _) acc | i > 0
364+ , i <= numargs =
365+ map (setpos spos) (args !! (i - 1)) ++ acc
366+ addTok t acc = setpos spos t : acc
367+ ts' <- getInput
368+ setInput $ foldr addTok ts' newtoks
369+ if n > 20 -- detect macro expansion loops
370+ then throwError $ PandocMacroLoop (T.unpack name)
371+ else doMacros (n + 1)
287372
288373setpos :: (Line, Column) -> Tok -> Tok
289374setpos spos (Tok _ tt txt) = Tok spos tt txt
290375
291376anyControlSeq :: PandocMonad m => LP m Tok
292−anyControlSeq = satisfyTok isCtrlSeq <* optional sp
293− where isCtrlSeq (Tok _ CtrlSeq _) = True
294− isCtrlSeq _ = False
377+anyControlSeq = satisfyTok isCtrlSeq
378+ where isCtrlSeq (Tok _ (CtrlSeq _) _) = True
379+ isCtrlSeq _ = False
295380
296381anySymbol :: PandocMonad m => LP m Tok
297382anySymbol = satisfyTok isSym
@@ -309,7 +394,7 @@ tokTypeIn toktypes (Tok _ tt _) = tt `elem` toktypes
309394
310395controlSeq :: PandocMonad m => Text -> LP m Tok
311396controlSeq name = satisfyTok isNamed
312− where isNamed (Tok _ CtrlSeq n) = n == T.singleton '\\' <> name
397+ where isNamed (Tok _ (CtrlSeq n) _) = n == name
313398 isNamed _ = False
314399
315400symbol :: PandocMonad m => Char -> LP m Tok
@@ -356,15 +441,13 @@ endline = try $ do
356441 notFollowedBy blankline
357442
358443blankline :: PandocMonad m => LP m ()
359−blankline = try $ do
360− skipMany (satisfyTok (tokTypeIn [Spaces, Newline]))
361− newlineTok
444+blankline = try $ skipMany whitespace *> newlineTok
362445
363446primEscape :: PandocMonad m => LP m Char
364447primEscape = do
365448 Tok _ toktype t <- satisfyTok (tokTypeIn [Esc1, Esc2])
366449 case toktype of
367− Esc1 -> case T.uncons t of
450+ Esc1 -> case T.uncons (T.drop 2 t) of
368451 Just (c, _)
369452 | c >= '\64' && c <= '\127' -> return (chr (ord c - 64))
370453 | otherwise -> return (chr (ord c + 64))
@@ -374,14 +457,13 @@ primEscape = do
374457 Nothing -> fail $ "Could not read: " ++ T.unpack t
375458 _ -> fail "Expected an Esc1 or Esc2 token" -- should not happen
376459
377−bgroup :: PandocMonad m => LP m ()
460+bgroup :: PandocMonad m => LP m Tok
378461bgroup = try $ do
379462 skipMany sp
380463 symbol '{' <|> controlSeq "bgroup" <|> controlSeq "begingroup"
381− return ()
382464
383−egroup :: PandocMonad m => LP m ()
384−egroup = () <$ (symbol '}' <|> controlSeq "egroup" <|> controlSeq "endgroup")
465+egroup :: PandocMonad m => LP m Tok
466+egroup = (symbol '}' <|> controlSeq "egroup" <|> controlSeq "endgroup")
385467
386468grouped :: (PandocMonad m, Monoid a) => LP m a -> LP m a
387469grouped parser = try $ do
@@ -391,10 +473,20 @@ grouped parser = try $ do
391473 try (grouped parser <* egroup) <|> (mconcat <$> manyTill parser egroup)
392474
393475braced :: PandocMonad m => LP m [Tok]
394−braced = try $ do
395− bgroup
396− -- {{a,b}} should be parsed the same as {a,b}
397− try (braced <* egroup) <|> manyTill anyTok egroup
476+braced = bgroup *> braced' 1
477+ where braced' (n :: Int) =
478+ handleEgroup n <|> handleBgroup n <|> handleOther n
479+ handleEgroup n = do
480+ t <- egroup
481+ if n == 1
482+ then return []
483+ else (t:) <$> braced' (n - 1)
484+ handleBgroup n = do
485+ t <- bgroup
486+ (t:) <$> braced' (n + 1)
487+ handleOther n = do
488+ t <- anyTok
489+ (t:) <$> braced' n
398490
399491bracketed :: PandocMonad m => Monoid a => LP m a -> LP m a
400492bracketed parser = try $ do
@@ -414,8 +506,17 @@ dimenarg = try $ do
414506
415507-- inline elements:
416508
417−inlineText :: PandocMonad m => LP m Inlines
418−inlineText = (str . T.unpack . untoken) <$> satisfyTok isWordTok
509+word :: PandocMonad m => LP m Inlines
510+word = (str . T.unpack . untoken) <$> satisfyTok isWordTok
511+
512+regularSymbol :: PandocMonad m => LP m Inlines
513+regularSymbol = (str . T.unpack . untoken) <$> satisfyTok isRegularSymbol
514+ where isRegularSymbol (Tok _ Symbol t) = not $ T.any isSpecial t
515+ isRegularSymbol _ = False
516+ isSpecial c = c `Set.member` specialChars
517+
518+specialChars :: Set.Set Char
519+specialChars = Set.fromList "#$%&~_^\\{}"
419520
420521isWordTok :: Tok -> Bool
421522isWordTok (Tok _ Word _) = True
@@ -435,9 +536,44 @@ doLHSverb =
435536 (codeWith ("",["haskell"],[]) . T.unpack . untokenize)
436537 <$> manyTill (satisfyTok (not . isNewlineTok)) (symbol '|')
437538
539+mkImage :: PandocMonad m => [(String, String)] -> String -> LP m Inlines
540+mkImage options src = do
541+ let replaceTextwidth (k,v) =
542+ case numUnit v of
543+ Just (num, "\\textwidth") -> (k, showFl (num * 100) ++ "%")
544+ _ -> (k, v)
545+ let kvs = map replaceTextwidth
546+ $ filter (\(k,_) -> k `elem` ["width", "height"]) options
547+ let attr = ("",[], kvs)
548+ let alt = str "image"
549+ case takeExtension src of
550+ "" -> do
551+ defaultExt <- getOption readerDefaultImageExtension
552+ return $ imageWith attr (addExtension src defaultExt) "" alt
553+ _ -> return $ imageWith attr src "" alt
554+
555+-- converts e.g. \SI{1}[\$]{} to "$ 1" or \SI{1}{\euro} to "1 €"
556+dosiunitx :: PandocMonad m => LP m Inlines
557+dosiunitx = do
558+ skipopts
559+ value <- tok
560+ valueprefix <- option "" $ bracketed tok
561+ unit <- tok
562+ let emptyOr160 "" = ""
563+ emptyOr160 _ = "\160"
564+ return . mconcat $ [valueprefix,
565+ emptyOr160 valueprefix,
566+ value,
567+ emptyOr160 unit,
568+ unit]
569+
438570lit :: String -> LP m Inlines
439571lit = pure . str
440572
573+removeDoubleQuotes :: Text -> Text
574+removeDoubleQuotes t =
575+ maybe t id $ T.stripPrefix "\"" t >>= T.stripSuffix "\""
576+
441577doubleQuote :: PandocMonad m => LP m Inlines
442578doubleQuote = do
443579 quoted' doubleQuoted (try $ count 2 $ symbol '`')
@@ -496,7 +632,9 @@ doverb = do
496632 marker <- case T.uncons t of
497633 Just (c, ts) | T.null ts -> return c
498634 _ -> mzero
499− (code . T.unpack . untokenize) <$> manyTill (verbTok marker) (symbol marker)
635+ withVerbatimMode $
636+ (code . T.unpack . untokenize) <$>
637+ manyTill (verbTok marker) (symbol marker)
500638
501639verbTok :: PandocMonad m => Char -> LP m Tok
502640verbTok stopchar = do
@@ -519,8 +657,9 @@ dolstinline = do
519657 Just (c, ts) | T.null ts -> return c
520658 _ -> mzero
521659 let stopchar = if marker == '{' then '}' else marker
522− (codeWith ("",classes,[]) . T.unpack . untokenize) <$>
523− manyTill (verbTok stopchar) (symbol stopchar)
660+ withVerbatimMode $
661+ (codeWith ("",classes,[]) . T.unpack . untokenize) <$>
662+ manyTill (verbTok stopchar) (symbol stopchar)
524663
525664keyval :: PandocMonad m => LP m (String, String)
526665keyval = try $ do
@@ -749,52 +888,129 @@ toksToString = T.unpack . untokenize
749888
750889mathDisplay :: String -> Inlines
751890mathDisplay = displayMath . trim
752−-- TODO we shouldn't need applyMacros
753−-- mathDisplay p = displayMath <$> (try p >>= applyMacros' . trim)
754891
755892mathInline :: String -> Inlines
756893mathInline = math . trim
757−-- TODO we shouldn't need applyMacros
758−-- mathInline p = math <$> (try p >>= applyMacros')
759894
760895dollarsMath :: PandocMonad m => LP m Inlines
761896dollarsMath = do
762897 symbol '$'
763898 display <- option False (True <$ symbol '$')
764− contents <- many1Till anyTok (symbol '$')
765− when display (() <$ symbol '$')
766− let constructor = if display then mathDisplay else mathInline
767− return $ constructor $ toksToString contents
899+ contents <- trim . toksToString <$>
900+ many (notFollowedBy (symbol '$') >> anyTok)
901+ if display
902+ then do
903+ mathDisplay contents <$ try (symbol '$' >> symbol '$')
904+ <|> (guard (null contents) >> return (mathInline ""))
905+ else mathInline contents <$ (symbol '$')
906+
907+-- citations
908+
909+addPrefix :: [Inline] -> [Citation] -> [Citation]
910+addPrefix p (k:ks) = k {citationPrefix = p ++ citationPrefix k} : ks
911+addPrefix _ _ = []
912+
913+addSuffix :: [Inline] -> [Citation] -> [Citation]
914+addSuffix s ks@(_:_) =
915+ let k = last ks
916+ in init ks ++ [k {citationSuffix = citationSuffix k ++ s}]
917+addSuffix _ _ = []
918+
919+simpleCiteArgs :: PandocMonad m => LP m [Citation]
920+simpleCiteArgs = try $ do
921+ first <- optionMaybe $ toList <$> opt
922+ second <- optionMaybe $ toList <$> opt
923+ keys <- try $ bgroup *> (manyTill citationLabel egroup)
924+ let (pre, suf) = case (first , second ) of
925+ (Just s , Nothing) -> (mempty, s )
926+ (Just s , Just t ) -> (s , t )
927+ _ -> (mempty, mempty)
928+ conv k = Citation { citationId = k
929+ , citationPrefix = []
930+ , citationSuffix = []
931+ , citationMode = NormalCitation
932+ , citationHash = 0
933+ , citationNoteNum = 0
934+ }
935+ return $ addPrefix pre $ addSuffix suf $ map conv keys
936+
937+citationLabel :: PandocMonad m => LP m String
938+citationLabel = do
939+ optional sp
940+ toksToString <$>
941+ (many1 (satisfyTok isWordTok <|> symbolIn bibtexKeyChar)
942+ <* optional sp
943+ <* optional (symbol ',')
944+ <* optional sp)
945+ where bibtexKeyChar = ".:;?!`'()/*@_+=-[]" :: [Char]
946+
947+cites :: PandocMonad m => CitationMode -> Bool -> LP m [Citation]
948+cites mode multi = try $ do
949+ cits <- if multi
950+ then many1 simpleCiteArgs
951+ else count 1 simpleCiteArgs
952+ let cs = concat cits
953+ return $ case mode of
954+ AuthorInText -> case cs of
955+ (c:rest) -> c {citationMode = mode} : rest
956+ [] -> []
957+ _ -> map (\a -> a {citationMode = mode}) cs
958+
959+citation :: PandocMonad m => String -> CitationMode -> Bool -> LP m Inlines
960+citation name mode multi = do
961+ (c,raw) <- withRaw $ cites mode multi
962+ return $ cite c (rawInline "latex" $ "\\" ++ name ++ (toksToString raw))
963+
964+handleCitationPart :: Inlines -> [Citation]
965+handleCitationPart ils =
966+ let isCite Cite{} = True
967+ isCite _ = False
968+ (pref, rest) = break isCite (toList ils)
969+ in case rest of
970+ (Cite cs _:suff) -> addPrefix pref $ addSuffix suff cs
971+ _ -> []
972+
973+complexNatbibCitation :: PandocMonad m => CitationMode -> LP m Inlines
974+complexNatbibCitation mode = try $ do
975+ (cs, raw) <-
976+ withRaw $ concat <$> do
977+ bgroup
978+ items <- mconcat <$>
979+ many1 (notFollowedBy (symbol ';') >> inline)
980+ `sepBy1` (symbol ';')
981+ egroup
982+ return $ map handleCitationPart items
983+ case cs of
984+ [] -> mzero
985+ (c:cits) -> return $ cite (c{ citationMode = mode }:cits)
986+ (rawInline "latex" $ "\\citetext" ++ toksToString raw)
987+
988+inNote :: Inlines -> Inlines
989+inNote ils =
990+ note $ para $ ils <> str "."
768991
769992inlineCommand' :: PandocMonad m => LP m Inlines
770993inlineCommand' = try $ do
771− cs@(Tok _ CtrlSeq t) <- anyControlSeq
772− let name = T.drop 1 t
994+ Tok _ (CtrlSeq name) cmd <- anyControlSeq
773995 guard $ name /= "begin" && name /= "end"
774− mbstar <- option Nothing (Just <$> symbol '*')
775− let names = name : maybe [] (const [name <> "*"]) mbstar
776− optional $ try $ symbol '{' >> symbol '}'
996+ (star, rawstar) <- withRaw $ option "" ("*" <$ symbol '*' <* optional sp)
997+ let name' = name <> star
998+ let names = ordNub [name', name] -- check non-starred as fallback
777999 let raw = do
7781000 guard $ not (isBlockCommand name)
7791001 (_, rawargs) <- withRaw
7801002 (skipangles *> skipopts *> option "" dimenarg *> many braced)
781− let rawcommand = T.unpack $ untokenize $
782− cs : maybe [] (:[]) mbstar ++ rawargs
1003+ let rawcommand = T.unpack $ cmd <> untokenize (rawstar ++ rawargs)
7831004 (guardEnabled Ext_raw_tex >> return (rawInline "latex" rawcommand))
7841005 <|> ignore rawcommand
785− -- transformed <- applyMacros' rawcommand -- TODO or get rid of
786− -- exts <- getOption readerExtensions
787− -- if transformed /= rawcommand
788− -- then parseFromString' inlines transformed
789− -- else if extensionEnabled Ext_raw_tex exts
790− -- then return $ rawInline "latex" rawcommand
791− -- else ignore rawcommand
7921006 lookupListDefault raw names inlineCommands
7931007
7941008tok :: PandocMonad m => LP m Inlines
795−tok = try $ grouped inline <|> inlineCommand' <|> singleChar
796− where singleChar = do
1009+tok = grouped inline <|> inlineCommand' <|> singleChar
1010+ where singleChar = try $ do
7971011 Tok (lin,col) toktype t <- satisfyTok (tokTypeIn [Word, Symbol])
1012+ guard $ not $ toktype == Symbol &&
1013+ T.any (`Set.member` specialChars) t
7981014 if T.length t > 1
7991015 then do
8001016 let (t1, t2) = (T.take 1 t, T.drop 1 t)
@@ -816,14 +1032,6 @@ rawopt = do
8161032skipopts :: PandocMonad m => LP m ()
8171033skipopts = skipMany rawopt
8181034
819−-- eat an optional argument and one or more arguments in braces
820−ignoreInlines :: PandocMonad m => Text -> (Text, LP m Inlines)
821−ignoreInlines name = (name, p)
822− where p = do oa <- optargs
823− let rawCommand = '\\' : T.unpack (name <> oa)
824− let doraw = guardRaw >> return (rawInline "latex" rawCommand)
825− doraw <|> ignore rawCommand
826−
8271035guardRaw :: PandocMonad m => LP m ()
8281036guardRaw = getOption readerExtensions >>= guard . extensionEnabled Ext_raw_tex
8291037
@@ -865,20 +1073,18 @@ unescapeURL (x:xs) = x:unescapeURL xs
8651073unescapeURL [] = ""
8661074
8671075mathEnvWith :: PandocMonad m
868− => (Inlines -> a) -> Maybe String -> String -> LP m a
1076+ => (Inlines -> a) -> Maybe Text -> Text -> LP m a
8691077mathEnvWith f innerEnv name = f . mathDisplay . inner <$> mathEnv name
8701078 where inner x = case innerEnv of
871− Nothing -> x
872− Just y -> "\\begin{" ++ y ++ "}\n" ++ x ++
873− "\\end{" ++ y ++ "}"
1079+ Nothing -> x
1080+ Just y -> "\\begin{" ++ T.unpack y ++ "}\n" ++ x ++
1081+ "\\end{" ++ T.unpack y ++ "}"
8741082
875−mathEnv :: PandocMonad m => String -> LP m String
1083+mathEnv :: PandocMonad m => Text -> LP m String
8761084mathEnv name = do
8771085 skipopts
8781086 optional blankline
879− let endEnv = try $ controlSeq "end" *>
880− (T.unpack . untokenize <$> braced) >>= guard . (== name)
881− res <- manyTill anyTok endEnv
1087+ res <- manyTill anyTok (end_ name)
8821088 return $ stripTrailingNewlines $ T.unpack $ untokenize res
8831089
8841090inlineEnvironment :: PandocMonad m => LP m Inlines
@@ -986,7 +1192,10 @@ inlineCommands = M.fromList $
9861192 , ("v", option (str "v") $ try $ tok >>= accent hacek)
9871193 , ("u", option (str "u") $ try $ tok >>= accent breve)
9881194 , ("i", lit "i")
989− , ("\\", linebreak <$ (optional (bracketed inline) *> spaces))
1195+ , ("\\", linebreak <$ (do inTableCell <- sInTableCell <$> getState
1196+ guard $ not inTableCell
1197+ optional (bracketed inline)
1198+ spaces))
9901199 , (",", lit "\8198")
9911200 , ("@", pure mempty)
9921201 , (" ", lit "\160")
@@ -1003,71 +1212,72 @@ inlineCommands = M.fromList $
10031212 , ("Verb", doverb)
10041213 , ("url", ((unescapeURL . T.unpack . untokenize) <$> braced) >>= \url ->
10051214 pure (link url "" (str url)))
1006−-- , ("href", (unescapeURL <$> braced <* optional sp) >>= \url ->
1007−-- tok >>= \lab ->
1008−-- pure (link url "" lab))
1009−-- , ("includegraphics", do options <- option [] keyvals
1010−-- src <- unescapeURL . removeDoubleQuotes <$> braced
1011−-- mkImage options src)
1215+ , ("href", (unescapeURL . toksToString <$>
1216+ braced <* optional sp) >>= \url ->
1217+ tok >>= \lab -> pure (link url "" lab))
1218+ , ("includegraphics", do options <- option [] keyvals
1219+ src <- unescapeURL . T.unpack .
1220+ removeDoubleQuotes . untokenize <$> braced
1221+ mkImage options src)
10121222 , ("enquote", enquote)
1013−-- , ("cite", citation "cite" NormalCitation False)
1014−-- , ("Cite", citation "Cite" NormalCitation False)
1015−-- , ("citep", citation "citep" NormalCitation False)
1016−-- , ("citep*", citation "citep*" NormalCitation False)
1017−-- , ("citeal", citation "citeal" NormalCitation False)
1018−-- , ("citealp", citation "citealp" NormalCitation False)
1019−-- , ("citealp*", citation "citealp*" NormalCitation False)
1020−-- , ("autocite", citation "autocite" NormalCitation False)
1021−-- , ("smartcite", citation "smartcite" NormalCitation False)
1022−-- , ("footcite", inNote <$> citation "footcite" NormalCitation False)
1023−-- , ("parencite", citation "parencite" NormalCitation False)
1024−-- , ("supercite", citation "supercite" NormalCitation False)
1025−-- , ("footcitetext", inNote <$> citation "footcitetext" NormalCitation False)
1026−-- , ("citeyearpar", citation "citeyearpar" SuppressAuthor False)
1027−-- , ("citeyear", citation "citeyear" SuppressAuthor False)
1028−-- , ("autocite*", citation "autocite*" SuppressAuthor False)
1029−-- , ("cite*", citation "cite*" SuppressAuthor False)
1030−-- , ("parencite*", citation "parencite*" SuppressAuthor False)
1031−-- , ("textcite", citation "textcite" AuthorInText False)
1032−-- , ("citet", citation "citet" AuthorInText False)
1033−-- , ("citet*", citation "citet*" AuthorInText False)
1034−-- , ("citealt", citation "citealt" AuthorInText False)
1035−-- , ("citealt*", citation "citealt*" AuthorInText False)
1036−-- , ("textcites", citation "textcites" AuthorInText True)
1037−-- , ("cites", citation "cites" NormalCitation True)
1038−-- , ("autocites", citation "autocites" NormalCitation True)
1039−-- , ("footcites", inNote <$> citation "footcites" NormalCitation True)
1040−-- , ("parencites", citation "parencites" NormalCitation True)
1041−-- , ("supercites", citation "supercites" NormalCitation True)
1042−-- , ("footcitetexts", inNote <$> citation "footcitetexts" NormalCitation True)
1043−-- , ("Autocite", citation "Autocite" NormalCitation False)
1044−-- , ("Smartcite", citation "Smartcite" NormalCitation False)
1045−-- , ("Footcite", citation "Footcite" NormalCitation False)
1046−-- , ("Parencite", citation "Parencite" NormalCitation False)
1047−-- , ("Supercite", citation "Supercite" NormalCitation False)
1048−-- , ("Footcitetext", inNote <$> citation "Footcitetext" NormalCitation False)
1049−-- , ("Citeyearpar", citation "Citeyearpar" SuppressAuthor False)
1050−-- , ("Citeyear", citation "Citeyear" SuppressAuthor False)
1051−-- , ("Autocite*", citation "Autocite*" SuppressAuthor False)
1052−-- , ("Cite*", citation "Cite*" SuppressAuthor False)
1053−-- , ("Parencite*", citation "Parencite*" SuppressAuthor False)
1054−-- , ("Textcite", citation "Textcite" AuthorInText False)
1055−-- , ("Textcites", citation "Textcites" AuthorInText True)
1056−-- , ("Cites", citation "Cites" NormalCitation True)
1057−-- , ("Autocites", citation "Autocites" NormalCitation True)
1058−-- , ("Footcites", citation "Footcites" NormalCitation True)
1059−-- , ("Parencites", citation "Parencites" NormalCitation True)
1060−-- , ("Supercites", citation "Supercites" NormalCitation True)
1061−-- , ("Footcitetexts", inNote <$> citation "Footcitetexts" NormalCitation True)
1062−-- , ("citetext", complexNatbibCitation NormalCitation)
1063−-- , ("citeauthor", (try (tok *> optional sp *> controlSeq "citetext") *>
1064−-- complexNatbibCitation AuthorInText)
1065−-- <|> citation "citeauthor" AuthorInText False)
1066−-- , ("nocite", mempty <$ (citation "nocite" NormalCitation False >>=
1067−-- addMeta "nocite"))
1223+ , ("cite", citation "cite" NormalCitation False)
1224+ , ("Cite", citation "Cite" NormalCitation False)
1225+ , ("citep", citation "citep" NormalCitation False)
1226+ , ("citep*", citation "citep*" NormalCitation False)
1227+ , ("citeal", citation "citeal" NormalCitation False)
1228+ , ("citealp", citation "citealp" NormalCitation False)
1229+ , ("citealp*", citation "citealp*" NormalCitation False)
1230+ , ("autocite", citation "autocite" NormalCitation False)
1231+ , ("smartcite", citation "smartcite" NormalCitation False)
1232+ , ("footcite", inNote <$> citation "footcite" NormalCitation False)
1233+ , ("parencite", citation "parencite" NormalCitation False)
1234+ , ("supercite", citation "supercite" NormalCitation False)
1235+ , ("footcitetext", inNote <$> citation "footcitetext" NormalCitation False)
1236+ , ("citeyearpar", citation "citeyearpar" SuppressAuthor False)
1237+ , ("citeyear", citation "citeyear" SuppressAuthor False)
1238+ , ("autocite*", citation "autocite*" SuppressAuthor False)
1239+ , ("cite*", citation "cite*" SuppressAuthor False)
1240+ , ("parencite*", citation "parencite*" SuppressAuthor False)
1241+ , ("textcite", citation "textcite" AuthorInText False)
1242+ , ("citet", citation "citet" AuthorInText False)
1243+ , ("citet*", citation "citet*" AuthorInText False)
1244+ , ("citealt", citation "citealt" AuthorInText False)
1245+ , ("citealt*", citation "citealt*" AuthorInText False)
1246+ , ("textcites", citation "textcites" AuthorInText True)
1247+ , ("cites", citation "cites" NormalCitation True)
1248+ , ("autocites", citation "autocites" NormalCitation True)
1249+ , ("footcites", inNote <$> citation "footcites" NormalCitation True)
1250+ , ("parencites", citation "parencites" NormalCitation True)
1251+ , ("supercites", citation "supercites" NormalCitation True)
1252+ , ("footcitetexts", inNote <$> citation "footcitetexts" NormalCitation True)
1253+ , ("Autocite", citation "Autocite" NormalCitation False)
1254+ , ("Smartcite", citation "Smartcite" NormalCitation False)
1255+ , ("Footcite", citation "Footcite" NormalCitation False)
1256+ , ("Parencite", citation "Parencite" NormalCitation False)
1257+ , ("Supercite", citation "Supercite" NormalCitation False)
1258+ , ("Footcitetext", inNote <$> citation "Footcitetext" NormalCitation False)
1259+ , ("Citeyearpar", citation "Citeyearpar" SuppressAuthor False)
1260+ , ("Citeyear", citation "Citeyear" SuppressAuthor False)
1261+ , ("Autocite*", citation "Autocite*" SuppressAuthor False)
1262+ , ("Cite*", citation "Cite*" SuppressAuthor False)
1263+ , ("Parencite*", citation "Parencite*" SuppressAuthor False)
1264+ , ("Textcite", citation "Textcite" AuthorInText False)
1265+ , ("Textcites", citation "Textcites" AuthorInText True)
1266+ , ("Cites", citation "Cites" NormalCitation True)
1267+ , ("Autocites", citation "Autocites" NormalCitation True)
1268+ , ("Footcites", citation "Footcites" NormalCitation True)
1269+ , ("Parencites", citation "Parencites" NormalCitation True)
1270+ , ("Supercites", citation "Supercites" NormalCitation True)
1271+ , ("Footcitetexts", inNote <$> citation "Footcitetexts" NormalCitation True)
1272+ , ("citetext", complexNatbibCitation NormalCitation)
1273+ , ("citeauthor", (try (tok *> optional sp *> controlSeq "citetext") *>
1274+ complexNatbibCitation AuthorInText)
1275+ <|> citation "citeauthor" AuthorInText False)
1276+ , ("nocite", mempty <$ (citation "nocite" NormalCitation False >>=
1277+ addMeta "nocite"))
10681278 , ("hypertarget", braced >> tok)
10691279-- -- siuntix
1070−-- , ("SI", dosiunitx)
1280+ , ("SI", dosiunitx)
10711281 -- hyphenat
10721282 , ("bshyp", lit "\\\173")
10731283 , ("fshyp", lit "/\173")
@@ -1080,16 +1290,18 @@ inlineCommands = M.fromList $
10801290 -- fontawesome
10811291 , ("faCheck", lit "\10003")
10821292 , ("faClose", lit "\10007")
1083− ] ++ map ignoreInlines
1293+ ]
1294+
10841295 -- these commands will be ignored unless --parse-raw is specified,
10851296 -- in which case they will appear as raw latex blocks:
1086− [ "index"
1087− , "hspace"
1088− , "vspace"
1089− , "newpage"
1090− , "clearpage"
1091− , "pagebreak"
1092− ]
1297+ -- [ "index"
1298+ -- , "hspace"
1299+ -- , "vspace"
1300+ -- , "newpage"
1301+ -- , "clearpage"
1302+ -- , "pagebreak"
1303+ -- , "noindent"
1304+ -- ]
10931305
10941306ttfamily :: PandocMonad m => LP m Inlines
10951307ttfamily = (code . stringify . toList) <$> tok
@@ -1102,27 +1314,42 @@ rawInlineOr name' fallback = do
11021314 else fallback
11031315
11041316getRawCommand :: PandocMonad m => Text -> LP m String
1105−getRawCommand name' = do
1317+getRawCommand txt = do
11061318 (_, rawargs) <- withRaw
11071319 (many (try (optional sp *> opt)) *>
11081320 option "" (try (optional sp *> dimenarg)) *>
11091321 many braced)
1110− return $ '\\' : T.unpack (name' <> untokenize rawargs)
1322+ return $ T.unpack (txt <> untokenize rawargs)
11111323
11121324isBlockCommand :: Text -> Bool
11131325isBlockCommand s =
11141326 s `M.member` (blockCommands :: M.Map Text (LP PandocPure Blocks))
11151327
1116−lookupListDefault :: (Ord k) => v -> [k] -> M.Map k v -> v
1328+isInlineCommand :: Text -> Bool
1329+isInlineCommand s =
1330+ s `M.member` (inlineCommands :: M.Map Text (LP PandocPure Inlines))
1331+ || s `Set.member` treatAsRawInline
1332+
1333+treatAsRawInline :: Set.Set Text
1334+treatAsRawInline = Set.fromList
1335+ [ "index"
1336+ , "hspace"
1337+ , "vspace"
1338+ , "newpage"
1339+ , "clearpage"
1340+ , "pagebreak"
1341+ , "noindent"
1342+ ]
1343+
1344+lookupListDefault :: (Show k, Ord k) => v -> [k] -> M.Map k v -> v
11171345lookupListDefault d = (fromMaybe d .) . lookupList
1118− where
1119− lookupList l m = msum $ map (`M.lookup` m) l
1346+ where lookupList l m = msum $ map (`M.lookup` m) l
11201347
11211348inline :: PandocMonad m => LP m Inlines
11221349inline = (mempty <$ comment)
11231350 <|> (space <$ whitespace)
11241351 <|> (softbreak <$ endline)
1125− <|> inlineText
1352+ <|> word
11261353 <|> inlineCommand'
11271354 <|> inlineEnvironment
11281355 <|> inlineGroup
@@ -1139,7 +1366,8 @@ inline = (mempty <$ comment)
11391366 <|> dollarsMath
11401367 <|> (guardEnabled Ext_literate_haskell *> symbol '|' *> doLHSverb)
11411368 <|> (str . (:[]) <$> primEscape)
1142− <|> (do res <- symbolIn "#&~^'`\"[]"
1369+ <|> regularSymbol
1370+ <|> (do res <- symbolIn "#^'`\"[]"
11431371 pos <- getPosition
11441372 let s = T.unpack (untoken res)
11451373 report $ ParsingUnescaped s pos
@@ -1150,6 +1378,37 @@ inlines = mconcat <$> many inline
11501378
11511379-- block elements:
11521380
1381+begin_ :: PandocMonad m => Text -> LP m ()
1382+begin_ t = (try $ do
1383+ controlSeq "begin"
1384+ spaces
1385+ symbol '{'
1386+ spaces
1387+ Tok _ Word txt <- satisfyTok isWordTok
1388+ spaces
1389+ symbol '}'
1390+ guard (t == txt)) <?> ("\\begin{" ++ T.unpack t ++ "}")
1391+
1392+end_ :: PandocMonad m => Text -> LP m ()
1393+end_ t = (try $ do
1394+ controlSeq "end"
1395+ spaces
1396+ symbol '{'
1397+ spaces
1398+ Tok _ Word txt <- satisfyTok isWordTok
1399+ spaces
1400+ symbol '}'
1401+ guard $ t == txt) <?> ("\\end{" ++ T.unpack t ++ "}")
1402+
1403+preamble :: PandocMonad m => LP m Blocks
1404+preamble = mempty <$ many preambleBlock
1405+ where preambleBlock = spaces1
1406+ <|> void include
1407+ <|> void macroDef
1408+ <|> void blockCommand
1409+ <|> void braced
1410+ <|> (notFollowedBy (begin_ "document") >> void anyTok)
1411+
11531412paragraph :: PandocMonad m => LP m Blocks
11541413paragraph = do
11551414 x <- trimInlines . mconcat <$> many1 inline
@@ -1159,11 +1418,12 @@ paragraph = do
11591418
11601419include :: PandocMonad m => LP m Blocks
11611420include = do
1162− (Tok _ _ name) <- controlSeq "include" <|> controlSeq "input" <|>
1421+ (Tok _ (CtrlSeq name) _) <-
1422+ controlSeq "include" <|> controlSeq "input" <|>
11631423 controlSeq "subfile" <|> controlSeq "usepackage"
11641424 skipMany $ bracketed inline -- skip options
11651425 fs <- (map trim . splitBy (==',') . T.unpack . untokenize) <$> braced
1166− let fs' = if name == "\\usepackage"
1426+ let fs' = if name == "usepackage"
11671427 then map (maybeAddExtension ".sty") fs
11681428 else map (maybeAddExtension ".tex") fs
11691429 dirs <- (splitBy (==':') . fromMaybe ".") <$> lookupEnv "TEXINPUTS"
@@ -1179,7 +1439,7 @@ ignoreBlocks :: PandocMonad m => Text -> (Text, LP m Blocks)
11791439ignoreBlocks name = (name, p)
11801440 where
11811441 p = do oa <- optargs
1182− let rawCommand = '\\' : T.unpack (name <> oa)
1442+ let rawCommand = T.unpack ("\\" <> name <> oa)
11831443 let doraw = guardRaw >> return (rawBlock "latex" rawCommand)
11841444 doraw <|> ignore rawCommand
11851445
@@ -1198,6 +1458,109 @@ authors = try $ do
11981458 egroup
11991459 addMeta "author" (map trimInlines auths)
12001460
1461+macroDef :: PandocMonad m => LP m Blocks
1462+macroDef = mempty <$ ((commandDef <|> environmentDef) <* doMacros 0)
1463+ where commandDef = do
1464+ (name, macro) <- newcommand
1465+ updateState $ \s -> s{ sMacros = M.insert name macro (sMacros s) }
1466+ environmentDef = do
1467+ (name, macro1, macro2) <- newenvironment
1468+ updateState $ \s -> s{ sMacros =
1469+ M.insert name macro1 (sMacros s) }
1470+ updateState $ \s -> s{ sMacros =
1471+ M.insert ("end" <> name) macro2 (sMacros s) }
1472+ -- @\newenvironment{envname}[n-args][default]{begin}{end}@
1473+ -- is equivalent to
1474+ -- @\newcommand{\envname}[n-args][default]{begin}@
1475+ -- @\newcommand{\endenvname}@
1476+
1477+newcommand :: PandocMonad m => LP m (Text, Macro)
1478+newcommand = do
1479+ pos <- getPosition
1480+ Tok _ (CtrlSeq mtype) _ <- controlSeq "newcommand" <|>
1481+ controlSeq "renewcommand" <|>
1482+ controlSeq "providecommand"
1483+ optional $ symbol '*'
1484+ symbol '{'
1485+ spaces
1486+ Tok _ (CtrlSeq name) txt <- withVerbatimMode anyControlSeq
1487+ spaces
1488+ symbol '}'
1489+ spaces
1490+ numargs <- option 0 $ try bracketedNum
1491+ spaces
1492+ optarg <- option Nothing $ Just <$> try bracketedToks
1493+ spaces
1494+ contents <- braced
1495+ when (mtype == "newcommand") $ do
1496+ macros <- sMacros <$> getState
1497+ case M.lookup name macros of
1498+ Just _ -> report $ MacroAlreadyDefined (T.unpack txt) pos
1499+ Nothing -> return ()
1500+ return (name, Macro numargs optarg contents)
1501+
1502+newenvironment :: PandocMonad m => LP m (Text, Macro, Macro)
1503+newenvironment = do
1504+ pos <- getPosition
1505+ Tok _ (CtrlSeq mtype) _ <- controlSeq "newenvironment" <|>
1506+ controlSeq "renewenvironment" <|>
1507+ controlSeq "provideenvironment"
1508+ optional $ symbol '*'
1509+ symbol '{'
1510+ spaces
1511+ Tok _ Word name <- satisfyTok isWordTok
1512+ spaces
1513+ symbol '}'
1514+ spaces
1515+ numargs <- option 0 $ try bracketedNum
1516+ spaces
1517+ optarg <- option Nothing $ Just <$> try bracketedToks
1518+ spaces
1519+ startcontents <- braced
1520+ spaces
1521+ endcontents <- braced
1522+ when (mtype == "newenvironment") $ do
1523+ macros <- sMacros <$> getState
1524+ case M.lookup name macros of
1525+ Just _ -> report $ MacroAlreadyDefined (T.unpack name) pos
1526+ Nothing -> return ()
1527+ return (name, Macro numargs optarg startcontents,
1528+ Macro 0 Nothing endcontents)
1529+
1530+bracketedToks :: PandocMonad m => LP m [Tok]
1531+bracketedToks = do
1532+ symbol '['
1533+ manyTill anyTok (symbol ']')
1534+
1535+bracketedNum :: PandocMonad m => LP m Int
1536+bracketedNum = do
1537+ ds <- untokenize <$> bracketedToks
1538+ case safeRead (T.unpack ds) of
1539+ Just i -> return i
1540+ _ -> return 0
1541+
1542+setCaption :: PandocMonad m => LP m Blocks
1543+setCaption = do
1544+ ils <- tok
1545+ mblabel <- option Nothing $
1546+ try $ spaces >> controlSeq "label" >> (Just <$> tok)
1547+ let ils' = case mblabel of
1548+ Just lab -> ils <> spanWith
1549+ ("",[],[("data-label", stringify lab)]) mempty
1550+ Nothing -> ils
1551+ updateState $ \st -> st{ sCaption = Just ils' }
1552+ return mempty
1553+
1554+looseItem :: PandocMonad m => LP m Blocks
1555+looseItem = do
1556+ inListItem <- sInListItem <$> getState
1557+ guard $ not inListItem
1558+ skipopts
1559+ return mempty
1560+
1561+resetCaption :: PandocMonad m => LP m ()
1562+resetCaption = updateState $ \st -> st{ sCaption = Nothing }
1563+
12011564section :: PandocMonad m => Attr -> Int -> LP m Blocks
12021565section (ident, classes, kvs) lvl = do
12031566 skipopts
@@ -1210,18 +1573,14 @@ section (ident, classes, kvs) lvl = do
12101573
12111574blockCommand :: PandocMonad m => LP m Blocks
12121575blockCommand = try $ do
1213− Tok _ CtrlSeq name <- anyControlSeq
1214− guard $ name /= "\\begin" && name /= "\\end"
1576+ Tok _ (CtrlSeq name) txt <- anyControlSeq
1577+ guard $ name /= "begin" && name /= "end"
12151578 star <- option "" ("*" <$ symbol '*' <* optional sp)
12161579 let name' = name <> star
1217− let names = name : [name' | name' /= name]
1218− let raw = rawBlock "latex" <$> getRawCommand name'
1219− -- TODO do we still need this?
1220− -- rawcommand <- getRawCommand name'
1221− -- transformed <- applyMacros' rawcommand
1222− -- guard $ transformed /= rawcommand
1223− -- notFollowedBy $ parseFromString' inlines transformed
1224− -- parseFromString' blocks transformed
1580+ let names = ordNub [name', name]
1581+ let raw = do
1582+ guard $ not (isInlineCommand name)
1583+ rawBlock "latex" <$> getRawCommand txt
12251584 lookupListDefault raw names blockCommands
12261585
12271586closing :: PandocMonad m => LP m Blocks
@@ -1243,8 +1602,8 @@ blockCommands = M.fromList $
12431602 [ ("par", mempty <$ skipopts)
12441603 , ("parbox", braced >> grouped blocks)
12451604 , ("title", mempty <$ (skipopts *>
1246− (grouped inline >>= addMeta "title")
1247− <|> (grouped block >>= addMeta "title")))
1605+ (grouped inline >>= addMeta "title")
1606+ <|> (grouped block >>= addMeta "title")))
12481607 , ("subtitle", mempty <$ (skipopts *> tok >>= addMeta "subtitle"))
12491608 , ("author", mempty <$ (skipopts *> authors))
12501609 -- -- in letter class, temp. store address & sig as title, author
@@ -1278,24 +1637,26 @@ blockCommands = M.fromList $
12781637 , ("hrule", pure horizontalRule)
12791638 , ("strut", pure mempty)
12801639 , ("rule", skipopts *> tok *> tok *> pure horizontalRule)
1281−-- , ("item", skipopts *> looseItem)
1282−-- , ("documentclass", skipopts *> braced *> preamble)
1640+ , ("item", looseItem)
1641+ , ("documentclass", skipopts *> braced *> preamble)
12831642 , ("centerline", (para . trimInlines) <$> (skipopts *> tok))
1284−-- , ("caption", skipopts *> setCaption)
1285−-- , ("bibliography", mempty <$ (skipopts *> braced >>=
1286−-- addMeta "bibliography" . splitBibs))
1287−-- , ("addbibresource", mempty <$ (skipopts *> braced >>=
1288−-- addMeta "bibliography" . splitBibs))
1289−-- -- includes
1290−-- , ("lstinputlisting", inputListing)
1291−-- , ("graphicspath", graphicsPath)
1292−-- -- hyperlink
1293− , ("hypertarget", braced >> grouped block)
1643+ , ("caption", skipopts *> setCaption)
1644+ , ("bibliography", mempty <$ (skipopts *> braced >>=
1645+ addMeta "bibliography" . splitBibs . toksToString))
1646+ , ("addbibresource", mempty <$ (skipopts *> braced >>=
1647+ addMeta "bibliography" . splitBibs . toksToString))
1648+ -- includes
1649+ , ("lstinputlisting", inputListing)
1650+ , ("graphicspath", graphicsPath)
1651+ -- hyperlink
1652+ , ("hypertarget", try $ braced >> grouped block)
12941653 ] ++ map ignoreBlocks
12951654 -- these commands will be ignored unless --parse-raw is specified,
12961655 -- in which case they will appear as raw latex blocks
1297− [ "newcommand", "renewcommand", "newenvironment", "renewenvironment"
1298− -- newcommand, etc. should be parsed by macro, but we need this
1656+ [ "newcommand", "renewcommand"
1657+ , "newenvironment", "renewenvironment"
1658+ , "providecommand", "provideenvironment"
1659+ -- newcommand, etc. should be parsed by macroDef, but we need this
12991660 -- here so these aren't parsed as inline commands to ignore
13001661 , "special", "pdfannot", "pdfstringdef"
13011662 , "bibliographystyle"
@@ -1315,63 +1676,52 @@ blockCommands = M.fromList $
13151676environments :: PandocMonad m => M.Map Text (LP m Blocks)
13161677environments = M.fromList
13171678 [ ("document", env "document" blocks)
1318−-- , ("abstract", mempty <$ (env "abstract" blocks >>= addMeta "abstract"))
1319−-- , ("letter", env "letter" letterContents)
1320−-- , ("minipage", env "minipage" $
1321−-- skipopts *> spaces' *> optional braced *> spaces' *> blocks)
1322−-- , ("figure", env "figure" $ skipopts *> figure)
1323−-- , ("subfigure", env "subfigure" $ skipopts *> tok *> figure)
1324−-- , ("center", env "center" blocks)
1325−-- , ("longtable", env "longtable" $
1326−-- resetCaption *> simpTable "longtable" False >>= addTableCaption)
1327−-- , ("table", env "table" $
1328−-- resetCaption *> skipopts *> blocks >>= addTableCaption)
1329−-- , ("tabular*", env "tabular" $ simpTable "tabular*" True)
1330−-- , ("tabularx", env "tabularx" $ simpTable "tabularx" True)
1331−-- , ("tabular", env "tabular" $ simpTable "tabular" False)
1332−-- , ("quote", blockQuote <$> env "quote" blocks)
1333−-- , ("quotation", blockQuote <$> env "quotation" blocks)
1334−-- , ("verse", blockQuote <$> env "verse" blocks)
1335−-- , ("itemize", bulletList <$> listenv "itemize" (many item))
1336−-- , ("description", definitionList <$> listenv "description" (many descItem))
1337−-- , ("enumerate", orderedList')
1338−-- , ("alltt", alltt =<< verbEnv "alltt")
1339−-- , ("code", guardEnabled Ext_literate_haskell *>
1340−-- (codeBlockWith ("",["sourceCode","literate","haskell"],[]) <$>
1341−-- verbEnv "code"))
1342−-- , ("comment", mempty <$ verbEnv "comment")
1343−-- , ("verbatim", codeBlock <$> verbEnv "verbatim")
1344−-- , ("Verbatim", fancyverbEnv "Verbatim")
1345−-- , ("BVerbatim", fancyverbEnv "BVerbatim")
1346−-- , ("lstlisting", do attr <- parseListingsOptions <$> option [] keyvals
1347−-- codeBlockWith attr <$> verbEnv "lstlisting")
1348−-- , ("minted", do options <- option [] keyvals
1349−-- lang <- grouped (many1 $ satisfy (/='}'))
1350−-- let kvs = [ (if k == "firstnumber"
1351−-- then "startFrom"
1352−-- else k, v) | (k,v) <- options ]
1353−-- let classes = [ lang | not (null lang) ] ++
1354−-- [ "numberLines" |
1355−-- lookup "linenos" options == Just "true" ]
1356−-- let attr = ("",classes,kvs)
1357−-- codeBlockWith attr <$> verbEnv "minted")
1358−-- , ("obeylines", parseFromString
1359−-- (para . trimInlines . mconcat <$> many inline) =<<
1360−-- intercalate "\\\\\n" . lines <$> verbEnv "obeylines")
1361−-- , ("displaymath", mathEnvWith para Nothing "displaymath")
1362−-- , ("equation", mathEnvWith para Nothing "equation")
1363−-- , ("equation*", mathEnvWith para Nothing "equation*")
1364−-- , ("gather", mathEnvWith para (Just "gathered") "gather")
1365−-- , ("gather*", mathEnvWith para (Just "gathered") "gather*")
1366−-- , ("multline", mathEnvWith para (Just "gathered") "multline")
1367−-- , ("multline*", mathEnvWith para (Just "gathered") "multline*")
1368−-- , ("eqnarray", mathEnvWith para (Just "aligned") "eqnarray")
1369−-- , ("eqnarray*", mathEnvWith para (Just "aligned") "eqnarray*")
1370−-- , ("align", mathEnvWith para (Just "aligned") "align")
1371−-- , ("align*", mathEnvWith para (Just "aligned") "align*")
1372−-- , ("alignat", mathEnvWith para (Just "aligned") "alignat")
1373−-- , ("alignat*", mathEnvWith para (Just "aligned") "alignat*")
1374−-- , ("tikzpicture", rawVerbEnv "tikzpicture")
1679+ , ("abstract", mempty <$ (env "abstract" blocks >>= addMeta "abstract"))
1680+ , ("letter", env "letter" letterContents)
1681+ , ("minipage", env "minipage" $
1682+ skipopts *> spaces *> optional braced *> spaces *> blocks)
1683+ , ("figure", env "figure" $ skipopts *> figure)
1684+ , ("subfigure", env "subfigure" $ skipopts *> tok *> figure)
1685+ , ("center", env "center" blocks)
1686+ , ("longtable", env "longtable" $
1687+ resetCaption *> simpTable "longtable" False >>= addTableCaption)
1688+ , ("table", env "table" $
1689+ resetCaption *> skipopts *> blocks >>= addTableCaption)
1690+ , ("tabular*", env "tabular" $ simpTable "tabular*" True)
1691+ , ("tabularx", env "tabularx" $ simpTable "tabularx" True)
1692+ , ("tabular", env "tabular" $ simpTable "tabular" False)
1693+ , ("quote", blockQuote <$> env "quote" blocks)
1694+ , ("quotation", blockQuote <$> env "quotation" blocks)
1695+ , ("verse", blockQuote <$> env "verse" blocks)
1696+ , ("itemize", bulletList <$> listenv "itemize" (many item))
1697+ , ("description", definitionList <$> listenv "description" (many descItem))
1698+ , ("enumerate", orderedList')
1699+ , ("alltt", alltt <$> env "alltt" blocks)
1700+ , ("code", guardEnabled Ext_literate_haskell *>
1701+ (codeBlockWith ("",["sourceCode","literate","haskell"],[]) <$>
1702+ verbEnv "code"))
1703+ , ("comment", mempty <$ verbEnv "comment")
1704+ , ("verbatim", codeBlock <$> verbEnv "verbatim")
1705+ , ("Verbatim", fancyverbEnv "Verbatim")
1706+ , ("BVerbatim", fancyverbEnv "BVerbatim")
1707+ , ("lstlisting", do attr <- parseListingsOptions <$> option [] keyvals
1708+ codeBlockWith attr <$> verbEnv "lstlisting")
1709+ , ("minted", minted)
1710+ , ("obeylines", obeylines)
1711+ , ("displaymath", mathEnvWith para Nothing "displaymath")
1712+ , ("equation", mathEnvWith para Nothing "equation")
1713+ , ("equation*", mathEnvWith para Nothing "equation*")
1714+ , ("gather", mathEnvWith para (Just "gathered") "gather")
1715+ , ("gather*", mathEnvWith para (Just "gathered") "gather*")
1716+ , ("multline", mathEnvWith para (Just "gathered") "multline")
1717+ , ("multline*", mathEnvWith para (Just "gathered") "multline*")
1718+ , ("eqnarray", mathEnvWith para (Just "aligned") "eqnarray")
1719+ , ("eqnarray*", mathEnvWith para (Just "aligned") "eqnarray*")
1720+ , ("align", mathEnvWith para (Just "aligned") "align")
1721+ , ("align*", mathEnvWith para (Just "aligned") "align*")
1722+ , ("alignat", mathEnvWith para (Just "aligned") "alignat")
1723+ , ("alignat*", mathEnvWith para (Just "aligned") "alignat*")
1724+ , ("tikzpicture", rawVerbEnv "tikzpicture")
13751725 ]
13761726
13771727environment :: PandocMonad m => LP m Blocks
@@ -1382,9 +1732,7 @@ environment = do
13821732 <|> rawEnv name
13831733
13841734env :: PandocMonad m => Text -> LP m a -> LP m a
1385−env name p = p <*
1386− (try (controlSeq "end" *> braced >>= guard . (== name) . untokenize)
1387− <?> ("\\end{" ++ T.unpack name ++ "}"))
1735+env name p = p <* end_ name
13881736
13891737rawEnv :: PandocMonad m => Text -> LP m Blocks
13901738rawEnv name = do
@@ -1404,525 +1752,355 @@ rawEnv name = do
14041752 report $ SkippedContent ("\\end{" ++ T.unpack name ++ "}") pos2
14051753 return bs
14061754
1407− -- raw' <- applyMacros' $ beginCommand <> untokenize raw
1408− -- if raw' /= beginCommand ++ raw
1409− -- then parseFromString' blocks raw'
1410− -- else if parseRaw
1411− -- then return $ rawBlock "latex" $ beginCommand ++ raw'
1412− -- else do
1413− -- unless parseRaw $ do
1414− -- report $ SkippedContent beginCommand pos1
1415− -- pos2 <- getPosition
1416− -- report $ SkippedContent ("\\end{" ++ name ++ "}") pos2
1417− -- return bs
1755+rawVerbEnv :: PandocMonad m => Text -> LP m Blocks
1756+rawVerbEnv name = do
1757+ pos <- getPosition
1758+ (_, raw) <- withRaw $ verbEnv name
1759+ let raw' = "\\begin{tikzpicture}" ++ toksToString raw
1760+ exts <- getOption readerExtensions
1761+ let parseRaw = extensionEnabled Ext_raw_tex exts
1762+ if parseRaw
1763+ then return $ rawBlock "latex" raw'
1764+ else do
1765+ report $ SkippedContent raw' pos
1766+ return mempty
1767+
1768+verbEnv :: PandocMonad m => Text -> LP m String
1769+verbEnv name = withVerbatimMode $ do
1770+ skipopts
1771+ optional blankline
1772+ res <- manyTill anyTok (end_ name)
1773+ return $ stripTrailingNewlines $ toksToString res
1774+
1775+fancyverbEnv :: PandocMonad m => Text -> LP m Blocks
1776+fancyverbEnv name = do
1777+ options <- option [] keyvals
1778+ let kvs = [ (if k == "firstnumber"
1779+ then "startFrom"
1780+ else k, v) | (k,v) <- options ]
1781+ let classes = [ "numberLines" |
1782+ lookup "numbers" options == Just "left" ]
1783+ let attr = ("",classes,kvs)
1784+ codeBlockWith attr <$> verbEnv name
1785+
1786+obeylines :: PandocMonad m => LP m Blocks
1787+obeylines = do
1788+ para . fromList . removeLeadingTrailingBreaks .
1789+ walk softBreakToHard . toList <$> env "obeylines" inlines
1790+ where softBreakToHard SoftBreak = LineBreak
1791+ softBreakToHard x = x
1792+ removeLeadingTrailingBreaks = reverse . dropWhile isLineBreak .
1793+ reverse . dropWhile isLineBreak
1794+ isLineBreak LineBreak = True
1795+ isLineBreak _ = False
1796+
1797+minted :: PandocMonad m => LP m Blocks
1798+minted = do
1799+ options <- option [] keyvals
1800+ lang <- toksToString <$> braced
1801+ let kvs = [ (if k == "firstnumber"
1802+ then "startFrom"
1803+ else k, v) | (k,v) <- options ]
1804+ let classes = [ lang | not (null lang) ] ++
1805+ [ "numberLines" |
1806+ lookup "linenos" options == Just "true" ]
1807+ let attr = ("",classes,kvs)
1808+ codeBlockWith attr <$> verbEnv "minted"
1809+
1810+letterContents :: PandocMonad m => LP m Blocks
1811+letterContents = do
1812+ bs <- blocks
1813+ st <- getState
1814+ -- add signature (author) and address (title)
1815+ let addr = case lookupMeta "address" (sMeta st) of
1816+ Just (MetaBlocks [Plain xs]) ->
1817+ para $ trimInlines $ fromList xs
1818+ _ -> mempty
1819+ return $ addr <> bs -- sig added by \closing
1820+
1821+figure :: PandocMonad m => LP m Blocks
1822+figure = try $ do
1823+ resetCaption
1824+ blocks >>= addImageCaption
1825+
1826+addImageCaption :: PandocMonad m => Blocks -> LP m Blocks
1827+addImageCaption = walkM go
1828+ where go (Image attr alt (src,tit))
1829+ | not ("fig:" `isPrefixOf` tit) = do
1830+ mbcapt <- sCaption <$> getState
1831+ return $ case mbcapt of
1832+ Just ils -> Image attr (toList ils) (src, "fig:" ++ tit)
1833+ Nothing -> Image attr alt (src,tit)
1834+ go x = return x
1835+
1836+graphicsPath :: PandocMonad m => LP m Blocks
1837+graphicsPath = do
1838+ ps <- map toksToString <$> (bgroup *> manyTill braced egroup)
1839+ getResourcePath >>= setResourcePath . (++ ps)
1840+ return mempty
1841+
1842+splitBibs :: String -> [Inlines]
1843+splitBibs = map (str . flip replaceExtension "bib" . trim) . splitBy (==',')
1844+
1845+alltt :: Blocks -> Blocks
1846+alltt = walk strToCode
1847+ where strToCode (Str s) = Code nullAttr s
1848+ strToCode Space = RawInline (Format "latex") "\\ "
1849+ strToCode SoftBreak = LineBreak
1850+ strToCode x = x
1851+
1852+parseListingsOptions :: [(String, String)] -> Attr
1853+parseListingsOptions options =
1854+ let kvs = [ (if k == "firstnumber"
1855+ then "startFrom"
1856+ else k, v) | (k,v) <- options ]
1857+ classes = [ "numberLines" |
1858+ lookup "numbers" options == Just "left" ]
1859+ ++ maybeToList (lookup "language" options
1860+ >>= fromListingsLanguage)
1861+ in (fromMaybe "" (lookup "label" options), classes, kvs)
1862+
1863+inputListing :: PandocMonad m => LP m Blocks
1864+inputListing = do
1865+ pos <- getPosition
1866+ options <- option [] keyvals
1867+ f <- filter (/='"') . toksToString <$> braced
1868+ dirs <- (splitBy (==':') . fromMaybe ".") <$> lookupEnv "TEXINPUTS"
1869+ mbCode <- readFileFromDirs dirs f
1870+ codeLines <- case mbCode of
1871+ Just s -> return $ lines s
1872+ Nothing -> do
1873+ report $ CouldNotLoadIncludeFile f pos
1874+ return []
1875+ let (ident,classes,kvs) = parseListingsOptions options
1876+ let language = case lookup "language" options >>= fromListingsLanguage of
1877+ Just l -> [l]
1878+ Nothing -> take 1 $ languagesByExtension (takeExtension f)
1879+ let firstline = fromMaybe 1 $ lookup "firstline" options >>= safeRead
1880+ let lastline = fromMaybe (length codeLines) $
1881+ lookup "lastline" options >>= safeRead
1882+ let codeContents = intercalate "\n" $ take (1 + lastline - firstline) $
1883+ drop (firstline - 1) codeLines
1884+ return $ codeBlockWith (ident,ordNub (classes ++ language),kvs) codeContents
1885+
1886+-- lists
1887+
1888+item :: PandocMonad m => LP m Blocks
1889+item = void blocks *> controlSeq "item" *> skipopts *> blocks
1890+
1891+descItem :: PandocMonad m => LP m (Inlines, [Blocks])
1892+descItem = do
1893+ blocks -- skip blocks before item
1894+ controlSeq "item"
1895+ optional sp
1896+ ils <- opt
1897+ bs <- blocks
1898+ return (ils, [bs])
1899+
1900+listenv :: PandocMonad m => Text -> LP m a -> LP m a
1901+listenv name p = try $ do
1902+ oldInListItem <- sInListItem `fmap` getState
1903+ updateState $ \st -> st{ sInListItem = True }
1904+ res <- env name p
1905+ updateState $ \st -> st{ sInListItem = oldInListItem }
1906+ return res
1907+
1908+orderedList' :: PandocMonad m => LP m Blocks
1909+orderedList' = try $ do
1910+ spaces
1911+ let markerSpec = do
1912+ symbol '['
1913+ ts <- toksToString <$> manyTill anyTok (symbol ']')
1914+ case runParser anyOrderedListMarker def "option" ts of
1915+ Right r -> return r
1916+ Left _ -> do
1917+ pos <- getPosition
1918+ report $ SkippedContent ("[" ++ ts ++ "]") pos
1919+ return (1, DefaultStyle, DefaultDelim)
1920+ (_, style, delim) <- option (1, DefaultStyle, DefaultDelim) markerSpec
1921+ spaces
1922+ optional $ try $ controlSeq "setlength"
1923+ *> grouped (count 1 $ controlSeq "itemindent")
1924+ *> braced
1925+ spaces
1926+ start <- option 1 $ try $ do pos <- getPosition
1927+ controlSeq "setcounter"
1928+ ctr <- toksToString <$> braced
1929+ guard $ "enum" `isPrefixOf` ctr
1930+ guard $ all (`elem` ['i','v']) (drop 4 ctr)
1931+ optional sp
1932+ num <- toksToString <$> braced
1933+ case safeRead num of
1934+ Just i -> return (i + 1 :: Int)
1935+ Nothing -> do
1936+ report $ SkippedContent
1937+ ("\\setcounter{" ++ ctr ++
1938+ "}{" ++ num ++ "}") pos
1939+ return 1
1940+ bs <- listenv "enumerate" (many item)
1941+ return $ orderedListWith (start, style, delim) bs
1942+
1943+-- tables
1944+
1945+hline :: PandocMonad m => LP m ()
1946+hline = try $ do
1947+ spaces
1948+ controlSeq "hline" <|>
1949+ -- booktabs rules:
1950+ controlSeq "toprule" <|>
1951+ controlSeq "bottomrule" <|>
1952+ controlSeq "midrule" <|>
1953+ controlSeq "endhead" <|>
1954+ controlSeq "endfirsthead"
1955+ spaces
1956+ optional $ bracketed inline
1957+ return ()
1958+
1959+lbreak :: PandocMonad m => LP m Tok
1960+lbreak = (controlSeq "\\" <|> controlSeq "tabularnewline") <* spaces
1961+
1962+amp :: PandocMonad m => LP m Tok
1963+amp = symbol '&'
1964+
1965+-- Split a Word into individual Symbols (for parseAligns)
1966+splitWordTok :: PandocMonad m => LP m ()
1967+splitWordTok = do
1968+ inp <- getInput
1969+ case inp of
1970+ (Tok spos Word t : rest) -> do
1971+ setInput $ map (Tok spos Symbol . T.singleton) (T.unpack t) ++ rest
1972+ _ -> return ()
1973+
1974+parseAligns :: PandocMonad m => LP m [(Alignment, Double, ([Tok], [Tok]))]
1975+parseAligns = try $ do
1976+ let maybeBar = skipMany $
1977+ sp <|> () <$ symbol '|' <|> () <$ (symbol '@' >> braced)
1978+ let cAlign = AlignCenter <$ symbol 'c'
1979+ let lAlign = AlignLeft <$ symbol 'l'
1980+ let rAlign = AlignRight <$ symbol 'r'
1981+ let parAlign = AlignLeft <$ symbol 'p'
1982+ -- aligns from tabularx
1983+ let xAlign = AlignLeft <$ symbol 'X'
1984+ let mAlign = AlignLeft <$ symbol 'm'
1985+ let bAlign = AlignLeft <$ symbol 'b'
1986+ let alignChar = splitWordTok *> ( cAlign <|> lAlign <|> rAlign <|> parAlign
1987+ <|> xAlign <|> mAlign <|> bAlign )
1988+ let alignPrefix = symbol '>' >> braced
1989+ let alignSuffix = symbol '<' >> braced
1990+ let colWidth = try $ do
1991+ symbol '{'
1992+ ds <- trim . toksToString <$> manyTill anyTok (controlSeq "linewidth")
1993+ spaces
1994+ symbol '}'
1995+ case safeRead ds of
1996+ Just w -> return w
1997+ Nothing -> return 0.0
1998+ let alignSpec = try $ do
1999+ spaces
2000+ pref <- option [] alignPrefix
2001+ spaces
2002+ al <- alignChar
2003+ width <- colWidth <|> option 0.0 (do s <- toksToString <$> braced
2004+ pos <- getPosition
2005+ report $ SkippedContent s pos
2006+ return 0.0)
2007+ spaces
2008+ suff <- option [] alignSuffix
2009+ return (al, width, (pref, suff))
2010+ bgroup
2011+ spaces
2012+ maybeBar
2013+ aligns' <- many (alignSpec <* maybeBar)
2014+ spaces
2015+ egroup
2016+ spaces
2017+ return aligns'
2018+
2019+parseTableRow :: PandocMonad m
2020+ => Text -- ^ table environment name
2021+ -> [([Tok], [Tok])] -- ^ pref/suffixes
2022+ -> LP m [Blocks]
2023+parseTableRow envname prefsufs = do
2024+ notFollowedBy (spaces *> end_ envname)
2025+ let cols = length prefsufs
2026+ -- add prefixes and suffixes in token stream:
2027+ let celltoks (pref, suff) = do
2028+ prefpos <- getPosition
2029+ contents <- many (notFollowedBy
2030+ (() <$ amp <|> () <$ lbreak <|> end_ envname)
2031+ >> anyTok)
2032+ suffpos <- getPosition
2033+ option [] (count 1 amp)
2034+ return $ map (setpos (sourceLine prefpos, sourceColumn prefpos)) pref
2035+ ++ contents ++
2036+ map (setpos (sourceLine suffpos, sourceColumn suffpos)) suff
2037+ rawcells <- sequence (map celltoks prefsufs)
2038+ oldInput <- getInput
2039+ cells <- sequence $ map (\ts -> setInput ts >> parseTableCell) rawcells
2040+ setInput oldInput
2041+ spaces
2042+ let numcells = length cells
2043+ guard $ numcells <= cols && numcells >= 1
2044+ guard $ cells /= [mempty]
2045+ -- note: a & b in a three-column table leaves an empty 3rd cell:
2046+ return $ cells ++ replicate (cols - numcells) mempty
2047+
2048+parseTableCell :: PandocMonad m => LP m Blocks
2049+parseTableCell = do
2050+ let plainify bs = case toList bs of
2051+ [Para ils] -> plain (fromList ils)
2052+ _ -> bs
2053+ updateState $ \st -> st{ sInTableCell = True }
2054+ cells <- plainify <$> blocks
2055+ updateState $ \st -> st{ sInTableCell = False }
2056+ return cells
2057+
2058+simpTable :: PandocMonad m => Text -> Bool -> LP m Blocks
2059+simpTable envname hasWidthParameter = try $ do
2060+ when hasWidthParameter $ () <$ (spaces >> tok)
2061+ skipopts
2062+ colspecs <- parseAligns
2063+ let (aligns, widths, prefsufs) = unzip3 colspecs
2064+ let cols = length colspecs
2065+ optional $ controlSeq "caption" *> skipopts *> setCaption
2066+ optional lbreak
2067+ spaces
2068+ skipMany hline
2069+ spaces
2070+ header' <- option [] $ try (parseTableRow envname prefsufs <*
2071+ lbreak <* many1 hline)
2072+ spaces
2073+ rows <- sepEndBy (parseTableRow envname prefsufs)
2074+ (lbreak <* optional (skipMany hline))
2075+ spaces
2076+ optional $ controlSeq "caption" *> skipopts *> setCaption
2077+ optional lbreak
2078+ spaces
2079+ let header'' = if null header'
2080+ then replicate cols mempty
2081+ else header'
2082+ lookAhead $ controlSeq "end" -- make sure we're at end
2083+ return $ table mempty (zip aligns widths) header'' rows
2084+
2085+addTableCaption :: PandocMonad m => Blocks -> LP m Blocks
2086+addTableCaption = walkM go
2087+ where go (Table c als ws hs rs) = do
2088+ mbcapt <- sCaption <$> getState
2089+ return $ case mbcapt of
2090+ Just ils -> Table (toList ils) als ws hs rs
2091+ Nothing -> Table c als ws hs rs
2092+ go x = return x
2093+
14182094
14192095block :: PandocMonad m => LP m Blocks
14202096block = (mempty <$ spaces1)
14212097 <|> environment
14222098 <|> include
1423− -- <|> macro
1424− <|> blockCommand
2099+ <|> macroDef
14252100 <|> paragraph
2101+ <|> blockCommand
14262102 <|> grouped block
14272103
14282104blocks :: PandocMonad m => LP m Blocks
14292105blocks = mconcat <$> many block
14302106
1431−{-
1432−-- graphicsPath :: PandocMonad m => LP m Blocks
1433−-- graphicsPath = do
1434−-- ps <- bgroup *> (manyTill braced egroup)
1435−-- getResourcePath >>= setResourcePath . (++ ps)
1436−-- return mempty
1437−--
1438−-- addMeta :: PandocMonad m => ToMetaValue a => String -> a -> LP m ()
1439−-- addMeta field val = updateState $ \st ->
1440−-- st{ sMeta = addMetaField field val $ sMeta st }
1441−--
1442−-- splitBibs :: String -> [Inlines]
1443−-- splitBibs = map (str . flip replaceExtension "bib" . trim) . splitBy (==',')
1444−--
1445−-- setCaption :: PandocMonad m => LP m Blocks
1446−-- setCaption = do
1447−-- ils <- tok
1448−-- mblabel <- option Nothing $
1449−-- try $ spaces' >> controlSeq "label" >> (Just <$> tok)
1450−-- let ils' = case mblabel of
1451−-- Just lab -> ils <> spanWith
1452−-- ("",[],[("data-label", stringify lab)]) mempty
1453−-- Nothing -> ils
1454−-- updateState $ \st -> st{ stateCaption = Just ils' }
1455−-- return mempty
1456−--
1457−-- resetCaption :: PandocMonad m => LP m ()
1458−-- resetCaption = updateState $ \st -> st{ stateCaption = Nothing }
1459−--
1460−-- mkImage :: PandocMonad m => [(String, String)] -> String -> LP m Inlines
1461−-- mkImage options src = do
1462−-- let replaceTextwidth (k,v) = case numUnit v of
1463−-- Just (num, "\\textwidth") -> (k, showFl (num * 100) ++ "%")
1464−-- _ -> (k, v)
1465−-- let kvs = map replaceTextwidth $ filter (\(k,_) -> k `elem` ["width", "height"]) options
1466−-- let attr = ("",[], kvs)
1467−-- let alt = str "image"
1468−-- case takeExtension src of
1469−-- "" -> do
1470−-- defaultExt <- getOption readerDefaultImageExtension
1471−-- return $ imageWith attr (addExtension src defaultExt) "" alt
1472−-- _ -> return $ imageWith attr src "" alt
1473−--
1474−-- inNote :: Inlines -> Inlines
1475−-- inNote ils =
1476−-- note $ para $ ils <> str "."
1477−--
1478−-
1479−-- -- converts e.g. \SI{1}[\$]{} to "$ 1" or \SI{1}{\euro} to "1 €"
1480−-- dosiunitx :: PandocMonad m => LP m Inlines
1481−-- dosiunitx = do
1482−-- skipopts
1483−-- value <- tok
1484−-- valueprefix <- option "" $ char '[' >> (mconcat <$> manyTill tok (char ']'))
1485−-- unit <- tok
1486−-- let emptyOr160 "" = ""
1487−-- emptyOr160 _ = "\160"
1488−-- return . mconcat $ [valueprefix,
1489−-- emptyOr160 valueprefix,
1490−-- value,
1491−-- emptyOr160 unit,
1492−-- unit]
1493−--
1494−-- skipangles :: PandocMonad m => LP m ()
1495−-- skipangles = skipMany rawangle
1496−--
1497−-- inlineText :: PandocMonad m => LP m Inlines
1498−-- inlineText = str <$> many1 inlineChar
1499−--
1500−-- inlineChar :: PandocMonad m => LP m Char
1501−-- inlineChar = noneOf "\\$%&~#{}^'`\"‘’“”-[] \t\n"
1502−--
1503−--
1504−-- inlineEnvironment :: PandocMonad m => LP m Inlines
1505−-- inlineEnvironment = try $ do
1506−-- controlSeq "begin"
1507−-- name <- braced
1508−-- M.findWithDefault mzero name inlineEnvironments
1509−--
1510−--
1511−-- rawVerbEnv :: PandocMonad m => String -> LP m Blocks
1512−-- rawVerbEnv name = do
1513−-- pos <- getPosition
1514−-- (_, raw) <- withRaw $ verbEnv name
1515−-- let raw' = "\\begin{tikzpicture}" ++ raw
1516−-- exts <- getOption readerExtensions
1517−-- let parseRaw = extensionEnabled Ext_raw_tex exts
1518−-- if parseRaw
1519−-- then return $ rawBlock "latex" raw'
1520−-- else do
1521−-- report $ SkippedContent raw' pos
1522−-- return mempty
1523−--
1524−-- ----
1525−--
1526−-- alltt :: PandocMonad m => String -> LP m Blocks
1527−-- alltt t = walk strToCode <$> parseFromString' blocks
1528−-- (substitute " " "\\ " $ substitute "%" "\\%" $
1529−-- intercalate "\\\\\n" $ lines t)
1530−-- where strToCode (Str s) = Code nullAttr s
1531−-- strToCode x = x
1532−--
1533−-- rawLaTeXBlock :: PandocMonad m => ParserT String LaTeXState String
1534−-- rawLaTeXBlock = snd <$> try (withRaw (environment <|> blockCommand))
1535−--
1536−-- rawLaTeXInline :: PandocMonad m => ParserT String LaTeXState Inline
1537−-- rawLaTeXInline = do
1538−-- raw <- (snd <$> withRaw inlineCommand)
1539−-- <|> (snd <$> withRaw inlineEnvironment)
1540−-- <|> (snd <$> withRaw blockCommand)
1541−-- RawInline "latex" <$> applyMacros' raw
1542−--
1543−-- addImageCaption :: PandocMonad m => Blocks -> LP m Blocks
1544−-- addImageCaption = walkM go
1545−-- where go (Image attr alt (src,tit))
1546−-- | not ("fig:" `isPrefixOf` tit) = do
1547−-- mbcapt <- stateCaption <$> getState
1548−-- return $ case mbcapt of
1549−-- Just ils -> Image attr (toList ils) (src, "fig:" ++ tit)
1550−-- Nothing -> Image attr alt (src,tit)
1551−-- go x = return x
1552−--
1553−-- addTableCaption :: PandocMonad m => Blocks -> LP m Blocks
1554−-- addTableCaption = walkM go
1555−-- where go (Table c als ws hs rs) = do
1556−-- mbcapt <- stateCaption <$> getState
1557−-- return $ case mbcapt of
1558−-- Just ils -> Table (toList ils) als ws hs rs
1559−-- Nothing -> Table c als ws hs rs
1560−-- go x = return x
1561−--
1562−--
1563−-- figure :: PandocMonad m => LP m Blocks
1564−-- figure = try $ do
1565−-- resetCaption
1566−-- blocks >>= addImageCaption
1567−--
1568−-- letterContents :: PandocMonad m => LP m Blocks
1569−-- letterContents = do
1570−-- bs <- blocks
1571−-- st <- getState
1572−-- -- add signature (author) and address (title)
1573−-- let addr = case lookupMeta "address" (sMeta st) of
1574−-- Just (MetaBlocks [Plain xs]) ->
1575−-- para $ trimInlines $ fromList xs
1576−-- _ -> mempty
1577−-- return $ addr <> bs -- sig added by \closing
1578−--
1579−--
1580−-- item :: PandocMonad m => LP m Blocks
1581−-- item = blocks *> controlSeq "item" *> skipopts *> blocks
1582−--
1583−-- looseItem :: PandocMonad m => LP m Blocks
1584−-- looseItem = do
1585−-- ctx <- stateParserContext `fmap` getState
1586−-- if ctx == ListItemState
1587−-- then mzero
1588−-- else return mempty
1589−--
1590−-- descItem :: PandocMonad m => LP m (Inlines, [Blocks])
1591−-- descItem = do
1592−-- blocks -- skip blocks before item
1593−-- controlSeq "item"
1594−-- optional sp
1595−-- ils <- opt
1596−-- bs <- blocks
1597−-- return (ils, [bs])
1598−--
1599−--
1600−-- listenv :: PandocMonad m => String -> LP m a -> LP m a
1601−-- listenv name p = try $ do
1602−-- oldCtx <- stateParserContext `fmap` getState
1603−-- updateState $ \st -> st{ stateParserContext = ListItemState }
1604−-- res <- env name p
1605−-- updateState $ \st -> st{ stateParserContext = oldCtx }
1606−-- return res
1607−--
1608−-- mathEnvWith :: PandocMonad m
1609−-- => (Inlines -> a) -> Maybe String -> String -> LP m a
1610−-- mathEnvWith f innerEnv name = f <$> mathDisplay (inner <$> mathEnv name)
1611−-- where inner x = case innerEnv of
1612−-- Nothing -> x
1613−-- Just y -> "\\begin{" ++ y ++ "}\n" ++ x ++
1614−-- "\\end{" ++ y ++ "}"
1615−--
1616−-- mathEnv :: PandocMonad m => String -> LP m String
1617−-- mathEnv name = do
1618−-- skipopts
1619−-- optional blankline
1620−-- let endEnv = try $ controlSeq "end" *> braced >>= guard . (== name)
1621−-- charMuncher = skipMany comment *>
1622−-- (many1 (noneOf "\\%") <|> try (string "\\%")
1623−-- <|> try (string "\\\\") <|> count 1 anyChar)
1624−-- res <- concat <$> manyTill charMuncher endEnv
1625−-- return $ stripTrailingNewlines res
1626−--
1627−-- verbEnv :: PandocMonad m => String -> LP m String
1628−-- verbEnv name = do
1629−-- skipopts
1630−-- optional blankline
1631−-- let endEnv = try $ controlSeq "end" *> braced >>= guard . (== name)
1632−-- charMuncher = anyChar
1633−-- res <- manyTill charMuncher endEnv
1634−-- return $ stripTrailingNewlines res
1635−--
1636−-- fancyverbEnv :: PandocMonad m => String -> LP m Blocks
1637−-- fancyverbEnv name = do
1638−-- options <- option [] keyvals
1639−-- let kvs = [ (if k == "firstnumber"
1640−-- then "startFrom"
1641−-- else k, v) | (k,v) <- options ]
1642−-- let classes = [ "numberLines" |
1643−-- lookup "numbers" options == Just "left" ]
1644−-- let attr = ("",classes,kvs)
1645−-- codeBlockWith attr <$> verbEnv name
1646−--
1647−-- orderedList' :: PandocMonad m => LP m Blocks
1648−-- orderedList' = try $ do
1649−-- optional sp
1650−-- (_, style, delim) <- option (1, DefaultStyle, DefaultDelim) $
1651−-- try $ char '[' *> anyOrderedListMarker <* char ']'
1652−-- spaces
1653−-- optional $ try $ controlSeq "setlength" *> grouped (controlSeq "itemindent") *> braced
1654−-- spaces
1655−-- start <- option 1 $ try $ do controlSeq "setcounter"
1656−-- grouped (string "enum" *> many1 (oneOf "iv"))
1657−-- optional sp
1658−-- num <- grouped (many1 digit)
1659−-- spaces
1660−-- return (read num + 1 :: Int)
1661−-- bs <- listenv "enumerate" (many item)
1662−-- return $ orderedListWith (start, style, delim) bs
1663−--
1664−-- paragraph :: PandocMonad m => LP m Blocks
1665−-- paragraph = do
1666−-- x <- trimInlines . mconcat <$> many1 inline
1667−-- if x == mempty
1668−-- then return mempty
1669−-- else return $ para x
1670−--
1671−-- preamble :: PandocMonad m => LP m Blocks
1672−-- preamble = mempty <$> manyTill preambleBlock beginDoc
1673−-- where beginDoc = lookAhead $ try $ controlSeq "begin" *> string "{document}"
1674−-- preambleBlock = void comment
1675−-- <|> void sp
1676−-- <|> void blanklines
1677−-- <|> void include
1678−-- <|> void macro
1679−-- <|> void blockCommand
1680−-- <|> void anyControlSeq
1681−-- <|> void braced
1682−-- <|> void anyChar
1683−--
1684−-- -------
1685−--
1686−-- -- citations
1687−--
1688−-- addPrefix :: [Inline] -> [Citation] -> [Citation]
1689−-- addPrefix p (k:ks) = k {citationPrefix = p ++ citationPrefix k} : ks
1690−-- addPrefix _ _ = []
1691−--
1692−-- addSuffix :: [Inline] -> [Citation] -> [Citation]
1693−-- addSuffix s ks@(_:_) =
1694−-- let k = last ks
1695−-- in init ks ++ [k {citationSuffix = citationSuffix k ++ s}]
1696−-- addSuffix _ _ = []
1697−--
1698−-- simpleCiteArgs :: PandocMonad m => LP m [Citation]
1699−-- simpleCiteArgs = try $ do
1700−-- first <- optionMaybe $ toList <$> opt
1701−-- second <- optionMaybe $ toList <$> opt
1702−-- keys <- try $ bgroup *> (manyTill citationLabel egroup)
1703−-- let (pre, suf) = case (first , second ) of
1704−-- (Just s , Nothing) -> (mempty, s )
1705−-- (Just s , Just t ) -> (s , t )
1706−-- _ -> (mempty, mempty)
1707−-- conv k = Citation { citationId = k
1708−-- , citationPrefix = []
1709−-- , citationSuffix = []
1710−-- , citationMode = NormalCitation
1711−-- , citationHash = 0
1712−-- , citationNoteNum = 0
1713−-- }
1714−-- return $ addPrefix pre $ addSuffix suf $ map conv keys
1715−--
1716−-- citationLabel :: PandocMonad m => LP m String
1717−-- citationLabel = optional sp *>
1718−-- (many1 (satisfy isBibtexKeyChar)
1719−-- <* optional sp
1720−-- <* optional (char ',')
1721−-- <* optional sp)
1722−-- where isBibtexKeyChar c = isAlphaNum c || c `elem` (".:;?!`'()/*@_+=-[]" :: String)
1723−--
1724−-- cites :: PandocMonad m => CitationMode -> Bool -> LP m [Citation]
1725−-- cites mode multi = try $ do
1726−-- cits <- if multi
1727−-- then many1 simpleCiteArgs
1728−-- else count 1 simpleCiteArgs
1729−-- let cs = concat cits
1730−-- return $ case mode of
1731−-- AuthorInText -> case cs of
1732−-- (c:rest) -> c {citationMode = mode} : rest
1733−-- [] -> []
1734−-- _ -> map (\a -> a {citationMode = mode}) cs
1735−--
1736−-- citation :: PandocMonad m => String -> CitationMode -> Bool -> LP m Inlines
1737−-- citation name mode multi = do
1738−-- (c,raw) <- withRaw $ cites mode multi
1739−-- return $ cite c (rawInline "latex" $ "\\" ++ name ++ raw)
1740−--
1741−-- complexNatbibCitation :: PandocMonad m => CitationMode -> LP m Inlines
1742−-- complexNatbibCitation mode = try $ do
1743−-- let ils = (toList . trimInlines . mconcat) <$>
1744−-- many (notFollowedBy (oneOf "\\};") >> inline)
1745−-- let parseOne = try $ do
1746−-- skipSpaces
1747−-- pref <- ils
1748−-- cit' <- inline -- expect a citation
1749−-- let citlist = toList cit'
1750−-- cits' <- case citlist of
1751−-- [Cite cs _] -> return cs
1752−-- _ -> mzero
1753−-- suff <- ils
1754−-- skipSpaces
1755−-- optional $ char ';'
1756−-- return $ addPrefix pref $ addSuffix suff cits'
1757−-- (c:cits, raw) <- withRaw $ grouped parseOne
1758−-- return $ cite (c{ citationMode = mode }:cits)
1759−-- (rawInline "latex" $ "\\citetext" ++ raw)
1760−--
1761−-- -- tables
1762−--
1763−-- parseAligns :: PandocMonad m => LP m [(Alignment, Double, (String, String))]
1764−-- parseAligns = try $ do
1765−-- bgroup
1766−-- let maybeBar = skipMany $ sp <|> () <$ char '|' <|> () <$ (char '@' >> braced)
1767−-- maybeBar
1768−-- let cAlign = AlignCenter <$ char 'c'
1769−-- let lAlign = AlignLeft <$ char 'l'
1770−-- let rAlign = AlignRight <$ char 'r'
1771−-- let parAlign = AlignLeft <$ char 'p'
1772−-- -- algins from tabularx
1773−-- let xAlign = AlignLeft <$ char 'X'
1774−-- let mAlign = AlignLeft <$ char 'm'
1775−-- let bAlign = AlignLeft <$ char 'b'
1776−-- let alignChar = cAlign <|> lAlign <|> rAlign <|> parAlign
1777−-- <|> xAlign <|> mAlign <|> bAlign
1778−-- let alignPrefix = char '>' >> braced
1779−-- let alignSuffix = char '<' >> braced
1780−-- let colWidth = try $ do
1781−-- char '{'
1782−-- ds <- many1 (oneOf "0123456789.")
1783−-- spaces
1784−-- string "\\linewidth"
1785−-- char '}'
1786−-- case safeRead ds of
1787−-- Just w -> return w
1788−-- Nothing -> return 0.0
1789−-- let alignSpec = do
1790−-- spaces
1791−-- pref <- option "" alignPrefix
1792−-- spaces
1793−-- al <- alignChar
1794−-- width <- colWidth <|> option 0.0 (do s <- braced
1795−-- pos <- getPosition
1796−-- report $ SkippedContent s pos
1797−-- return 0.0)
1798−-- spaces
1799−-- suff <- option "" alignSuffix
1800−-- return (al, width, (pref, suff))
1801−-- aligns' <- sepEndBy alignSpec maybeBar
1802−-- spaces
1803−-- egroup
1804−-- spaces
1805−-- return $ aligns'
1806−--
1807−-- hline :: PandocMonad m => LP m ()
1808−-- hline = try $ do
1809−-- spaces'
1810−-- controlSeq "hline" <|>
1811−-- -- booktabs rules:
1812−-- controlSeq "toprule" <|>
1813−-- controlSeq "bottomrule" <|>
1814−-- controlSeq "midrule" <|>
1815−-- controlSeq "endhead" <|>
1816−-- controlSeq "endfirsthead"
1817−-- spaces'
1818−-- optional $ bracketed (many1 (satisfy (/=']')))
1819−-- return ()
1820−--
1821−-- lbreak :: PandocMonad m => LP m ()
1822−-- lbreak = () <$ try (spaces' *>
1823−-- (controlSeq "\\" <|> controlSeq "tabularnewline") <*
1824−-- spaces')
1825−--
1826−-- amp :: PandocMonad m => LP m ()
1827−-- amp = () <$ try (spaces' *> char '&' <* spaces')
1828−--
1829−-- parseTableRow :: PandocMonad m
1830−-- => String -- ^ table environment name
1831−-- -> [(String, String)] -- ^ pref/suffixes
1832−-- -> LP m [Blocks]
1833−-- parseTableRow envname prefsufs = try $ do
1834−-- let cols = length prefsufs
1835−-- let tableCellRaw = concat <$> many
1836−-- (do notFollowedBy amp
1837−-- notFollowedBy lbreak
1838−-- notFollowedBy $ () <$ try (string ("\\end{" ++ envname ++ "}"))
1839−-- many1 (noneOf "&%\n\r\\")
1840−-- <|> try (string "\\&")
1841−-- <|> count 1 anyChar)
1842−-- let plainify bs = case toList bs of
1843−-- [Para ils] -> plain (fromList ils)
1844−-- _ -> bs
1845−-- rawcells <- sepBy1 tableCellRaw amp
1846−-- guard $ length rawcells == cols
1847−-- let rawcells' = zipWith (\c (p, s) -> p ++ trim c ++ s) rawcells prefsufs
1848−-- let tableCell = plainify <$> blocks
1849−-- cells' <- mapM (parseFromString' tableCell) rawcells'
1850−-- let numcells = length cells'
1851−-- guard $ numcells <= cols && numcells >= 1
1852−-- guard $ cells' /= [mempty]
1853−-- -- note: a & b in a three-column table leaves an empty 3rd cell:
1854−-- let cells'' = cells' ++ replicate (cols - numcells) mempty
1855−-- spaces'
1856−-- return cells''
1857−--
1858−-- spaces' :: PandocMonad m => LP m ()
1859−-- spaces' = spaces *> skipMany (comment *> spaces)
1860−--
1861−-- simpTable :: PandocMonad m => String -> Bool -> LP m Blocks
1862−-- simpTable envname hasWidthParameter = try $ do
1863−-- when hasWidthParameter $ () <$ (spaces' >> tok)
1864−-- skipopts
1865−-- colspecs <- parseAligns
1866−-- let (aligns, widths, prefsufs) = unzip3 colspecs
1867−-- let cols = length colspecs
1868−-- optional $ controlSeq "caption" *> skipopts *> setCaption
1869−-- optional lbreak
1870−-- spaces'
1871−-- skipMany hline
1872−-- spaces'
1873−-- header' <- option [] $ try (parseTableRow envname prefsufs <*
1874−-- lbreak <* many1 hline)
1875−-- spaces'
1876−-- rows <- sepEndBy (parseTableRow envname prefsufs)
1877−-- (lbreak <* optional (skipMany hline))
1878−-- spaces'
1879−-- optional $ controlSeq "caption" *> skipopts *> setCaption
1880−-- optional lbreak
1881−-- spaces'
1882−-- let header'' = if null header'
1883−-- then replicate cols mempty
1884−-- else header'
1885−-- lookAhead $ controlSeq "end" -- make sure we're at end
1886−-- return $ table mempty (zip aligns widths) header'' rows
1887−--
1888−-- removeDoubleQuotes :: String -> String
1889−-- removeDoubleQuotes ('"':xs) =
1890−-- case reverse xs of
1891−-- '"':ys -> reverse ys
1892−-- _ -> '"':xs
1893−-- removeDoubleQuotes xs = xs
1894−--
1895−-- inputListing :: PandocMonad m => LP m Blocks
1896−-- inputListing = do
1897−-- pos <- getPosition
1898−-- options <- option [] keyvals
1899−-- f <- filter (/='"') <$> braced
1900−-- dirs <- (splitBy (==':') . fromMaybe ".") <$> lookupEnv "TEXINPUTS"
1901−-- mbCode <- readFileFromDirs dirs f
1902−-- codeLines <- case mbCode of
1903−-- Just s -> return $ lines s
1904−-- Nothing -> do
1905−-- report $ CouldNotLoadIncludeFile f pos
1906−-- return []
1907−-- let (ident,classes,kvs) = parseListingsOptions options
1908−-- let language = case lookup "language" options >>= fromListingsLanguage of
1909−-- Just l -> [l]
1910−-- Nothing -> take 1 $ languagesByExtension (takeExtension f)
1911−-- let firstline = fromMaybe 1 $ lookup "firstline" options >>= safeRead
1912−-- let lastline = fromMaybe (length codeLines) $
1913−-- lookup "lastline" options >>= safeRead
1914−-- let codeContents = intercalate "\n" $ take (1 + lastline - firstline) $
1915−-- drop (firstline - 1) codeLines
1916−-- return $ codeBlockWith (ident,ordNub (classes ++ language),kvs) codeContents
1917−--
1918−-- parseListingsOptions :: [(String, String)] -> Attr
1919−-- parseListingsOptions options =
1920−-- let kvs = [ (if k == "firstnumber"
1921−-- then "startFrom"
1922−-- else k, v) | (k,v) <- options ]
1923−-- classes = [ "numberLines" |
1924−-- lookup "numbers" options == Just "left" ]
1925−-- ++ maybeToList (lookup "language" options
1926−-- >>= fromListingsLanguage)
1927−-- in (fromMaybe "" (lookup "label" options), classes, kvs)
1928−-}
test/Tests/Readers/LaTeX.hs+2−1
@@ -58,7 +58,8 @@ tests = [ testGroup "basic"
5858 , "blank lines + space + comments" =:
5959 "% my comment\n\n \n % another\n\nhi" =?> para "hi"
6060 , "comment in paragraph" =:
61− "hi % this is a comment\nthere\n" =?> para "hi there"
61+ "hi % this is a comment\nthere\n" =?>
62+ para ("hi" <> softbreak <> "there")
6263 ]
6364
6465 , testGroup "code blocks"
test/latex-reader.latex−1
@@ -4,7 +4,6 @@
44\setlength{\parindent}{0pt}
55\setlength{\parskip}{6pt plus 2pt minus 1pt}
66
7−\newcommand{\textsubscript}[1]{\ensuremath{_{\scriptsize\textrm{#1}}}}
87\usepackage[breaklinks=true,unicode=true]{hyperref}
98\usepackage[normalem]{ulem}
109% avoid problems with \sout in headers with hyperref:
test/latex-reader.native+1−1
@@ -261,7 +261,7 @@ Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "John",Spa
261261,Header 1 ("latex",[],[]) [Str "LaTeX"]
262262,BulletList
263263 [[Para [Cite [Citation {citationId = "smith.1899", citationPrefix = [], citationSuffix = [Str "22-23"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [RawInline (Format "latex") "\\cite[22-23]{smith.1899}"]]]
264− ,[Para [RawInline (Format "latex") "\\doublespacing\n"]]
264+ ,[Para [RawInline (Format "latex") "\\doublespacing"]]
265265 ,[Para [Math InlineMath "2+2=4"]]
266266 ,[Para [Math InlineMath "x \\in y"]]
267267 ,[Para [Math InlineMath "\\alpha \\wedge \\omega"]]