@@ -22,15 +22,13 @@ module Text.Pandoc.Writers.Markdown (
2222 writePlain) where
2323import Control.Monad.Reader
2424import Control.Monad.State.Strict
25−import Data.Char (isAlphaNum, isDigit)
2625import Data.Default
27−import Data.List (find, intersperse, sortOn, transpose)
26+import Data.List (intersperse, sortOn, transpose)
2827import qualified Data.Map as M
2928import Data.Maybe (fromMaybe, mapMaybe)
3029import qualified Data.Set as Set
3130import Data.Text (Text)
3231import qualified Data.Text as T
33−import Network.HTTP (urlEncode)
3432import Text.HTML.TagSoup (Tag (..), isTagText, parseTags)
3533import Text.Pandoc.Class.PandocMonad (PandocMonad, report)
3634import Text.Pandoc.Definition
@@ -44,59 +42,11 @@ import Text.Pandoc.Templates (renderTemplate)
4442import Text.DocTemplates (Val(..), Context(..), FromContext(..))
4543import Text.Pandoc.Walk
4644import Text.Pandoc.Writers.HTML (writeHtml5String)
47−import Text.Pandoc.Writers.Math (texMathToInlines)
48−import Text.Pandoc.XML (toHtml5Entities)
49−import Data.Coerce (coerce)
50−
51−type Notes = [[Block]]
52−type Ref = (Text, Target, Attr)
53−type Refs = [Ref]
54−
55−type MD m = ReaderT WriterEnv (StateT WriterState m)
56−
57−evalMD :: PandocMonad m => MD m a -> WriterEnv -> WriterState -> m a
58−evalMD md env st = evalStateT (runReaderT md env) st
59−
60−data WriterEnv = WriterEnv { envInList :: Bool
61− , envVariant :: MarkdownVariant
62− , envRefShortcutable :: Bool
63− , envBlockLevel :: Int
64− , envEscapeSpaces :: Bool
65− }
66−
67−data MarkdownVariant =
68− PlainText
69− | Commonmark
70− | Markdown
71− deriving (Show, Eq)
72−
73−instance Default WriterEnv
74− where def = WriterEnv { envInList = False
75− , envVariant = Markdown
76− , envRefShortcutable = True
77− , envBlockLevel = 0
78− , envEscapeSpaces = False
79− }
80−
81−data WriterState = WriterState { stNotes :: Notes
82− , stPrevRefs :: Refs
83− , stRefs :: Refs
84− , stKeys :: M.Map Key
85− (M.Map (Target, Attr) Int)
86− , stLastIdx :: Int
87− , stIds :: Set.Set Text
88− , stNoteNum :: Int
89− }
90−
91−instance Default WriterState
92− where def = WriterState{ stNotes = []
93− , stPrevRefs = []
94− , stRefs = []
95− , stKeys = M.empty
96− , stLastIdx = 0
97− , stIds = Set.empty
98− , stNoteNum = 1
99− }
45+import Text.Pandoc.Writers.Markdown.Inline (inlineListToMarkdown)
46+import Text.Pandoc.Writers.Markdown.Types (MarkdownVariant(..),
47+ WriterState(..),
48+ WriterEnv(..),
49+ Ref, Refs, MD, evalMD)
10050
10151-- | Convert Pandoc to Markdown.
10252writeMarkdown :: PandocMonad m => WriterOptions -> Pandoc -> m Text
@@ -297,49 +247,6 @@ noteToMarkdown opts num blocks = do
297247 then hang (writerTabStop opts) (marker <> spacer) contents
298248 else marker <> spacer <> contents
299249
300−-- | Escape special characters for Markdown.
301−escapeText :: WriterOptions -> Text -> Text
302−escapeText opts = T.pack . go . T.unpack
303− where
304− go [] = []
305− go (c:cs) =
306− case c of
307− '<' | isEnabled Ext_all_symbols_escapable opts ->
308− '\\' : '<' : go cs
309− | otherwise -> "<" ++ go cs
310− '>' | isEnabled Ext_all_symbols_escapable opts ->
311− '\\' : '>' : go cs
312− | otherwise -> ">" ++ go cs
313− '@' | isEnabled Ext_citations opts ->
314− case cs of
315− (d:_)
316− | isAlphaNum d || d == '_'
317− -> '\\':'@':go cs
318− _ -> '@':go cs
319− _ | c `elem` ['\\','`','*','_','[',']','#'] ->
320− '\\':c:go cs
321− '|' | isEnabled Ext_pipe_tables opts -> '\\':'|':go cs
322− '^' | isEnabled Ext_superscript opts -> '\\':'^':go cs
323− '~' | isEnabled Ext_subscript opts ||
324− isEnabled Ext_strikeout opts -> '\\':'~':go cs
325− '$' | isEnabled Ext_tex_math_dollars opts -> '\\':'$':go cs
326− '\'' | isEnabled Ext_smart opts -> '\\':'\'':go cs
327− '"' | isEnabled Ext_smart opts -> '\\':'"':go cs
328− '-' | isEnabled Ext_smart opts ->
329− case cs of
330− '-':_ -> '\\':'-':go cs
331− _ -> '-':go cs
332− '.' | isEnabled Ext_smart opts ->
333− case cs of
334− '.':'.':rest -> '\\':'.':'.':'.':go rest
335− _ -> '.':go cs
336− _ -> case cs of
337− '_':x:xs
338− | isEnabled Ext_intraword_underscores opts
339− , isAlphaNum c
340− , isAlphaNum x -> c : '_' : x : go xs
341− _ -> c : go cs
342−
343250attrsToMarkdown :: Attr -> Doc Text
344251attrsToMarkdown attribs = braces $ hsep [attribId, attribClasses, attribKeys]
345252 where attribId = case attribs of
@@ -912,499 +819,7 @@ blockListToMarkdown opts blocks = do
912819 | otherwise = RawBlock "markdown" " \n"
913820 mconcat <$> mapM (blockToMarkdown opts) (fixBlocks blocks)
914821
915−getKey :: Doc Text -> Key
916−getKey = toKey . render Nothing
917−
918−findUsableIndex :: [Text] -> Int -> Int
919−findUsableIndex lbls i = if tshow i `elem` lbls
920− then findUsableIndex lbls (i + 1)
921− else i
922−
923−getNextIndex :: PandocMonad m => MD m Int
924−getNextIndex = do
925− prevRefs <- gets stPrevRefs
926− refs <- gets stRefs
927− i <- (+ 1) <$> gets stLastIdx
928− modify $ \s -> s{ stLastIdx = i }
929− let refLbls = map (\(r,_,_) -> r) $ prevRefs ++ refs
930− return $ findUsableIndex refLbls i
931−
932−-- | Get reference for target; if none exists, create unique one and return.
933−-- Prefer label if possible; otherwise, generate a unique key.
934−getReference :: PandocMonad m => Attr -> Doc Text -> Target -> MD m Text
935−getReference attr label target = do
936− refs <- gets stRefs
937− case find (\(_,t,a) -> t == target && a == attr) refs of
938− Just (ref, _, _) -> return ref
939− Nothing -> do
940− keys <- gets stKeys
941− let key = getKey label
942− let rawkey = coerce key
943− case M.lookup key keys of
944− Nothing -> do -- no other refs with this label
945− (lab', idx) <- if T.null rawkey ||
946− T.length rawkey > 999 ||
947− T.any (\c -> c == '[' || c == ']') rawkey
948− then do
949− i <- getNextIndex
950− return (tshow i, i)
951− else
952− return (render Nothing label, 0)
953− modify (\s -> s{
954− stRefs = (lab', target, attr) : refs,
955− stKeys = M.insert (getKey label)
956− (M.insert (target, attr) idx mempty)
957− (stKeys s) })
958− return lab'
959−
960− Just km -> -- we have refs with this label
961− case M.lookup (target, attr) km of
962− Just i -> do
963− let lab' = render Nothing $
964− label <> if i == 0
965− then mempty
966− else literal (tshow i)
967− -- make sure it's in stRefs; it may be
968− -- a duplicate that was printed in a previous
969− -- block:
970− when ((lab', target, attr) `notElem` refs) $
971− modify (\s -> s{
972− stRefs = (lab', target, attr) : refs })
973− return lab'
974− Nothing -> do -- but this one is to a new target
975− i <- getNextIndex
976− let lab' = tshow i
977− modify (\s -> s{
978− stRefs = (lab', target, attr) : refs,
979− stKeys = M.insert key
980− (M.insert (target, attr) i km)
981− (stKeys s) })
982− return lab'
983−
984−-- | Convert list of Pandoc inline elements to markdown.
985−inlineListToMarkdown :: PandocMonad m => WriterOptions -> [Inline] -> MD m (Doc Text)
986−inlineListToMarkdown opts lst = do
987− inlist <- asks envInList
988− go (if inlist then avoidBadWrapsInList lst else lst)
989− where go [] = return empty
990− go (x@Math{}:y@(Str t):zs)
991− | T.all isDigit (T.take 1 t) -- starts with digit -- see #7058
992− = liftM2 (<>) (inlineToMarkdown opts x)
993− (go (RawInline (Format "html") "<!-- -->" : y : zs))
994− go (i:is) = case i of
995− Link {} -> case is of
996− -- If a link is followed by another link, or '[', '(' or ':'
997− -- then we don't shortcut
998− Link {}:_ -> unshortcutable
999− Space:Link {}:_ -> unshortcutable
1000− Space:(Str(thead -> Just '[')):_ -> unshortcutable
1001− Space:(RawInline _ (thead -> Just '[')):_ -> unshortcutable
1002− Space:(Cite _ _):_ -> unshortcutable
1003− SoftBreak:Link {}:_ -> unshortcutable
1004− SoftBreak:(Str(thead -> Just '[')):_ -> unshortcutable
1005− SoftBreak:(RawInline _ (thead -> Just '[')):_ -> unshortcutable
1006− SoftBreak:(Cite _ _):_ -> unshortcutable
1007− LineBreak:Link {}:_ -> unshortcutable
1008− LineBreak:(Str(thead -> Just '[')):_ -> unshortcutable
1009− LineBreak:(RawInline _ (thead -> Just '[')):_ -> unshortcutable
1010− LineBreak:(Cite _ _):_ -> unshortcutable
1011− (Cite _ _):_ -> unshortcutable
1012− Str (thead -> Just '['):_ -> unshortcutable
1013− Str (thead -> Just '('):_ -> unshortcutable
1014− Str (thead -> Just ':'):_ -> unshortcutable
1015− (RawInline _ (thead -> Just '[')):_ -> unshortcutable
1016− (RawInline _ (thead -> Just '(')):_ -> unshortcutable
1017− (RawInline _ (thead -> Just ':')):_ -> unshortcutable
1018− (RawInline _ (T.stripPrefix " [" -> Just _ )):_ -> unshortcutable
1019− _ -> shortcutable
1020− _ -> shortcutable
1021− where shortcutable = liftM2 (<>) (inlineToMarkdown opts i) (go is)
1022− unshortcutable = do
1023− iMark <- local
1024− (\env -> env { envRefShortcutable = False })
1025− (inlineToMarkdown opts i)
1026− fmap (iMark <>) (go is)
1027− thead = fmap fst . T.uncons
1028−
1029−isSp :: Inline -> Bool
1030−isSp Space = True
1031−isSp SoftBreak = True
1032−isSp _ = False
1033−
1034−avoidBadWrapsInList :: [Inline] -> [Inline]
1035−avoidBadWrapsInList [] = []
1036−avoidBadWrapsInList (s:Str (T.uncons -> Just ('>',cs)):xs) | isSp s =
1037− Str (" >" <> cs) : avoidBadWrapsInList xs
1038−avoidBadWrapsInList [s, Str (T.uncons -> Just (c, cs))]
1039− | T.null cs && isSp s && c `elem` ['-','*','+'] = [Str $ T.pack [' ', c]]
1040−avoidBadWrapsInList (s:Str (T.uncons -> Just (c, cs)):Space:xs)
1041− | T.null cs && isSp s && c `elem` ['-','*','+'] =
1042− Str (T.pack [' ', c]) : Space : avoidBadWrapsInList xs
1043−avoidBadWrapsInList (s:Str cs:Space:xs)
1044− | isSp s && isOrderedListMarker cs =
1045− Str (" " <> cs) : Space : avoidBadWrapsInList xs
1046−avoidBadWrapsInList [s, Str cs]
1047− | isSp s && isOrderedListMarker cs = [Str $ " " <> cs]
1048−avoidBadWrapsInList (x:xs) = x : avoidBadWrapsInList xs
1049−
1050−isOrderedListMarker :: Text -> Bool
1051−isOrderedListMarker xs = not (T.null xs) && (T.last xs `elem` ['.',')']) &&
1052− isRight (runParser (anyOrderedListMarker >> eof)
1053− defaultParserState "" xs)
1054−
1055−isRight :: Either a b -> Bool
1056−isRight (Right _) = True
1057−isRight (Left _) = False
1058−
1059−-- | Convert Pandoc inline element to markdown.
1060−inlineToMarkdown :: PandocMonad m => WriterOptions -> Inline -> MD m (Doc Text)
1061−inlineToMarkdown opts (Span ("",["emoji"],kvs) [Str s]) =
1062− case lookup "data-emoji" kvs of
1063− Just emojiname | isEnabled Ext_emoji opts ->
1064− return $ ":" <> literal emojiname <> ":"
1065− _ -> inlineToMarkdown opts (Str s)
1066−inlineToMarkdown opts (Span attrs ils) = do
1067− variant <- asks envVariant
1068− contents <- inlineListToMarkdown opts ils
1069− return $ case attrs of
1070− (_,["csl-block"],_) -> (cr <>)
1071− (_,["csl-left-margin"],_) -> (cr <>)
1072− (_,["csl-indent"],_) -> (cr <>)
1073− _ -> id
1074− $ case variant of
1075− PlainText -> contents
1076− _ | attrs == nullAttr -> contents
1077− | isEnabled Ext_bracketed_spans opts ->
1078− let attrs' = if attrs /= nullAttr
1079− then attrsToMarkdown attrs
1080− else empty
1081− in "[" <> contents <> "]" <> attrs'
1082− | isEnabled Ext_raw_html opts ||
1083− isEnabled Ext_native_spans opts ->
1084− tagWithAttrs "span" attrs <> contents <> literal "</span>"
1085− | otherwise -> contents
1086−inlineToMarkdown _ (Emph []) = return empty
1087−inlineToMarkdown opts (Emph lst) = do
1088− variant <- asks envVariant
1089− contents <- inlineListToMarkdown opts lst
1090− return $ case variant of
1091− PlainText
1092− | isEnabled Ext_gutenberg opts -> "_" <> contents <> "_"
1093− | otherwise -> contents
1094− _ -> "*" <> contents <> "*"
1095−inlineToMarkdown _ (Underline []) = return empty
1096−inlineToMarkdown opts (Underline lst) = do
1097− variant <- asks envVariant
1098− contents <- inlineListToMarkdown opts lst
1099− case variant of
1100− PlainText -> return contents
1101− _ | isEnabled Ext_bracketed_spans opts ->
1102− return $ "[" <> contents <> "]" <> "{.ul}"
1103− | isEnabled Ext_native_spans opts ->
1104− return $ tagWithAttrs "span" ("", ["underline"], [])
1105− <> contents
1106− <> literal "</span>"
1107− | isEnabled Ext_raw_html opts ->
1108− return $ "<u>" <> contents <> "</u>"
1109− | otherwise -> inlineToMarkdown opts (Emph lst)
1110−inlineToMarkdown _ (Strong []) = return empty
1111−inlineToMarkdown opts (Strong lst) = do
1112− variant <- asks envVariant
1113− case variant of
1114− PlainText ->
1115− inlineListToMarkdown opts $
1116− if isEnabled Ext_gutenberg opts
1117− then capitalize lst
1118− else lst
1119− _ -> do
1120− contents <- inlineListToMarkdown opts lst
1121− return $ "**" <> contents <> "**"
1122−inlineToMarkdown _ (Strikeout []) = return empty
1123−inlineToMarkdown opts (Strikeout lst) = do
1124− contents <- inlineListToMarkdown opts lst
1125− return $ if isEnabled Ext_strikeout opts
1126− then "~~" <> contents <> "~~"
1127− else if isEnabled Ext_raw_html opts
1128− then "<s>" <> contents <> "</s>"
1129− else contents
1130−inlineToMarkdown _ (Superscript []) = return empty
1131−inlineToMarkdown opts (Superscript lst) =
1132− local (\env -> env {envEscapeSpaces = envVariant env == Markdown}) $ do
1133− contents <- inlineListToMarkdown opts lst
1134− if isEnabled Ext_superscript opts
1135− then return $ "^" <> contents <> "^"
1136− else if isEnabled Ext_raw_html opts
1137− then return $ "<sup>" <> contents <> "</sup>"
1138− else
1139− case traverse toSuperscriptInline lst of
1140− Just xs' | not (writerPreferAscii opts)
1141− -> inlineListToMarkdown opts xs'
1142− _ -> do
1143− let rendered = render Nothing contents
1144− return $
1145− case mapM toSuperscript (T.unpack rendered) of
1146− Just r -> literal $ T.pack r
1147− Nothing -> literal $ "^(" <> rendered <> ")"
1148−inlineToMarkdown _ (Subscript []) = return empty
1149−inlineToMarkdown opts (Subscript lst) =
1150− local (\env -> env {envEscapeSpaces = envVariant env == Markdown}) $ do
1151− contents <- inlineListToMarkdown opts lst
1152− if isEnabled Ext_subscript opts
1153− then return $ "~" <> contents <> "~"
1154− else if isEnabled Ext_raw_html opts
1155− then return $ "<sub>" <> contents <> "</sub>"
1156− else
1157− case traverse toSubscriptInline lst of
1158− Just xs' | not (writerPreferAscii opts)
1159− -> inlineListToMarkdown opts xs'
1160− _ -> do
1161− let rendered = render Nothing contents
1162− return $
1163− case mapM toSuperscript (T.unpack rendered) of
1164− Just r -> literal $ T.pack r
1165− Nothing -> literal $ "_(" <> rendered <> ")"
1166−inlineToMarkdown opts (SmallCaps lst) = do
1167− variant <- asks envVariant
1168− if variant /= PlainText &&
1169− (isEnabled Ext_raw_html opts || isEnabled Ext_native_spans opts)
1170− then inlineToMarkdown opts (Span ("",["smallcaps"],[]) lst)
1171− else inlineListToMarkdown opts $ capitalize lst
1172−inlineToMarkdown opts (Quoted SingleQuote lst) = do
1173− contents <- inlineListToMarkdown opts lst
1174− return $ if isEnabled Ext_smart opts
1175− then "'" <> contents <> "'"
1176− else
1177− if writerPreferAscii opts
1178− then "‘" <> contents <> "’"
1179− else "‘" <> contents <> "’"
1180−inlineToMarkdown opts (Quoted DoubleQuote lst) = do
1181− contents <- inlineListToMarkdown opts lst
1182− return $ if isEnabled Ext_smart opts
1183− then "\"" <> contents <> "\""
1184− else
1185− if writerPreferAscii opts
1186− then "“" <> contents <> "”"
1187− else "“" <> contents <> "”"
1188−inlineToMarkdown opts (Code attr str) = do
1189− let tickGroups = filter (T.any (== '`')) $ T.group str
1190− let longest = if null tickGroups
1191− then 0
1192− else maximum $ map T.length tickGroups
1193− let marker = T.replicate (longest + 1) "`"
1194− let spacer = if longest == 0 then "" else " "
1195− let attrs = if isEnabled Ext_inline_code_attributes opts && attr /= nullAttr
1196− then attrsToMarkdown attr
1197− else empty
1198− variant <- asks envVariant
1199− case variant of
1200− PlainText -> return $ literal str
1201− _ -> return $ literal
1202− (marker <> spacer <> str <> spacer <> marker) <> attrs
1203−inlineToMarkdown opts (Str str) = do
1204− variant <- asks envVariant
1205− let str' = (if writerPreferAscii opts
1206− then toHtml5Entities
1207− else id) .
1208− (if isEnabled Ext_smart opts
1209− then unsmartify opts
1210− else id) .
1211− (if variant == PlainText
1212− then id
1213− else escapeText opts) $ str
1214− return $ literal str'
1215−inlineToMarkdown opts (Math InlineMath str) =
1216− case writerHTMLMathMethod opts of
1217− WebTeX url -> inlineToMarkdown opts
1218− (Image nullAttr [Str str] (url <> T.pack (urlEncode $ T.unpack str), str))
1219− _ | isEnabled Ext_tex_math_dollars opts ->
1220− return $ "$" <> literal str <> "$"
1221− | isEnabled Ext_tex_math_single_backslash opts ->
1222− return $ "\\(" <> literal str <> "\\)"
1223− | isEnabled Ext_tex_math_double_backslash opts ->
1224− return $ "\\\\(" <> literal str <> "\\\\)"
1225− | otherwise -> do
1226− variant <- asks envVariant
1227− texMathToInlines InlineMath str >>=
1228− inlineListToMarkdown opts .
1229− (if variant == PlainText then makeMathPlainer else id)
1230−inlineToMarkdown opts (Math DisplayMath str) =
1231− case writerHTMLMathMethod opts of
1232− WebTeX url -> (\x -> blankline <> x <> blankline) `fmap`
1233− inlineToMarkdown opts (Image nullAttr [Str str]
1234− (url <> T.pack (urlEncode $ T.unpack str), str))
1235− _ | isEnabled Ext_tex_math_dollars opts ->
1236− return $ "$$" <> literal str <> "$$"
1237− | isEnabled Ext_tex_math_single_backslash opts ->
1238− return $ "\\[" <> literal str <> "\\]"
1239− | isEnabled Ext_tex_math_double_backslash opts ->
1240− return $ "\\\\[" <> literal str <> "\\\\]"
1241− | otherwise -> (\x -> cr <> x <> cr) `fmap`
1242− (texMathToInlines DisplayMath str >>= inlineListToMarkdown opts)
1243−inlineToMarkdown opts il@(RawInline f str) = do
1244− let tickGroups = filter (T.any (== '`')) $ T.group str
1245− let numticks = if null tickGroups
1246− then 1
1247− else 1 + maximum (map T.length tickGroups)
1248− variant <- asks envVariant
1249− let Format fmt = f
1250− let rawAttribInline = return $
1251− literal (T.replicate numticks "`") <> literal str <>
1252− literal (T.replicate numticks "`") <> literal "{=" <> literal fmt <> literal "}"
1253− let renderEmpty = mempty <$ report (InlineNotRendered il)
1254− case variant of
1255− PlainText -> renderEmpty
1256− Commonmark
1257− | f `elem` ["gfm", "commonmark", "commonmark_x", "markdown"]
1258− -> return $ literal str
1259− Markdown
1260− | f `elem` ["markdown", "markdown_github", "markdown_phpextra",
1261− "markdown_mmd", "markdown_strict"]
1262− -> return $ literal str
1263− _ | isEnabled Ext_raw_attribute opts -> rawAttribInline
1264− | f `elem` ["html", "html5", "html4"]
1265− , isEnabled Ext_raw_html opts
1266− -> return $ literal str
1267− | f `elem` ["latex", "tex"]
1268− , isEnabled Ext_raw_tex opts
1269− -> return $ literal str
1270− _ -> renderEmpty
1271−
1272−
1273−inlineToMarkdown opts LineBreak = do
1274− variant <- asks envVariant
1275− if variant == PlainText || isEnabled Ext_hard_line_breaks opts
1276− then return cr
1277− else return $
1278− if isEnabled Ext_escaped_line_breaks opts
1279− then "\\" <> cr
1280− else " " <> cr
1281−inlineToMarkdown _ Space = do
1282− escapeSpaces <- asks envEscapeSpaces
1283− return $ if escapeSpaces then "\\ " else space
1284−inlineToMarkdown opts SoftBreak = do
1285− escapeSpaces <- asks envEscapeSpaces
1286− let space' = if escapeSpaces then "\\ " else space
1287− return $ case writerWrapText opts of
1288− WrapNone -> space'
1289− WrapAuto -> space'
1290− WrapPreserve -> cr
1291−inlineToMarkdown opts (Cite [] lst) = inlineListToMarkdown opts lst
1292−inlineToMarkdown opts (Cite (c:cs) lst)
1293− | not (isEnabled Ext_citations opts) = inlineListToMarkdown opts lst
1294− | otherwise =
1295− if citationMode c == AuthorInText
1296− then do
1297− suffs <- inlineListToMarkdown opts $ citationSuffix c
1298− rest <- mapM convertOne cs
1299− let inbr = suffs <+> joincits rest
1300− br = if isEmpty inbr then empty else char '[' <> inbr <> char ']'
1301− return $ literal ("@" <> citationId c) <+> br
1302− else do
1303− cits <- mapM convertOne (c:cs)
1304− return $ literal "[" <> joincits cits <> literal "]"
1305− where
1306− joincits = hcat . intersperse (literal "; ") . filter (not . isEmpty)
1307− convertOne Citation { citationId = k
1308− , citationPrefix = pinlines
1309− , citationSuffix = sinlines
1310− , citationMode = m }
1311− = do
1312− pdoc <- inlineListToMarkdown opts pinlines
1313− sdoc <- inlineListToMarkdown opts sinlines
1314− let k' = literal (modekey m <> "@" <> k)
1315− r = case sinlines of
1316− Str (T.uncons -> Just (y,_)):_ | y `elem` (",;]@" :: String) -> k' <> sdoc
1317− _ -> k' <+> sdoc
1318− return $ pdoc <+> r
1319− modekey SuppressAuthor = "-"
1320− modekey _ = ""
1321−inlineToMarkdown opts lnk@(Link attr txt (src, tit)) = do
1322− variant <- asks envVariant
1323− linktext <- inlineListToMarkdown opts txt
1324− let linktitle = if T.null tit
1325− then empty
1326− else literal $ " \"" <> tit <> "\""
1327− let srcSuffix = fromMaybe src (T.stripPrefix "mailto:" src)
1328− let useAuto = isURI src &&
1329− case txt of
1330− [Str s] | escapeURI s == srcSuffix -> True
1331− _ -> False
1332− let useRefLinks = writerReferenceLinks opts && not useAuto
1333− shortcutable <- asks envRefShortcutable
1334− let useShortcutRefLinks = shortcutable &&
1335− isEnabled Ext_shortcut_reference_links opts
1336− reftext <- if useRefLinks
1337− then literal <$> getReference attr linktext (src, tit)
1338− else return mempty
1339− case variant of
1340− PlainText
1341− | useAuto -> return $ literal srcSuffix
1342− | otherwise -> return linktext
1343− _ | useAuto -> return $ "<" <> literal srcSuffix <> ">"
1344− | useRefLinks ->
1345− let first = "[" <> linktext <> "]"
1346− second = if getKey linktext == getKey reftext
1347− then if useShortcutRefLinks
1348− then ""
1349− else "[]"
1350− else "[" <> reftext <> "]"
1351− in return $ first <> second
1352− | isEnabled Ext_raw_html opts
1353− , not (isEnabled Ext_link_attributes opts)
1354− , attr /= nullAttr -> -- use raw HTML to render attributes
1355− literal . T.strip <$>
1356− writeHtml5String opts{ writerTemplate = Nothing }
1357− (Pandoc nullMeta [Plain [lnk]])
1358− | otherwise -> return $
1359− "[" <> linktext <> "](" <> literal src <> linktitle <> ")" <>
1360− linkAttributes opts attr
1361−inlineToMarkdown opts img@(Image attr alternate (source, tit))
1362− | isEnabled Ext_raw_html opts &&
1363− not (isEnabled Ext_link_attributes opts) &&
1364− attr /= nullAttr = -- use raw HTML
1365− literal . T.strip <$>
1366− writeHtml5String opts{ writerTemplate = Nothing } (Pandoc nullMeta [Plain [img]])
1367− | otherwise = do
1368− variant <- asks envVariant
1369− let txt = if null alternate || alternate == [Str source]
1370− -- to prevent autolinks
1371− then [Str ""]
1372− else alternate
1373− linkPart <- inlineToMarkdown opts (Link attr txt (source, tit))
1374− return $ case variant of
1375− PlainText -> "[" <> linkPart <> "]"
1376− _ -> "!" <> linkPart
1377−inlineToMarkdown opts (Note contents) = do
1378− modify (\st -> st{ stNotes = contents : stNotes st })
1379− st <- get
1380− let ref = literal $ writerIdentifierPrefix opts <> tshow (stNoteNum st + length (stNotes st) - 1)
1381− if isEnabled Ext_footnotes opts
1382− then return $ "[^" <> ref <> "]"
1383− else return $ "[" <> ref <> "]"
1384−
1385−makeMathPlainer :: [Inline] -> [Inline]
1386−makeMathPlainer = walk go
1387− where
1388− go (Emph xs) = Span nullAttr xs
1389− go x = x
1390−
1391822lineBreakToSpace :: Inline -> Inline
1392823lineBreakToSpace LineBreak = Space
1393824lineBreakToSpace SoftBreak = Space
1394825lineBreakToSpace x = x
1395−
1396−toSubscriptInline :: Inline -> Maybe Inline
1397−toSubscriptInline Space = Just Space
1398−toSubscriptInline (Span attr ils) = Span attr <$> traverse toSubscriptInline ils
1399−toSubscriptInline (Str s) = Str . T.pack <$> traverse toSubscript (T.unpack s)
1400−toSubscriptInline LineBreak = Just LineBreak
1401−toSubscriptInline SoftBreak = Just SoftBreak
1402−toSubscriptInline _ = Nothing
1403−
1404−toSuperscriptInline :: Inline -> Maybe Inline
1405−toSuperscriptInline Space = Just Space
1406−toSuperscriptInline (Span attr ils) = Span attr <$> traverse toSuperscriptInline ils
1407−toSuperscriptInline (Str s) = Str . T.pack <$> traverse toSuperscript (T.unpack s)
1408−toSuperscriptInline LineBreak = Just LineBreak
1409−toSuperscriptInline SoftBreak = Just SoftBreak
1410−toSuperscriptInline _ = Nothing