git.iko.soy/typeable/pandoc
on master help sign in
Split out T.P.Writers.LaTeX.Citation.
commit190765f867a7dff079202bcc6fa466a18e83f74e authorJohn MacFarlane <jgm@berkeley.edu> dateTue 2 Mar 2021, 21:57 (6 years ago) parent3f52e83
3 files changed+188−142
pandoc.cabal+1 src/Text/Pandoc/Writers/LaTeX.hs+6−142 src/Text/Pandoc/Writers/LaTeX/Citation.hs+181
pandoc.cabal+1
@@ -666,6 +666,7 @@ library
666666 Text.Pandoc.Writers.LaTeX.Table,
667667 Text.Pandoc.Writers.LaTeX.Lang,
668668 Text.Pandoc.Writers.LaTeX.Types,
669+ Text.Pandoc.Writers.LaTeX.Citation,
669670 Text.Pandoc.Writers.Markdown.Types,
670671 Text.Pandoc.Writers.Markdown.Inline,
671672 Text.Pandoc.Writers.Roff,
src/Text/Pandoc/Writers/LaTeX.hs+6−142
@@ -20,9 +20,8 @@ module Text.Pandoc.Writers.LaTeX (
2020 ) where
2121import Control.Applicative ((<|>))
2222import Control.Monad.State.Strict
23−import Data.Char (isAlphaNum, isAscii, isDigit, isLetter, isSpace,
24− isPunctuation, ord)
25−import Data.List (foldl', intersperse, nubBy, (\\), uncons)
23+import Data.Char (isAlphaNum, isAscii, isDigit, isLetter, isSpace, ord)
24+import Data.List (intersperse, nubBy, (\\), uncons)
2625import Data.Maybe (catMaybes, fromMaybe, isJust, mapMaybe, isNothing)
2726import qualified Data.Map as M
2827import Data.Text (Text)
@@ -44,6 +43,8 @@ import Text.Pandoc.Slides
4443import Text.Pandoc.Walk (query, walk, walkM)
4544import Text.Pandoc.Writers.LaTeX.Caption (getCaption)
4645import Text.Pandoc.Writers.LaTeX.Table (tableToLaTeX)
46+import Text.Pandoc.Writers.LaTeX.Citation (citationsToNatbib,
47+ citationsToBiblatex)
4748import Text.Pandoc.Writers.LaTeX.Types (LW, WriterState (..), startingState)
4849import Text.Pandoc.Writers.LaTeX.Lang (toPolyglossiaEnv, toPolyglossia,
4950 toBabel)
@@ -1026,8 +1027,8 @@ inlineToLaTeX (Cite cits lst) = do
10261027 st <- get
10271028 let opts = stOptions st
10281029 case writerCiteMethod opts of
1029− Natbib -> citationsToNatbib cits
1030− Biblatex -> citationsToBiblatex cits
1030+ Natbib -> citationsToNatbib inlineListToLaTeX cits
1031+ Biblatex -> citationsToBiblatex inlineListToLaTeX cits
10311032 _ -> inlineListToLaTeX lst
10321033
10331034inlineToLaTeX (Code (_,classes,kvs) str) = do
@@ -1238,143 +1239,6 @@ protectCode x = [x]
12381239setEmptyLine :: PandocMonad m => Bool -> LW m ()
12391240setEmptyLine b = modify $ \st -> st{ stEmptyLine = b }
12401241
1241−citationsToNatbib :: PandocMonad m => [Citation] -> LW m (Doc Text)
1242−citationsToNatbib
1243− [one]
1244− = citeCommand c p s k
1245− where
1246− Citation { citationId = k
1247− , citationPrefix = p
1248− , citationSuffix = s
1249− , citationMode = m
1250− }
1251− = one
1252− c = case m of
1253− AuthorInText -> "citet"
1254− SuppressAuthor -> "citeyearpar"
1255− NormalCitation -> "citep"
1256−
1257−citationsToNatbib cits
1258− | noPrefix (tail cits) && noSuffix (init cits) && ismode NormalCitation cits
1259− = citeCommand "citep" p s ks
1260− where
1261− noPrefix = all (null . citationPrefix)
1262− noSuffix = all (null . citationSuffix)
1263− ismode m = all ((==) m . citationMode)
1264− p = citationPrefix $
1265− head cits
1266− s = citationSuffix $
1267− last cits
1268− ks = T.intercalate ", " $ map citationId cits
1269−
1270−citationsToNatbib (c:cs) | citationMode c == AuthorInText = do
1271− author <- citeCommand "citeauthor" [] [] (citationId c)
1272− cits <- citationsToNatbib (c { citationMode = SuppressAuthor } : cs)
1273− return $ author <+> cits
1274−
1275−citationsToNatbib cits = do
1276− cits' <- mapM convertOne cits
1277− return $ text "\\citetext{" <> foldl' combineTwo empty cits' <> text "}"
1278− where
1279− combineTwo a b | isEmpty a = b
1280− | otherwise = a <> text "; " <> b
1281− convertOne Citation { citationId = k
1282− , citationPrefix = p
1283− , citationSuffix = s
1284− , citationMode = m
1285− }
1286− = case m of
1287− AuthorInText -> citeCommand "citealt" p s k
1288− SuppressAuthor -> citeCommand "citeyear" p s k
1289− NormalCitation -> citeCommand "citealp" p s k
1290−
1291−citeCommand :: PandocMonad m
1292− => Text -> [Inline] -> [Inline] -> Text -> LW m (Doc Text)
1293−citeCommand c p s k = do
1294− args <- citeArguments p s k
1295− return $ literal ("\\" <> c) <> args
1296−
1297−type Prefix = [Inline]
1298−type Suffix = [Inline]
1299−type CiteId = Text
1300−data CiteGroup = CiteGroup Prefix Suffix [CiteId]
1301−
1302−citeArgumentsList :: PandocMonad m
1303− => CiteGroup -> LW m (Doc Text)
1304−citeArgumentsList (CiteGroup _ _ []) = return empty
1305−citeArgumentsList (CiteGroup pfxs sfxs ids) = do
1306− pdoc <- inlineListToLaTeX pfxs
1307− sdoc <- inlineListToLaTeX sfxs'
1308− return $ optargs pdoc sdoc <>
1309− braces (literal (T.intercalate "," (reverse ids)))
1310− where sfxs' = stripLocatorBraces $ case sfxs of
1311− (Str t : r) -> case T.uncons t of
1312− Just (x, xs)
1313− | T.null xs
1314− , isPunctuation x -> dropWhile (== Space) r
1315− | isPunctuation x -> Str xs : r
1316− _ -> sfxs
1317− _ -> sfxs
1318− optargs pdoc sdoc = case (isEmpty pdoc, isEmpty sdoc) of
1319− (True, True ) -> empty
1320− (True, False) -> brackets sdoc
1321− (_ , _ ) -> brackets pdoc <> brackets sdoc
1322−
1323−citeArguments :: PandocMonad m
1324− => [Inline] -> [Inline] -> Text -> LW m (Doc Text)
1325−citeArguments p s k = citeArgumentsList (CiteGroup p s [k])
1326−
1327−-- strip off {} used to define locator in pandoc-citeproc; see #5722
1328−stripLocatorBraces :: [Inline] -> [Inline]
1329−stripLocatorBraces = walk go
1330− where go (Str xs) = Str $ T.filter (\c -> c /= '{' && c /= '}') xs
1331− go x = x
1332−
1333−citationsToBiblatex :: PandocMonad m => [Citation] -> LW m (Doc Text)
1334−citationsToBiblatex
1335− [one]
1336− = citeCommand cmd p s k
1337− where
1338− Citation { citationId = k
1339− , citationPrefix = p
1340− , citationSuffix = s
1341− , citationMode = m
1342− } = one
1343− cmd = case m of
1344− SuppressAuthor -> "autocite*"
1345− AuthorInText -> "textcite"
1346− NormalCitation -> "autocite"
1347−
1348−citationsToBiblatex (c:cs)
1349− | all (\cit -> null (citationPrefix cit) && null (citationSuffix cit)) (c:cs)
1350− = do
1351− let cmd = case citationMode c of
1352− SuppressAuthor -> "\\autocite*"
1353− AuthorInText -> "\\textcite"
1354− NormalCitation -> "\\autocite"
1355− return $ text cmd <>
1356− braces (literal (T.intercalate "," (map citationId (c:cs))))
1357− | otherwise
1358− = do
1359− let cmd = case citationMode c of
1360− SuppressAuthor -> "\\autocites*"
1361− AuthorInText -> "\\textcites"
1362− NormalCitation -> "\\autocites"
1363−
1364− groups <- mapM citeArgumentsList (reverse (foldl' grouper [] (c:cs)))
1365−
1366− return $ text cmd <> mconcat groups
1367−
1368− where grouper prev cit = case prev of
1369− ((CiteGroup oPfx oSfx ids):rest)
1370− | null oSfx && null pfx -> CiteGroup oPfx sfx (cid:ids) : rest
1371− _ -> CiteGroup pfx sfx [cid] : prev
1372− where pfx = citationPrefix cit
1373− sfx = citationSuffix cit
1374− cid = citationId cit
1375−
1376−citationsToBiblatex _ = return empty
1377−
13781242-- Extract a key from divs and spans
13791243extract :: Text -> Block -> [Text]
13801244extract key (Div attr _) = lookKey key attr
src/Text/Pandoc/Writers/LaTeX/Citation.hsnew file+181
@@ -0,0 +1,181 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{- |
3+ Module : Text.Pandoc.Writers.LaTeX.Citation
4+ Copyright : Copyright (C) 2006-2021 John MacFarlane
5+ License : GNU GPL, version 2 or above
6+
7+ Maintainer : John MacFarlane <jgm@berkeley.edu>
8+ Stability : alpha
9+ Portability : portable
10+-}
11+module Text.Pandoc.Writers.LaTeX.Citation
12+ ( citationsToNatbib,
13+ citationsToBiblatex
14+ ) where
15+
16+import Data.Text (Text)
17+import Data.Char (isPunctuation)
18+import qualified Data.Text as T
19+import Text.Pandoc.Class.PandocMonad (PandocMonad)
20+import Text.Pandoc.Definition
21+import Data.List (foldl')
22+import Text.DocLayout (Doc, brackets, empty, (<+>), text, isEmpty, literal,
23+ braces)
24+import Text.Pandoc.Walk
25+import Text.Pandoc.Writers.LaTeX.Types ( LW )
26+
27+citationsToNatbib :: PandocMonad m
28+ => ([Inline] -> LW m (Doc Text))
29+ -> [Citation]
30+ -> LW m (Doc Text)
31+citationsToNatbib inlineListToLaTeX [one]
32+ = citeCommand inlineListToLaTeX c p s k
33+ where
34+ Citation { citationId = k
35+ , citationPrefix = p
36+ , citationSuffix = s
37+ , citationMode = m
38+ }
39+ = one
40+ c = case m of
41+ AuthorInText -> "citet"
42+ SuppressAuthor -> "citeyearpar"
43+ NormalCitation -> "citep"
44+
45+citationsToNatbib inlineListToLaTeX cits
46+ | noPrefix (tail cits) && noSuffix (init cits) && ismode NormalCitation cits
47+ = citeCommand inlineListToLaTeX "citep" p s ks
48+ where
49+ noPrefix = all (null . citationPrefix)
50+ noSuffix = all (null . citationSuffix)
51+ ismode m = all ((==) m . citationMode)
52+ p = citationPrefix $
53+ head cits
54+ s = citationSuffix $
55+ last cits
56+ ks = T.intercalate ", " $ map citationId cits
57+
58+citationsToNatbib inlineListToLaTeX (c:cs)
59+ | citationMode c == AuthorInText = do
60+ author <- citeCommand inlineListToLaTeX "citeauthor" [] [] (citationId c)
61+ cits <- citationsToNatbib inlineListToLaTeX
62+ (c { citationMode = SuppressAuthor } : cs)
63+ return $ author <+> cits
64+
65+citationsToNatbib inlineListToLaTeX cits = do
66+ cits' <- mapM convertOne cits
67+ return $ text "\\citetext{" <> foldl' combineTwo empty cits' <> text "}"
68+ where
69+ citeCommand' = citeCommand inlineListToLaTeX
70+ combineTwo a b | isEmpty a = b
71+ | otherwise = a <> text "; " <> b
72+ convertOne Citation { citationId = k
73+ , citationPrefix = p
74+ , citationSuffix = s
75+ , citationMode = m
76+ }
77+ = case m of
78+ AuthorInText -> citeCommand' "citealt" p s k
79+ SuppressAuthor -> citeCommand' "citeyear" p s k
80+ NormalCitation -> citeCommand' "citealp" p s k
81+
82+citeCommand :: PandocMonad m
83+ => ([Inline] -> LW m (Doc Text))
84+ -> Text
85+ -> [Inline]
86+ -> [Inline]
87+ -> Text
88+ -> LW m (Doc Text)
89+citeCommand inlineListToLaTeX c p s k = do
90+ args <- citeArguments inlineListToLaTeX p s k
91+ return $ literal ("\\" <> c) <> args
92+
93+type Prefix = [Inline]
94+type Suffix = [Inline]
95+type CiteId = Text
96+data CiteGroup = CiteGroup Prefix Suffix [CiteId]
97+
98+citeArgumentsList :: PandocMonad m
99+ => ([Inline] -> LW m (Doc Text))
100+ -> CiteGroup
101+ -> LW m (Doc Text)
102+citeArgumentsList _inlineListToLaTeX (CiteGroup _ _ []) = return empty
103+citeArgumentsList inlineListToLaTeX (CiteGroup pfxs sfxs ids) = do
104+ pdoc <- inlineListToLaTeX pfxs
105+ sdoc <- inlineListToLaTeX sfxs'
106+ return $ optargs pdoc sdoc <>
107+ braces (literal (T.intercalate "," (reverse ids)))
108+ where sfxs' = stripLocatorBraces $ case sfxs of
109+ (Str t : r) -> case T.uncons t of
110+ Just (x, xs)
111+ | T.null xs
112+ , isPunctuation x -> dropWhile (== Space) r
113+ | isPunctuation x -> Str xs : r
114+ _ -> sfxs
115+ _ -> sfxs
116+ optargs pdoc sdoc = case (isEmpty pdoc, isEmpty sdoc) of
117+ (True, True ) -> empty
118+ (True, False) -> brackets sdoc
119+ (_ , _ ) -> brackets pdoc <> brackets sdoc
120+
121+citeArguments :: PandocMonad m
122+ => ([Inline] -> LW m (Doc Text))
123+ -> [Inline]
124+ -> [Inline]
125+ -> Text
126+ -> LW m (Doc Text)
127+citeArguments inlineListToLaTeX p s k =
128+ citeArgumentsList inlineListToLaTeX (CiteGroup p s [k])
129+
130+-- strip off {} used to define locator in pandoc-citeproc; see #5722
131+stripLocatorBraces :: [Inline] -> [Inline]
132+stripLocatorBraces = walk go
133+ where go (Str xs) = Str $ T.filter (\c -> c /= '{' && c /= '}') xs
134+ go x = x
135+
136+citationsToBiblatex :: PandocMonad m
137+ => ([Inline] -> LW m (Doc Text))
138+ -> [Citation] -> LW m (Doc Text)
139+citationsToBiblatex inlineListToLaTeX [one]
140+ = citeCommand inlineListToLaTeX cmd p s k
141+ where
142+ Citation { citationId = k
143+ , citationPrefix = p
144+ , citationSuffix = s
145+ , citationMode = m
146+ } = one
147+ cmd = case m of
148+ SuppressAuthor -> "autocite*"
149+ AuthorInText -> "textcite"
150+ NormalCitation -> "autocite"
151+
152+citationsToBiblatex inlineListToLaTeX (c:cs)
153+ | all (\cit -> null (citationPrefix cit) && null (citationSuffix cit)) (c:cs)
154+ = do
155+ let cmd = case citationMode c of
156+ SuppressAuthor -> "\\autocite*"
157+ AuthorInText -> "\\textcite"
158+ NormalCitation -> "\\autocite"
159+ return $ text cmd <>
160+ braces (literal (T.intercalate "," (map citationId (c:cs))))
161+ | otherwise
162+ = do
163+ let cmd = case citationMode c of
164+ SuppressAuthor -> "\\autocites*"
165+ AuthorInText -> "\\textcites"
166+ NormalCitation -> "\\autocites"
167+
168+ groups <- mapM (citeArgumentsList inlineListToLaTeX)
169+ (reverse (foldl' grouper [] (c:cs)))
170+
171+ return $ text cmd <> mconcat groups
172+
173+ where grouper prev cit = case prev of
174+ ((CiteGroup oPfx oSfx ids):rest)
175+ | null oSfx && null pfx -> CiteGroup oPfx sfx (cid:ids) : rest
176+ _ -> CiteGroup pfx sfx [cid] : prev
177+ where pfx = citationPrefix cit
178+ sfx = citationSuffix cit
179+ cid = citationId cit
180+
181+citationsToBiblatex _ _ = return empty