@@ -1,4 +1,6 @@
1{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE MultiParamTypeClasses #-}
2{-# LANGUAGE ScopedTypeVariables #-}
4{-# LANGUAGE ScopedTypeVariables #-}
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)
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))
68+import Text.Pandoc.Extensions (getDefaultExtensions)
69+import Debug.Trace (traceShowId)
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
72parseLaTeX :: PandocMonad m => LP m Pandoc
83parseLaTeX :: PandocMonad m => LP m Pandoc
73parseLaTeX = do
84parseLaTeX = do
74 bs <- blocks
85 bs <- blocks
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'
102+testParser :: LP PandocIO a -> Text -> IO a
104+ res <- runIOorExplode (runParserT p defaultLaTeXState{
105+ sOptions = def{ readerExtensions =
106+ getDefaultExtensions "latex" }} "name" (tokenize t))
108+ Left e -> error (show e)
111+data Macro = Macro -- TODO
114+data LaTeXState = LaTeXState{ sOptions :: ReaderOptions
116+ , sQuoteContext :: QuoteContext
117+ , sMacros :: [Macro]
118+ , sContainers :: [String]
119+ , sHeaders :: M.Map Inlines String
120+ , sLogMessages :: [LogMessage]
121+ , sIdentifiers :: Set.Set String }
124+defaultLaTeXState :: LaTeXState
125+defaultLaTeXState = LaTeXState{ sOptions = def
127+ , sQuoteContext = NoQuote
130+ , sHeaders = M.empty
132+ , sIdentifiers = Set.empty }
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 }
141+ newState <- getState
142+ setState newState { sQuoteContext = oldQuoteContext }
145+instance HasLogMessages LaTeXState where
146+ addLogMessage msg st = st{ sLogMessages = msg : sLogMessages st }
147+ getLogMessages st = reverse $ sLogMessages st
149+instance HasIdentifierList LaTeXState where
150+ extractIdentifierList = sIdentifiers
151+ updateIdentifierList f st = st{ sIdentifiers = f $ sIdentifiers st }
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 }
158+instance HasHeaderMap LaTeXState where
159+ extractHeaderMap = sHeaders
160+ updateHeaderMap f st = st{ sHeaders = f $ sHeaders st }
162+instance HasReaderOptions LaTeXState where
163+ extractReaderOptions = sOptions
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 }
171+instance Default LaTeXState where
172+ def = defaultLaTeXState
91−type LP m = ParserT String ParserState m
174+type LP m = ParserT [Tok] LaTeXState m
93−anyControlSeq :: PandocMonad m => LP m String
176+rawLaTeXBlock :: PandocMonad m => ParserT String LaTeXState m String
94−anyControlSeq = do
177+rawLaTeXBlock = mzero
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
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)
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)
201+ Tok (lin, col) Newline (T.singleton '\n')
202+ : totoks (lin + 1,1) rest
204+ let (sps, rest') = T.span isSpaceOrTab t
205+ in Tok (lin, col) Spaces sps
206+ : totoks (lin, col + T.length sps) rest'
208+ let (ws, rest') = T.span isAlphaNum t
209+ in Tok (lin, col) Word ws
210+ : totoks (lin, col + T.length ws) rest'
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'
216+ case T.uncons rest of
217+ Nothing -> [Tok (lin, col) Symbol (T.singleton c)]
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
227+ Tok (lin, col) CtrlSeq (T.pack [c,d])
228+ : totoks (lin, col + 2) rest'
230+ case T.uncons rest of
231+ Just ('^', rest') ->
232+ case T.uncons rest' of
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'''
240+ Tok (lin, col) Esc1 (T.pack ['^','^',d])
241+ : totoks (lin, col + 3) rest''
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
250+ Tok (lin, col) Symbol (T.singleton c) : totoks (lin, col + 1) rest
252+ where isSpaceOrTab ' ' = True
253+ isSpaceOrTab '\t' = True
254+ isSpaceOrTab _ = False
256+isLowerHex :: Char -> Bool
257+isLowerHex x = x >= '0' && x <= '9' || x >= 'a' && x <= 'f'
259+untokenize :: [Tok] -> Text
260+untokenize = mconcat . map untoken
262+untoken :: Tok -> Text
263+untoken (Tok _ _ t) = t
265+-- we put withMacros in front of satisfyTok, so it works on everything
266+satisfyTok :: PandocMonad m => (Tok -> Bool) -> LP m Tok
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
276+-- TODO use something other than LaTeXState
277+doMacros :: PandocMonad m => LP m ()
280+ macros <- sMacros <$> getState
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
289+setpos :: (Line, Column) -> Tok -> Tok
290+setpos spos (Tok _ tt txt) = Tok spos tt txt
292+anyControlSeq :: PandocMonad m => LP m Tok
293+anyControlSeq = satisfyTok isCtrlSeq <* optional sp
294+ where isCtrlSeq (Tok _ CtrlSeq _) = True
297+anySymbol :: PandocMonad m => LP m Tok
298+anySymbol = satisfyTok isSym
299+ where isSym (Tok _ Symbol _) = True
302+spaces :: PandocMonad m => LP m ()
303+spaces = skipMany (satisfyTok (tokTypeIn [Comment, Spaces, Newline]))
305+spaces1 :: PandocMonad m => LP m ()
306+spaces1 = skipMany1 (satisfyTok (tokTypeIn [Comment, Spaces, Newline]))
308+tokTypeIn :: [TokType] -> Tok -> Bool
309+tokTypeIn toktypes (Tok _ tt _) = tt `elem` toktypes
311+controlSeq :: PandocMonad m => Text -> LP m Tok
312+controlSeq name = satisfyTok isNamed
313+ where isNamed (Tok _ CtrlSeq n) = n == T.singleton '\\' <> name
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'
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
118sp :: PandocMonad m => LP m ()
330sp :: PandocMonad m => LP m ()
119sp = whitespace <|> endline
331sp = whitespace <|> endline
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)
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
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)
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)
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)
353+endline :: PandocMonad m => LP m ()
357+ notFollowedBy blankline
359+blankline :: PandocMonad m => LP m ()
361+ skipMany (satisfyTok (tokTypeIn [Spaces, Newline]))
364+primEscape :: PandocMonad m => LP m Char
366+ Tok _ toktype t <- satisfyTok (tokTypeIn [Esc1, Esc2])
368+ Esc1 -> case T.uncons t of
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
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
154− <|> () <$ controlSeq "bgroup"
155− <|> () <$ controlSeq "begingroup"
381+ symbol '{' <|> controlSeq "bgroup" <|> controlSeq "begingroup"
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")
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
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)
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)
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
181−bracketed parser = try $ char '[' *> (mconcat <$> manyTill parser (char ']'))
403+ mconcat <$> manyTill parser (symbol ']')
183−mathDisplay :: PandocMonad m => LP m String -> LP m Inlines
184−mathDisplay p = displayMath <$> (try p >>= applyMacros' . trim)
186−mathInline :: PandocMonad m => LP m String -> LP m Inlines
187−mathInline p = math <$> (try p >>= applyMacros')
189−mathChars :: PandocMonad m => LP m String
191− concat <$> many (escapedChar
192− <|> (snd <$> withRaw braced)
193− <|> many1 (satisfy isOrdChar))
194− where escapedChar = try $ do char '\\'
197− isOrdChar '$' = False
198− isOrdChar '{' = False
199− isOrdChar '}' = False
200− isOrdChar '\\' = False
203−quoted' :: PandocMonad m => (Inlines -> Inlines) -> LP m String -> LP m () -> LP m Inlines
204−quoted' f starter ender = do
206− smart <- extensionEnabled Ext_smart <$> getOption readerExtensions
209− ils <- many (notFollowedBy ender >> inline)
210− (ender >> return (f (mconcat ils))) <|>
211− (<> mconcat ils) <$>
212− lit (case startchs of
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
226−singleQuote :: PandocMonad m => LP m Inlines
416+-- inline elements:
228− smart <- extensionEnabled Ext_smart <$> getOption readerExtensions
230− then quoted' singleQuoted (string "`") (try $ char '\'' >> notFollowedBy letter)
231− <|> quoted' singleQuoted (string "‘") (try $ char '’' >> notFollowedBy letter)
232− else str <$> many1 (oneOf "`\'‘’")
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)
240− <|> inlineEnvironment
242− <|> (char '-' *> option (str "-")
243− (char '-' *> option (str "–") (str "—" <$ char '-')))
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 "#&~^'`\"[]"
257− report $ ParsingUnescaped [res] pos
260−inlines :: PandocMonad m => LP m Inlines
421+isWordTok :: Tok -> Bool
261−inlines = mconcat <$> many (notFollowedBy (char '}') *> inline)
422+isWordTok (Tok _ Word _) = True
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
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 '|')
282−blocks :: PandocMonad m => LP m Blocks
283−blocks = mconcat <$> many block
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)) *>
290− return $ '\\' : name' ++ snd rawargs
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 '"')
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
467+quoted' :: PandocMonad m
468+ => (Inlines -> Inlines)
472+quoted' f starter ender = do
473+ startchs <- (T.unpack . untokenize) <$> starter
474+ smart <- extensionEnabled Ext_smart <$> getOption readerExtensions
477+ ils <- many (notFollowedBy ender >> inline)
478+ (ender >> return (f (mconcat ils))) <|>
479+ (<> mconcat ils) <$>
480+ lit (case startchs of
311−inBrackets :: Inlines -> Inlines
486+enquote :: PandocMonad m => LP m Inlines
312−inBrackets x = str "[" <> x <> str "]"
487+enquote = do
489+ quoteContext <- sQuoteContext <$> getState
490+ if quoteContext == InDoubleQuote
491+ then singleQuoted <$> withQuoteContext InSingleQuote tok
492+ else doubleQuoted <$> withQuoteContext InDoubleQuote tok
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)
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
508+ let (t1, t2) = T.splitAt i txt
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
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
522+ let stopchar = if marker == '{' then '}' else marker
523+ (codeWith ("",classes,[]) . T.unpack . untokenize) <$>
524+ manyTill (verbTok stopchar) (symbol stopchar)
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` [".",":","-","|","\\"]
531+ val <- option [] $ do
533+ braced <|> (many1 (satisfyTok isWordTok <|> satisfyTok isSpecSym))
535+ optional (symbol ',')
537+ return (T.unpack key, T.unpack . untokenize $ val)
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 ']')
332− report $ SkippedContent raw pos
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
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"))
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)
374− , ("frametitle", section nullAttr 3)
375− , ("framesubtitle", section nullAttr 4)
377− , ("opening", (para . trimInlines) <$> (skipopts *> tok))
378− , ("closing", skipopts *> closing)
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))
392− , ("lstinputlisting", inputListing)
393− , ("graphicspath", graphicsPath)
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.
410− , "markboth", "markright", "markleft"
411− , "hspace", "vspace"
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' = "Ó"
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' = "Ê"
427−splitBibs :: String -> [Inlines]
593+circ 'O' = "Ô"
428−splitBibs = map (str . flip replaceExtension "bib" . trim) . splitBy (==',')
594+circ 'U' = "Û"
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' = "Ĵ"
442−resetCaption :: PandocMonad m => LP m ()
616+tilde :: Char -> String
443−resetCaption = updateState $ \st -> st{ stateCaption = Nothing }
617+tilde 'A' = "Ã"
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' = "ö"
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' = "ӳ"
657+dot :: Char -> String
669+macron :: Char -> String
682+cedilla :: Char -> String
697+hacek :: Char -> String
733+breve :: Char -> String
748+toksToString :: [Tok] -> String
749+toksToString = T.unpack . untokenize
751+mathDisplay :: String -> Inlines
752+mathDisplay = displayMath . trim
753+-- TODO we shouldn't need applyMacros
754+-- mathDisplay p = displayMath <$> (try p >>= applyMacros' . trim)
756+mathInline :: String -> Inlines
757+mathInline = math . trim
758+-- TODO we shouldn't need applyMacros
759+-- mathInline p = math <$> (try p >>= applyMacros')
761+dollarsMath :: PandocMonad m => LP m Inlines
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
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
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])
801+ let (t1, t2) = (T.take 1 t, T.drop 1 t)
803+ setInput $ (Tok (lin, col + 1) toktype t2) : inp
804+ return $ str (T.unpack t1)
805+ else return $ str (T.unpack t)
807+opt :: PandocMonad m => LP m Inlines
808+opt = bracketed inline
810+rawopt :: PandocMonad m => LP m Text
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
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
828+guardRaw :: PandocMonad m => LP m ()
829+guardRaw = getOption readerExtensions >>= guard . extensionEnabled Ext_raw_tex
831+optargs :: PandocMonad m => LP m Text
833+ (_, raw) <- withRaw (skipopts *> skipMany (try $ optional sp *> braced))
834+ return $ untokenize raw
836+-- opts in angle brackets are used in beamer
837+rawangle :: PandocMonad m => LP m ()
840+ () <$ manyTill anyTok (symbol '>')
842+skipangles :: PandocMonad m => LP m ()
843+skipangles = skipMany rawangle
845+ignore :: (Monoid a, PandocMonad m) => String -> ParserT s u m a
848+ report $ SkippedContent raw pos
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
855+ nxt <- option (Tok (0,0) Word "") (lookAhead anyTok)
856+ let raw = takeWhile (/= nxt) inp
857+ return (result, raw)
859+inBrackets :: Inlines -> Inlines
860+inBrackets x = str "[" <> x <> str "]"
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
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
873+ Just y -> "\\begin{" ++ y ++ "}\n" ++ x ++
874+ "\\end{" ++ y ++ "}"
876+mathEnv :: PandocMonad m => String -> LP m String
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
885+inlineEnvironment :: PandocMonad m => LP m Inlines
886+inlineEnvironment = try $ do
888+ name <- untokenize <$> braced
889+ M.findWithDefault mzero name inlineEnvironments
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*")
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
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
713− defaultExt <- getOption readerDefaultImageExtension
714− return $ imageWith attr (addExtension src defaultExt) "" alt
715− _ -> return $ imageWith attr src "" alt
717−inNote :: Inlines -> Inlines
719− note $ para $ ils <> str "."
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
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)
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)
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)
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)
1128+ <|> inlineEnvironment
1131+ option (str "-") (symbol '-' *>
1132+ option (str "–") (str "—" <$ symbol '-')))
1135+ <|> (str "”" <$ try (symbol '\'' >> symbol '\''))
1136+ <|> (str "”" <$ symbol '”')
1137+ <|> (str "’" <$ symbol '\'')
1138+ <|> (str "’" <$ symbol '’')
1139+ <|> (str "\160" <$ symbol '~')
1141+ <|> (guardEnabled Ext_literate_haskell *> symbol '|' *> doLHSverb)
1142+ <|> (str . (:[]) <$> primEscape)
1143+ <|> (do res <- symbolIn "#&~^'`\"[]"
1145+ let s = T.unpack (untoken res)
1146+ report $ ParsingUnescaped s pos
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
755− valueprefix <- option "" $ char '[' >> (mconcat <$> manyTill tok (char ']'))
757− let emptyOr160 "" = ""
758− emptyOr160 _ = "\160"
759− return . mconcat $ [valueprefix,
760− emptyOr160 valueprefix,
765−lit :: String -> LP m Inlines
1152+-- block elements:
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
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'
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
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
842−tilde :: Char -> String
855−umlaut :: Char -> String
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' = "ӳ"
883−dot :: Char -> String
895−macron :: Char -> String
908−cedilla :: Char -> String
923−hacek :: Char -> String
959−breve :: Char -> String
974−tok :: PandocMonad m => LP m Inlines
975−tok = try $ grouped inline <|> inlineCommand <|> str <$> count 1 inlineChar
977−opt :: PandocMonad m => LP m Inlines
978−opt = bracketed inline
980−rawopt :: PandocMonad m => LP m String
982− contents <- bracketed (many1 (noneOf "[]") <|> try (string "\\]") <|>
983− try (string "\\[") <|> rawopt)
985− return $ "[" ++ contents ++ "]"
987−skipopts :: PandocMonad m => LP m ()
988−skipopts = skipMany rawopt
990−-- opts in angle brackets are used in beamer
991−rawangle :: PandocMonad m => LP m ()
994− skipMany (noneOf ">")
998−skipangles :: PandocMonad m => LP m ()
999−skipangles = skipMany rawangle
1001−inlineText :: PandocMonad m => LP m Inlines
1002−inlineText = str <$> many1 inlineChar
1004−inlineChar :: PandocMonad m => LP m Char
1005−inlineChar = noneOf "\\$%&~#{}^'`\"‘’“”-[] \t\n"
1007−environment :: PandocMonad m => LP m Blocks
1011− M.findWithDefault mzero name environments
1014−inlineEnvironment :: PandocMonad m => LP m Inlines
1015−inlineEnvironment = try $ do
1018− M.findWithDefault mzero name inlineEnvironments
1020−rawEnv :: PandocMonad m => String -> LP m Blocks
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'
1032− then return $ rawBlock "latex" $ beginCommand ++ raw'
1034− unless parseRaw $ do
1035− report $ SkippedContent beginCommand pos1
1036− pos2 <- getPosition
1037− report $ SkippedContent ("\\end{" ++ name ++ "}") pos2
1040−rawVerbEnv :: PandocMonad m => String -> LP m Blocks
1041−rawVerbEnv name = do
1043− (_, raw) <- withRaw $ verbEnv name
1044− let raw' = "\\begin{tikzpicture}" ++ raw
1045− exts <- getOption readerExtensions
1046− let parseRaw = extensionEnabled Ext_raw_tex exts
1048− then return $ rawBlock "latex" raw'
1050− report $ SkippedContent raw' pos
1055−maybeAddExtension :: String -> FilePath -> FilePath
1056−maybeAddExtension ext fp =
1057− if null (takeExtension fp)
1058− then addExtension fp ext
1061−include :: PandocMonad m => LP m Blocks
1065− name <- try (string "include")
1066− <|> try (string "input")
1067− <|> try (string "subfile")
1068− <|> string "usepackage"
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'
1078−inputListing :: PandocMonad m => LP m Blocks
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
1088− report $ CouldNotLoadIncludeFile f pos
1090− let (ident,classes,kvs) = parseListingsOptions options
1091− let language = case lookup "language" options >>= fromListingsLanguage of
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
1101−parseListingsOptions :: [(String, String)] -> Attr
1102−parseListingsOptions options =
1103− let kvs = [ (if k == "firstnumber"
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)
1114−keyval :: PandocMonad m => LP m (String, String)
1116− key <- many1 alphaNum
1117− val <- option "" $ char '=' >> braced <|> (many1 (alphaNum <|> oneOf ".:-|\\"))
1119− optional (char ',')
1187+addMeta :: PandocMonad m => ToMetaValue a => String -> a -> LP m ()
1188+addMeta field val = updateState $ \st ->
1189+ st{ sMeta = addMetaField field val $ sMeta st }
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
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))
1137−rawLaTeXInline :: PandocMonad m => LP m Inline
1139− raw <- (snd <$> withRaw inlineCommand)
1140− <|> (snd <$> withRaw inlineEnvironment)
1141− <|> (snd <$> withRaw blockCommand)
1142− RawInline "latex" <$> applyMacros' raw
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)
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
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"],[]) <$>
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"
1200− else k, v) | (k,v) <- options ]
1201− let classes = [ lang | not (null lang) ] ++
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")
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
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
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
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"))
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)
1273+ , ("frametitle", section nullAttr 3)
1274+ , ("framesubtitle", section nullAttr 4)
1276+ , ("opening", (para . trimInlines) <$> (skipopts *> tok))
1277+ , ("closing", skipopts *> closing)
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))
1291+-- , ("lstinputlisting", inputListing)
1292+-- , ("graphicspath", graphicsPath)
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.
1309+ , "markboth", "markright", "markleft"
1310+ , "hspace", "vspace"
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"],[]) <$>
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"
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")
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
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 ++ "}"))
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 }
1284− updateState $ \st -> st{ stateParserContext = oldCtx }
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
1292− Just y -> "\\begin{" ++ y ++ "}\n" ++ x ++
1293− "\\end{" ++ y ++ "}"
1295−mathEnv :: PandocMonad m => String -> LP m String
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
1306−verbEnv :: PandocMonad m => String -> LP m String
1310− let endEnv = try $ controlSeq "end" *> braced >>= guard . (== name)
1311− charMuncher = anyChar
1312− res <- manyTill charMuncher endEnv
1313− return $ stripTrailingNewlines res
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
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
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
1355− <|> void blanklines
1358− <|> void blockCommand
1359− <|> void anyControlSeq
1367−addPrefix :: [Inline] -> [Citation] -> [Citation]
1368−addPrefix p (k:ks) = k {citationPrefix = p ++ citationPrefix k} : ks
1371−addSuffix :: [Inline] -> [Citation] -> [Citation]
1372−addSuffix s ks@(_:_) =
1374− in init ks ++ [k {citationSuffix = citationSuffix k ++ s}]
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
1391− , citationNoteNum = 0
1393− return $ addPrefix pre $ addSuffix suf $ map conv keys
1395−citationLabel :: PandocMonad m => LP m String
1396−citationLabel = optional sp *>
1397− (many1 (satisfy isBibtexKeyChar)
1399− <* optional (char ',')
1401− where isBibtexKeyChar c = isAlphaNum c || c `elem` (".:;?!`'()/*@_+=-[]" :: String)
1403−cites :: PandocMonad m => CitationMode -> Bool -> LP m [Citation]
1404−cites mode multi = try $ do
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
1413− _ -> map (\a -> a {citationMode = mode}) cs
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)
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
1427− cit' <- inline -- expect a citation
1428− let citlist = toList cit'
1429− cits' <- case citlist of
1430− [Cite cs _] -> return cs
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)
1442−parseAligns :: PandocMonad m => LP m [(Alignment, Double, (String, String))]
1443−parseAligns = try $ do
1445− let maybeBar = skipMany $ sp <|> () <$ char '|' <|> () <$ (char '@' >> braced)
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
1461− ds <- many1 (oneOf "0123456789.")
1463− string "\\linewidth"
1465− case safeRead ds of
1467− Nothing -> return 0.0
1470− pref <- option "" alignPrefix
1473− width <- colWidth <|> option 0.0 (do s <- braced
1475− report $ SkippedContent s pos
1478− suff <- option "" alignSuffix
1479− return (al, width, (pref, suff))
1480− aligns' <- sepEndBy alignSpec maybeBar
1486−hline :: PandocMonad m => LP m ()
1489− controlSeq "hline" <|>
1491− controlSeq "toprule" <|>
1492− controlSeq "bottomrule" <|>
1493− controlSeq "midrule" <|>
1494− controlSeq "endhead" <|>
1495− controlSeq "endfirsthead"
1497− optional $ bracketed (many1 (satisfy (/=']')))
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"
1572−removeDoubleQuotes xs = xs
1504+--
1505+-- inlineEnvironment :: PandocMonad m => LP m Inlines
1506+-- inlineEnvironment = try $ do
1507+-- controlSeq "begin"
1509+-- M.findWithDefault mzero name inlineEnvironments
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
1520+-- then return $ rawBlock "latex" raw'
1522+-- report $ SkippedContent raw' pos
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
1534+-- rawLaTeXBlock :: PandocMonad m => ParserT String LaTeXState String
1535+-- rawLaTeXBlock = snd <$> try (withRaw (environment <|> blockCommand))
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
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)
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
1564+-- figure :: PandocMonad m => LP m Blocks
1565+-- figure = try $ do
1567+-- blocks >>= addImageCaption
1569+-- letterContents :: PandocMonad m => LP m Blocks
1570+-- letterContents = do
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
1578+-- return $ addr <> bs -- sig added by \closing
1581+-- item :: PandocMonad m => LP m Blocks
1582+-- item = blocks *> controlSeq "item" *> skipopts *> blocks
1584+-- looseItem :: PandocMonad m => LP m Blocks
1586+-- ctx <- stateParserContext `fmap` getState
1587+-- if ctx == ListItemState
1589+-- else return mempty
1591+-- descItem :: PandocMonad m => LP m (Inlines, [Blocks])
1593+-- blocks -- skip blocks before item
1594+-- controlSeq "item"
1598+-- return (ils, [bs])
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 }
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
1614+-- Just y -> "\\begin{" ++ y ++ "}\n" ++ x ++
1615+-- "\\end{" ++ y ++ "}"
1617+-- mathEnv :: PandocMonad m => String -> LP m String
1618+-- mathEnv name = do
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
1628+-- verbEnv :: PandocMonad m => String -> LP m String
1629+-- verbEnv name = do
1631+-- optional blankline
1632+-- let endEnv = try $ controlSeq "end" *> braced >>= guard . (== name)
1633+-- charMuncher = anyChar
1634+-- res <- manyTill charMuncher endEnv
1635+-- return $ stripTrailingNewlines res
1637+-- fancyverbEnv :: PandocMonad m => String -> LP m Blocks
1638+-- fancyverbEnv name = do
1639+-- options <- option [] keyvals
1640+-- let kvs = [ (if k == "firstnumber"
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
1648+-- orderedList' :: PandocMonad m => LP m Blocks
1649+-- orderedList' = try $ do
1651+-- (_, style, delim) <- option (1, DefaultStyle, DefaultDelim) $
1652+-- try $ char '[' *> anyOrderedListMarker <* char ']'
1654+-- optional $ try $ controlSeq "setlength" *> grouped (controlSeq "itemindent") *> braced
1656+-- start <- option 1 $ try $ do controlSeq "setcounter"
1657+-- grouped (string "enum" *> many1 (oneOf "iv"))
1659+-- num <- grouped (many1 digit)
1661+-- return (read num + 1 :: Int)
1662+-- bs <- listenv "enumerate" (many item)
1663+-- return $ orderedListWith (start, style, delim) bs
1665+-- paragraph :: PandocMonad m => LP m Blocks
1667+-- x <- trimInlines . mconcat <$> many1 inline
1669+-- then return mempty
1670+-- else return $ para x
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
1677+-- <|> void blanklines
1680+-- <|> void blockCommand
1681+-- <|> void anyControlSeq
1689+-- addPrefix :: [Inline] -> [Citation] -> [Citation]
1690+-- addPrefix p (k:ks) = k {citationPrefix = p ++ citationPrefix k} : ks
1691+-- addPrefix _ _ = []
1693+-- addSuffix :: [Inline] -> [Citation] -> [Citation]
1694+-- addSuffix s ks@(_:_) =
1696+-- in init ks ++ [k {citationSuffix = citationSuffix k ++ s}]
1697+-- addSuffix _ _ = []
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
1715+-- return $ addPrefix pre $ addSuffix suf $ map conv keys
1717+-- citationLabel :: PandocMonad m => LP m String
1718+-- citationLabel = optional sp *>
1719+-- (many1 (satisfy isBibtexKeyChar)
1721+-- <* optional (char ',')
1723+-- where isBibtexKeyChar c = isAlphaNum c || c `elem` (".:;?!`'()/*@_+=-[]" :: String)
1725+-- cites :: PandocMonad m => CitationMode -> Bool -> LP m [Citation]
1726+-- cites mode multi = try $ do
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
1735+-- _ -> map (\a -> a {citationMode = mode}) cs
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)
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
1749+-- cit' <- inline -- expect a citation
1750+-- let citlist = toList cit'
1751+-- cits' <- case citlist of
1752+-- [Cite cs _] -> return cs
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)
1764+-- parseAligns :: PandocMonad m => LP m [(Alignment, Double, (String, String))]
1765+-- parseAligns = try $ do
1767+-- let maybeBar = skipMany $ sp <|> () <$ char '|' <|> () <$ (char '@' >> braced)
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
1783+-- ds <- many1 (oneOf "0123456789.")
1785+-- string "\\linewidth"
1787+-- case safeRead ds of
1788+-- Just w -> return w
1789+-- Nothing -> return 0.0
1790+-- let alignSpec = do
1792+-- pref <- option "" alignPrefix
1795+-- width <- colWidth <|> option 0.0 (do s <- braced
1796+-- pos <- getPosition
1797+-- report $ SkippedContent s pos
1800+-- suff <- option "" alignSuffix
1801+-- return (al, width, (pref, suff))
1802+-- aligns' <- sepEndBy alignSpec maybeBar
1808+-- hline :: PandocMonad m => LP m ()
1811+-- controlSeq "hline" <|>
1812+-- -- booktabs rules:
1813+-- controlSeq "toprule" <|>
1814+-- controlSeq "bottomrule" <|>
1815+-- controlSeq "midrule" <|>
1816+-- controlSeq "endhead" <|>
1817+-- controlSeq "endfirsthead"
1819+-- optional $ bracketed (many1 (satisfy (/=']')))
1822+-- lbreak :: PandocMonad m => LP m ()
1823+-- lbreak = () <$ try (spaces' *>
1824+-- (controlSeq "\\" <|> controlSeq "tabularnewline") <*
1827+-- amp :: PandocMonad m => LP m ()
1828+-- amp = () <$ try (spaces' *> char '&' <* spaces')
1830+-- parseTableRow :: PandocMonad m
1831+-- => String -- ^ table environment name
1832+-- -> [(String, String)] -- ^ pref/suffixes
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)
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
1859+-- spaces' :: PandocMonad m => LP m ()
1860+-- spaces' = spaces *> skipMany (comment *> spaces)
1862+-- simpTable :: PandocMonad m => String -> Bool -> LP m Blocks
1863+-- simpTable envname hasWidthParameter = try $ do
1864+-- when hasWidthParameter $ () <$ (spaces' >> tok)
1866+-- colspecs <- parseAligns
1867+-- let (aligns, widths, prefsufs) = unzip3 colspecs
1868+-- let cols = length colspecs
1869+-- optional $ controlSeq "caption" *> skipopts *> setCaption
1874+-- header' <- option [] $ try (parseTableRow envname prefsufs <*
1875+-- lbreak <* many1 hline)
1877+-- rows <- sepEndBy (parseTableRow envname prefsufs)
1878+-- (lbreak <* optional (skipMany hline))
1880+-- optional $ controlSeq "caption" *> skipopts *> setCaption
1883+-- let header'' = if null header'
1884+-- then replicate cols mempty
1886+-- lookAhead $ controlSeq "end" -- make sure we're at end
1887+-- return $ table mempty (zip aligns widths) header'' rows
1889+-- removeDoubleQuotes :: String -> String
1890+-- removeDoubleQuotes ('"':xs) =
1891+-- case reverse xs of
1892+-- '"':ys -> reverse ys
1894+-- removeDoubleQuotes xs = xs
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
1906+-- report $ CouldNotLoadIncludeFile f pos
1908+-- let (ident,classes,kvs) = parseListingsOptions options
1909+-- let language = case lookup "language" options >>= fromListingsLanguage of
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
1919+-- parseListingsOptions :: [(String, String)] -> Attr
1920+-- parseListingsOptions options =
1921+-- let kvs = [ (if k == "firstnumber"
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)