citeproc 0.3.0.3 → 0.3.0.4
raw patch · 18 files changed
+922/−334 lines, 18 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- CHANGELOG.md +14/−0
- README.md +1/−1
- citeproc.cabal +1/−1
- locales/it-IT.xml +62/−31
- man/citeproc.1 +2/−2
- src/Citeproc/Eval.hs +381/−293
- test/Spec.hs +1/−1
- test/csl/collapse_AuthorCollapseNoDateSorted.txt +7/−1
- test/csl/disambiguate_PrimaryNameWithNonDroppingParticle.txt +57/−0
- test/csl/disambiguate_PrimaryNameWithParticle.txt +65/−0
- test/csl/name_InTextMarkupInitialize.txt +69/−0
- test/csl/name_InTextMarkupNormalizeInitials.txt +69/−0
- test/csl/position_IbidWithLocator.txt +1/−1
- test/csl/substitute_SubstituteOnlyOnceString.txt +43/−0
- test/csl/substitute_SubstituteOnlyOnceTerm.txt +49/−0
- test/csl/substitute_SubstituteOnlyOnceTermEmpty.txt +52/−0
- test/csl/substitute_SubstituteOnlyOnceVariable.txt +45/−0
- test/extra/pandoc6813.txt +3/−3
CHANGELOG.md view
@@ -1,5 +1,19 @@ # citeproc changelog +## 0.3.0.4++ * Improve disambiguation code. Add type signatures,+ move some functions to the top-level, and make the+ logic clearer and more efficient.+ * Re-render after each stage of ambiguity resolution+ instead of relying on analysis of names and dates.+ This is necessary especially for styles like+ chicago-note-bibliography which use titles in+ citations. Closes #44. No measurable+ performance impact.+ * Update test suite from upstream.+ * Update `it-IT` locale.+ ## 0.3.0.3 * Fix author-only citations (#43). We got bad results with some
README.md view
@@ -89,7 +89,7 @@ Although this library is much more accurate in implementing the CSL spec than pandoc-citeproc was, it still fails some of the-tests from the CSL test suite (63/845). However, most of the+tests from the CSL test suite (67/862). However, most of the failures are on minor corner cases, and in many cases the expected behavior goes beyond what is required by the CSL spec. (For example, we intentionally refrain from capitalizing
citeproc.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: citeproc-version: 0.3.0.3+version: 0.3.0.4 synopsis: Generates citations and bibliography from CSL styles. description: citeproc parses CSL style files and uses them to generate a list of formatted citations and bibliography
locales/it-IT.xml view
@@ -4,6 +4,9 @@ <translator> <name>FI App Development</name> </translator>+ <translator>+ <name>Monica Thuegaz</name>+ </translator> <rights license="http://creativecommons.org/licenses/by-sa/3.0/">This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License</rights> <updated>2012-07-04T23:31:02+00:00</updated> </info>@@ -25,12 +28,13 @@ <term name="anonymous">anonimo</term> <term name="anonymous" form="short">anon.</term> <term name="at">a</term>- <term name="available at">available at</term>+ <term name="available at">disponibile su</term>+ <term name="available at" form="short">su</term> <term name="by">di</term> <term name="circa">circa</term> <term name="circa" form="short">c.</term> <term name="cited">citato</term>- <term name="edition">+ <term name="edition" gender="feminine"> <single>edizione</single> <multiple>edizioni</multiple> </term>@@ -39,6 +43,8 @@ <term name="forthcoming">futuro</term> <term name="from">da</term> <term name="ibid">ibid.</term>+ <term name="ibid">ibidem</term>+ <term name="ibid" form="short">ibid.</term> <term name="in">in</term> <term name="in press">in stampa</term> <term name="internet">internet</term>@@ -46,7 +52,7 @@ <term name="letter">lettera</term> <term name="no date">senza data</term> <term name="no date" form="short">s.d.</term>- <term name="online">in linea</term>+ <term name="online">online</term> <term name="presented at">presentato al</term> <term name="reference"> <single>reference</single>@@ -57,8 +63,8 @@ <multiple>refs.</multiple> </term> <term name="retrieved">recuperato</term>- <term name="scale">scale</term>- <term name="version">version</term>+ <term name="scale">scala</term>+ <term name="version">versione</term> <!-- ANNO DOMINI; BEFORE CHRIST --> <term name="ad">d.C.</term>@@ -73,18 +79,40 @@ <!-- ORDINALS --> <term name="ordinal">º</term>+ <term name="ordinal" gender-form="masculine">º</term>+ <term name="ordinal" gender-form="feminine">ª</term> <!-- LONG ORDINALS --> <term name="long-ordinal-01">prima</term>+ <term name="long-ordinal-01" gender-form="masculine">primo</term>+ <term name="long-ordinal-01" gender-form="feminine">prima</term> <term name="long-ordinal-02">seconda</term>+ <term name="long-ordinal-02" gender-form="masculine">secondo</term>+ <term name="long-ordinal-02" gender-form="feminine">seconda</term> <term name="long-ordinal-03">terza</term>+ <term name="long-ordinal-03" gender-form="masculine">terzo</term>+ <term name="long-ordinal-03" gender-form="feminine">terza</term> <term name="long-ordinal-04">quarta</term>+ <term name="long-ordinal-04" gender-form="masculine">quarto</term>+ <term name="long-ordinal-04" gender-form="feminine">quarta</term> <term name="long-ordinal-05">quinta</term>+ <term name="long-ordinal-05" gender-form="masculine">quinto</term>+ <term name="long-ordinal-05" gender-form="feminine">quinta</term> <term name="long-ordinal-06">sesta</term>+ <term name="long-ordinal-06" gender-form="masculine">sesto</term>+ <term name="long-ordinal-06" gender-form="feminine">sesta</term> <term name="long-ordinal-07">settima</term>+ <term name="long-ordinal-07" gender-form="masculine">settimo</term>+ <term name="long-ordinal-07" gender-form="feminine">settima</term> <term name="long-ordinal-08">ottava</term>+ <term name="long-ordinal-08" gender-form="masculine">ottavo</term>+ <term name="long-ordinal-08" gender-form="feminine">ottava</term> <term name="long-ordinal-09">nona</term>+ <term name="long-ordinal-09" gender-form="masculine">nono</term>+ <term name="long-ordinal-09" gender-form="feminine">nona</term> <term name="long-ordinal-10">decima</term>+ <term name="long-ordinal-10" gender-form="masculine">decimo</term>+ <term name="long-ordinal-10" gender-form="feminine">decima</term> <!-- LONG LOCATOR FORMS --> <term name="book">@@ -107,7 +135,7 @@ <single>foglio</single> <multiple>fogli</multiple> </term>- <term name="issue">+ <term name="issue" gender="masculine"> <single>numero</single> <multiple>numeri</multiple> </term>@@ -151,14 +179,17 @@ <single>verso</single> <multiple>versi</multiple> </term>- <term name="volume">+ <term name="volume" gender="masculine"> <single>volume</single> <multiple>volumi</multiple> </term> <!-- SHORT LOCATOR FORMS --> <term name="book" form="short">lib.</term>- <term name="chapter" form="short">cap.</term>+ <term name="chapter" form="short">+ <single>cap.</single>+ <multiple>capp.</multiple>+ </term> <term name="column" form="short">col.</term> <term name="figure" form="short">fig.</term> <term name="folio" form="short">fgl.</term>@@ -187,7 +218,7 @@ </term> <term name="volume" form="short"> <single>vol.</single>- <multiple>vol.</multiple>+ <multiple>voll.</multiple> </term> <!-- SYMBOL LOCATOR FORMS -->@@ -214,8 +245,8 @@ <multiple>editors</multiple> </term> <term name="illustrator">- <single>illustrator</single>- <multiple>illustrators</multiple>+ <single>illustratore</single>+ <multiple>illustratori</multiple> </term> <term name="translator"> <single>traduttore</single>@@ -229,7 +260,7 @@ <!-- SHORT ROLE FORMS --> <term name="director" form="short"> <single>dir.</single>- <multiple>dirs.</multiple>+ <multiple>dir.</multiple> </term> <term name="editor" form="short"> <single>a c. di</single>@@ -237,11 +268,11 @@ </term> <term name="editorial-director" form="short"> <single>ed.</single>- <multiple>eds.</multiple>+ <multiple>ed.</multiple> </term> <term name="illustrator" form="short"> <single>ill.</single>- <multiple>ills.</multiple>+ <multiple>ill.</multiple> </term> <term name="translator" form="short"> <single>trad.</single>@@ -254,37 +285,37 @@ <!-- VERB ROLE FORMS --> <term name="container-author" form="verb">di</term>- <term name="director" form="verb">directed by</term>+ <term name="director" form="verb">diretto da</term> <term name="editor" form="verb">a cura di</term> <term name="editorial-director" form="verb">edited by</term>- <term name="illustrator" form="verb">illustrated by</term>+ <term name="illustrator" form="verb">illustrato da</term> <term name="interviewer" form="verb">intervista di</term> <term name="recipient" form="verb">a</term>- <term name="reviewed-author" form="verb">by</term>+ <term name="reviewed-author" form="verb">di</term> <term name="translator" form="verb">tradotto da</term> <term name="editortranslator" form="verb">a cura di e tradotto da</term> <!-- SHORT VERB ROLE FORMS -->- <term name="director" form="verb-short">dir.</term>+ <term name="director" form="verb-short">dir. da</term> <term name="editor" form="verb-short">a c. di</term> <term name="editorial-director" form="verb-short">ed.</term>- <term name="illustrator" form="verb-short">illus.</term>+ <term name="illustrator" form="verb-short">ill. da</term> <term name="translator" form="verb-short">trad. da</term> <term name="editortranslator" form="verb-short">a c. di e trad. da</term> <!-- LONG MONTH FORMS -->- <term name="month-01">gennaio</term>- <term name="month-02">febbraio</term>- <term name="month-03">marzo</term>- <term name="month-04">aprile</term>- <term name="month-05">maggio</term>- <term name="month-06">giugno</term>- <term name="month-07">luglio</term>- <term name="month-08">agosto</term>- <term name="month-09">settembre</term>- <term name="month-10">ottobre</term>- <term name="month-11">novembre</term>- <term name="month-12">dicembre</term>+ <term name="month-01" gender="masculine">gennaio</term>+ <term name="month-02" gender="masculine">febbraio</term>+ <term name="month-03" gender="masculine">marzo</term>+ <term name="month-04" gender="masculine">aprile</term>+ <term name="month-05" gender="masculine">maggio</term>+ <term name="month-06" gender="masculine">giugno</term>+ <term name="month-07" gender="masculine">luglio</term>+ <term name="month-08" gender="masculine">agosto</term>+ <term name="month-09" gender="masculine">settembre</term>+ <term name="month-10" gender="masculine">ottobre</term>+ <term name="month-11" gender="masculine">novembre</term>+ <term name="month-12" gender="masculine">dicembre</term> <!-- SHORT MONTH FORMS --> <term name="month-01" form="short">gen.</term>
man/citeproc.1 view
@@ -1,6 +1,6 @@-.\" Automatically generated by Pandoc 2.11.3.1+.\" Automatically generated by Pandoc 2.11.4 .\"-.TH "citeproc" "1" "" "citeproc 0.3.0.3" ""+.TH "citeproc" "1" "" "citeproc 0.3.0.4" "" .hy .SH NAME .PP
src/Citeproc/Eval.hs view
@@ -14,7 +14,7 @@ import Data.Containers.ListUtils (nubOrdOn, nubOrd) import Safe (headMay, headDef, lastMay, initSafe, tailSafe, maximumMay) import Data.Maybe-import Control.Monad (foldM, zipWithM, when, unless)+import Control.Monad (foldM, foldM_, zipWithM, when, unless) import qualified Data.Map as M import qualified Data.Set as Set import Data.Coerce (coerce)@@ -375,14 +375,19 @@ | otherwise = 0 numberOfMatches _ _ = 0 +--+-- Disambiguation+--+ data DisambData = DisambData { ddItem :: ItemId , ddNames :: [Name] , ddDates :: [Date]+ , ddRendered :: Text } deriving (Eq, Ord, Show) -disambiguateCitations :: CiteprocOutput a+disambiguateCitations :: forall a . CiteprocOutput a => Style a -> M.Map ItemId [SortKeyValue] -> [Citation a]@@ -390,18 +395,16 @@ disambiguateCitations style bibSortKeyMap citations = do refs <- unReferenceMap <$> gets stateRefMap let refIds = M.keys refs- let citeIds = Set.fromList $- concatMap (map citationItemId . citationItems) citations- let ghostCitations = [Citation Nothing Nothing- [CitationItem ident Nothing Nothing- NormalCite Nothing Nothing]- | ident <- refIds- , not (ident `Set.member` citeIds)]- allCites <- withRWST (\ctx st -> (ctx,- st { stateLastCitedMap = mempty- , stateNoteMap = mempty })) $- mapM (evalLayout False (styleCitation style))- (zip [1..] (citations ++ ghostCitations))+ let citeIds = concatMap (map citationItemId . citationItems) citations+ let citeIdsSet = Set.fromList citeIds+ let ghostItems = [ ident+ | ident <- refIds+ , not (ident `Set.member` citeIdsSet)]+ -- note that citations must go first, and order must be preserved:+ -- we use a "basic item" that strips off prefixes, suffixes, locators+ let allItems = map basicItem $ citeIds ++ ghostItems+ allCites <- renderItems allItems+ mblang <- asks (localeLanguage . contextLocale) styleOpts <- asks contextStyleOptions let strategy = styleDisambiguation styleOpts@@ -456,228 +459,274 @@ (unReferenceMap $ stateRefMap st) refIds } -- redo citations- withRWST (\ctx st -> (ctx,- st { stateLastCitedMap = mempty- , stateNoteMap = mempty })) $- mapM (evalLayout False (styleCitation style))- (zip [1..] (citations ++ ghostCitations))+ renderItems allItems case getAmbiguities allCites' of- [] -> return $ take (length citations) allCites'- ambiguities -> do- analyzeAmbiguities mblang bibSortKeyMap strategy (map snd ambiguities)- withRWST (\ctx st -> (ctx,- st { stateLastCitedMap = mempty- , stateNoteMap = mempty })) $- mapM (evalLayout False (styleCitation style)) (zip [1..] citations)+ [] -> return ()+ ambiguities -> analyzeAmbiguities mblang strategy ambiguities+ withRWST (\ctx st -> (ctx,+ st { stateLastCitedMap = mempty+ , stateNoteMap = mempty })) $+ mapM (evalLayout False (styleCitation style)) (zip [1..] citations) where++ renderItems :: [CitationItem a] -> Eval a [Output a]+ renderItems = withRWST (\ctx st -> (ctx,+ st { stateLastCitedMap = mempty+ , stateNoteMap = mempty })) .+ mapM (\item ->+ Tagged (TagItem NormalCite (citationItemId item)) . grouped <$>+ evalItem (styleCitation style) ([], item))++ refreshAmbiguities :: [[DisambData]] -> Eval a [[DisambData]]+ refreshAmbiguities = fmap (concatMap getAmbiguities) .+ mapM (renderItems . map (basicItem . ddItem))+ analyzeAmbiguities :: Maybe Lang- -> M.Map ItemId [SortKeyValue] -> DisambiguationStrategy -> [[DisambData]] -> Eval a ()- analyzeAmbiguities mblang bibSortKeyMap' strategy ambiguities = do+ analyzeAmbiguities mblang strategy ambiguities = do -- add names to et al.- as1 <- if disambiguateAddNames strategy- then mapM (tryAddNames mblang- (disambiguateAddGivenNames strategy))- ambiguities- else return ambiguities- as2 <- case disambiguateAddGivenNames strategy of- Just ByCite -> mapM (tryAddGivenNames mblang) as1- _ -> return as1- as3 <- if disambiguateAddYearSuffix strategy- then do- addYearSuffixes bibSortKeyMap' as2- return []- else return as2- mapM_ tryDisambiguateCondition as3-- isDisambiguated :: Maybe Lang- -> Maybe GivenNameDisambiguationRule- -> Int -- et al min- -> [DisambData]- -> DisambData- -> Bool- isDisambiguated mblang mbrule etAlMin xs x =- all (\y -> x == y || disambiguatedName y /= disambiguatedName x) xs- where- disambiguatedName = nameParts . take etAlMin . ddNames- nameParts =- case mbrule of- Just AllNames -> id- Just AllNamesWithInitials ->- map (\name -> name{ nameGiven = initialize mblang True False ""- <$> nameGiven name })- Just PrimaryName ->- \case- [] -> []- (z:zs) -> z : map (\name -> name{ nameGiven = Nothing }) zs- Just PrimaryNameWithInitials ->- \case- [] -> []- (z:zs) -> z{ nameGiven =- initialize mblang True False "" <$> nameGiven z } :- map (\name -> name{ nameGiven = Nothing }) zs- Just ByCite -> id -- hints will be added later- _ -> map (\name -> name{ nameGiven = Nothing })-- tryAddNames mblang mbrule bs =- (case mbrule of- Just ByCite -> bs <$ tryAddGivenNames mblang bs- _ -> return bs) >>= go 1- -- if ByCite, we want to make sure that- -- tryAddGivenNames is still applied, as- -- calculation of "add names" assumes this.- where- maxnames = fromMaybe 0 . maximumMay . map (length . ddNames)- go n as- | n > maxnames as = return as- | otherwise = do- let ds = filter (isDisambiguated mblang mbrule n as) as- if null ds- then go (n + 1) as- else do- modify $ \st ->- st{ stateRefMap = ReferenceMap- $ foldr (setEtAlNames (Just n) . ddItem)- (unReferenceMap $ stateRefMap st) as }- go (n + 1) (as \\ ds)-- tryAddGivenNames :: Maybe Lang- -> [DisambData]- -> Eval a [DisambData]- tryAddGivenNames mblang as = do- let correspondingNames =- map (zip (map ddItem as)) $ transpose $ map ddNames as- go [] _ = return []- go (as' :: [DisambData]) (ns :: [(ItemId, Name)]) = do- hintedIds <- Set.fromList . catMaybes <$>- mapM (addNameHint mblang (map snd ns)) ns- return $ filter (\x -> ddItem x `Set.notMember` hintedIds) as'- foldM go as correspondingNames+ return ambiguities+ >>= (\as ->+ (if not (null as) && disambiguateAddNames strategy+ then do+ mapM_ (tryAddNames mblang (disambiguateAddGivenNames strategy)) as+ refreshAmbiguities as+ else+ return as))+ >>= (\as ->+ (case disambiguateAddGivenNames strategy of+ Just ByCite | not (null as) -> do+ mapM_ (tryAddGivenNames mblang) as+ refreshAmbiguities as+ _ -> return as))+ >>= (\as ->+ (if not (null as) && disambiguateAddYearSuffix strategy+ then do+ addYearSuffixes bibSortKeyMap as+ refreshAmbiguities as+ else return as))+ >>= mapM_ tryDisambiguateCondition - addYearSuffixes bibSortKeyMap' as = do- let allitems = concat as- let companions a =- sortOn- (\it -> M.lookup (ddItem it) bibSortKeyMap')- (concat [ x | x <- as, a `elem` x ])- let groups = Set.map companions $ Set.fromList allitems- let addYearSuffix item suff =- modify $ \st ->- st{ stateRefMap = ReferenceMap- $ setYearSuffix suff item- $ unReferenceMap- $ stateRefMap st }- mapM_ (\xs -> zipWithM addYearSuffix (map ddItem xs) [1..]) groups+basicItem :: ItemId -> CitationItem a+basicItem iid = CitationItem+ { citationItemId = iid+ , citationItemLabel = Nothing+ , citationItemLocator = Nothing+ , citationItemType = NormalCite+ , citationItemPrefix = Nothing+ , citationItemSuffix = Nothing+ } - tryDisambiguateCondition as =- case as of- [] -> return ()- xs -> modify $ \st ->- st{ stateRefMap = ReferenceMap- $ foldr (setDisambCondition True . ddItem)- (unReferenceMap (stateRefMap st))- xs }+isDisambiguated :: Maybe Lang+ -> Maybe GivenNameDisambiguationRule+ -> Int -- et al min+ -> [DisambData]+ -> DisambData+ -> Bool+isDisambiguated mblang mbrule etAlMin xs x =+ all (\y -> x == y || disambiguatedName y /= disambiguatedName x) xs+ where+ disambiguatedName = nameParts . take etAlMin . ddNames+ nameParts =+ case mbrule of+ Just AllNames -> id+ Just AllNamesWithInitials ->+ map (\name -> name{ nameGiven = initialize mblang True False ""+ <$> nameGiven name })+ Just PrimaryName ->+ \case+ [] -> []+ (z:zs) -> z : map (\name -> name{ nameGiven = Nothing }) zs+ Just PrimaryNameWithInitials ->+ \case+ [] -> []+ (z:zs) -> z{ nameGiven =+ initialize mblang True False "" <$> nameGiven z } :+ map (\name -> name{ nameGiven = Nothing }) zs+ Just ByCite -> id -- hints will be added later+ _ -> map (\name -> name{ nameGiven = Nothing }) - alterReferenceDisambiguation f r =- r{ referenceDisambiguation = f <$>- case referenceDisambiguation r of- Nothing -> Just- DisambiguationData- { disambYearSuffix = Nothing- , disambNameMap = mempty- , disambEtAlNames = Nothing- , disambCondition = False- }- Just x -> Just x }+tryAddNames :: Maybe Lang+ -> Maybe GivenNameDisambiguationRule+ -> [DisambData]+ -> Eval a ()+tryAddNames mblang mbrule bs =+ (case mbrule of+ Just ByCite -> bs <$ tryAddGivenNames mblang bs+ _ -> return bs) >>= go 1+ -- if ByCite, we want to make sure that+ -- tryAddGivenNames is still applied, as+ -- calculation of "add names" assumes this.+ where+ maxnames = fromMaybe 0 . maximumMay . map (length . ddNames)+ go n as+ | n > maxnames as = return ()+ | otherwise = do+ let ds = filter (isDisambiguated mblang mbrule n as) as+ if null ds+ then go (n + 1) as+ else do+ modify $ \st ->+ st{ stateRefMap = ReferenceMap+ $ foldr (setEtAlNames (Just n) . ddItem)+ (unReferenceMap $ stateRefMap st) as }+ go (n + 1) (as \\ ds) - initialsMatch mblang x y =- case (nameGiven x, nameGiven y) of- (Just x', Just y') ->- initialize mblang True False "" x' ==- initialize mblang True False "" y'- _ -> False+tryAddGivenNames :: Maybe Lang+ -> [DisambData]+ -> Eval a ()+tryAddGivenNames mblang as = do+ let correspondingNames =+ map (zip (map ddItem as)) $ transpose $ map ddNames as+ go [] _ = return []+ go (as' :: [DisambData]) (ns :: [(ItemId, Name)]) = do+ hintedIds <- Set.fromList . catMaybes <$>+ mapM (addNameHint mblang (map snd ns)) ns+ return $ filter (\x -> ddItem x `Set.notMember` hintedIds) as'+ foldM_ go as correspondingNames - addNameHint mblang names (item, name) = do- let familyMatches = [n | n <- names- , n /= name- , nameFamily n == nameFamily name]- case familyMatches of- [] -> return Nothing- _ -> do- let hint = if any (initialsMatch mblang name) familyMatches- then AddGivenName- else AddInitials+addYearSuffixes :: M.Map ItemId [SortKeyValue]+ -> [[DisambData]]+ -> Eval a ()+addYearSuffixes bibSortKeyMap' as = do+ let allitems = concat as+ let companions a =+ sortOn+ (\it -> M.lookup (ddItem it) bibSortKeyMap')+ (concat [ x | x <- as, a `elem` x ])+ let groups = Set.map companions $ Set.fromList allitems+ let addYearSuffix item suff = modify $ \st -> st{ stateRefMap = ReferenceMap- $ setNameHint hint name item- $ unReferenceMap (stateRefMap st) }- return $ Just item+ $ setYearSuffix suff item+ $ unReferenceMap+ $ stateRefMap st }+ mapM_ (\xs -> zipWithM addYearSuffix (map ddItem xs) [1..]) groups - setNameHint hint name = M.adjust- (alterReferenceDisambiguation- (\d -> d{ disambNameMap =- M.insert name hint- (disambNameMap d) }))+tryDisambiguateCondition :: [DisambData] -> Eval a ()+tryDisambiguateCondition as =+ case as of+ [] -> return ()+ xs -> modify $ \st ->+ st{ stateRefMap = ReferenceMap+ $ foldr (setDisambCondition True . ddItem)+ (unReferenceMap (stateRefMap st))+ xs } - setEtAlNames x = M.adjust- (alterReferenceDisambiguation- (\d -> d{ disambEtAlNames = x }))+alterReferenceDisambiguation :: (DisambiguationData -> DisambiguationData)+ -> Reference a+ -> Reference a+alterReferenceDisambiguation f r =+ r{ referenceDisambiguation = f <$>+ case referenceDisambiguation r of+ Nothing -> Just+ DisambiguationData+ { disambYearSuffix = Nothing+ , disambNameMap = mempty+ , disambEtAlNames = Nothing+ , disambCondition = False+ }+ Just x -> Just x } - setYearSuffix x = M.adjust- (alterReferenceDisambiguation- (\d -> d{ disambYearSuffix = Just x }))+initialsMatch :: Maybe Lang -> Name -> Name -> Bool+initialsMatch mblang x y =+ case (nameGiven x, nameGiven y) of+ (Just x', Just y') ->+ initialize mblang True False "" x' ==+ initialize mblang True False "" y'+ _ -> False - setDisambCondition x = M.adjust- (alterReferenceDisambiguation- (\d -> d{ disambCondition = x }))+addNameHint :: Maybe Lang -> [Name] -> (ItemId, Name) -> Eval a (Maybe ItemId)+addNameHint mblang names (item, name) = do+ let familyMatches = [n | n <- names+ , n /= name+ , nameFamily n == nameFamily name]+ case familyMatches of+ [] -> return Nothing+ _ -> do+ let hint = if any (initialsMatch mblang name) familyMatches+ then AddGivenName+ else AddInitials+ modify $ \st ->+ st{ stateRefMap = ReferenceMap+ $ setNameHint hint name item+ $ unReferenceMap (stateRefMap st) }+ return $ Just item - getAmbiguities cs =- filter ((> 1) . length . snd)- $ mapMaybe- (\zs ->- case zs of- ("",_):_ -> Nothing -- no printed form of citation- (t, _):_ -> Just (t, map toDisambData $- nubOrdOn fst $ map snd zs)- [] -> Nothing)- $ groupBy (\(x,_) (y,_) -> x == y)- $ sortOn fst- [let (tags, texts) = unzip $ takeNamesOrDate (universe x) in- (T.unwords texts, (iid, (getNames tags, getDates tags)))- | (Tagged (TagItem ty iid) x) <- concatMap universe cs,- ty /= AuthorOnly]+setNameHint :: NameHints -> Name -> ItemId+ -> M.Map ItemId (Reference a) -> M.Map ItemId (Reference a)+setNameHint hint name = M.adjust+ (alterReferenceDisambiguation+ (\d -> d{ disambNameMap =+ M.insert name hint+ (disambNameMap d) })) - toDisambData (id', (ns', ds')) = DisambData id' ns' ds'+setEtAlNames :: Maybe Int -> ItemId+ -> M.Map ItemId (Reference a) -> M.Map ItemId (Reference a)+setEtAlNames x = M.adjust+ (alterReferenceDisambiguation+ (\d -> d{ disambEtAlNames = x })) - -- take names, date, or citation-label (which also gets year suffix).- takeNamesOrDate :: CiteprocOutput a => [Output a] -> [(Tag, Text)]- takeNamesOrDate (Tagged t@TagNames{} x : xs) =- (t, outputToText x) : takeNamesOrDate xs- takeNamesOrDate (Tagged t@TagDate{} x : xs) =- (t, outputToText x) : takeNamesOrDate xs- takeNamesOrDate (Tagged t@TagCitationLabel x : xs) =- (t, outputToText x) : takeNamesOrDate xs- takeNamesOrDate (_ : xs) =- takeNamesOrDate xs- takeNamesOrDate [] = []+setYearSuffix :: Int -> ItemId+ -> M.Map ItemId (Reference a) -> M.Map ItemId (Reference a)+setYearSuffix x = M.adjust+ (alterReferenceDisambiguation+ (\d -> d{ disambYearSuffix = Just x })) - getNames :: [Tag] -> [Name]- getNames (TagNames _ _ ns : xs) = ns ++ getNames xs- getNames (_ : xs) = getNames xs- getNames [] = []+setDisambCondition :: Bool -> ItemId+ -> M.Map ItemId (Reference a) -> M.Map ItemId (Reference a)+setDisambCondition x = M.adjust+ (alterReferenceDisambiguation+ (\d -> d{ disambCondition = x })) - getDates :: [Tag] -> [Date]- getDates (TagDate d : xs) = d : getDates xs- getDates (_ : xs) = getDates xs- getDates [] = []+getAmbiguities :: CiteprocOutput a => [Output a] -> [[DisambData]]+getAmbiguities =+ mapMaybe+ (\zs ->+ case zs of+ [] -> Nothing+ [_] -> Nothing+ (z:_) ->+ case ddRendered z of+ "" -> Nothing+ _ -> case nubOrdOn ddItem zs of+ ys@(_:_:_) -> Just ys -- > 1 ambiguous entry+ _ -> Nothing)+ . groupBy (\x y -> ddRendered x == ddRendered y)+ . sortOn ddRendered+ . map toDisambData +toDisambData :: CiteprocOutput a => Output a -> DisambData+toDisambData (Tagged (TagItem NormalCite iid) x) =+ let xs = universe x+ ns' = getNames xs+ ds' = getDates xs+ t = outputToText x+ in DisambData iid ns' ds' t+ where+ getNames :: [Output a] -> [Name]+ getNames (Tagged (TagNames _ _ ns) _ : xs)+ = ns ++ getNames xs+ getNames (_ : xs) = getNames xs+ getNames [] = [] + getDates :: [Output a] -> [Date]+ getDates (Tagged (TagDate d) _ : xs)+ = d : getDates xs+ getDates (_ : xs) = getDates xs+ getDates [] = []+toDisambData _ = error "toDisambData called without tagged item" -groupAndCollapseCitations :: CiteprocOutput a++--+-- Grouping and collapsing+--++groupAndCollapseCitations :: forall a . CiteprocOutput a => Text -> Maybe Text -> Maybe Text@@ -701,10 +750,13 @@ where -- Note that we cannot assume we've sorted by name, -- so we can't just use Data.ListgroupBy+ groupWith :: (b -> b -> Bool) -> [b] -> [[b]] groupWith _ [] = [] groupWith isMatched (z:zs) = (z : filter (isMatched z) zs) : groupWith isMatched (filter (not . isMatched z) zs)++ collapseRange :: [Output a] -> [Output a] -> [Output a] collapseRange ys zs | length ys >= 3 , Just yhead <- headMay ys@@ -719,6 +771,8 @@ if null zs then [] else maybe NullOutput literal (formatDelimiter f) : zs++ collapseGroup :: Collapsing -> [Output a] -> [Output a] -> [Output a] collapseGroup _ [] zs = zs collapseGroup collapseType (y:ys) zs = let ys' = y : map (transform removeNames) ys@@ -754,6 +808,8 @@ [d | Tagged (TagYearSuffix d) _ <- universe y]) of ([c],[d]) -> d == c + 1 _ -> False++ rangifyGroup :: [Output a] -> Output a rangifyGroup zs | length zs >= 3 , Just zhead <- headMay zs@@ -763,10 +819,14 @@ rangifyGroup [z] = z rangifyGroup zs = Formatted mempty{ formatDelimiter = yearSuffixDelim } zs++ yearSuffixGroup :: Bool -> [Output a] -> Output a yearSuffixGroup _ [x] = x yearSuffixGroup useRanges zs = Formatted mempty{ formatDelimiter = yearSuffixDelim } $ if useRanges then collapseRanges zs else zs++ collapseYearSuffix :: Collapsing -> [Output a] -> [Output a] collapseYearSuffix CollapseYearSuffix zs = reverse $ yearSuffixGroup False cur : items where@@ -776,12 +836,19 @@ where (cur, items) = foldl' (goYearSuffix True) ([], []) zs collapseYearSuffix _ zs = zs++ getDates :: Output a -> [Date] getDates x = [d | Tagged (TagDate d) _ <- universe x]++ getYears :: Output a -> [[Maybe Int]] getYears x = [map (\case DateParts (y:_) -> Just y _ -> Nothing) (dateParts d) | d <- getDates x , isNothing (dateLiteral d)]++ goYearSuffix :: Bool -> ([Output a], [Output a]) -> Output a+ -> ([Output a], [Output a]) goYearSuffix useRanges (cur, items) item = case cur of [] -> ([item], items)@@ -791,6 +858,7 @@ items) | otherwise -> ([item], yearSuffixGroup useRanges (z:zs) : items) + isAdjacentCitationNumber :: Output a -> Output a -> Bool isAdjacentCitationNumber (Tagged (TagItem _ _) (Formatted _f1 [Tagged (TagCitationNumber n1) _xs1]))@@ -802,8 +870,10 @@ (Tagged (TagItem _ _) (Tagged (TagCitationNumber n2) _xs2)) = n2 == n1 + 1 isAdjacentCitationNumber _ _ = False++ sameNames :: Output a -> Output a -> Bool sameNames x y =- case (extractTags x, extractTags y) of+ case (extractTagged x, extractTagged y) of (Just (Tagged (TagNames t1 _nf1 ns1) ws1), Just (Tagged (TagNames t2 _nf2 ns2) ws2)) -> t1 == t2 && (if ns1 == ns2@@ -815,7 +885,9 @@ -> True -- case where title is substituted _ -> False- extractTags x =++ extractTagged :: Output a -> Maybe (Output a)+ extractTagged x = let items = [y | y@(Tagged (TagItem ty _) _) <- universe x , ty /= AuthorOnly] names = [y | y@(Tagged TagNames{} _) <- concatMap universe items]@@ -823,6 +895,7 @@ in if null items then Nothing else listToMaybe names <|> listToMaybe dates+ groupAndCollapseCitations _ _ _ _ x = x takeSeq :: Show a => (a -> a -> Bool) -> [a] -> ([a], [a])@@ -838,6 +911,9 @@ ([],_) -> [] (xs,ys) -> xs : groupSuccessive isAdjacent ys +--+-- Sorting+-- evalSortKeys :: CiteprocOutput a => Layout a@@ -914,11 +990,13 @@ -- correctly in a citation starting with an author-only: -- the combination AuthorOnly [SuppressAuthor] should not -- count against a later Ibid citation.- let positions = case citationItems citation of- (c:_) | citationItemType c == AuthorOnly -> [0..]- _ -> [1..]- items <- mapM evalItem (zip positions (citationItems citation))+ let positionsInCitation =+ case citationItems citation of+ (c:_) | citationItemType c == AuthorOnly -> [0..]+ _ -> [1..] + items <- mapM evalItem' (zip positionsInCitation (citationItems citation))+ -- see display_SecondFieldAlignMigratePunctuation.txt let moveSuffixInsideDisplay zs = case (lastMay zs, formatSuffix formatting) of@@ -941,93 +1019,22 @@ where formatting = layoutFormatting layout - mbNoteNumber = citationNoteNumber citation secondFieldAlign (x:xs) = formatted mempty{ formatDisplay = Just DisplayLeftMargin } [x] : [formatted mempty{ formatDisplay = Just DisplayRightInline } xs] secondFieldAlign [] = [] - evalItem (positionInCitation :: Int, item) = do- refmap <- gets stateRefMap- position <- if isBibliography- then return []- else getPosition item- citationGroupNumber- mbNoteNumber- positionInCitation-- let addLangToFormatting lang (Formatted f xs) =- Formatted f{ formatLang = Just lang } xs- addLangToFormatting _ x = x-- xs <- case lookupReference (citationItemId item) refmap of- Just ref -> withRWST- (\ctx st ->- (ctx{ contextLocator = citationItemLocator item- , contextLabel = citationItemLabel item- , contextPosition = position- },- st{ stateReference = ref- , stateUsedYearSuffix = False }))- $ do xs <- mconcat <$> mapM eElement (layoutElements layout)- let mblang = parseLang <$>- (lookupVariable "language" ref- >>= valToText)- return $- case mblang of- Nothing -> xs- Just lang -> map- (transform (addLangToFormatting lang)) xs- Nothing -> do- warn $ "citation " <> unItemId (citationItemId item) <>- " not found"- return [Literal $ addFontWeight BoldWeight- $ fromText $ unItemId (citationItemId item) <> "?"]-+ evalItem' (positionInCitation :: Int, item) = do styleOpts <- asks contextStyleOptions let isNote = styleIsNoteStyle styleOpts+ position <- getPosition citationGroupNumber (citationNoteNumber citation)+ item positionInCitation + xs <- evalItem layout (position, item) -- we only update the map in the citations section unless isBibliography $ do- lastCitedMap <- gets stateLastCitedMap- let notenum = NumVal $ fromMaybe citationGroupNumber mbNoteNumber- -- keep track of how many citations in each note.- -- two citations from the same note don't count as "alone"- -- for ibid purposes. See citation-style-language/documentation#121- case mbNoteNumber of- Nothing -> return ()- Just n -> modify $ \st ->- st{ stateNoteMap = M.alter- (maybe- (Just (Set.singleton (citationItemId item)))- (Just . Set.insert (citationItemId item)))- n- (stateNoteMap st) }- case M.lookup (citationItemId item) lastCitedMap of- Nothing | isNote -> -- first citation- modify $ \st ->- st{ stateRefMap = ReferenceMap $- M.adjust (\ref -> ref{ referenceVariables =- M.insert "first-reference-note-number" notenum- (referenceVariables ref)})- (citationItemId item)- (unReferenceMap $ stateRefMap st) }- _ -> return ()-- unless (citationItemType item == AuthorOnly) $- modify $ \st ->- st{ stateLastCitedMap =- M.insert (citationItemId item)- (citationGroupNumber, mbNoteNumber, positionInCitation,- (case citationItems citation of- [_] -> True- [x,y] -> citationItemId x == citationItemId y- && citationItemType x == AuthorOnly- && citationItemType y == SuppressAuthor- _ -> False),- citationItemLabel item,- citationItemLocator item)- lastCitedMap }+ updateRefMap citationGroupNumber citation item+ updateLastCitedMap citationGroupNumber positionInCitation citation item return $ maybe id (\pref x -> grouped [Literal pref, x])@@ -1057,7 +1064,88 @@ else id) $ xs +evalItem :: CiteprocOutput a+ => Layout a -> ([Position], CitationItem a) -> Eval a [Output a]+evalItem layout (position, item) = do+ refmap <- gets stateRefMap + let addLangToFormatting lang (Formatted f xs) =+ Formatted f{ formatLang = Just lang } xs+ addLangToFormatting _ x = x++ case lookupReference (citationItemId item) refmap of+ Just ref -> withRWST+ (\ctx st ->+ (ctx{ contextLocator = citationItemLocator item+ , contextLabel = citationItemLabel item+ , contextPosition = position+ },+ st{ stateReference = ref+ , stateUsedYearSuffix = False }))+ $ do xs <- mconcat <$> mapM eElement (layoutElements layout)+ let mblang = parseLang <$>+ (lookupVariable "language" ref+ >>= valToText)+ return $+ case mblang of+ Nothing -> xs+ Just lang -> map+ (transform (addLangToFormatting lang)) xs+ Nothing -> do+ warn $ "citation " <> unItemId (citationItemId item) <>+ " not found"+ return [Literal $ addFontWeight BoldWeight+ $ fromText $ unItemId (citationItemId item) <> "?"]++++updateRefMap :: Int -> Citation a -> CitationItem a -> Eval a ()+updateRefMap citationGroupNumber citation item = do+ isNote <- asks (styleIsNoteStyle . contextStyleOptions)+ lastCitedMap <- gets stateLastCitedMap+ let notenum = NumVal $ fromMaybe citationGroupNumber (citationNoteNumber citation)+ -- keep track of how many citations in each note.+ -- two citations from the same note don't count as "alone"+ -- for ibid purposes. See citation-style-language/documentation#121+ case citationNoteNumber citation of+ Nothing -> return ()+ Just n -> modify $ \st ->+ st{ stateNoteMap = M.alter+ (maybe+ (Just (Set.singleton (citationItemId item)))+ (Just . Set.insert (citationItemId item)))+ n+ (stateNoteMap st) }+ case M.lookup (citationItemId item) lastCitedMap of+ Nothing | isNote -> -- first citation+ modify $ \st ->+ st{ stateRefMap = ReferenceMap $+ M.adjust (\ref -> ref{ referenceVariables =+ M.insert "first-reference-note-number" notenum+ (referenceVariables ref)})+ (citationItemId item)+ (unReferenceMap $ stateRefMap st) }+ _ -> return ()++updateLastCitedMap :: Int -> Int -> Citation a -> CitationItem a -> Eval a ()+updateLastCitedMap citationGroupNumber positionInCitation citation item = do+ unless (citationItemType item == AuthorOnly) $+ modify $ \st ->+ st{ stateLastCitedMap =+ M.insert (citationItemId item)+ (citationGroupNumber, citationNoteNumber citation,+ positionInCitation,+ (case citationItems citation of+ [_] -> True+ [x,y] -> citationItemId x == citationItemId y+ && citationItemType x == AuthorOnly+ && citationItemType y == SuppressAuthor+ _ -> False),+ citationItemLabel item,+ citationItemLocator item)+ $ stateLastCitedMap st }++ -- | The first-occurring element tagged as Names will be -- treated as the "author"; generally that is the author -- but it might be the editor if it's an edited volume.@@ -1080,8 +1168,8 @@ go (Tagged tg x) = Tagged tg (go x) go x = x -getPosition :: CitationItem a -> Int -> Maybe Int -> Int -> Eval a [Position]-getPosition item groupNum mbNoteNum posInGroup = do+getPosition :: Int -> Maybe Int -> CitationItem a -> Int -> Eval a [Position]+getPosition groupNum mbNoteNum item posInGroup = do lastCitedMap <- gets stateLastCitedMap noteMap <- gets stateNoteMap case M.lookup (citationItemId item) lastCitedMap of
test/Spec.hs view
@@ -318,7 +318,7 @@ (length (skipped counts)) case length (failed counts) + length (errored counts) of 0 -> exitSuccess- n | n <= 63 -> do+ n | n <= 67 -> do putStrLn "We have passed all the CSL tests we expect to..." exitSuccess | otherwise -> exitWith $ ExitFailure n
test/csl/collapse_AuthorCollapseNoDateSorted.txt view
@@ -2,11 +2,17 @@ citation <<===== MODE =====<< +>>===== DESCRIPTION =====>>+If warnings for empty cites in a multiple-cite citation are printed,+they should be printed only if the first cite in the series renders+nothing, and the layout suffix should be preserved.+<<===== DESCRIPTION =====<< + >>===== RESULT =====>>-(Smith 325AD, 2000, [CSL STYLE ERROR: reference with no printed form.])+(Smith 325AD, 2000) <<===== RESULT =====<< >>===== CITATION-ITEMS =====>>
+ test/csl/disambiguate_PrimaryNameWithNonDroppingParticle.txt view
@@ -0,0 +1,57 @@+>>===== MODE =====>>+citation+<<===== MODE =====<<++++>>===== RESULT =====>>+(dos Santos et al. 2016; Santos et al. 2020)+<<===== RESULT =====<<+++>>===== CSL =====>>+<style + xmlns="http://purl.org/net/xbiblio/csl"+ class="in-text" version="1.0" + demote-non-dropping-particle="never" + page-range-format="expanded">+ <info>+ <id />+ <title />+ <updated>2009-08-10T04:49:00+09:00</updated>+ </info>+ <citation + et-al-min="3" + et-al-use-first="1" + disambiguate-add-year-suffix="true" + disambiguate-add-names="true" + disambiguate-add-givenname="true" + collapse="year" + givenname-disambiguation-rule="primary-name">+ <layout delimiter="; " prefix="(" suffix=")">+ <group delimiter=" ">+ <names delimiter=", " variable="author">+ <name form="short" and="symbol" delimiter=", " initialize-with=". "/>+ </names>+ <date variable="issued">+ <date-part name="year" />+ </date>+ <text variable="year-suffix" />+ </group>+ </layout>+ </citation>+</style>+<<===== CSL =====<<+++>>===== INPUT =====>>+[+ {"id":"dosSantos2016CurrentState","accessed":{"date-parts":[[2020,11,3]]},"author":[{"family":"Santos","given":"Rafael G","non-dropping-particle":"dos"},{"family":"Balthazar","given":"Fermanda M"},{"family":"Bouso","given":"José C"},{"family":"Hallak","given":"Jaime EC"}],"container-title":"Journal of Psychopharmacology","container-title-short":"J Psychopharmacol","DOI":"10.1177/0269881116652578","ISSN":"0269-8811","issue":"12","issued":{"date-parts":[[2016,12,1]]},"language":"en","page":"1230-1247","publisher":"SAGE Publications Ltd STM","source":"SAGE Journals","title":"The current state of research on ayahuasca: A systematic review of human studies assessing psychiatric symptoms, neuropsychological functioning, and neuroimaging","title-short":"The current state of research on ayahuasca","type":"article-journal","URL":"https://doi.org/10.1177/0269881116652578","volume":"30"},+ {"id":"Santos2020BiodiversityVCarboline","accessed":{"date-parts":[[2020,11,3]]},"author":[{"family":"Santos","given":"Beatriz Werneck Lopes"},{"family":"Oliveira","given":"Regina Célia","dropping-particle":"de"},{"family":"Sonsin-Oliveira","given":"Julia"},{"family":"Fagg","given":"Christopher William"},{"family":"Barbosa","given":"José Beethoven Figueiredo"},{"family":"Caldas","given":"Eloisa Dutra"}],"container-title":"Plants","DOI":"10.3390/plants9070870","issue":"7","issued":{"date-parts":[[2020,7]]},"language":"en","number":"7","page":"870","publisher":"Multidisciplinary Digital Publishing Institute","source":"www.mdpi.com","title":"Biodiversity of β-Carboline Profile of Banisteriopsis caapi and Ayahuasca, a Plant and a Brew with Neuropharmacological Potential","type":"article-journal","URL":"https://www.mdpi.com/2223-7747/9/7/870","volume":"9"}+]+<<===== INPUT =====<<+++>>===== VERSION =====>>+1.0+<<===== VERSION =====<<
+ test/csl/disambiguate_PrimaryNameWithParticle.txt view
@@ -0,0 +1,65 @@+>>===== MODE =====>>+citation+<<===== MODE =====<<++++>>===== RESULT =====>>+dos Smith; Smith+<<===== RESULT =====<<+++>>===== CSL =====>>+<style + xmlns="http://purl.org/net/xbiblio/csl"+ class="note"+ demote-non-dropping-particle="never"+ version="1.0">+ <info>+ <id />+ <title />+ <updated>2009-08-10T04:49:00+09:00</updated>+ </info>+ <citation+ disambiguate-add-givenname="true"+ givenname-disambiguation-rule="primary-name">+ <layout delimiter="; ">+ <names delimiter=", " variable="author">+ <name and="text" delimiter-precedes-last="never" name-as-sort-order="first" initialize-with="." form="short"/>+ </names>+ </layout>+ </citation>+</style>+<<===== CSL =====<<+++>>===== INPUT =====>>+[+ {+ "author": [+ {+ "family": "dos Smith",+ "given": "Alan"+ }+ ],+ "id": "ITEM-1",+ "type": "book"+ },+ {+ "author": [+ {+ "family": "Smith",+ "given": "Beatrice"+ }+ ],+ "id": "ITEM-2",+ "type": "book"+ }+]+<<===== INPUT =====<<+++>>===== VERSION =====>>+1.0+<<===== VERSION =====<<+
+ test/csl/name_InTextMarkupInitialize.txt view
@@ -0,0 +1,69 @@+>>===== MODE =====>>+bibliography+<<===== MODE =====<<++++>>===== RESULT =====>>+<div class="csl-bib-body">+ <div class="csl-entry"><b>Doe</b>, <b>J.</b> Q.</div>+ <div class="csl-entry"><b>Doe</b>, <b>J.</b>-Q.</div>+</div>+<<===== RESULT =====<<+++>>===== CSL =====>>+<style + xmlns="http://purl.org/net/xbiblio/csl"+ class="in-text"+ version="1.0">+ <info>+ <id />+ <title />+ <updated>2009-08-10T04:49:00+09:00</updated>+ </info>+ <macro name="author">+ <names variable="author">+ <name name-as-sort-order="all" and="symbol" sort-separator=", " initialize-with=". " delimiter=", " delimiter-precedes-last="always"/>+ </names>+ </macro>+ <citation>+ <layout>+ <text macro="author"/>+ </layout>+ </citation>+ <bibliography>+ <layout>+ <text macro="author"/>+ </layout>+ </bibliography>+</style>+++<<===== CSL =====<<+++>>===== INPUT =====>>+[+ {+ "author": [+ {+ "family": "<b>Doe</b>",+ "given": "<b>John</b> Quiggly"+ }+ ],+ "id": "ITEM-1",+ "type": "book"+ },+ {+ "author": [+ {+ "family": "<b>Doe</b>",+ "given": "<b>John</b>-Quiggly"+ }+ ],+ "id": "ITEM-2",+ "type": "book"+ }+]+<<===== INPUT =====<<
+ test/csl/name_InTextMarkupNormalizeInitials.txt view
@@ -0,0 +1,69 @@+>>===== MODE =====>>+bibliography+<<===== MODE =====<<++++>>===== RESULT =====>>+<div class="csl-bib-body">+ <div class="csl-entry"><b>Doe</b>, <b>J.</b> Q.</div>+ <div class="csl-entry"><b>Doe</b>, <b>J.</b>-Q.</div>+</div>+<<===== RESULT =====<<+++>>===== CSL =====>>+<style + xmlns="http://purl.org/net/xbiblio/csl"+ class="in-text"+ version="1.0">+ <info>+ <id />+ <title />+ <updated>2009-08-10T04:49:00+09:00</updated>+ </info>+ <macro name="author">+ <names variable="author">+ <name name-as-sort-order="all" and="symbol" sort-separator=", " initialize-with=". " delimiter=", " delimiter-precedes-last="always"/>+ </names>+ </macro>+ <citation>+ <layout>+ <text macro="author"/>+ </layout>+ </citation>+ <bibliography>+ <layout>+ <text macro="author"/>+ </layout>+ </bibliography>+</style>+++<<===== CSL =====<<+++>>===== INPUT =====>>+[+ {+ "author": [+ {+ "family": "<b>Doe</b>",+ "given": "<b>J</b> Q"+ }+ ],+ "id": "ITEM-1",+ "type": "book"+ },+ {+ "author": [+ {+ "family": "<b>Doe</b>",+ "given": "<b>J</b>-Q"+ }+ ],+ "id": "ITEM-2",+ "type": "book"+ }+]+<<===== INPUT =====<<
test/csl/position_IbidWithLocator.txt view
@@ -23,7 +23,7 @@ "id": "ITEM-1", "label": "page", "locator": "23",- "position": 4+ "position": 3 } ] ]
+ test/csl/substitute_SubstituteOnlyOnceString.txt view
@@ -0,0 +1,43 @@+>>===== MODE =====>>+citation+<<===== MODE =====<<++++>>===== RESULT =====>>+a+<<===== RESULT =====<<+++>>===== CSL =====>>+<style + xmlns="http://purl.org/net/xbiblio/csl"+ class="note"+ version="1.0">+ <info>+ <id />+ <title />+ <updated>2019-08-11T16:56:00+02:00</updated>+ </info>+ <citation>+ <layout>+ <names>+ <substitute>+ <text value="a"/>+ <text value="b"/>+ </substitute>+ </names>+ </layout>+ </citation>+</style>+<<===== CSL =====<<+++>>===== INPUT =====>>+[+ {+ "id": "ITEM-1",+ "type": "book"+ }+]+<<===== INPUT =====<<
+ test/csl/substitute_SubstituteOnlyOnceTerm.txt view
@@ -0,0 +1,49 @@+>>===== MODE =====>>+citation+<<===== MODE =====<<++++>>===== RESULT =====>>+Editor Term+<<===== RESULT =====<<+++>>===== CSL =====>>+<style + xmlns="http://purl.org/net/xbiblio/csl"+ class="note"+ version="1.0">+ <info>+ <id />+ <title />+ <updated>2019-08-11T16:56:00+02:00</updated>+ </info>+ <locale>+ <terms>+ <term name="author">Author Term</term>+ <term name="editor">Editor Term</term>+ </terms>+ </locale>+ <citation>+ <layout>+ <names>+ <substitute>+ <text term="editor"/>+ <text term="author"/>+ </substitute>+ </names>+ </layout>+ </citation>+</style>+<<===== CSL =====<<+++>>===== INPUT =====>>+[+ {+ "id": "ITEM-1",+ "type": "book"+ }+]+<<===== INPUT =====<<
+ test/csl/substitute_SubstituteOnlyOnceTermEmpty.txt view
@@ -0,0 +1,52 @@+>>===== MODE =====>>+citation+<<===== MODE =====<<++++>>===== RESULT =====>>+Content:+<<===== RESULT =====<<+++>>===== CSL =====>>+<style + xmlns="http://purl.org/net/xbiblio/csl"+ class="note"+ version="1.0">+ <info>+ <id />+ <title />+ <updated>2019-08-11T16:56:00+02:00</updated>+ </info>+ <locale>+ <terms>+ <term name="author">Author Term Value</term>+ <term name="editor"></term>+ </terms>+ </locale>+ <citation>+ <layout>+ <group delimiter=" ">+ <group><text value="Content:"/></group>+ <names variable="translator">+ <substitute>+ <text term="editor"/>+ <text term="author"/>+ </substitute>+ </names>+ </group>+ </layout>+ </citation>+</style>+<<===== CSL =====<<+++>>===== INPUT =====>>+[+ {+ "id": "ITEM-1",+ "type": "book"+ }+]+<<===== INPUT =====<<
+ test/csl/substitute_SubstituteOnlyOnceVariable.txt view
@@ -0,0 +1,45 @@+>>===== MODE =====>>+citation+<<===== MODE =====<<++++>>===== RESULT =====>>+Title+<<===== RESULT =====<<+++>>===== CSL =====>>+<style + xmlns="http://purl.org/net/xbiblio/csl"+ class="note"+ version="1.0">+ <info>+ <id />+ <title />+ <updated>2019-08-11T16:56:00+02:00</updated>+ </info>+ <citation>+ <layout>+ <names>+ <substitute>+ <text variable="title"/>+ <text variable="container-title"/>+ </substitute>+ </names>+ </layout>+ </citation>+</style>+<<===== CSL =====<<+++>>===== INPUT =====>>+[+ {+ "id": "ITEM-1",+ "type": "book",+ "title": "Title",+ "container-title": "ContainerTitle"+ }+]+<<===== INPUT =====<<
test/extra/pandoc6813.txt view
@@ -3,9 +3,9 @@ <<===== MODE =====<< >>===== RESULT =====>>-Doe, <i>Title A</i>, n.d.-Doe, <i>Title B</i>, n.d.-Doe, <i>Title B</i>, n.d.+Doe, <i>Title A</i>.+Doe, <i>Title B</i>.+Doe, <i>Title B</i>. <<===== RESULT =====<< >>===== CSL =====>>