2008-08-12 01:23:17 +00:00
|
|
|
{-# LANGUAGE PatternGuards #-}
|
2008-08-04 03:15:34 +00:00
|
|
|
{-
|
|
|
|
Copyright (C) 2008 Andrea Rossato <andrea.rossato@ing.unitn.it>
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
-}
|
|
|
|
|
|
|
|
{- |
|
|
|
|
Module : Text.Pandoc.Biblio
|
2011-01-01 10:26:10 -08:00
|
|
|
Copyright : Copyright (C) 2008-2010 Andrea Rossato
|
2008-08-04 03:15:34 +00:00
|
|
|
License : GNU GPL, version 2 or above
|
|
|
|
|
2010-10-27 18:22:45 -07:00
|
|
|
Maintainer : Andrea Rossato <andrea.rossato@unitn.it>
|
2008-08-04 03:15:34 +00:00
|
|
|
Stability : alpha
|
|
|
|
Portability : portable
|
|
|
|
-}
|
|
|
|
|
|
|
|
module Text.Pandoc.Biblio ( processBiblio ) where
|
|
|
|
|
|
|
|
import Data.List
|
2010-10-27 18:22:45 -07:00
|
|
|
import Data.Unique
|
2010-12-13 21:33:08 +01:00
|
|
|
import Data.Char ( isDigit, isPunctuation )
|
2010-11-11 22:35:04 -08:00
|
|
|
import qualified Data.Map as M
|
2010-10-27 18:22:45 -07:00
|
|
|
import Text.CSL hiding ( Cite(..), Citation(..) )
|
|
|
|
import qualified Text.CSL as CSL ( Cite(..) )
|
2008-08-04 03:15:34 +00:00
|
|
|
import Text.Pandoc.Definition
|
2010-12-24 13:39:27 -08:00
|
|
|
import Text.Pandoc.Generic
|
2010-11-27 07:08:32 -08:00
|
|
|
import Text.Pandoc.Shared (stringify)
|
|
|
|
import Text.ParserCombinators.Parsec
|
|
|
|
import Control.Monad
|
2008-08-04 03:15:34 +00:00
|
|
|
|
2008-08-12 00:52:08 +00:00
|
|
|
-- | Process a 'Pandoc' document by adding citations formatted
|
|
|
|
-- according to a CSL style, using 'citeproc' from citeproc-hs.
|
2010-11-20 08:11:30 -08:00
|
|
|
processBiblio :: FilePath -> [Reference] -> Pandoc -> IO Pandoc
|
|
|
|
processBiblio cslfile r p
|
2008-08-04 03:15:34 +00:00
|
|
|
= if null r then return p
|
|
|
|
else do
|
2010-11-20 08:11:30 -08:00
|
|
|
csl <- readCSLFile cslfile
|
2010-12-24 13:39:27 -08:00
|
|
|
p' <- bottomUpM setHash p
|
2010-11-20 22:00:17 -08:00
|
|
|
let (nts,grps) = if styleClass csl == "note"
|
|
|
|
then let cits = queryWith getCite p'
|
2010-11-02 21:10:33 -07:00
|
|
|
ncits = map (queryWith getCite) $ queryWith getNote p'
|
|
|
|
needNt = cits \\ concat ncits
|
|
|
|
in (,) needNt $ getNoteCitations needNt p'
|
2010-11-20 22:00:17 -08:00
|
|
|
else (,) [] $ queryWith getCitation p'
|
2010-11-27 11:28:11 -08:00
|
|
|
result = citeproc procOpts csl r (setNearNote csl $
|
|
|
|
map (map toCslCite) grps)
|
2010-11-11 22:35:04 -08:00
|
|
|
cits_map = M.fromList $ zip grps (citations result)
|
2010-11-06 14:43:23 -07:00
|
|
|
biblioList = map (renderPandoc' csl) (bibliography result)
|
2010-12-24 13:39:27 -08:00
|
|
|
Pandoc m b = bottomUp (procInlines $ processCite csl cits_map) p'
|
2010-11-02 21:10:33 -07:00
|
|
|
return . generateNotes nts . Pandoc m $ b ++ biblioList
|
2008-08-04 03:15:34 +00:00
|
|
|
|
2008-08-12 00:52:08 +00:00
|
|
|
-- | Substitute 'Cite' elements with formatted citations.
|
2010-11-11 22:35:04 -08:00
|
|
|
processCite :: Style -> M.Map [Citation] [FormattedOutput] -> [Inline] -> [Inline]
|
2010-11-11 21:30:34 -08:00
|
|
|
processCite _ _ [] = []
|
|
|
|
processCite s cs (i:is)
|
|
|
|
| Cite t _ <- i = process t ++ processCite s cs is
|
|
|
|
| otherwise = i : processCite s cs is
|
2008-08-04 03:15:34 +00:00
|
|
|
where
|
2010-11-16 07:15:30 -08:00
|
|
|
addNt t x = if null x then [] else [Cite t $ renderPandoc s x]
|
2010-11-11 22:35:04 -08:00
|
|
|
process t = case M.lookup t cs of
|
2010-11-11 21:30:34 -08:00
|
|
|
Just x -> if isTextualCitation t && x /= []
|
2010-11-28 07:55:33 -08:00
|
|
|
then renderPandoc s [head x] ++
|
|
|
|
if tail x /= []
|
|
|
|
then Space : addNt t (tail x)
|
|
|
|
else []
|
2010-11-16 07:15:30 -08:00
|
|
|
else [Cite t $ renderPandoc s x]
|
2008-08-04 03:15:34 +00:00
|
|
|
Nothing -> [Str ("Error processing " ++ show t)]
|
|
|
|
|
2010-11-11 21:30:34 -08:00
|
|
|
isTextualCitation :: [Citation] -> Bool
|
|
|
|
isTextualCitation (c:_) = citationMode c == AuthorInText
|
|
|
|
isTextualCitation _ = False
|
|
|
|
|
2008-08-12 00:52:08 +00:00
|
|
|
-- | Retrieve all citations from a 'Pandoc' docuument. To be used with
|
2009-02-28 07:39:45 +00:00
|
|
|
-- 'queryWith'.
|
2010-10-27 18:22:45 -07:00
|
|
|
getCitation :: Inline -> [[Citation]]
|
|
|
|
getCitation i | Cite t _ <- i = [t]
|
2010-11-02 21:10:33 -07:00
|
|
|
| otherwise = []
|
2010-10-27 18:22:45 -07:00
|
|
|
|
|
|
|
getNote :: Inline -> [Inline]
|
|
|
|
getNote i | Note _ <- i = [i]
|
|
|
|
| otherwise = []
|
|
|
|
|
|
|
|
getCite :: Inline -> [Inline]
|
|
|
|
getCite i | Cite _ _ <- i = [i]
|
2008-08-04 03:15:34 +00:00
|
|
|
| otherwise = []
|
2010-10-27 18:22:45 -07:00
|
|
|
|
2010-11-02 21:10:33 -07:00
|
|
|
getNoteCitations :: [Inline] -> Pandoc -> [[Citation]]
|
|
|
|
getNoteCitations needNote
|
|
|
|
= let mvCite i = if i `elem` needNote then Note [Para [i]] else i
|
2010-12-24 13:39:27 -08:00
|
|
|
setNote = bottomUp mvCite
|
2010-11-02 21:10:33 -07:00
|
|
|
getCits = concat . flip (zipWith $ setCiteNoteNum) [1..] .
|
|
|
|
map (queryWith getCite) . queryWith getNote . setNote
|
|
|
|
in queryWith getCitation . getCits
|
2010-10-27 18:22:45 -07:00
|
|
|
|
|
|
|
setHash :: Citation -> IO Citation
|
2010-11-26 12:06:56 -08:00
|
|
|
setHash (Citation i p s cm nn _)
|
|
|
|
= hashUnique `fmap` newUnique >>= return . Citation i p s cm nn
|
2010-10-27 18:22:45 -07:00
|
|
|
|
2010-11-02 21:10:33 -07:00
|
|
|
generateNotes :: [Inline] -> Pandoc -> Pandoc
|
2010-12-24 13:39:27 -08:00
|
|
|
generateNotes needNote = bottomUp (mvCiteInNote needNote)
|
2010-10-27 18:22:45 -07:00
|
|
|
|
2010-11-02 21:10:33 -07:00
|
|
|
procInlines :: ([Inline] -> [Inline]) -> Block -> Block
|
|
|
|
procInlines f b
|
|
|
|
| Plain inls <- b = Plain $ f inls
|
|
|
|
| Para inls <- b = Para $ f inls
|
|
|
|
| Header i inls <- b = Header i $ f inls
|
|
|
|
| otherwise = b
|
|
|
|
|
|
|
|
mvCiteInNote :: [Inline] -> Block -> Block
|
|
|
|
mvCiteInNote is = procInlines mvCite
|
|
|
|
where
|
|
|
|
mvCite :: [Inline] -> [Inline]
|
|
|
|
mvCite inls
|
2010-11-06 14:43:23 -07:00
|
|
|
| x:i:xs <- inls, startWithPunct xs
|
2010-11-11 21:30:34 -08:00
|
|
|
, x == Space, i `elem_` is = switch i xs ++ mvCite (tailFirstInlineStr xs)
|
2010-11-02 21:10:33 -07:00
|
|
|
| x:i:xs <- inls
|
2010-11-11 21:30:34 -08:00
|
|
|
, x == Space, i `elem_` is = mvInNote i : mvCite xs
|
2010-11-03 12:58:29 -07:00
|
|
|
| i:xs <- inls, i `elem_` is
|
2010-11-11 21:30:34 -08:00
|
|
|
, startWithPunct xs = switch i xs ++ mvCite (tailFirstInlineStr xs)
|
|
|
|
| i:xs <- inls, Note _ <- i = checkNt i : mvCite xs
|
|
|
|
| i:xs <- inls = i : mvCite xs
|
2010-11-02 21:10:33 -07:00
|
|
|
| otherwise = []
|
2010-11-11 21:30:34 -08:00
|
|
|
elem_ x xs = case x of Cite cs _ -> (Cite cs []) `elem` xs; _ -> False
|
|
|
|
switch i xs = Str (headInline xs) : mvInNote i : []
|
2010-11-02 21:10:33 -07:00
|
|
|
mvInNote i
|
2010-11-03 12:58:29 -07:00
|
|
|
| Cite t o <- i = Note [Para [Cite t $ sanitize o]]
|
|
|
|
| otherwise = Note [Para [i ]]
|
|
|
|
sanitize i
|
2010-11-11 21:30:34 -08:00
|
|
|
| endWithPunct i = toCapital i
|
|
|
|
| otherwise = toCapital (i ++ [Str "."])
|
2010-11-03 12:58:29 -07:00
|
|
|
|
2010-11-02 21:10:33 -07:00
|
|
|
checkPt i
|
|
|
|
| Cite c o : xs <- i
|
2010-11-06 14:43:23 -07:00
|
|
|
, endWithPunct o, startWithPunct xs
|
|
|
|
, endWithPunct o = Cite c (initInline o) : checkPt xs
|
|
|
|
| x:xs <- i = x : checkPt xs
|
|
|
|
| otherwise = []
|
2010-12-24 13:39:27 -08:00
|
|
|
checkNt = bottomUp $ procInlines checkPt
|
2010-11-02 21:10:33 -07:00
|
|
|
|
2010-10-27 18:22:45 -07:00
|
|
|
setCiteNoteNum :: [Inline] -> Int -> [Inline]
|
|
|
|
setCiteNoteNum ((Cite cs o):xs) n = Cite (setCitationNoteNum n cs) o : setCiteNoteNum xs n
|
|
|
|
setCiteNoteNum _ _ = []
|
|
|
|
|
|
|
|
setCitationNoteNum :: Int -> [Citation] -> [Citation]
|
|
|
|
setCitationNoteNum i = map $ \c -> c { citationNoteNum = i}
|
|
|
|
|
|
|
|
toCslCite :: Citation -> CSL.Cite
|
2010-11-16 20:31:22 -08:00
|
|
|
toCslCite c
|
2010-11-26 12:06:56 -08:00
|
|
|
= let (l, s) = locatorWords $ citationSuffix c
|
2010-11-27 07:08:32 -08:00
|
|
|
(la,lo) = parseLocator l
|
2010-11-16 20:31:22 -08:00
|
|
|
citMode = case citationMode c of
|
2010-11-11 21:30:34 -08:00
|
|
|
AuthorInText -> (True, False)
|
2010-11-06 14:43:23 -07:00
|
|
|
SuppressAuthor -> (False,True )
|
|
|
|
NormalCitation -> (False,False)
|
2010-12-13 21:33:08 +01:00
|
|
|
s' = case s of
|
|
|
|
[] -> []
|
|
|
|
(Str (y:_) : _) | isPunctuation y -> s
|
|
|
|
_ -> Str "," : Space : s
|
2010-11-16 20:31:22 -08:00
|
|
|
in emptyCite { CSL.citeId = citationId c
|
2010-11-17 15:36:17 -08:00
|
|
|
, CSL.citePrefix = PandocText $ citationPrefix c
|
2010-12-13 21:33:08 +01:00
|
|
|
, CSL.citeSuffix = PandocText $ s'
|
2010-10-27 18:22:45 -07:00
|
|
|
, CSL.citeLabel = la
|
|
|
|
, CSL.citeLocator = lo
|
2010-11-16 20:31:22 -08:00
|
|
|
, CSL.citeNoteNumber = show $ citationNoteNum c
|
2010-11-11 21:30:34 -08:00
|
|
|
, CSL.authorInText = fst citMode
|
2010-11-06 14:43:23 -07:00
|
|
|
, CSL.suppressAuthor = snd citMode
|
2010-11-16 20:31:22 -08:00
|
|
|
, CSL.citeHash = citationHash c
|
2010-10-27 18:22:45 -07:00
|
|
|
}
|
2010-11-26 12:06:56 -08:00
|
|
|
|
2010-11-27 07:08:32 -08:00
|
|
|
locatorWords :: [Inline] -> (String, [Inline])
|
|
|
|
locatorWords inp =
|
2010-11-27 11:28:28 -08:00
|
|
|
case parse pLocatorWords "suffix" inp of
|
2010-11-27 07:08:32 -08:00
|
|
|
Right r -> r
|
|
|
|
Left _ -> ("",inp)
|
|
|
|
|
2010-11-27 11:28:28 -08:00
|
|
|
pLocatorWords :: GenParser Inline st (String, [Inline])
|
|
|
|
pLocatorWords = do
|
|
|
|
l <- pLocator
|
|
|
|
s <- getInput -- rest is suffix
|
|
|
|
if length l > 0 && last l == ','
|
|
|
|
then return (init l, Str "," : s)
|
|
|
|
else return (l, s)
|
|
|
|
|
2010-11-27 07:08:32 -08:00
|
|
|
pMatch :: (Inline -> Bool) -> GenParser Inline st Inline
|
|
|
|
pMatch condition = try $ do
|
|
|
|
t <- anyToken
|
|
|
|
guard $ condition t
|
|
|
|
return t
|
|
|
|
|
|
|
|
pSpace :: GenParser Inline st Inline
|
|
|
|
pSpace = pMatch (== Space)
|
|
|
|
|
|
|
|
pLocator :: GenParser Inline st String
|
|
|
|
pLocator = try $ do
|
|
|
|
optional $ pMatch (== Str ",")
|
|
|
|
optional pSpace
|
|
|
|
f <- many1 (notFollowedBy pSpace >> anyToken)
|
|
|
|
gs <- many1 pWordWithDigits
|
|
|
|
return $ stringify f ++ (' ' : unwords gs)
|
|
|
|
|
|
|
|
pWordWithDigits :: GenParser Inline st String
|
|
|
|
pWordWithDigits = try $ do
|
|
|
|
pSpace
|
|
|
|
r <- many1 (notFollowedBy pSpace >> anyToken)
|
|
|
|
let s = stringify r
|
|
|
|
guard $ any isDigit s
|
|
|
|
return s
|
2010-11-26 12:06:56 -08:00
|
|
|
|