diff options
author | Karl Berry <karl@freefriends.org> | 2022-12-25 21:19:05 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2022-12-25 21:19:05 +0000 |
commit | 6f630003fb5786cf7cd7c9357181f48c5b5ca5aa (patch) | |
tree | 3b2e6bba65cbcb83a9f77ba9738782a968e1a4cf /Master/texmf-dist/scripts | |
parent | e0738f3b15024b575173a514b73597ba701088c1 (diff) |
citation-style-language (25dec22)
git-svn-id: svn://tug.org/texlive/trunk@65357 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/scripts')
21 files changed, 4064 insertions, 1704 deletions
diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex-data.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex-data.lua index c7b2ff13063..e5b4aa5a787 100644 --- a/Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex-data.lua +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex-data.lua @@ -3,6 +3,12 @@ return { description = "BibTeX CSL mapping", types = { + abstract = { + csl = nil, + }, + ancienttext = { + csl = "classic", + }, archival = { csl = "collection", }, @@ -12,6 +18,15 @@ return { article = { csl = "article-journal", }, + ["article-journal"] = { + csl = "article-journal", + }, + ["article-magazine"] = { + csl = "article-magazine", + }, + ["article-newspaper"] = { + csl = "article-newspaper", + }, artifactdataset = { csl = "dataset", }, @@ -33,21 +48,81 @@ return { bibnote = { csl = nil, }, + bill = { + csl = "bill", + }, book = { csl = "book", }, + bookinarticle = { + csl = nil, + }, bookinbook = { csl = "chapter", }, + bookincollection = { + csl = nil, + }, + bookininarticle = { + csl = nil, + }, + bookininbook = { + csl = nil, + }, + bookinincollection = { + csl = nil, + }, + bookininproceedings = { + csl = nil, + }, + bookininreference = { + csl = nil, + }, + bookinjournal = { + csl = nil, + }, + bookinmathesis = { + csl = nil, + }, + bookinphdthesis = { + csl = nil, + }, + bookinproceedings = { + csl = nil, + }, + bookinreference = { + csl = nil, + }, + bookinthesis = { + csl = nil, + }, booklet = { csl = "pamphlet", }, + broadcast = { + csl = "broadcast", + }, brochure = { csl = "pamphlet", }, - cconference = { + catalog = { csl = nil, }, + cconference = { + csl = "paper-conference", + }, + chapter = { + csl = "chapter", + }, + classic = { + csl = "classic", + }, + classictext = { + csl = "classic", + }, + codefragment = { + csl = "software", + }, collection = { csl = "book", }, @@ -60,9 +135,18 @@ return { commented = { csl = nil, }, + committee = { + csl = nil, + }, conference = { csl = "paper-conference", }, + conferencepaper = { + csl = "paper-conference", + }, + constitution = { + csl = "legislation", + }, ctan = { csl = "software", }, @@ -84,54 +168,138 @@ return { customf = { csl = nil, }, + data = { + csl = nil, + }, database = { csl = "dataset", }, dataset = { csl = "dataset", }, + degree = { + csl = nil, + }, dictionary = { csl = "book", }, docthesis = { csl = "thesis", }, + document = { + csl = "document", + }, electronic = { csl = "webpage", }, + entry = { + csl = "entry", + }, + ["entry-dictionary"] = { + csl = "entry-dictionary", + }, + ["entry-encyclopedia"] = { + csl = "entry-encyclopedia", + }, eulegislation = { csl = "legislation", }, + event = { + csl = "event", + }, + exhibcatalog = { + csl = nil, + }, + figure = { + csl = "figure", + }, footnote = { csl = nil, }, + funding = { + csl = nil, + }, game = { csl = "software", }, + gitcommit = { + csl = nil, + }, govpub = { csl = "regulation", }, + graphic = { + csl = "graphic", + }, habthesis = { csl = "thesis", }, heading = { csl = nil, }, + hearing = { + csl = "hearing", + }, hidden = { csl = nil, }, image = { csl = "graphic", }, + inarticle = { + csl = nil, + }, + inaudio = { + csl = nil, + }, inbook = { csl = "chapter", }, + incatalog = { + csl = nil, + }, incollection = { csl = "chapter", }, + incommentary = { + csl = nil, + }, + inexhibcatalog = { + csl = nil, + }, + ininarticle = { + csl = nil, + }, + ininbook = { + csl = nil, + }, + inincollection = { + csl = nil, + }, + ininproceedings = { + csl = nil, + }, + ininreference = { + csl = nil, + }, + inlexicon = { + csl = nil, + }, inloosecollection = { csl = "chapter", }, + inmathesis = { + csl = nil, + }, + inmovie = { + csl = nil, + }, + inmusic = { + csl = nil, + }, + inphdthesis = { + csl = nil, + }, inpress = { csl = "article", }, @@ -147,6 +315,15 @@ return { internet = { csl = "webpage", }, + interview = { + csl = "interview", + }, + inthesis = { + csl = nil, + }, + invideo = { + csl = nil, + }, journalpart = { csl = nil, }, @@ -154,20 +331,32 @@ return { csl = "periodical", }, jurisdiction = { - csl = nil, + csl = "legal_case", }, jurthesis = { csl = "thesis", }, + legadminmaterial = { + csl = "regulation", + }, legal = { csl = "treaty", }, + legal_case = { + csl = "legal_case", + }, legislation = { csl = "legislation", }, + legmaterial = { + csl = "legislation", + }, letter = { csl = "personal_communication", }, + letters = { + csl = "personal_communication", + }, lexicon = { csl = "book", }, @@ -177,6 +366,9 @@ return { manual = { csl = "report", }, + manuscript = { + csl = "manuscript", + }, map = { csl = "map", }, @@ -192,33 +384,54 @@ return { misc = { csl = "document", }, + mlasource = { + csl = nil, + }, monograph = { csl = "book", }, monography = { csl = "book", }, + motion_picture = { + csl = "motion_picture", + }, movie = { csl = "motion_picture", }, music = { csl = "song", }, + musical_score = { + csl = "musical_score", + }, mvbook = { csl = "book", }, mvcollection = { csl = "book", }, + mvcommentary = { + csl = nil, + }, + mvlexicon = { + csl = nil, + }, mvproceedings = { csl = "book", }, mvreference = { csl = "book", }, + nameonly = { + csl = nil, + }, news = { csl = "article-newspaper", }, + newsarticle = { + csl = "article-newspaper", + }, newspaper = { csl = "article-newspaper", }, @@ -228,6 +441,12 @@ return { other = { csl = nil, }, + pamphlet = { + csl = "pamphlet", + }, + ["paper-conference"] = { + csl = "paper-conference", + }, patent = { csl = "patent", }, @@ -237,9 +456,18 @@ return { periodical = { csl = "periodical", }, + personal_communication = { + csl = "personal_communication", + }, phdthesis = { csl = "thesis", }, + post = { + csl = "post", + }, + ["post-weblog"] = { + csl = "post-weblog", + }, preamble = { csl = nil, }, @@ -258,6 +486,9 @@ return { reference = { csl = "book", }, + regulation = { + csl = "regulation", + }, report = { csl = "report", }, @@ -267,6 +498,15 @@ return { review = { csl = "review", }, + ["review-book"] = { + csl = "review-book", + }, + school = { + csl = nil, + }, + seminarpaper = { + csl = nil, + }, set = { csl = nil, }, @@ -276,12 +516,27 @@ return { software = { csl = "software", }, + softwaremodule = { + csl = "software", + }, + softwareversion = { + csl = nil, + }, + song = { + csl = "song", + }, + speech = { + csl = "speech", + }, standard = { csl = "standard", }, string = { csl = nil, }, + student = { + csl = nil, + }, suppbook = { csl = "chapter", }, @@ -291,6 +546,12 @@ return { suppperiodical = { csl = "article", }, + talk = { + csl = "speech", + }, + teaching = { + csl = nil, + }, techreport = { csl = "report", }, @@ -300,6 +561,9 @@ return { thesis = { csl = "thesis", }, + treaty = { + csl = "treaty", + }, uklegislation = { csl = "legislation", }, @@ -313,7 +577,7 @@ return { csl = "webpage", }, website = { - csl = nil, + csl = "webpage", }, www = { csl = "webpage", @@ -327,11 +591,27 @@ return { csl = "abstract", type = "literal", }, + abstractloc = { + csl = nil, + type = "literal", + }, + abstracturl = { + csl = nil, + type = "verbatim", + }, + accessed = { + csl = "accessed", + }, account = { csl = nil, }, acronym = { csl = nil, + type = "literal", + }, + actor = { + csl = "performer", + type = "name", }, add = { csl = nil, @@ -352,11 +632,28 @@ return { }, advisor = { csl = nil, + type = "name", }, afterword = { csl = nil, type = "name", }, + alternatetitle = { + csl = nil, + type = "literal", + }, + alternatetitlescript = { + csl = nil, + type = "literal", + }, + amendment = { + csl = nil, + type = "literal", + }, + amount = { + csl = nil, + type = "integer", + }, annotate = { csl = "note", }, @@ -375,12 +672,33 @@ return { annotelanguage = { csl = nil, }, + anonstring = { + csl = nil, + type = "name", + }, + appentry = { + csl = nil, + type = "literal", + }, applicant = { csl = nil, }, + arachne = { + csl = nil, + type = "verbatim", + }, archive = { csl = "archive", }, + ["archive-place"] = { + csl = "archive-place", + }, + archive_collection = { + csl = "archive_collection", + }, + archive_location = { + csl = "archive_location", + }, archiveprefix = { csl = "archive", type = "literal", @@ -388,8 +706,21 @@ return { archname = { csl = "archive", }, + arthist = { + csl = nil, + type = "integer", + }, + arthistdate = { + csl = nil, + type = "date", + }, + article = { + csl = nil, + type = "literal", + }, articleno = { csl = nil, + type = "literal", }, arxiv = { csl = nil, @@ -401,16 +732,27 @@ return { csl = "author", type = "name", }, + authoraddon = { + csl = nil, + type = "name", + }, authorcountry = { csl = nil, + type = "literal", }, authorfa = { csl = nil, }, + authority = { + csl = "authority", + }, authortype = { csl = nil, type = "key", }, + ["available-date"] = { + csl = "available-date", + }, bibsource = { csl = nil, }, @@ -426,16 +768,92 @@ return { birthyear = { csl = nil, }, + blogsubtitle = { + csl = nil, + type = "literal", + }, + blogtitle = { + csl = "title", + type = "literal", + }, + blogtitleaddon = { + csl = nil, + type = "literal", + }, + book = { + csl = nil, + type = "range", + }, bookaddress = { + csl = "publisher-place", + }, + bookafterword = { csl = nil, + type = "name", + }, + bookannotator = { + csl = nil, + type = "name", }, bookauthor = { csl = "container-author", type = "name", }, + bookauthortype = { + csl = nil, + type = "literal", + }, + bookbooksubtitle = { + csl = nil, + type = "literal", + }, + bookbooktitle = { + csl = nil, + type = "literal", + }, + bookbooktitleaddon = { + csl = nil, + type = "literal", + }, + bookbooktitlekey = { + csl = nil, + type = "key", + }, + bookbookvolume = { + csl = nil, + type = "literal", + }, + bookcommentator = { + csl = nil, + type = "name", + }, + bookcredits = { + csl = nil, + type = "literal", + }, + bookeditor = { + csl = "editor", + type = "name", + }, + bookforeword = { + csl = nil, + type = "name", + }, + bookineditor = { + csl = nil, + type = "name", + }, + bookintroduction = { + csl = nil, + type = "name", + }, booklanguage = { csl = nil, }, + bookoriglanguage = { + csl = nil, + type = "key", + }, bookpages = { csl = nil, }, @@ -443,6 +861,10 @@ return { csl = nil, type = "key", }, + books = { + csl = nil, + type = "literal", + }, booksubtitle = { csl = nil, type = "literal", @@ -455,6 +877,29 @@ return { csl = nil, type = "literal", }, + booktitlekey = { + csl = nil, + type = "key", + }, + booktitlenote = { + csl = nil, + type = "literal", + }, + booktranslator = { + csl = "translator", + type = "name", + }, + bookvolume = { + csl = nil, + type = "literal", + }, + bookyear = { + csl = nil, + type = "literal", + }, + ["call-number"] = { + csl = "call-number", + }, caption = { csl = nil, }, @@ -467,6 +912,10 @@ return { casenumber = { csl = nil, }, + catalog = { + csl = nil, + type = "literal", + }, category = { csl = nil, }, @@ -474,18 +923,37 @@ return { csl = nil, }, chair = { - csl = nil, + csl = "chair", }, chapter = { csl = "chapter-number", type = "literal", }, + ["chapter-number"] = { + csl = "chapter-number", + }, + citation = { + csl = nil, + type = "literal", + }, citedate = { csl = nil, }, city = { csl = nil, }, + classes = { + csl = nil, + type = "entrykey", + }, + claves = { + csl = nil, + type = "literal", + }, + claves_definition = { + csl = nil, + type = "literal", + }, code = { csl = nil, }, @@ -494,10 +962,28 @@ return { }, collaboration = { csl = nil, + type = "literal", }, collator = { csl = nil, }, + collection = { + csl = nil, + type = "literal", + }, + ["collection-editor"] = { + csl = "collection-editor", + }, + ["collection-number"] = { + csl = "collection-number", + }, + ["collection-title"] = { + csl = "collection-title", + }, + columns = { + csl = nil, + type = "literal", + }, commentator = { csl = nil, type = "name", @@ -505,8 +991,23 @@ return { commit = { csl = nil, }, + commithash = { + csl = nil, + type = "literal", + }, + committee = { + csl = nil, + type = "name", + }, compiler = { + csl = "compiler", + }, + composer = { + csl = "composer", + }, + concentration = { csl = nil, + type = "literal", }, condition = { csl = nil, @@ -520,49 +1021,88 @@ return { ["conference-year"] = { csl = nil, }, + ["container-author"] = { + csl = "container-author", + }, + ["container-title"] = { + csl = "container-title", + }, + ["container-title-short"] = { + csl = "container-title-short", + }, + contents = { + csl = nil, + type = "literal", + }, + contributor = { + csl = "contributor", + }, + contributora = { + csl = nil, + type = "literal", + }, + contributorb = { + csl = nil, + type = "literal", + }, + copublisher = { + csl = nil, + type = "literal", + }, copy = { csl = nil, }, country = { - csl = nil, + csl = "jurisdiction", }, credits = { csl = nil, + type = "literal", }, crossref = { csl = nil, type = "entrykey", }, - ["ctrl-article-title"] = { - csl = nil, + curator = { + csl = "curator", }, - ["ctrl-chapter-title"] = { + currency = { csl = nil, + type = "literal", }, - ["ctrl-doi"] = { - csl = nil, + custom = { + csl = "custom", }, - ["ctrl-etal-firstonly"] = { + dataset = { csl = nil, }, - ["ctrl-etal-number"] = { + date = { + csl = "issued", + type = "date", + }, + ["date+an"] = { csl = nil, + type = "literal", }, - ["ctrl-link-doi"] = { + datea = { csl = nil, + type = "literal", }, - ["ctrl-use-doi-all"] = { + dateaddon = { csl = nil, + type = "literal", }, - ["ctrl-use-title"] = { + dateb = { csl = nil, + type = "literal", }, - dataset = { + datetype = { csl = nil, + type = "literal", }, - date = { - csl = "issued", - type = "date", + dating = { + csl = nil, + type = "literal", }, day = { csl = nil, @@ -570,11 +1110,28 @@ return { dayfiled = { csl = nil, }, + decision = { + csl = nil, + type = "literal", + }, + decisionname = { + csl = nil, + type = "literal", + }, definition = { csl = nil, }, + degree = { + csl = nil, + type = "literal", + }, + degreelist = { + csl = nil, + type = "entrykey", + }, department = { csl = nil, + type = "literal", }, description = { csl = nil, @@ -583,11 +1140,22 @@ return { csl = nil, }, dimensions = { - csl = nil, + csl = "dimensions", + }, + director = { + csl = "director", + type = "name", }, dissyear = { csl = nil, }, + distinctURL = { + csl = nil, + type = "literal", + }, + division = { + csl = "division", + }, doi = { csl = "DOI", type = "verbatim", @@ -598,10 +1166,22 @@ return { dummy = { csl = nil, }, + duration = { + csl = nil, + type = "literal", + }, + durationtype = { + csl = nil, + type = "literal", + }, edition = { csl = "edition", type = "literal", }, + editioncredits = { + csl = nil, + type = "literal", + }, editor = { csl = "editor", type = "name", @@ -610,6 +1190,14 @@ return { csl = nil, type = "name", }, + editoraaddon = { + csl = nil, + type = "literal", + }, + editoraddon = { + csl = nil, + type = "name", + }, editoratype = { csl = nil, type = "key", @@ -630,6 +1218,9 @@ return { csl = nil, type = "key", }, + ["editorial-director"] = { + csl = "editorial-director", + }, editortype = { csl = nil, type = "key", @@ -641,6 +1232,10 @@ return { email = { csl = nil, }, + endbookyear = { + csl = nil, + type = "literal", + }, endnumber = { csl = nil, }, @@ -650,6 +1245,10 @@ return { endyear = { csl = nil, }, + englishabstract = { + csl = nil, + type = "literal", + }, englishtitle = { csl = nil, }, @@ -672,6 +1271,18 @@ return { csl = nil, type = "literal", }, + eprintdate = { + csl = nil, + type = "date", + }, + eprintday = { + csl = nil, + type = "datepart", + }, + eprintmonth = { + csl = nil, + type = "datepart", + }, eprints = { csl = nil, }, @@ -679,6 +1290,10 @@ return { csl = "archive", type = "literal", }, + eprintyear = { + csl = nil, + type = "datepart", + }, erratumeid = { csl = nil, }, @@ -697,10 +1312,34 @@ return { erratumyear = { csl = nil, }, + event = { + csl = "event-title", + }, + ["event-date"] = { + csl = "event-date", + }, + ["event-place"] = { + csl = "event-place", + }, + ["event-title"] = { + csl = "event-title", + }, eventdate = { csl = "event-date", type = "date", }, + eventdatelanguage = { + csl = nil, + type = "verbatim", + }, + eventnumber = { + csl = nil, + type = "integer", + }, + eventsubtitle = { + csl = nil, + type = "literal", + }, eventtitle = { csl = "event-title", type = "literal", @@ -709,13 +1348,64 @@ return { csl = nil, type = "literal", }, + eventtype = { + csl = nil, + type = "literal", + }, eventyear = { csl = nil, }, + execdirector = { + csl = "director", + type = "name", + }, + execproducer = { + csl = "executive-producer", + type = "name", + }, execute = { csl = nil, type = "code", }, + ["executive-producer"] = { + csl = "executive-producer", + }, + exhibfirstdate = { + csl = "event-date", + type = "date", + }, + exhibfirstlocation = { + csl = "event-place", + type = "literal", + }, + exhibfirstmuseum = { + csl = nil, + type = "literal", + }, + exhibseconddate = { + csl = nil, + type = "date", + }, + exhibsecondlocation = { + csl = nil, + type = "literal", + }, + exhibsecondmuseum = { + csl = nil, + type = "literal", + }, + exhibthirddate = { + csl = nil, + type = "date", + }, + exhibthirdlocation = { + csl = nil, + type = "literal", + }, + exhibthirdmuseum = { + csl = nil, + type = "literal", + }, faddress = { csl = nil, }, @@ -725,6 +1415,14 @@ return { fax = { csl = nil, }, + festschrift = { + csl = nil, + type = "name", + }, + festschriftaddon = { + csl = nil, + type = "literal", + }, file = { csl = nil, type = "verbatim", @@ -751,9 +1449,21 @@ return { fpublisher = { csl = nil, }, + frenchabstract = { + csl = nil, + type = "literal", + }, ftitle = { csl = nil, }, + fulleventdate = { + csl = nil, + type = "date", + }, + funder = { + csl = nil, + type = "literal", + }, furtherresp = { csl = nil, }, @@ -764,14 +1474,37 @@ return { csl = nil, type = "literal", }, + genre = { + csl = "genre", + }, germanpages = { csl = nil, }, + gpa = { + csl = nil, + type = "literal", + }, group = { csl = nil, }, + guest = { + csl = "guest", + }, + hal_id = { + csl = nil, + type = "verbatim", + }, + hal_version = { + csl = "version", + type = "verbatim", + }, + handle = { + csl = nil, + type = "literal", + }, heading = { csl = nil, + type = "literal", }, hereafter = { csl = nil, @@ -780,6 +1513,13 @@ return { csl = nil, type = "name", }, + honors = { + csl = nil, + type = "literal", + }, + host = { + csl = "host", + }, howcited = { csl = nil, }, @@ -802,7 +1542,23 @@ return { csl = nil, }, illustrator = { + csl = "illustrator", + }, + impactfactor = { csl = nil, + type = "literal", + }, + inafterword = { + csl = nil, + type = "name", + }, + inannotator = { + csl = nil, + type = "name", + }, + incommentator = { + csl = nil, + type = "name", }, indexsorttitle = { csl = nil, @@ -812,10 +1568,37 @@ return { csl = nil, type = "literal", }, + ineditor = { + csl = nil, + type = "name", + }, + inforeword = { + csl = nil, + type = "name", + }, + inintroduction = { + csl = nil, + type = "name", + }, institution = { csl = "publisher", type = "literal", }, + interviewee = { + csl = nil, + type = "name", + }, + interviewer = { + csl = "interviewer", + }, + intranslator = { + csl = nil, + type = "name", + }, + introducedin = { + csl = nil, + type = "literal", + }, introduction = { csl = nil, type = "name", @@ -824,10 +1607,11 @@ return { csl = nil, }, inventor = { - csl = nil, + csl = "author", }, ipc = { csl = nil, + type = "literal", }, isan = { csl = nil, @@ -862,6 +1646,13 @@ return { csl = "issue", type = "literal", }, + issued = { + csl = "issued", + }, + issues = { + csl = nil, + type = "literal", + }, issuesubtitle = { csl = nil, type = "literal", @@ -881,10 +1672,21 @@ return { jfmnumber = { csl = nil, }, + jointauthor = { + csl = nil, + type = "name", + }, + jointauthortype = { + csl = nil, + type = "literal", + }, journal = { csl = "container-title", type = "literal", }, + journalabbreviation = { + csl = "container-title-short", + }, journalsubtitle = { csl = nil, type = "literal", @@ -900,16 +1702,30 @@ return { csl = nil, type = "literal", }, + journalyear = { + csl = nil, + type = "literal", + }, + jstor = { + csl = nil, + type = "verbatim", + }, juraauthor = { csl = nil, }, juratitle = { csl = nil, }, + jurisdiction = { + csl = "jurisdiction", + }, key = { csl = nil, type = "literal", }, + keyword = { + csl = "keyword", + }, keywords = { csl = nil, type = "keyword", @@ -935,10 +1751,15 @@ return { }, lastaccessed = { csl = "accessed", + type = "literal", }, lastchecked = { csl = "accessed", }, + layer = { + csl = nil, + type = "literal", + }, lccn = { csl = nil, }, @@ -946,6 +1767,14 @@ return { csl = nil, type = "literal", }, + librarylocation = { + csl = "archive-place", + type = "literal", + }, + license = { + csl = "license", + type = "literal", + }, lista = { csl = nil, type = "literal", @@ -977,6 +1806,14 @@ return { csl = "publisher-place", type = "literal", }, + locationa = { + csl = nil, + type = "literal", + }, + locationb = { + csl = nil, + type = "literal", + }, madadurltest = { csl = nil, }, @@ -986,10 +1823,22 @@ return { main1 = { csl = nil, }, + mainauthor = { + csl = nil, + type = "name", + }, + maineditor = { + csl = nil, + type = "name", + }, mainsubtitle = { csl = nil, type = "literal", }, + maintainer = { + csl = nil, + type = "name", + }, maintitle = { csl = nil, type = "literal", @@ -998,6 +1847,18 @@ return { csl = nil, type = "literal", }, + maintitlenote = { + csl = nil, + type = "literal", + }, + maintranslator = { + csl = nil, + type = "name", + }, + major = { + csl = nil, + type = "literal", + }, majorcode = { csl = nil, }, @@ -1015,6 +1876,7 @@ return { }, media = { csl = "medium", + type = "key", }, medium = { csl = "medium", @@ -1022,6 +1884,10 @@ return { meeting = { csl = nil, }, + minor = { + csl = nil, + type = "literal", + }, misctitle = { csl = nil, }, @@ -1044,8 +1910,17 @@ return { mrnumber = { csl = nil, }, + multieventdate = { + csl = nil, + type = "literal", + }, + multivenue = { + csl = nil, + type = "literal", + }, name = { csl = nil, + type = "name", }, namea = { csl = nil, @@ -1075,6 +1950,10 @@ return { csl = nil, type = "key", }, + narrator = { + csl = "narrator", + type = "name", + }, nationality = { csl = nil, }, @@ -1084,8 +1963,13 @@ return { newpage = { csl = nil, }, + newsdate = { + csl = nil, + type = "date", + }, newspaper = { csl = "container-title", + type = "literal", }, nickname = { csl = nil, @@ -1119,18 +2003,57 @@ return { csl = "number", type = "literal", }, + ["number-of-pages"] = { + csl = "number-of-pages", + }, + ["number-of-volumes"] = { + csl = "number-of-volumes", + }, + numbera = { + csl = nil, + type = "literal", + }, + numberb = { + csl = nil, + type = "literal", + }, + numcites = { + csl = nil, + type = "integer", + }, + numlectures = { + csl = nil, + type = "integer", + }, numpages = { + csl = "number-of-pages", + type = "literal", + }, + numstudents = { csl = nil, + type = "integer", }, oaddress = { + csl = "original-publisher-place", + }, + officialpages = { csl = nil, + type = "literal", + }, + officialvolume = { + csl = nil, + type = "literal", + }, + openaccess = { + csl = nil, + type = "literal", }, options = { csl = nil, type = "option", }, opublisher = { - csl = nil, + csl = "original-publisher", }, ["org-short"] = { csl = nil, @@ -1139,10 +2062,40 @@ return { csl = "publisher", type = "literal", }, + organizer = { + csl = "organizer", + }, origdate = { csl = "original-date", type = "date", }, + ["origdate+an"] = { + csl = nil, + type = "literal", + }, + origdatetype = { + csl = nil, + type = "literal", + }, + origin = { + csl = nil, + type = "literal", + }, + ["original-author"] = { + csl = "original-author", + }, + ["original-date"] = { + csl = "original-date", + }, + ["original-publisher"] = { + csl = "original-publisher", + }, + ["original-publisher-place"] = { + csl = "original-publisher-place", + }, + ["original-title"] = { + csl = "original-title", + }, originaladdress = { csl = "original-publisher-place", }, @@ -1185,13 +2138,24 @@ return { csl = "original-publisher", type = "literal", }, + origrealdate = { + csl = nil, + type = "date", + }, origtitle = { csl = "original-title", type = "literal", }, + owner = { + csl = nil, + type = "name", + }, oyear = { csl = nil, }, + page = { + csl = "page", + }, pagename = { csl = nil, }, @@ -1210,10 +2174,41 @@ return { paper = { csl = nil, }, + pardate = { + csl = nil, + type = "date", + }, + parpages = { + csl = nil, + type = "range", + }, + parreporter = { + csl = nil, + type = "literal", + }, + parseries = { + csl = nil, + type = "literal", + }, part = { csl = "part", type = "literal", }, + ["part-title"] = { + csl = "part-title", + }, + parts = { + csl = nil, + type = "literal", + }, + parttitle = { + csl = nil, + type = "literal", + }, + parvolume = { + csl = nil, + type = "integer", + }, patentid = { csl = nil, }, @@ -1221,6 +2216,13 @@ return { csl = nil, type = "verbatim", }, + peerreview = { + csl = nil, + type = "literal", + }, + performer = { + csl = "performer", + }, phone = { csl = nil, }, @@ -1230,18 +2232,36 @@ return { pii = { csl = nil, }, + pmc = { + csl = nil, + type = "literal", + }, pmcid = { csl = "PMCID", + type = "literal", + }, + pmid = { + csl = "PMID", + type = "literal", }, prebibitem = { csl = nil, }, preface = { csl = nil, + type = "name", }, preprint = { csl = nil, }, + presentationtype = { + csl = nil, + type = "literal", + }, + presenter = { + csl = nil, + type = "name", + }, presort = { csl = nil, type = "literal", @@ -1256,35 +2276,92 @@ return { printing = { csl = nil, }, + ["printing-number"] = { + csl = "printing-number", + }, prioritycountry = { csl = nil, + type = "literal", }, prioritydate = { csl = nil, + type = "date", }, prioritynumber = { csl = nil, + type = "literal", + }, + producer = { + csl = "producer", + type = "name", + }, + production = { + csl = nil, + type = "literal", + }, + protocollessurl = { + csl = nil, + type = "uri", }, pseudonym = { csl = nil, }, publication = { csl = nil, + type = "literal", }, publicationdate = { csl = nil, + type = "date", + }, + publicprice = { + csl = nil, + type = "literal", }, publisher = { csl = "publisher", type = "literal", }, + ["publisher-place"] = { + csl = "publisher-place", + }, + publishera = { + csl = nil, + type = "literal", + }, + publisherb = { + csl = nil, + type = "literal", + }, pubmed = { csl = "PMID", }, pubstate = { - csl = nil, + csl = "status", type = "key", }, + rawauthor = { + csl = nil, + type = "literal", + }, + realauthor = { + csl = nil, + type = "name", + }, + realdate = { + csl = nil, + type = "date", + }, + realeditor = { + csl = nil, + type = "name", + }, + recipient = { + csl = "recipient", + }, + references = { + csl = "references", + }, related = { csl = nil, type = "entrykey", @@ -1301,6 +2378,14 @@ return { csl = nil, type = "literal", }, + reportnumber = { + csl = nil, + type = "literal", + }, + repository = { + csl = nil, + type = "uri", + }, ["reprinted-from"] = { csl = nil, }, @@ -1316,33 +2401,129 @@ return { }, requestnumber = { csl = nil, + type = "literal", }, responsible = { csl = nil, }, + revdauthor = { + csl = nil, + type = "name", + }, + revdeditor = { + csl = nil, + type = "name", + }, + revdshorttitle = { + csl = nil, + type = "literal", + }, + revdsubtitle = { + csl = nil, + type = "literal", + }, + revdtitle = { + csl = nil, + type = "literal", + }, + revdtitleaddon = { + csl = nil, + type = "literal", + }, review = { csl = nil, }, + ["reviewed-author"] = { + csl = "reviewed-author", + }, + ["reviewed-genre"] = { + csl = "reviewed-genre", + }, + ["reviewed-title"] = { + csl = "reviewed-title", + }, + reviewer = { + csl = nil, + type = "name", + }, + reviewerinstitution = { + csl = nil, + type = "literal", + }, revision = { csl = nil, }, + role = { + csl = nil, + type = "key", + }, + scale = { + csl = "scale", + }, school = { csl = "publisher", type = "literal", }, + science = { + csl = nil, + type = "literal", + }, score = { csl = nil, }, + scribe = { + csl = nil, + type = "name", + }, + script = { + csl = nil, + type = "literal", + }, + ["script-writer"] = { + csl = "script-writer", + }, + scriptwriter = { + csl = nil, + type = "name", + }, section = { + csl = "section", + type = "literal", + }, + semesters = { csl = nil, + type = "entrykey", }, series = { csl = "collection-title", type = "literal", }, + ["series-creator"] = { + csl = "series-creator", + }, + seriesaddon = { + csl = nil, + type = "literal", + }, seriesedition = { csl = nil, }, + serieseditor = { + csl = nil, + type = "name", + }, + seriesseries = { + csl = nil, + type = "literal", + }, + setlabel = { + csl = nil, + type = "literal", + }, + shelfmark = { + csl = nil, + type = "literal", + }, short = { csl = nil, }, @@ -1353,10 +2534,30 @@ return { csl = nil, type = "name", }, + shortblog = { + csl = nil, + type = "literal", + }, + shortbookauthor = { + csl = nil, + type = "name", + }, + shortbooktitle = { + csl = nil, + type = "literal", + }, + shortcollection = { + csl = nil, + type = "literal", + }, shorteditor = { csl = nil, type = "name", }, + shortform = { + csl = nil, + type = "literal", + }, shorthand = { csl = nil, type = "literal", @@ -1365,10 +2566,34 @@ return { csl = nil, type = "literal", }, + shortissuetitle = { + csl = nil, + type = "literal", + }, shortjournal = { csl = "container-title-short", type = "literal", }, + shortlabeltitle = { + csl = nil, + type = "literal", + }, + shortlibrary = { + csl = nil, + type = "literal", + }, + shortmaintitle = { + csl = nil, + type = "literal", + }, + shortorganization = { + csl = nil, + type = "literal", + }, + shortserie = { + csl = nil, + type = "literal", + }, shortseries = { csl = nil, type = "literal", @@ -1376,10 +2601,22 @@ return { shortsubarchive = { csl = nil, }, + shortsubseries = { + csl = nil, + type = "literal", + }, + shortsubsubseries = { + csl = nil, + type = "literal", + }, shorttitle = { csl = "title-short", type = "literal", }, + shorttranslator = { + csl = nil, + type = "name", + }, shorturl = { csl = nil, }, @@ -1395,12 +2632,20 @@ return { sig4 = { csl = nil, }, + sign = { + csl = nil, + type = "literal", + }, size = { csl = nil, }, slaccitation = { csl = nil, }, + smfprice = { + csl = nil, + type = "literal", + }, ["sort-short"] = { csl = nil, }, @@ -1418,6 +2663,10 @@ return { csl = nil, type = "name", }, + sortshelfmark = { + csl = nil, + type = "literal", + }, sortshorthand = { csl = nil, type = "literal", @@ -1426,6 +2675,10 @@ return { csl = nil, type = "literal", }, + sortvolume = { + csl = nil, + type = "literal", + }, sortyear = { csl = nil, type = "integer", @@ -1433,8 +2686,17 @@ return { source1 = { csl = nil, }, + specdata = { + csl = nil, + type = "literal", + }, + speciality = { + csl = nil, + type = "literal", + }, specialitycode = { csl = nil, + type = "literal", }, ssedition = { csl = nil, @@ -1455,10 +2717,11 @@ return { csl = nil, }, status = { - csl = nil, + csl = "status", + type = "key", }, stdcode = { - csl = nil, + csl = "number", }, stitle = { csl = nil, @@ -1469,16 +2732,86 @@ return { subarchive = { csl = nil, }, + submitted = { + csl = "submitted", + }, + subnumber = { + csl = nil, + type = "integer", + }, + subseries = { + csl = nil, + type = "literal", + }, + subsubnumber = { + csl = nil, + type = "literal", + }, + subsubseries = { + csl = nil, + type = "literal", + }, subtitle = { csl = nil, type = "literal", }, + summarytitle = { + csl = nil, + type = "literal", + }, + supervisor = { + csl = nil, + type = "name", + }, + supplement = { + csl = "supplement", + }, + supplemental = { + csl = nil, + type = "literal", + }, + supplementala = { + csl = nil, + type = "literal", + }, + supplementalb = { + csl = nil, + type = "literal", + }, + support = { + csl = nil, + type = "literal", + }, + swhid = { + csl = nil, + type = "verbatim", + }, + swhidcore = { + csl = nil, + type = "verbatim", + }, symposium = { csl = nil, }, + systemreq = { + csl = nil, + type = "literal", + }, text = { csl = nil, }, + thesisdate = { + csl = nil, + type = "date", + }, + thesistype = { + csl = nil, + type = "verbatim", + }, + thumbnail = { + csl = nil, + type = "verbatim", + }, timestamp = { csl = nil, }, @@ -1486,12 +2819,36 @@ return { csl = "title", type = "literal", }, + ["title-short"] = { + csl = "title-short", + }, + titlea = { + csl = nil, + type = "literal", + }, titleaddon = { csl = nil, type = "literal", }, + titleb = { + csl = nil, + type = "literal", + }, + titleisdescription = { + csl = nil, + type = "literal", + }, + titlekey = { + csl = nil, + type = "key", + }, titlenote = { csl = nil, + type = "literal", + }, + titletype = { + csl = nil, + type = "literal", }, totalpages = { csl = nil, @@ -1509,6 +2866,26 @@ return { csl = "translator", type = "name", }, + translatora = { + csl = nil, + type = "name", + }, + translatoratype = { + csl = nil, + type = "key", + }, + translatorb = { + csl = nil, + type = "name", + }, + translatorbtype = { + csl = nil, + type = "key", + }, + translatortype = { + csl = nil, + type = "key", + }, transnumber = { csl = nil, }, @@ -1528,12 +2905,20 @@ return { csl = "genre", type = "key", }, + typeaddon = { + csl = nil, + type = "literal", + }, typeoflit = { csl = nil, }, umfnumber = { csl = nil, }, + update = { + csl = nil, + type = "date", + }, updated = { csl = nil, }, @@ -1548,17 +2933,26 @@ return { csl = "accessed", type = "date", }, + urldescription = { + csl = nil, + type = "literal", + }, urlnewline = { csl = nil, }, + urls = { + csl = nil, + type = "uri", + }, urltype = { csl = nil, }, urlyear = { - csl = nil, + csl = "accessed", }, urn = { csl = nil, + type = "verbatim", }, usera = { csl = nil, @@ -1584,6 +2978,10 @@ return { csl = nil, type = "literal", }, + usualtitle = { + csl = nil, + type = "literal", + }, value = { csl = nil, }, @@ -1607,26 +3005,124 @@ return { csl = "version", type = "literal", }, + versiona = { + csl = nil, + type = "literal", + }, + versionb = { + csl = nil, + type = "literal", + }, versiontype = { csl = nil, }, volformat = { csl = nil, }, + volsorder = { + csl = nil, + type = "literal", + }, volume = { csl = "volume", type = "integer", }, + ["volume-title"] = { + csl = "volume-title", + }, + ["volume-title-short"] = { + csl = "volume-title-short", + }, volumes = { csl = "number-of-volumes", type = "integer", }, volumetitle = { + csl = "volume-title", + }, + with = { + csl = nil, + type = "name", + }, + withauthor = { + csl = nil, + type = "name", + }, + withauthortype = { + csl = nil, + type = "literal", + }, + withbookauthor = { + csl = nil, + type = "name", + }, + withbookauthortype = { + csl = nil, + type = "literal", + }, + withbookeditor = { + csl = nil, + type = "name", + }, + withbookeditortype = { + csl = nil, + type = "literal", + }, + withbooktranslator = { + csl = nil, + type = "name", + }, + withbooktranslatortype = { + csl = nil, + type = "literal", + }, + witheditor = { + csl = nil, + type = "name", + }, + witheditortype = { + csl = nil, + type = "literal", + }, + withmainauthor = { csl = nil, + type = "name", + }, + withmainauthortype = { + csl = nil, + type = "literal", + }, + withmaineditor = { + csl = nil, + type = "name", + }, + withmaineditortype = { + csl = nil, + type = "literal", + }, + withmaintranslator = { + csl = nil, + type = "name", + }, + withmaintranslatortype = { + csl = nil, + type = "literal", + }, + withtranslator = { + csl = nil, + type = "name", + }, + withtranslatortype = { + csl = nil, + type = "literal", }, word = { csl = nil, }, + writer = { + csl = nil, + type = "name", + }, xdata = { csl = nil, type = "entrykey", @@ -1654,6 +3150,10 @@ return { zblnumber = { csl = nil, }, + zenon = { + csl = nil, + type = "verbatim", + }, zip = { csl = nil, }, diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-cli.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-cli.lua index 90237284736..4938267d3a1 100644 --- a/Master/texmf-dist/scripts/citation-style-language/citeproc-cli.lua +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-cli.lua @@ -143,6 +143,12 @@ local function process_aux_file(aux_file) end local style_class = engine:get_style_class() + if style_class == "in-text" then + for _, citation in ipairs(citations) do + citation.properties.noteIndex = 0 + end + end + local citation_strings = core.process_citations(engine, citations) -- util.debug(citation_strings) @@ -153,7 +159,7 @@ local function process_aux_file(aux_file) local citation_id = citation.citationID if citation_id ~= "@nocite" then local citation_str = citation_strings[citation_id] - output_string = output_string .. string.format("\\cslcite{%s}{{%s}{%s}}\n", citation_id, style_class, citation_str) + output_string = output_string .. string.format("\\cslcite{%s}{%s}\n", citation_id, citation_str) end end diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-context.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-context.lua index 08be158bcfe..f0c42e64fc6 100644 --- a/Master/texmf-dist/scripts/citation-style-language/citeproc-context.lua +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-context.lua @@ -265,7 +265,7 @@ function Context:get_macro(name) end function Context:get_simple_term(name, form, plural) - assert(self.locale) + -- assert(self.locale) return self.locale:get_simple_term(name, form, plural) end diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-element.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-element.lua index 197616cb088..4ef2b7c420a 100644 --- a/Master/texmf-dist/scripts/citation-style-language/citeproc-element.lua +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-element.lua @@ -95,6 +95,34 @@ function Element:process_children_nodes(node) end +function Element.make_name_inheritance(name, node) + name:set_attribute(node, "and") + name:set_attribute(node, "delimiter-precedes-et-al") + name:set_attribute(node, "delimiter-precedes-last") + name:set_number_attribute(node, "et-al-min") + name:set_number_attribute(node, "et-al-use-first") + name:set_number_attribute(node, "et-al-subsequent-min") + name:set_number_attribute(node, "et-al-subsequent-use-first") + name:set_bool_attribute(node, "et-al-use-last") + name:set_bool_attribute(node, "initialize") + name:set_attribute(node, "initialize-with") + name:set_attribute(node, "name-as-sort-order") + name:set_attribute(node, "sort-separator") + local delimiter = node:get_attribute("name-delimiter") + if delimiter then + name.delimiter = delimiter + end + local form = node:get_attribute("name-form") + if form then + name.form = form + end + local names_delimiter = node:get_attribute("names-delimiter") + if names_delimiter then + name.names_delimiter = names_delimiter + end +end + + function Element:build_ir(engine, state, context) return self:build_children_ir(engine, state, context) end @@ -430,7 +458,8 @@ end function Element:format_ordinal_number_parts(number_parts, form, gender, context) for i = 1, 2 do local part = number_parts[i] - if string.match(part, "%d+") then + -- Values like "2nd" are kept the in the original form. + if string.match(part, "^%d+$") then local number = tonumber(part) if form == "long-ordinal" and number >= 1 and number <= 10 then number_parts[i] = context:get_simple_term(string.format("long-ordinal-%02d", number)) diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-engine.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-engine.lua index d59c3bb4ea1..e8a77450eba 100644 --- a/Master/texmf-dist/scripts/citation-style-language/citeproc-engine.lua +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-engine.lua @@ -9,21 +9,16 @@ local engine = {} local dom = require("luaxml-domobject") local nodes = require("citeproc-nodes") +local Element = require("citeproc-element").Element local Style = require("citeproc-node-style").Style local Locale = require("citeproc-node-locale").Locale local Context = require("citeproc-context").Context local IrState = require("citeproc-context").IrState -local Rendered = require("citeproc-ir-node").Rendered -local YearSuffix = require("citeproc-ir-node").YearSuffix +local InlineElement = require("citeproc-output").InlineElement -- local OutputFormat = require("citeproc-output").OutputFormat local LatexWriter = require("citeproc-output").LatexWriter local HtmlWriter = require("citeproc-output").HtmlWriter local SortStringFormat = require("citeproc-output").SortStringFormat -local DisamStringFormat = require("citeproc-output").DisamStringFormat -local InlineElement = require("citeproc-output").InlineElement -local Micro = require("citeproc-output").Micro -local Formatted = require("citeproc-output").Formatted -local PlainText = require("citeproc-output").PlainText local util = require("citeproc-util") @@ -56,8 +51,7 @@ function CiteProc.new(sys, style, lang, force_lang) o.opt = { -- Similar to citeproc-js's development_extensions.wrap_url_and_doi - url_link = false, - doi_link = false, -- also applied to PMID and PMCID + wrap_url_and_doi = false, title_link = false, } @@ -173,16 +167,10 @@ function CiteProc:updateUncitedItems(uncited_ids) self.note_citations_map = {} end + function CiteProc:processCitationCluster(citation, citationsPre, citationsPost) -- util.debug(citation) - -- util.debug(citationsPre) - -- Fix missing noteIndex: sort_CitationNumberPrimaryAscendingViaMacroCitation.txt - if not citation.properties then - citation.properties = {} - end - if not citation.properties.noteIndex then - citation.properties.noteIndex = 0 - end + citation = self:normalize_citation_input(citation) -- Registor citation self.registry.citations_by_id[citation.citationID] = citation @@ -228,6 +216,7 @@ function CiteProc:processCitationCluster(citation, citationsPre, citationsPost) local output = {} local tainted_citation_ids = self:get_tainted_citaion_ids(citation_note_pairs) + tainted_citation_ids[citation.citationID] = true -- util.debug(tainted_citation_ids) -- params.bibchange = #tainted_citation_ids > 0 @@ -235,19 +224,37 @@ function CiteProc:processCitationCluster(citation, citationsPre, citationsPost) local citation_ = self.registry.citations_by_id[citation_id] local citation_index = citation_.citation_index - local citation_str = self:build_citation_str(citation_) + + local mode = citation_.properties.mode + if mode == "suppress-author" and self.style.class == "note" then + mode = nil + end + local citation_elemement = self.style.citation + if mode == "author-only" and self.style.intext then + citation_elemement = self.style.intext + end + + local citation_str = citation_elemement:build_citation_str(citation_, self) table.insert(output, {citation_index, citation_str, citation_id}) end - -- util.debug(output) return {params, output} end --- A variant of processCitationCluster() for easy use with LaTeX. --- It should be run after refreshing the registry (updateItems()) with all items -function CiteProc:process_citation(citation) - -- util.debug(citation) - -- util.debug(citationsPre) +function CiteProc:normalize_citation_input(citation) + citation = util.deep_copy(citation) + + if not citation.citationID then + citation.citationID = "CITATION-" .. tostring(#self.registry.citation_list) + end + + if not citation.citationItems then + citation.citationItems = {} + end + for i, cite_item in ipairs(citation.citationItems) do + citation.citationItems[i] = self:normalize_cite_item(cite_item) + end + -- Fix missing noteIndex: sort_CitationNumberPrimaryAscendingViaMacroCitation.txt if not citation.properties then citation.properties = {} @@ -256,6 +263,48 @@ function CiteProc:process_citation(citation) citation.properties.noteIndex = 0 end + return citation +end + +function CiteProc:normalize_cite_item(cite_item) + -- Shallow copy + cite_item = util.clone(cite_item) + cite_item.id = tostring(cite_item.id) + + -- Use "page" as locator label if missing + -- label_PluralWithAmpersand.txt + if cite_item.locator and not cite_item.label then + cite_item.label = "page" + end + + if cite_item.prefix then + -- Assert CSL rich-text or HTML-like tagged string + if cite_item.prefix == "" then + cite_item.prefix = nil + else + cite_item.prefix = InlineElement:parse(cite_item.prefix) + end + end + if cite_item.suffix then + if cite_item.suffix == "" then + cite_item.suffix = nil + else + cite_item.suffix = InlineElement:parse(cite_item.suffix) + end + end + + return cite_item +end + +-- A variant of processCitationCluster() for easy use with LaTeX. +-- It should be run after refreshing the registry (updateItems()) with all items +function CiteProc:process_citation(citation) + -- util.debug(citation) + -- util.debug(citationsPre) + -- Fix missing noteIndex: sort_CitationNumberPrimaryAscendingViaMacroCitation.txt + + citation = self:normalize_citation_input(citation) + -- Registor citation self.registry.citations_by_id[citation.citationID] = citation @@ -276,12 +325,21 @@ function CiteProc:process_citation(citation) -- self:updateItems(item_ids) for i, cite_item in ipairs(citation.citationItems) do - cite_item.id = tostring(cite_item.id) self:get_item(cite_item.id) end local tainted_citation_ids = self:get_tainted_citaion_ids(citation_note_pairs) - local citation_str = self:build_citation_str(citation) + + local mode = citation.properties.mode + if mode == "suppress-author" and self.style.class == "note" then + mode = nil + end + local citation_elemement = self.style.citation + if mode == "author-only" and self.style.intext then + citation_elemement = self.style.intext + end + + local citation_str = citation_elemement:build_citation_str(citation, self) return citation_str end @@ -310,9 +368,14 @@ function CiteProc:get_tainted_citaion_ids(citation_note_pairs) local previous_cite for _, cite_item in ipairs(citation.citationItems) do - tainted = self:set_cite_item_position(cite_item, note_number, previous_cite, previous_citation) - self.cite_last_note_numbers[cite_item.id] = note_number - previous_cite = cite_item + tainted = self:set_cite_item_position(cite_item, note_number, previous_cite, previous_citation, citation) + + -- https://citeproc-js.readthedocs.io/en/latest/csl-json/markup.html#citations + -- Citations within the main text of the document have a noteIndex of zero. + if (self.style.class == "note" and note_number > 0) or citation.properties.mode ~= "author-only" then + self.cite_last_note_numbers[cite_item.id] = note_number + previous_cite = cite_item + end end if tainted then @@ -323,7 +386,9 @@ function CiteProc:get_tainted_citaion_ids(citation_note_pairs) self.note_citations_map[note_number] = {} end table.insert(self.note_citations_map[note_number], citation.citationID) - previous_citation = citation + if citation.properties.mode ~= "author-only" then + previous_citation = citation + end end -- Update tainted citation ids because of citation-number's change @@ -339,9 +404,20 @@ function CiteProc:get_tainted_citaion_ids(citation_note_pairs) return tainted_citation_ids end -function CiteProc:set_cite_item_position(cite_item, note_number, previous_cite, previous_citation) +function CiteProc:set_cite_item_position(cite_item, note_number, previous_cite, previous_citation, citation) local position = util.position_map["first"] + -- https://citeproc-js.readthedocs.io/en/latest/csl-json/markup.html#citations + -- Citations within the main text of the document have a noteIndex of zero. + if self.style.class == "note" and note_number == 0 then + cite_item.position_level = position + return false + elseif citation.properties.mode == "author-only" then + -- discretionary_IbidInAuthorDateStyleWithoutIntext.txt + cite_item.position_level = position + return false + end + local first_reference_note_number = self.cite_first_note_numbers[cite_item.id] if first_reference_note_number then position = util.position_map["subsequent"] @@ -429,1163 +505,24 @@ function CiteProc:_get_cite_position(item, preceding_cite) end end -function CiteProc:build_citation_str(citation) - -- util.debug(citation.citationID) - local items = {} - for i, cite_item in ipairs(citation.citationItems) do - cite_item.id = tostring(cite_item.id) - -- util.debug(cite_item.id) - - -- Use "page" as locator label if missing - -- label_PluralWithAmpersand.txt - if cite_item.locator and not cite_item.label then - cite_item.label = "page" - end - - table.insert(items, cite_item) - end - - if self.registry.requires_sorting then - self:sort_bibliography() - end - - local citation_str = self:build_cluster(items) - return citation_str -end - -function CiteProc:build_cluster(citation_items) - local output_format = self.output_format - local irs = {} - citation_items = self:sorted_citation_items(citation_items) - for _, cite_item in ipairs(citation_items) do - local ir = self:build_fully_disambiguated_ir(cite_item, output_format) - table.insert(irs, ir) - end - - if self.style.citation.cite_grouping then - irs = self:group_cites(irs) - else - local citation_collapse = self.style.citation.collapse - if citation_collapse == "year" or citation_collapse == "year-suffix" or - citation_collapse == "year-suffix-ranged" then - irs = self:group_cites(irs) - end - end - - if self.style.citation.collapse then - self:collapse_cites(irs) - end - - -- Capitalize first - for i, ir in ipairs(irs) do - -- local layout_prefix - -- local layout_affixes = self.style.citation.layout.affixes - -- if layout_affixes then - -- layout_prefix = layout_affixes.prefix - -- end - local prefix = citation_items[i].prefix - if prefix then - if prefix and string.match(prefix, "[.!?]%s*$") and #util.split(util.strip(prefix)) > 1 then - ir:capitalize_first_term() - end - else - local delimiter = self.style.citation.layout.delimiter - if i == 1 or not delimiter or string.match(delimiter, "[.!?]%s*$") then - ir:capitalize_first_term() - end - end - end - - -- util.debug(irs) - - local citation_delimiter = self.style.citation.layout.delimiter - local citation_stream = {} - - local context = Context:new() - context.engine = self - context.style = self.style - context.area = self.style.citation - context.in_bibliography = false - context.locale = self:get_locale(self.lang) - context.name_inheritance = self.style.citation.name_inheritance - context.format = output_format - - local previous_ir - for i, ir in ipairs(irs) do - local cite_prefix = citation_items[i].prefix - local cite_suffix = citation_items[i].suffix - if not ir.collapse_suppressed then - local ir_inlines = ir:flatten(output_format) - if #ir_inlines > 0 then - -- Make sure ir_inlines has outputs contents. - -- collapse_AuthorCollapseNoDateSorted.txt - if previous_ir then - if previous_ir.own_delimiter then - table.insert(citation_stream, PlainText:new(previous_ir.own_delimiter)) - elseif citation_delimiter and not (cite_prefix and util.startswith(cite_prefix, ",")) then - table.insert(citation_stream, PlainText:new(citation_delimiter)) - end - end - - if cite_prefix then - table.insert(citation_stream, Micro:new(InlineElement:parse(cite_prefix, context))) - end - - -- util.debug(ir) - util.extend(citation_stream, ir_inlines) - previous_ir = ir - - if cite_suffix then - table.insert(citation_stream, Micro:new(InlineElement:parse(cite_suffix, context))) - end - end - end - end - -- util.debug(citation_stream) - - if context.area.layout.affixes then - local affixes = context.area.layout.affixes - if affixes.prefix then - table.insert(citation_stream, 1, PlainText:new(affixes.prefix)) - end - if affixes.suffix then - table.insert(citation_stream, PlainText:new(affixes.suffix)) - end - end - -- util.debug(citation_stream) - - if #citation_stream > 0 and context.area.layout.formatting then - citation_stream = {Formatted:new(citation_stream, context.area.layout.formatting)} - end - - if #citation_stream == 0 then - citation_stream = {PlainText:new("[CSL STYLE ERROR: reference with no printed form.]")} - end - - -- util.debug(citation_stream) - local str = output_format:output(citation_stream, context) - str = util.strip(str) - - return str -end - -function CiteProc:sorted_citation_items(items) - local citation_sort = self.style.citation.sort - if not citation_sort then - return items - end - - local state = IrState:new() - local context = Context:new() - context.engine = self - context.style = self.style - context.area = self.style.citation - context.in_bibliography = false - context.locale = self:get_locale(self.lang) - context.name_inheritance = self.style.citation.name_inheritance - context.format = SortStringFormat:new() - -- context.id = id - context.cite = nil - -- context.reference = self:get_item(id) - - items = citation_sort:sort(items, state, context) - return items -end - -function CiteProc:build_fully_disambiguated_ir(cite_item, output_format) - local cite_ir = self:build_ambiguous_ir(cite_item, output_format) - -- util.debug(cite_ir) - cite_ir = self:disambiguate_add_givenname(cite_ir) - cite_ir = self:disambiguate_add_names(cite_ir) - cite_ir = self:disambiguate_conditionals(cite_ir) - cite_ir = self:disambiguate_add_year_suffix(cite_ir) - return cite_ir -end - -function CiteProc:build_ambiguous_ir(cite_item, output_format) - local state = IrState:new(self.style) - cite_item.id = tostring(cite_item.id) - local context = Context:new() - context.engine = self - context.style = self.style - context.area = self.style.citation - context.locale = self:get_locale(self.lang) - context.name_inheritance = self.style.citation.name_inheritance - context.format = output_format - context.id = cite_item.id - context.cite = cite_item - -- context.reference = self:get_item(cite_item.id) - context.reference = self.registry.registry[cite_item.id] - - local ir - if context.reference then - ir = self.style.citation:build_ir(self, state, context) - else - ir = Rendered:new({Formatted:new({PlainText:new(cite_item.id)}, {["font-weight"] = "bold"})}, self) - end - - ir.cite_item = cite_item - ir.reference = context.reference - ir.ir_index = #self.disam_irs + 1 - table.insert(self.disam_irs, ir) - ir.is_ambiguous = false - ir.disam_level = 0 - - -- Formattings like font-style are ignored for disambiguation. - local disam_format = DisamStringFormat:new() - local inlines = ir:flatten(disam_format) - local disam_str = disam_format:output(inlines, context) - ir.disam_str = disam_str - - if not self.cite_irs_by_output[disam_str] then - self.cite_irs_by_output[disam_str] = {} - end - - for ir_index, ir_ in pairs(self.cite_irs_by_output[disam_str]) do - if ir_.cite_item.id ~= cite_item.id then - ir.is_ambiguous = true - break - end - end - self.cite_irs_by_output[disam_str][ir.ir_index] = ir - - return ir -end - -function CiteProc:disambiguate_add_givenname(cite_ir) - if self.style.citation.disambiguate_add_givenname then - -- util.debug("disambiguate_add_givenname: " .. cite_ir.cite_item.id) - - local gn_disam_rule = self.style.citation.givenname_disambiguation_rule - if gn_disam_rule == "all-names" or gn_disam_rule == "all-names-with-initials" then - cite_ir = self:disambiguate_add_givenname_all_names(cite_ir) - elseif gn_disam_rule == "primary-name" or gn_disam_rule == "primary-name-with-initials" then - cite_ir = self:disambiguate_add_givenname_primary_name(cite_ir) - elseif gn_disam_rule == "by-cite" then - cite_ir = self:disambiguate_add_givenname_by_cite(cite_ir) - end - end - return cite_ir -end - --- TODO: reorganize this code -function CiteProc:disambiguate_add_givenname_all_names(cite_ir) - -- util.debug("disambiguate_add_givenname_all_names: " .. cite_ir.cite_item.id) - if not cite_ir.person_name_irs or #cite_ir.person_name_irs == 0 then - return cite_ir - end - - -- util.debug(cite_ir.disam_str) - - for _, person_name_ir in ipairs(cite_ir.person_name_irs) do - local name_output = person_name_ir.name_output - -- util.debug(name_output) - - if not person_name_ir.person_name_index then - person_name_ir.person_name_index = #self.person_names + 1 - table.insert(self.person_names, person_name_ir) - end - - if not self.person_names_by_output[name_output] then - self.person_names_by_output[name_output] = {} - end - self.person_names_by_output[name_output][person_name_ir.person_name_index] = person_name_ir - - local ambiguous_name_irs = {} - local ambiguous_same_output_irs = {} - - for _, pn_ir in pairs(self.person_names_by_output[person_name_ir.name_output]) do - if pn_ir.full_name ~= person_name_ir.full_name then - table.insert(ambiguous_name_irs, pn_ir) - end - if pn_ir.name_output == person_name_ir.name_output then - table.insert(ambiguous_same_output_irs, pn_ir) - end - end - - -- util.debug(person_name_ir.name_output) - -- util.debug(person_name_ir.full_name) - -- util.debug(#ambiguous_name_irs) - -- util.debug(person_name_ir.disam_variants_index) - -- util.debug(person_name_ir.disam_variants) - - while person_name_ir.disam_variants_index < #person_name_ir.disam_variants do - if #ambiguous_name_irs == 0 then - break - end - - for _, pn_ir in ipairs(ambiguous_same_output_irs) do - -- expand one name - if pn_ir.disam_variants_index < #pn_ir.disam_variants then - pn_ir.disam_variants_index = pn_ir.disam_variants_index + 1 - pn_ir.name_output = pn_ir.disam_variants[pn_ir.disam_variants_index] - pn_ir.inlines = pn_ir.disam_inlines[pn_ir.name_output] - - if not self.person_names_by_output[pn_ir.name_output] then - self.person_names_by_output[pn_ir.name_output] = {} - end - self.person_names_by_output[pn_ir.name_output][pn_ir.person_name_index] = pn_ir - end - end - - -- util.debug(person_name_ir.name_output) - - -- update ambiguous_name_irs and ambiguous_same_output_irs - ambiguous_name_irs = {} - ambiguous_same_output_irs = {} - for _, pn_ir in pairs(self.person_names_by_output[person_name_ir.name_output]) do - if pn_ir.full_name ~= person_name_ir.full_name then - -- util.debug(pn_ir.full_name .. ": " .. pn_ir.name_output) - table.insert(ambiguous_name_irs, pn_ir) - end - if pn_ir.name_output == person_name_ir.name_output then - table.insert(ambiguous_same_output_irs, pn_ir) - end - end - - end - end - - -- update cite_ir output - local disam_format = DisamStringFormat:new() - local inlines = cite_ir:flatten(disam_format) - local disam_str = disam_format:output(inlines, context) - cite_ir.disam_str = disam_str - if not self.cite_irs_by_output[disam_str] then - self.cite_irs_by_output[disam_str] = {} - end - self.cite_irs_by_output[disam_str][cite_ir.ir_index] = cite_ir - - -- update ambiguous_cite_irs and ambiguous_same_output_irs - local ambiguous_cite_irs = {} - for ir_index, ir_ in pairs(self.cite_irs_by_output[cite_ir.disam_str]) do - -- util.debug(ir_.cite_item.id) - if ir_.cite_item.id ~= cite_ir.cite_item.id then - table.insert(ambiguous_cite_irs, ir_) - end - end - if #ambiguous_cite_irs == 0 then - cite_ir.is_ambiguous = false - end - - return cite_ir -end - -function CiteProc:disambiguate_add_givenname_primary_name(cite_ir) - if not cite_ir.person_name_irs or #cite_ir.person_name_irs == 0 then - return cite_ir - end - local person_name_ir = cite_ir.person_name_irs[1] - local name_output = person_name_ir.name_output - -- util.debug(name_output) - - if not person_name_ir.person_name_index then - person_name_ir.person_name_index = #self.person_names + 1 - table.insert(self.person_names, person_name_ir) - end - if not self.person_names_by_output[name_output] then - self.person_names_by_output[name_output] = {} - end - self.person_names_by_output[name_output][person_name_ir.person_name_index] = person_name_ir - - local ambiguous_name_irs = {} - local ambiguous_same_output_irs = {} - - for _, pn_ir in pairs(self.person_names_by_output[person_name_ir.name_output]) do - if pn_ir.full_name ~= person_name_ir.full_name then - table.insert(ambiguous_name_irs, pn_ir) - end - if pn_ir.name_output == person_name_ir.name_output then - table.insert(ambiguous_same_output_irs, pn_ir) - end - end - - for _, name_variant in ipairs(person_name_ir.disam_variants) do - if #ambiguous_name_irs == 0 then - break - end - - for _, pn_ir in ipairs(ambiguous_same_output_irs) do - -- expand one name - if pn_ir.disam_variants_index < #pn_ir.disam_variants then - pn_ir.disam_variants_index = pn_ir.disam_variants_index + 1 - pn_ir.name_output = pn_ir.disam_variants[pn_ir.disam_variants_index] - pn_ir.inlines = pn_ir.disam_inlines[pn_ir.name_output] - - if not self.person_names_by_output[pn_ir.name_output] then - self.person_names_by_output[pn_ir.name_output] = {} - end - self.person_names_by_output[pn_ir.name_output][person_name_ir.person_name_index] = person_name_ir - end - end - - -- update ambiguous_name_irs and ambiguous_same_output_irs - ambiguous_name_irs = {} - ambiguous_same_output_irs = {} - for _, pn_ir in pairs(self.person_names_by_output[person_name_ir.name_output]) do - if pn_ir.full_name ~= person_name_ir.full_name then - table.insert(ambiguous_name_irs, pn_ir) - end - if pn_ir.name_output == person_name_ir.name_output then - table.insert(ambiguous_same_output_irs, pn_ir) - end - end - end - - return cite_ir -end - -function CiteProc:disambiguate_add_givenname_by_cite(cite_ir) - if not cite_ir.is_ambiguous then - return cite_ir - end - if not cite_ir.person_name_irs or #cite_ir.person_name_irs == 0 then - return cite_ir - end - - for _, ir_ in ipairs(self.disam_irs) do - -- util.debug(ir_.cite_item.id) - -- util.debug(ir_.disam_str) - end - - local disam_format = DisamStringFormat:new() - - local ambiguous_cite_irs = {} - local ambiguous_same_output_irs = {} - - for ir_index, ir_ in pairs(self.cite_irs_by_output[cite_ir.disam_str]) do - if ir_.cite_item.id ~= cite_ir.cite_item.id then - table.insert(ambiguous_cite_irs, ir_) - end - if ir_.disam_str == cite_ir.disam_str then - table.insert(ambiguous_same_output_irs, ir_) - end - end - - for i, person_name_ir in ipairs(cite_ir.person_name_irs) do - -- util.debug(person_name_ir.name_output) - -- util.debug(person_name_ir.disam_variants) - if #ambiguous_cite_irs == 0 then - cite_ir.is_ambiguous = false - break - end - - -- util.debug(person_name_ir.disam_variants) - while person_name_ir.disam_variants_index < #person_name_ir.disam_variants do - -- util.debug(person_name_ir.name_output) - - local is_different_name = false - for _, ir_ in ipairs(ambiguous_cite_irs) do - if ir_.person_name_irs[i] then - if ir_.person_name_irs[i].full_name ~= person_name_ir.full_name then - -- util.debug(ir_.cite_item.id) - is_different_name = true - break - end - end - end - -- util.debug(is_different_name) - if not is_different_name then - break - end - - for _, ir_ in ipairs(ambiguous_same_output_irs) do - -- util.debug(ir_.cite_item.id) - local person_name_ir_ = ir_.person_name_irs[i] - if person_name_ir_ then - if person_name_ir_.disam_variants_index < #person_name_ir_.disam_variants then - person_name_ir_.disam_variants_index = person_name_ir_.disam_variants_index + 1 - local disam_variant = person_name_ir_.disam_variants[person_name_ir_.disam_variants_index] - person_name_ir_.name_output = disam_variant - -- util.debug(disam_variant) - person_name_ir_.inlines = person_name_ir_.disam_inlines[disam_variant] - -- Update cite ir output - local inlines = ir_:flatten(disam_format) - local disam_str = disam_format:output(inlines, context) - ir_.disam_str = disam_str - if not self.cite_irs_by_output[disam_str] then - self.cite_irs_by_output[disam_str] = {} - end - self.cite_irs_by_output[disam_str][ir_.ir_index] = ir_ - end - end - end - - -- update ambiguous_cite_irs and ambiguous_same_output_irs - ambiguous_cite_irs = {} - ambiguous_same_output_irs = {} - for ir_index, ir_ in pairs(self.cite_irs_by_output[cite_ir.disam_str]) do - -- util.debug(ir_.cite_item.id) - if ir_.cite_item.id ~= cite_ir.cite_item.id then - table.insert(ambiguous_cite_irs, ir_) - end - if ir_.disam_str == cite_ir.disam_str then - table.insert(ambiguous_same_output_irs, ir_) - end - end - - -- util.debug(#ambiguous_cite_irs) - - if #ambiguous_cite_irs == 0 then - cite_ir.is_ambiguous = false - return cite_ir - end - - end - end - - return cite_ir -end - -local function find_first_name_ir(ir) - if ir._type == "NameIr" then - return ir - end - if ir.children then - for _, child in ipairs(ir.children) do - local name_ir = find_first_name_ir(child) - if name_ir then - return name_ir - end - end - end - return nil -end - -function CiteProc:disambiguate_add_names(cite_ir) - if not self.style.citation.disambiguate_add_names then - return cite_ir - end - - if not cite_ir.name_ir then - cite_ir.name_ir = find_first_name_ir(cite_ir) - end - local name_ir = cite_ir.name_ir - - if not cite_ir.is_ambiguous then - return cite_ir - end - - if not name_ir or not name_ir.et_al_abbreviation then - return cite_ir - end - - -- util.debug("disambiguate_add_names: " .. cite_ir.cite_item.id) - - if name_ir then - -- util.debug(cite_ir.disam_str) - -- util.debug(cite_ir.name_ir.full_name_str) - -- util.debug(cite_ir.is_ambiguous) - end - - local disam_format = DisamStringFormat:new() - - while cite_ir.is_ambiguous do - if #cite_ir.name_ir.hidden_name_irs == 0 then - break - end - - local ambiguous_cite_irs = {} - local ambiguous_same_output_irs = {} - for _, ir_ in pairs(self.cite_irs_by_output[cite_ir.disam_str]) do - if ir_.cite_item.id ~= cite_ir.cite_item.id then - table.insert(ambiguous_cite_irs, ir_) - end - if ir_.disam_str == cite_ir.disam_str then - table.insert(ambiguous_same_output_irs, ir_) - end - end - - -- util.debug(#ambiguous_same_output_irs) - if #ambiguous_cite_irs == 0 then - cite_ir.is_ambiguous = false - break - end - - -- check if the cite can be (fully) disambiguated by adding names - local can_be_disambuguated = false - for _, ir_ in ipairs(ambiguous_cite_irs) do - if ir_.name_ir.full_name_str ~= cite_ir.name_ir.full_name_str then - can_be_disambuguated = true - break - end - end - -- util.debug(can_be_disambuguated) - if not can_be_disambuguated then - break - end - - for _, ir_ in ipairs(ambiguous_same_output_irs) do - local added_person_name_ir = ir_.name_ir.name_inheritance:expand_one_name(ir_.name_ir) - if added_person_name_ir then - -- util.debug("Updated: " .. ir_.cite_item.id) - table.insert(ir_.person_name_irs, added_person_name_ir) - - if not added_person_name_ir.person_name_index then - added_person_name_ir.person_name_index = #self.person_names + 1 - table.insert(self.person_names, added_person_name_ir) - end - local name_output = added_person_name_ir.name_output - if not self.person_names_by_output[name_output] then - self.person_names_by_output[name_output] = {} - end - self.person_names_by_output[name_output][added_person_name_ir.person_name_index] = added_person_name_ir - - -- Update ir output - local inlines = ir_:flatten(disam_format) - local disam_str = disam_format:output(inlines, context) - -- util.debug(disam_str) - ir_.disam_str = disam_str - if not self.cite_irs_by_output[disam_str] then - self.cite_irs_by_output[disam_str] = {} - end - self.cite_irs_by_output[disam_str][ir_.ir_index] = ir_ - end - end - - -- util.debug("disambiguate_add_givenname") - - if self.style.citation.disambiguate_add_givenname then - local gn_disam_rule = self.style.citation.givenname_disambiguation_rule - if gn_disam_rule == "all-names" or gn_disam_rule == "all-names-with-initials" then - cite_ir = self:disambiguate_add_givenname_all_names(cite_ir) - elseif gn_disam_rule == "by-cite" then - cite_ir = self:disambiguate_add_givenname_by_cite(cite_ir) - end - end - - cite_ir.is_ambiguous = self:check_ambiguity(cite_ir) - - for _, ir_ in ipairs(self.disam_irs) do - -- util.debug(ir_.cite_item.id .. ": " .. ir_.disam_str) - end - - end - - - return cite_ir -end - -function CiteProc:collect_irs_with_disambiguate_branch(ir) - local irs_with_disambiguate_branch = {} - if ir.children then - for i, child_ir in ipairs(ir.children) do - if child_ir.disambiguate_branch_ir then - table.insert(irs_with_disambiguate_branch, child_ir) - elseif child_ir.children then - util.extend(irs_with_disambiguate_branch, - self:collect_irs_with_disambiguate_branch(child_ir)) - end - end - end - return irs_with_disambiguate_branch -end - -function CiteProc:disambiguate_conditionals(cite_ir) - -- util.debug(cite_ir) - - cite_ir.irs_with_disambiguate_branch = self:collect_irs_with_disambiguate_branch(cite_ir) - - local disam_format = DisamStringFormat:new() - - while cite_ir.is_ambiguous do - if #cite_ir.irs_with_disambiguate_branch == 0 then - break - end - - -- util.debug(cite_ir.cite_item.id) - -- util.debug(cite_ir.disam_str) - - -- update ambiguous_same_output_irs - local ambiguous_same_output_irs = {} - for _, ir_ in pairs(self.cite_irs_by_output[cite_ir.disam_str]) do - if ir_.disam_str == cite_ir.disam_str then - table.insert(ambiguous_same_output_irs, ir_) - end - end - - for _, ir_ in ipairs(ambiguous_same_output_irs) do - if #ir_.irs_with_disambiguate_branch > 0 then - -- Disambiguation is incremental - -- disambiguate_IncrementalExtraText.txt - local condition_ir = ir_.irs_with_disambiguate_branch[1] - condition_ir.children[1] = condition_ir.disambiguate_branch_ir - condition_ir.group_var = condition_ir.disambiguate_branch_ir.group_var - table.remove(ir_.irs_with_disambiguate_branch, 1) - -- disambiguate_DisambiguateTrueReflectedInBibliography.txt - ir_.reference.disambiguate = true - - -- Update ir output - local inlines = ir_:flatten(disam_format) - local disam_str = disam_format:output(inlines, context) - -- util.debug("update: " .. ir_.cite_item.id .. ": " .. disam_str) - ir_.disam_str = disam_str - if not self.cite_irs_by_output[disam_str] then - self.cite_irs_by_output[disam_str] = {} - end - self.cite_irs_by_output[disam_str][ir_.ir_index] = ir_ - end - end - - cite_ir.is_ambiguous = self:check_ambiguity(cite_ir) - -- util.debug(cite_ir.is_ambiguous) - for _, ir_ in ipairs(self.disam_irs) do - -- util.debug(ir_.cite_item.id .. ": " .. ir_.disam_str) - end - - end - return cite_ir -end - -function CiteProc:check_ambiguity(cite_ir) - for _, ir_ in pairs(self.cite_irs_by_output[cite_ir.disam_str]) do - if ir_.cite_item.id ~= cite_ir.cite_item.id then - return true - end - end - return false -end - -function CiteProc:get_same_output_irs(cite_ir) - local ambiguous_same_output_irs = {} - for _, ir_ in pairs(self.cite_irs_by_output[cite_ir.disam_str]) do - if ir_.disam_str == cite_ir.disam_str then - table.insert(ambiguous_same_output_irs, ir_) - end - end - return ambiguous_same_output_irs -end - -function CiteProc:disambiguate_add_year_suffix(cite_ir) - if not cite_ir.is_ambiguous or not self.style.citation.disambiguate_add_year_suffix then - return cite_ir - end - - local same_output_irs = self:get_same_output_irs(cite_ir) - - table.sort(same_output_irs, function (a, b) - -- return a.ir_index < b.ir_index - return a.reference["citation-number"] < b.reference["citation-number"] - end) - - local year_suffix_number = 0 - -- util.debug(cite_ir) - - local disam_format = DisamStringFormat:new() - - for _, ir_ in ipairs(same_output_irs) do - ir_.reference.year_suffix_number = nil - end - - for _, ir_ in ipairs(same_output_irs) do - -- print(ir_.cite_item.id) - -- print(ir_.reference) - if not ir_.reference.year_suffix_number then - year_suffix_number = year_suffix_number + 1 - ir_.reference.year_suffix_number = year_suffix_number - ir_.reference["year-suffix"] = self:render_year_suffix(year_suffix_number) - end - - if not ir_.year_suffix_irs then - ir_.year_suffix_irs = self:collect_year_suffix_irs(ir_) - if #ir_.year_suffix_irs == 0 then - -- By default, the year-suffix is appended the first year rendered through cs:date - local year_ir = self:find_first_year_ir(ir_) - -- util.debug(year_ir) - if year_ir then - local year_suffix_ir = YearSuffix:new({}, self.style.citation) - table.insert(year_ir.children, year_suffix_ir) - table.insert(ir_.year_suffix_irs, year_suffix_ir) - end - end - end - - for _, year_suffix_ir in ipairs(ir_.year_suffix_irs) do - year_suffix_ir.inlines = {PlainText:new(ir_.reference["year-suffix"])} - year_suffix_ir.year_suffix_number = ir_.reference.year_suffix_number - year_suffix_ir.group_var = "important" - end - - local inlines = ir_:flatten(disam_format) - local disam_str = disam_format:output(inlines, context) - -- util.debug("update: " .. ir_.cite_item.id .. ": " .. disam_str) - ir_.disam_str = disam_str - if not self.cite_irs_by_output[disam_str] then - self.cite_irs_by_output[disam_str] = {} - end - self.cite_irs_by_output[disam_str][ir_.ir_index] = ir_ - - end - - cite_ir.is_ambiguous = false - - return cite_ir -end - -function CiteProc:collect_year_suffix_irs(ir) - local year_suffix_irs = {} - if ir.children then - for i, child_ir in ipairs(ir.children) do - if child_ir._type == "YearSuffix" then - table.insert(year_suffix_irs, child_ir) - elseif child_ir.children then - util.extend(year_suffix_irs, - self:collect_year_suffix_irs(child_ir)) - end - end - end - return year_suffix_irs -end - -function CiteProc:find_first_year_ir(ir) - if ir.is_year then - return ir - end - if ir.children then - for _, child_ir in ipairs(ir.children) do - local year_ir = self:find_first_year_ir(child_ir) - if year_ir then - return year_ir - end - end - end - return nil -end - -function CiteProc:render_year_suffix(year_suffix_number) - if year_suffix_number <= 0 then - return nil - end - local year_suffix = "" - while year_suffix_number > 0 do - local i = (year_suffix_number - 1) % 26 - year_suffix = string.char(i + 97) .. year_suffix - year_suffix_number = (year_suffix_number - 1) // 26 - end - -- util.debug(year_suffix) - return year_suffix -end - -local function find_first(ir, check) - if check(ir) then - return ir - end - if ir.children then - for _, child in ipairs(ir.children) do - local target_ir = find_first(child, check) - if target_ir then - return target_ir - end - end - end - return nil -end - -function CiteProc:group_cites(irs) - local disam_format = DisamStringFormat:new() - for _, ir in ipairs(irs) do - local first_names_ir = ir.first_names_ir - if not first_names_ir then - first_names_ir = find_first(ir, function (ir_) - return ir_._element == "names" - end) - if first_names_ir then - local inlines = first_names_ir:flatten(disam_format) - first_names_ir.disam_str = disam_format:output(inlines, context) - end - ir.first_names_ir = first_names_ir - end - end - - local irs_by_name = {} - local name_list = {} - - for _, ir in ipairs(irs) do - local name_str = "" - if ir.first_names_ir then - name_str = ir.first_names_ir.disam_str - end - if not irs_by_name[name_str] then - irs_by_name[name_str] = {} - table.insert(name_list, name_str) - end - table.insert(irs_by_name[name_str], ir) - end - - local grouped = {} - for _, name_str in ipairs(name_list) do - local irs_with_same_name = irs_by_name[name_str] - for i, ir in ipairs(irs_with_same_name) do - if i < #irs_with_same_name then - ir.own_delimiter = self.style.citation.cite_group_delimiter - end - table.insert(grouped, ir) - end - end - return grouped -end - -function CiteProc:collapse_cites(irs) - if self.style.citation.collapse == "citation-number" then - self:collapse_cites_by_citation_number(irs) - elseif self.style.citation.collapse == "year" then - self:collapse_cites_by_year(irs) - elseif self.style.citation.collapse == "year-suffix" then - self:collapse_cites_by_year_suffix(irs) - elseif self.style.citation.collapse == "year-suffix-ranged" then - self:collapse_cites_by_year_suffix_ranged(irs) - end -end - -function CiteProc:collapse_cites_by_citation_number(irs) - local cite_groups = {} - local current_group = {} - local previous_citation_number - for i, ir in ipairs(irs) do - local citation_number - local only_citation_number_ir = self:get_only_citation_number(ir) - if only_citation_number_ir then - -- Other irs like locators are not rendered. - -- collapse_CitationNumberRangesWithAffixesGrouped.txt - citation_number = only_citation_number_ir.citation_number - end - if i == 1 then - table.insert(current_group, ir) - elseif citation_number and previous_citation_number and - previous_citation_number + 1 == citation_number then - table.insert(current_group, ir) - else - table.insert(cite_groups, current_group) - current_group = {ir} - end - previous_citation_number = citation_number - end - table.insert(cite_groups, current_group) - - for _, cite_group in ipairs(cite_groups) do - if #cite_group >= 3 then - cite_group[1].own_delimiter = util.unicode["en dash"] - for i = 2, #cite_group - 1 do - cite_group[i].collapse_suppressed = true - end - cite_group[#cite_group].own_delimiter = self.style.citation.after_collapse_delimiter - end - end -end - -function CiteProc:get_only_citation_number(ir) - if ir.citation_number then - return ir - end - if not ir.children then - return nil - end - local only_citation_number_ir - for _, child in ipairs(ir.children) do - if child.group_var ~= "missing" then - local citation_number_ir = self:get_only_citation_number(child) - if citation_number_ir then - if only_citation_number_ir then - return nil - else - only_citation_number_ir = citation_number_ir - end - else - return false - end - end - end - return only_citation_number_ir -end - -function CiteProc:collapse_cites_by_year(irs) - local cite_groups = {{}} - local previous_name_str - for i, ir in ipairs(irs) do - local name_str - if ir.first_names_ir then - name_str = ir.first_names_ir.disam_str - end - if i == 1 then - table.insert(cite_groups[#cite_groups], ir) - elseif name_str and name_str == previous_name_str then - -- ir.fist_names_ir was set in the cite grouping stage - -- TODO: and not previous cite suffix - table.insert(cite_groups[#cite_groups], ir) - else - table.insert(cite_groups, {ir}) - end - previous_name_str = name_str - end - - for _, cite_group in ipairs(cite_groups) do - if #cite_group > 1 then - for i, cite_ir in ipairs(cite_group) do - if i > 1 and cite_ir.first_names_ir then - cite_ir.first_names_ir.collapse_suppressed = true - end - if i == #cite_group then - cite_ir.own_delimiter = self.style.citation.after_collapse_delimiter - elseif i < #cite_group then - -- The delimiter depends on the citation > sort. - -- https://github.com/citation-style-language/test-suite/issues/39#issuecomment-687901688 - if cite_ir.cite_item.locator then - -- Special hack for - cite_ir.own_delimiter = self.style.citation.after_collapse_delimiter - elseif self.style.citation.cite_grouping then - if self.style.citation.sort then - cite_ir.own_delimiter = self.style.citation.cite_group_delimiter - else - cite_ir.own_delimiter = self.style.citation.layout.delimiter - end - else - if self.style.citation.sort then - cite_ir.own_delimiter = self.style.citation.cite_group_delimiter - else - -- disambiguate_YearCollapseWithInstitution.txt - -- disambiguate_InitializeWithButNoDisambiguation.txt ? - cite_ir.own_delimiter = self.style.citation.layout.delimiter - end - end - end - end - end - end -end - -local function find_rendered_year_suffix(ir) - if ir._type == "YearSuffix" then - return ir - end - if ir.children then - for _, child in ipairs(ir.children) do - if child.group_var ~= "missing" then - local year_suffix = find_rendered_year_suffix(child) - if year_suffix then - return year_suffix - end - end - end - end - return nil -end - -function CiteProc:collapse_cites_by_year_suffix(irs) - self:collapse_cites_by_year(irs) - -- Group by disam_str - -- The year-suffix is ommitted in DisamStringFormat - local cite_groups = {{}} - local previous_ir - local previous_year_suffix - for i, ir in ipairs(irs) do - local year_suffix = find_rendered_year_suffix(ir) - ir.rendered_year_suffix_ir = year_suffix - if i == 1 then - table.insert(cite_groups[#cite_groups], ir) - elseif year_suffix and previous_ir.disam_str == ir.disam_str and previous_year_suffix then - -- TODO: and not previous cite suffix - table.insert(cite_groups[#cite_groups], ir) - else - table.insert(cite_groups, {ir}) - end - previous_ir = ir - previous_year_suffix = year_suffix - end - - for _, cite_group in ipairs(cite_groups) do - if #cite_group > 1 then - for i, cite_ir in ipairs(cite_group) do - if i > 1 then - -- cite_ir.children = {cite_ir.rendered_year_suffix_ir} - -- Set the collapse_suppressed flag rather than removing the child irs. - -- This leaves the disamb ir structure unchanged. - self:suppress_ir_except_child(cite_ir, cite_ir.rendered_year_suffix_ir) - end - if i < #cite_group then - if self.style.citation.cite_grouping then - -- In the current citeproc-js impplementation, explicitly set - -- cite-group-delimiter takes precedence over year-suffix-delimiter. - -- May be changed in the future. - -- https://github.com/citation-style-language/test-suite/issues/50 - cite_ir.own_delimiter = self.style.citation.cite_group_delimiter - else - cite_ir.own_delimiter = self.style.citation.year_suffix_delimiter - end - elseif i == #cite_group then - cite_ir.own_delimiter = self.style.citation.after_collapse_delimiter - end - end - end - end -end - -function CiteProc:suppress_ir_except_child(ir, target) - if ir == target then - ir.collapse_suppressed = false - return false - end - ir.collapse_suppressed = true - if ir.children then - for _, child in ipairs(ir.children) do - if child.group_var ~= "missing" and not child.collapse_suppressed then - if not self:suppress_ir_except_child(child, target) then - ir.collapse_suppressed = false - end - end - end - end - return ir.collapse_suppressed -end - -function CiteProc:collapse_cites_by_year_suffix_ranged(irs) - self:collapse_cites_by_year_suffix(irs) - -- Group by disam_str - local cite_groups = {{}} - local previous_ir - local previous_year_suffix - for i, ir in ipairs(irs) do - local year_suffix_ir = find_rendered_year_suffix(ir) - ir.rendered_year_suffix_ir = year_suffix_ir - if i == 1 then - table.insert(cite_groups[#cite_groups], ir) - elseif year_suffix_ir and previous_ir.disam_str == ir.disam_str and previous_year_suffix and - year_suffix_ir.year_suffix_number == previous_year_suffix.year_suffix_number + 1 then - -- TODO: and not previous cite suffix - table.insert(cite_groups[#cite_groups], ir) - else - table.insert(cite_groups, {ir}) - end - previous_ir = ir - previous_year_suffix = year_suffix_ir - end - - for _, cite_group in ipairs(cite_groups) do - if #cite_group > 2 then - for i, cite_ir in ipairs(cite_group) do - if i == 1 then - cite_ir.own_delimiter = util.unicode["en dash"] - elseif i < #cite_group then - cite_ir.collapse_suppressed = true - end - end - end - end -end - function CiteProc:makeCitationCluster(citation_items) + local special_form = nil local items = {} + for i, cite_item in ipairs(citation_items) do - cite_item.id = tostring(cite_item.id) + cite_item = self:normalize_cite_item(cite_item) local item_data = self:get_item(cite_item.id) -- Create a wrapper of the orignal item from registry so that -- it may hold different `locator` or `position` values for cites. local item = setmetatable(cite_item, {__index = item_data}) - -- Use "page" as locator label if missing - -- label_PluralWithAmpersand.txt - if item.locator and not item.label then - item.label = "page" + if not special_form then + for _, form in ipairs({"author-only", "suppress-author", "coposite"}) do + if cite_item[form] then + special_form = form + end + end end item.position_level = util.position_map["first"] @@ -1618,7 +555,12 @@ function CiteProc:makeCitationCluster(citation_items) self:sort_bibliography() end - local res = self:build_cluster(items) + local citation_element = self.style.citation + if special_form == "author-only" and self.style.intext then + citation_element = self.style.intext + end + + local res = citation_element:build_cluster(items, self) -- local context = { -- build = {}, @@ -1641,42 +583,14 @@ function CiteProc:makeBibliography() return {{}, {}} end - local output_format = self.output_format - local res = {} self.registry.longest_label = "" self.registry.maxoffset = 0 for _, id in ipairs(self:get_sorted_refs()) do - local ref = self.registry.registry[id] - - local state = IrState:new() - local context = Context:new() - context.engine = self - context.style = self.style - context.area = self.style.bibliography - context.in_bibliography = true - context.locale = self:get_locale(self.lang) - context.name_inheritance = self.style.bibliography.name_inheritance - context.format = output_format - context.id = id - context.cite = nil - context.reference = self:get_item(id) - - local ir = self.style.bibliography:build_ir(self, state, context) - -- util.debug(ir) - ir.reference = context.reference - - -- Add year-suffix - self:add_bibliography_year_suffix(ir) - - -- The layout output may be empty: sort_OmittedBibRefNonNumericStyle.txt - if ir then - local flat = ir:flatten(output_format) - local str = output_format:output_bibliography_entry(flat, context) - table.insert(res, str) - end + local str = self.style.bibliography:build_bibliography_str(id, self) + table.insert(res, str) end local bib_start = self.output_format.markups["bibstart"] @@ -1709,34 +623,6 @@ function CiteProc:get_sorted_refs() return self.registry.reflist end -function CiteProc:add_bibliography_year_suffix(ir) - if not ir.reference.year_suffix_number then - return - end - - local year_suffix_number = ir.reference.year_suffix_number - - if not ir.year_suffix_irs then - ir.year_suffix_irs = self:collect_year_suffix_irs(ir) - if #ir.year_suffix_irs == 0 then - local year_ir = self:find_first_year_ir(ir) - -- util.debug(year_ir) - if year_ir then - local year_suffix_ir = YearSuffix:new({}, self.style.citation) - table.insert(year_ir.children, year_suffix_ir) - table.insert(ir.year_suffix_irs, year_suffix_ir) - end - end - end - - for _, year_suffix_ir in ipairs(ir.year_suffix_irs) do - year_suffix_ir.inlines = {PlainText:new(ir.reference["year-suffix"])} - year_suffix_ir.group_var = "important" - end - - -end - function CiteProc:set_output_format(format) if format == "latex" then self.output_format = LatexWriter:new() @@ -1746,13 +632,11 @@ function CiteProc:set_output_format(format) end function CiteProc:enable_linking() - self.opt.url_link = true - self.opt.doi_link = true + self.opt.wrap_url_and_doi = true end function CiteProc:disable_linking() - self.opt.url_link = false - self.opt.doi_link = false + self.opt.wrap_url_and_doi = false end function CiteProc.create_element_tree(node) @@ -1806,6 +690,7 @@ function CiteProc:_retrieve_item(id) return nil end + -- TODO: normalize data input item.id = tostring(item.id) for key, value in pairs(item) do @@ -1985,6 +870,22 @@ function CiteProc:get_style_class() end +local Macro = Element:derive("macro") + +function Macro:from_node(node) + local o = Macro:new() + o.children = {} + o:set_attribute(node, "name") + o:process_children_nodes(node) + return o +end + +function Macro:build_ir(engine, state, context) + local ir = self:build_group_ir(engine, state, context) + return ir +end + + engine.CiteProc = CiteProc return engine diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-ir-node.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-ir-node.lua index 01cca4abf66..50fe34b0593 100644 --- a/Master/texmf-dist/scripts/citation-style-language/citeproc-ir-node.lua +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-ir-node.lua @@ -74,6 +74,35 @@ function IrNode:capitalize_first_term() end end +function IrNode:collect_year_suffix_irs() + local year_suffix_irs = {} + if self.children then + for i, child_ir in ipairs(self.children) do + if child_ir._type == "YearSuffix" then + table.insert(year_suffix_irs, child_ir) + elseif child_ir.children then + util.extend(year_suffix_irs, + child_ir:collect_year_suffix_irs()) + end + end + end + return year_suffix_irs +end + +function IrNode:find_first_year_ir() + if self.is_year then + return self + end + if self.children then + for _, child_ir in ipairs(self.children) do + local year_ir = child_ir:find_first_year_ir() + if year_ir then + return year_ir + end + end + end + return nil +end local Rendered = IrNode:derive("Rendered") diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-latex-core.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-latex-core.lua index 325217862bb..0fa0b40ca31 100644 --- a/Master/texmf-dist/scripts/citation-style-language/citeproc-latex-core.lua +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-latex-core.lua @@ -10,6 +10,7 @@ local citeproc = require("citeproc") local bibtex -- = require("citeproc-bibtex") -- load on demand local util = citeproc.util require("lualibs") +local latex_parser = require("citeproc-latex-parser") core.locale_file_format = "csl-locales-%s.xml" @@ -74,7 +75,13 @@ local function read_data_file(data_file) local csl_items = nil if extension == ".json" then - csl_items = utilities.json.tolua(contents) + local status, res = pcall(utilities.json.tolua, contents) + if status and res then + csl_items = res + else + util.error(string.format('JSON decoding error in file "%s"', data_file)) + csl_items = {} + end elseif extension == ".bib" then bibtex = bibtex or require("citeproc-bibtex") csl_items = bibtex.parse(contents) @@ -169,14 +176,21 @@ end function core.make_citation(citation_info) -- `citation_info`: "citationID={ITEM-1@2},citationItems={{id={ITEM-1},label={page},locator={6}}},properties={noteIndex={3}}" local citation = parse_latex_prop(citation_info) - assert(citation.citationID) - assert(citation.citationItems) - assert(citation.properties) + -- assert(citation.citationID) + -- assert(citation.citationItems) + -- assert(citation.properties) - citation.citationItems = parse_latex_seq(citation.citationItems) + citation.citationItems = parse_latex_seq(citation.citationItems) for i, item in ipairs(citation.citationItems) do - citation.citationItems[i] = parse_latex_prop(item) + local citation_item = parse_latex_prop(item) + if citation_item.prefix then + citation_item.prefix = latex_parser.convert_latex_to_rich_text(citation_item.prefix) + end + if citation_item.suffix then + citation_item.suffix = latex_parser.convert_latex_to_rich_text(citation_item.suffix) + end + citation.citationItems[i] = citation_item end citation.properties = parse_latex_prop(citation.properties) @@ -269,21 +283,37 @@ function core.make_bibliography(engine) local res = "" - local bib_options = "" - if params["hangingindent"] then - bib_options = bib_options .. "\n hanging-indent = true," - end - if params["linespacing"] then - bib_options = bib_options .. string.format("\n line-spacing = %d,", params["linespacing"]) - end - if params["entryspacing"] then - bib_options = bib_options .. string.format("\n entry-spacing = %d,", params["entryspacing"]) + local bib_options = {} + bib_options["class"] = engine:get_style_class() + local bib_option_list = {"class"} + + local bib_option_map = { + ["entry-spacing"] = "entryspacing", + ["line-spacing"] = "linespacing", + ["hanging-indent"] = "hangingindent", + } + local bib_option_order = { + "class", + "hanging-indent", + "line-spacing", + "entry-spacing", + } + + for option, param in pairs(bib_option_map) do + if params[param] then + bib_options[option] = params[param] + end end - if bib_options ~= "" then - bib_options = "\\cslsetup{" .. bib_options .. "\n}\n\n" - res = res .. bib_options + local bib_options_str = "\\cslsetup{\n" + for _, option in ipairs(bib_option_order) do + local value = bib_options[option] + if value then + bib_options_str = bib_options_str .. string.format(" %s = %s,\n", option, tostring(value)) + end end + bib_options_str = bib_options_str .. "}\n" + res = res .. bib_options_str .. "\n" -- util.debug(params.bibstart) if params.bibstart then diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-latex-parser.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-latex-parser.lua new file mode 100644 index 00000000000..1dcc2d4c779 --- /dev/null +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-latex-parser.lua @@ -0,0 +1,259 @@ +-- +-- Copyright (c) 2021-2022 Zeping Lee +-- Released under the MIT license. +-- Repository: https://github.com/zepinglee/citeproc-lua +-- + +local latex_parser = {} + +local lpeg = require("lpeg") +local unicode = require("unicode") +local bibtex_data = require("citeproc-bibtex-data") +local util = require("citeproc-util") + + +function latex_parser.get_latex_grammar() + local P = lpeg.P + local R = lpeg.R + local S = lpeg.S + local C = lpeg.C + local Cc = lpeg.Cc + local Cf = lpeg.Cf + local Cg = lpeg.Cg + local Cmt = lpeg.Cmt + local Cp = lpeg.Cp + local Ct = lpeg.Ct + local V = lpeg.V + + local space = S(" \t\r\n")^0 + local specials = P"\\$" / "$" + + P"\\%" / "%" + + P"\\&" / "&" + + P"\\#" / "#" + + P"\\_" / "_" + + P"\\{" / "{" + + P"\\}" / "}" + + P"~" / util.unicode["no-break space"] + local control_sequence = C(P"\\" * (R("AZ", "az")^1 + 1) * space) / function (cs) + return { + type = "control_sequence", + name = util.rstrip(cs), + raw = cs, + } + end + local math = P"$" * C((P"\\$" + 1 - S"$")^0) * P"$" / function (math_text) + return { + type = "math", + contents = math_text, + } + end + local ligatures = P"``" / util.unicode["left double quotation mark"] + + P"`" / util.unicode["left single quotation mark"] + + P"''" / util.unicode["right double quotation mark"] + + P"'" / util.unicode["right single quotation mark"] + + P"---" / util.unicode["em dash"] + + P"--" / util.unicode["en dash"] + local plain_text = C(1 - S"{}$\\") + local latex_grammar = P{ + "latex_text"; + latex_text = Ct((specials + control_sequence + math + ligatures + specials + V"group" + plain_text)^0), + group = P"{" * V"latex_text" * P"}" / function (group_contents) + return { + type = "group", + contents = group_contents, + } + end, + } + return latex_grammar +end + + +latex_parser.latex_grammar = latex_parser.get_latex_grammar() + + +function latex_parser.convert_latex_to_rich_text(str) + local ast = latex_parser.latex_grammar:match(str) + latex_parser.convert_accents_to_unicode(ast) + local res = latex_parser.convert_ast_to_rich_text(ast) + return res +end + + +local format_commands_with_argment = { + ["\\textbf"] = "bold", + ["\\textit"] = "italic", + ["\\textsl"] = "italic", + ["\\emph"] = "italic", + ["\\enquote"] = "quote", + ["\\textsc"] = "sc", + ["\\sout"] = "strike", -- from ulem package + ["\\st"] = "strike", -- from soul package + ["\\textsuperscript"] = "sup", + ["\\textsubscript"] = "sub", +} + +local format_commands_without_argment = { + ["\\bf"] = "bold", + ["\\bfseries"] = "bold", + ["\\it"] = "italic", + ["\\itshape"] = "italic", + ["\\sl"] = "italic", + ["\\slshape"] = "italic", + ["\\em"] = "italic", + ["\\scshape"] = "sc", + ["\\sc"] = "sc", +} + + +function latex_parser.convert_accents_to_unicode(tokens) + local res = "" + local i = 1 + while i <= #tokens do + if type(token) == "table" and token.type == "control_sequence" then + local cs = token + local code_point = bibtex_data.unicode_commands[cs.name] + if code_point then + local unicode_char + if type(code_point) == "string" then + unicode_char = utf8.char(tonumber(code_point, 16)) + tokens[i] = unicode_char + + elseif type(code_point) == "table" then + -- The command takes an argument (\"{o}) + local arg + if i < #tokens then + local next_token = tokens[i + 1] + if type(next_token) == "string" then + arg = next_token + elseif type(next_token) == "table" then + if next_token.type == "control_sequence" then + arg = next_token.name + elseif next_token.type == "group" then + if #next_token.contents == 0 then + arg = "{}" + elseif #next_token.contents == 1 then + next_token = next_token.contents[1] + if type(next_token) == "string" then + arg = next_token + elseif type(next_token) == "table" then + arg = next_token.name + end + end + end + end + end + if arg and code_point[arg] then + unicode_char = utf8.char(tonumber(code_point[arg], 16)) + tokens[i] = unicode_char + table.remove(tokens, i + 1) + end + end + end + end + i = i + 1 + end + return res +end + + + +function latex_parser.convert_ast_to_rich_text(tokens) + local res = {} + + local tmp_str = "" + local i = 1 + while i <= #tokens do + local token = tokens[i] + if type(token) == "string" then + tmp_str = tmp_str .. token + elseif type(token) == "table" then + if tmp_str ~= "" then + table.insert(res, tmp_str) + tmp_str = "" + end + if token.type == "control_sequence" then + local format_with_argment = format_commands_with_argment[token.name] + local format_without_argment = format_commands_without_argment[token.name] + if format_with_argment then + if i < #tokens then + local next_token = tokens[i + 1] + local rich_text + if type(next_token) == "string" then + rich_text = {[format_with_argment] = next_token} + elseif type(next_token) == "table" then + if next_token.type == "control_sequence" then + local content = {code = next_token.raw} + rich_text = {[format_with_argment] = {content}} + elseif next_token.type == "group" then + rich_text = {[format_with_argment] = latex_parser.convert_ast_to_rich_text(next_token.contents)} + elseif next_token.type == "math" then + local content = latex_parser.convert_ast_to_rich_text(next_token.contents) + rich_text = {[format_with_argment] = {{["math-tex"] = content}}} + end + end + if rich_text then + table.insert(res, rich_text) + i = i + 1 + end + else + table.insert(res, {code = token.raw}) + end + + elseif format_without_argment then + local rest_tokens = {} + for j = i + 1, #tokens do + table.insert(rest_tokens, tokens[j]) + end + local rich_text = {[format_without_argment] = latex_parser.convert_ast_to_rich_text(rest_tokens)} + table.insert(res, rich_text) + i = #tokens + + else + local rich_text = {code = token.raw} + table.insert(res, rich_text) + + end + + elseif token.type == "group" then + table.insert(res, {code = "{"}) + for _, rich_text in ipairs(latex_parser.convert_ast_to_rich_text(token.contents)) do + table.insert(res, rich_text) + end + table.insert(res, {code = "}"}) + + elseif token.type == "math" then + table.insert(res, {["math-tex"] = token.contents}) + end + + end + i = i + 1 + end + + if tmp_str ~= "" then + table.insert(res, tmp_str) + end + + -- Merge tokens + for i = #res - 1, 1, -1 do + local token = res[i] + local next_token = res[i + 1] + local token_type = type(token) + local next_token_type = type(next_token) + if token_type == "string" and next_token_type == "string" then + res[i] = token .. next_token + table.remove(res, i + 1) + elseif token_type == "table" and token.code and + next_token_type == "table" and next_token.code then + token.code = token.code .. next_token.code + table.remove(res, i + 1) + end + end + + if #res == 1 and type(res[1]) == "string" then + res = res[1] + end + + return res +end + +return latex_parser diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-latex.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-latex.lua index 4b05f0bdd64..4b17ce35efc 100644 --- a/Master/texmf-dist/scripts/citation-style-language/citeproc-latex.lua +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-latex.lua @@ -87,11 +87,11 @@ end function csl.cite(citation_info) -- "citationID={ITEM-UNAVAILABLE@1},citationItems={{id={ITEM-UNAVAILABLE}}},properties={noteIndex={1}}" - -- util.debug(citation_info) if not csl.engine then csl.error("CSL engine is not initialized.") end + -- util.debub(citation_info) local citation = core.make_citation(citation_info) local citation_str @@ -103,8 +103,11 @@ function csl.cite(citation_info) citation_str = csl.engine:process_citation(citation) end - tex.sprint(string.format("{%s}{%s}", csl.style_class, citation_str)) + -- util.debug(citation_str) -- tex.sprint(citation_str) + -- tex.setcatcode(35, 12) -- # + -- tex.setcatcode(37, 12) -- % + token.set_macro("l__csl_citation_tl", citation_str) table.insert(csl.citations_pre, {citation.citationID, citation.properties.noteIndex}) end diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-node-bibliography.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-node-bibliography.lua new file mode 100644 index 00000000000..c6e1575a51f --- /dev/null +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-node-bibliography.lua @@ -0,0 +1,307 @@ +-- +-- Copyright (c) 2021-2022 Zeping Lee +-- Released under the MIT license. +-- Repository: https://github.com/zepinglee/citeproc-lua +-- + +local bibliography_module = {} + +local dom = require("luaxml-domobject") + +local Context = require("citeproc-context").Context +local IrState = require("citeproc-context").IrState +local Element = require("citeproc-element").Element +local IrNode = require("citeproc-ir-node").IrNode +local Rendered = require("citeproc-ir-node").Rendered +local SeqIr = require("citeproc-ir-node").SeqIr +local PlainText = require("citeproc-output").PlainText +local DisamStringFormat = require("citeproc-output").DisamStringFormat +local YearSuffix = require("citeproc-ir-node").YearSuffix +local util = require("citeproc-util") + + +local Bibliography = Element:derive("bibliography", { + hanging_indent = false, + line_spacing = 1, + entry_spacing = 1, + subsequent_author_substitute_rule = "complete-all" +}) + +function Bibliography:from_node(node, style) + local o = Bibliography:new() + o.children = {} + + o:process_children_nodes(node) + + -- o.layouts = nil -- CSL-M extension + + for _, child in ipairs(o.children) do + local element_name = child.element_name + if element_name == "layout" then + o.layout = child + elseif element_name == "sort" then + o.sort = child + end + end + + -- Whitespace + o:set_bool_attribute(node, "hanging-indent") + o:set_attribute(node, "second-field-align") + o:set_number_attribute(node, "line-spacing") + o:set_number_attribute(node, "entry-spacing") + + -- Reference Grouping + o:set_attribute(node, "subsequent-author-substitute") + o:set_attribute(node, "subsequent-author-substitute-rule") + + local name_inheritance = require("citeproc-node-names").Name:new() + for key, value in pairs(style.name_inheritance) do + if value ~= nil then + name_inheritance[key] = value + end + end + Element.make_name_inheritance(name_inheritance, node) + o.name_inheritance = name_inheritance + + return o +end + +function Bibliography:build_bibliography_str(id, engine) + local output_format = engine.output_format + + local state = IrState:new() + local context = Context:new() + context.engine = engine + context.style = engine.style + context.area = self + context.in_bibliography = true + context.locale = engine:get_locale(engine.lang) + context.name_inheritance = self.name_inheritance + context.format = output_format + context.id = id + context.cite = nil + context.reference = engine:get_item(id) + + local ir = self:build_ir(engine, state, context) + -- util.debug(ir) + ir.reference = context.reference + + -- Add year-suffix + self:add_bibliography_year_suffix(ir, engine) + + -- The layout output may be empty: sort_OmittedBibRefNonNumericStyle.txt + if not ir then + return nil + end + + local flat = ir:flatten(output_format) + local str = output_format:output_bibliography_entry(flat, context) + return str +end + +function Bibliography:build_ir(engine, state, context) + if not self.layout then + util.error("Missing bibliography layout.") + end + local ir = self.layout:build_ir(engine, state, context) + -- util.debug(ir) + if self.second_field_align == "flush" and #ir.children >= 2 then + ir.children[1].display = "left-margin" + local right_inline_ir = SeqIr:new(util.slice(ir.children, 2), self) + right_inline_ir.display = "right-inline" + if ir.affixes then + right_inline_ir.affixes = ir.affixes + right_inline_ir.formatting = ir.formatting + ir.affixes = nil + ir.formatting = nil + end + ir.children = {ir.children[1], right_inline_ir} + end + + if self.subsequent_author_substitute then + self:substitute_subsequent_authors(engine, ir) + end + + if not ir then + ir = Rendered:new({PlainText:new("[CSL STYLE ERROR: reference with no printed form.]")}, self) + end + return ir +end + +function Bibliography:substitute_subsequent_authors(engine, ir) + ir.first_name_ir = self:find_first_name_ir(ir) -- should be a SeqIr wiht _element = "names" + if not ir.first_name_ir then + engine.previous_bib_names_ir = nil + return + end + if self.subsequent_author_substitute_rule == "complete-all" then + self:substitute_subsequent_authors_complete_all(engine, ir) + elseif self.subsequent_author_substitute_rule == "complete-each" then + self:substitute_subsequent_authors_complete_each(engine, ir) + elseif self.subsequent_author_substitute_rule == "partial-each" then + self:substitute_subsequent_authors_partial_each(engine, ir) + elseif self.subsequent_author_substitute_rule == "partial-first" then + self:substitute_subsequent_authors_partial_first(engine, ir) + end + engine.previous_bib_names_ir = ir.first_name_ir +end + +function Bibliography:find_first_name_ir(ir) + if ir._type == "NameIr" then + return ir + elseif ir.children then + for _, child_ir in ipairs(ir.children) do + local first_name_ir = self:find_first_name_ir(child_ir) + if first_name_ir then + return first_name_ir + end + end + end + return nil +end + +function Bibliography:substitute_subsequent_authors_complete_all(engine, ir) + local bib_names_str = "" + + if #ir.first_name_ir.person_name_irs > 0 then + for _, person_name_ir in ipairs(ir.first_name_ir.person_name_irs) do + if bib_names_str ~= "" then + bib_names_str = bib_names_str .. " " + end + local name_variants = person_name_ir.disam_variants + bib_names_str = bib_names_str .. name_variants[#name_variants] + end + else + -- In case of a <text variable="title"/> in <substitute> + local disam_format = DisamStringFormat:new() + local inlines = ir.first_name_ir:flatten(disam_format) + bib_names_str = disam_format:output(inlines) + end + ir.first_name_ir.bib_names_str = bib_names_str + + if engine.previous_bib_names_ir and + engine.previous_bib_names_ir.bib_names_str == bib_names_str then + local text = self.subsequent_author_substitute + if text == "" then + ir.first_name_ir.children = {} + ir.first_name_ir.group_var = "missing" + else + -- the output of label is not substituted + -- util.debug(ir.first_name_ir) + ir.first_name_ir.children = {Rendered:new({PlainText:new(text)}, self)} + end + end +end + +function Bibliography:substitute_subsequent_authors_complete_each(engine, ir) + local bib_names_str = "" + + if #ir.first_name_ir.person_name_irs > 0 then + for _, person_name_ir in ipairs(ir.first_name_ir.person_name_irs) do + if bib_names_str ~= "" then + bib_names_str = bib_names_str .. " " + end + local name_variants = person_name_ir.disam_variants + bib_names_str = bib_names_str .. name_variants[#name_variants] + end + else + -- In case of a <text variable="title"/> in <substitute> + local disam_format = DisamStringFormat:new() + local inlines = ir.first_name_ir:flatten(disam_format) + bib_names_str = disam_format:output(inlines) + end + ir.first_name_ir.bib_names_str = bib_names_str + + if engine.previous_bib_names_ir and + engine.previous_bib_names_ir.bib_names_str == bib_names_str then + local text = self.subsequent_author_substitute + if #ir.first_name_ir.person_name_irs > 0 then + for _, person_name_ir in ipairs(ir.first_name_ir.person_name_irs) do + person_name_ir.inlines = {PlainText:new(text)} + end + else + -- In case of a <text variable="title"/> in <substitute> + if text == "" then + ir.first_name_ir.children = {} + ir.first_name_ir.group_var = "missing" + else + ir.first_name_ir.children = {Rendered:new({PlainText:new(text)}, self)} + end + end + end +end + +function Bibliography:substitute_subsequent_authors_partial_each(engine, ir) + local bib_names_str = "" + + if #ir.first_name_ir.person_name_irs > 0 then + if engine.previous_bib_names_ir then + for i, person_name_ir in ipairs(ir.first_name_ir.person_name_irs) do + local prev_name_ir = engine.previous_bib_names_ir.person_names[i] + if prev_name_ir then + local prev_name_variants = prev_name_ir.disam_variants + local prev_full_name_str = prev_name_variants[#prev_name_variants] + local name_variants = person_name_ir.disam_variants + local full_name_str = name_variants[#name_variants] + if prev_full_name_str == full_name_str then + local text = self.subsequent_author_substitute + person_name_ir.inlines = {PlainText:new(text)} + else + break + end + end + end + end + else + -- In case of a <text variable="title"/> in <substitute> + local disam_format = DisamStringFormat:new() + local inlines = ir.first_name_ir:flatten(disam_format) + bib_names_str = disam_format:output(inlines) + ir.first_name_ir.bib_names_str = bib_names_str + if engine.previous_bib_names_ir and + engine.previous_bib_names_ir.bib_names_str == bib_names_str then + local text = self.subsequent_author_substitute + if text == "" then + ir.first_name_ir.children = {} + ir.first_name_ir.group_var = "missing" + else + ir.first_name_ir.children = {Rendered:new({PlainText:new(text)}, self)} + end + end + end +end + +function Bibliography:substitute_subsequent_authors_partial_first(engine, ir) +end + +function Bibliography:add_bibliography_year_suffix(ir, engine) + if not ir.reference.year_suffix_number then + return + end + + local year_suffix_number = ir.reference.year_suffix_number + + if not ir.year_suffix_irs then + ir.year_suffix_irs = ir:collect_year_suffix_irs() + if #ir.year_suffix_irs == 0 then + local year_ir = ir:find_first_year_ir() + -- util.debug(year_ir) + if year_ir then + local year_suffix_ir = YearSuffix:new({}, engine.style.citation) + table.insert(year_ir.children, year_suffix_ir) + table.insert(ir.year_suffix_irs, year_suffix_ir) + end + end + end + + for _, year_suffix_ir in ipairs(ir.year_suffix_irs) do + year_suffix_ir.inlines = {PlainText:new(ir.reference["year-suffix"])} + year_suffix_ir.group_var = "important" + end +end + + +bibliography_module.Bibliography = Bibliography + + +return bibliography_module diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-node-citation.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-node-citation.lua new file mode 100644 index 00000000000..6c7f0827469 --- /dev/null +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-node-citation.lua @@ -0,0 +1,1417 @@ + +-- +-- Copyright (c) 2021-2022 Zeping Lee +-- Released under the MIT license. +-- Repository: https://github.com/zepinglee/citeproc-lua +-- + +local citation_module = {} + +local dom = require("luaxml-domobject") + +local Context = require("citeproc-context").Context +local IrState = require("citeproc-context").IrState +local Element = require("citeproc-element").Element +local IrNode = require("citeproc-ir-node").IrNode +local Rendered = require("citeproc-ir-node").Rendered +local SeqIr = require("citeproc-ir-node").SeqIr +local YearSuffix = require("citeproc-ir-node").YearSuffix +local Micro = require("citeproc-output").Micro +local Formatted = require("citeproc-output").Formatted +local PlainText = require("citeproc-output").PlainText +local InlineElement = require("citeproc-output").InlineElement +local DisamStringFormat = require("citeproc-output").DisamStringFormat +local SortStringFormat = require("citeproc-output").SortStringFormat +local util = require("citeproc-util") + + +local Citation = Element:derive("citation", { + givenname_disambiguation_rule = "by-cite", + -- https://github.com/citation-style-language/schema/issues/338 + -- The cite_group_delimiter may be changed to inherit the delimiter in citaion > layout. + cite_group_delimiter = ", ", + near_note_distance = 5, +}) + +function Citation:from_node(node, style) + + local o = self:new() + o.children = {} + + o:process_children_nodes(node) + + -- o.layouts = nil -- CSL-M extension + + for _, child in ipairs(o.children) do + local element_name = child.element_name + if element_name == "layout" then + o.layout = child + elseif element_name == "sort" then + o.sort = child + end + end + + -- Disambiguation + o:set_bool_attribute(node, "disambiguate-add-givenname") + o:set_attribute(node, "givenname-disambiguation-rule") + o:set_bool_attribute(node, "disambiguate-add-names") + o:set_bool_attribute(node, "disambiguate-add-year-suffix") + + -- Cite Grouping + o:set_attribute(node, "cite-group-delimiter") + -- In the current citeproc-js implementation and test suite, + -- cite grouping is activated by setting the cite-group-delimiter + -- attribute or the collapse attributes on cs:citation. + -- It may be changed to an independent procedure. + -- https://github.com/citation-style-language/schema/issues/338 + if node:get_attribute("cite-group-delimiter") then + o.cite_grouping = true + else + o.cite_grouping = false + end + + + -- Cite Collapsing + o:set_attribute(node, "collapse") + o:set_attribute(node, "year-suffix-delimiter") + if not o.year_suffix_delimiter then + o.year_suffix_delimiter = o.layout.delimiter + end + o:set_attribute(node, "after-collapse-delimiter") + if not o.after_collapse_delimiter then + o.after_collapse_delimiter = o.layout.delimiter + end + + -- Note Distance + o:set_number_attribute(node, "near-note-distance") + + local name_inheritance = require("citeproc-node-names").Name:new() + for key, value in pairs(style.name_inheritance) do + if value ~= nil then + name_inheritance[key] = value + end + end + Element.make_name_inheritance(name_inheritance, node) + o.name_inheritance = name_inheritance + + -- update_mode = "plain" or "numeric" or "position" (or "both"?) + + return o +end + +function Citation:build_citation_str(citation, engine) + if engine.registry.requires_sorting then + engine:sort_bibliography() + end + + local citation_str = self:build_cluster(citation.citationItems, engine, citation.properties) + return citation_str +end + +-- Formatting is stripped from the author-only and composite renderings +-- of the author name +local function remove_name_formatting(ir) + if ir._element == "name" then + ir.formatting = nil + end + if ir.children then + for _, child in ipairs(ir.children) do + remove_name_formatting(child) + end + end +end + +function Citation:build_cluster(citation_items, engine, properties) + properties = properties or {} + local output_format = engine.output_format + local irs = {} + citation_items = self:sorted_citation_items(citation_items, engine) + for _, cite_item in ipairs(citation_items) do + local ir = self:build_fully_disambiguated_ir(cite_item, output_format, engine, properties) + table.insert(irs, ir) + end + + -- Special citation forms + -- https://citeproc-js.readthedocs.io/en/latest/running.html#special-citation-forms + self:_apply_special_citation_form(irs, properties, output_format, engine) + + if self.cite_grouping then + irs = self:group_cites(irs) + else + local citation_collapse = self.collapse + if citation_collapse == "year" or citation_collapse == "year-suffix" or + citation_collapse == "year-suffix-ranged" then + irs = self:group_cites(irs) + end + end + + if self.collapse then + self:collapse_cites(irs) + end + + -- Capitalize first + for i, ir in ipairs(irs) do + -- local layout_prefix + -- local layout_affixes = self.layout.affixes + -- if layout_affixes then + -- layout_prefix = layout_affixes.prefix + -- end + local prefix = citation_items[i].prefix + if prefix then + -- Prefix is inlines + local right_most_str = prefix[#prefix]:get_right_most_string() + if string.match(right_most_str, "[.!?]%s*$") and InlineElement.has_space(prefix) then + ir:capitalize_first_term() + end + else + local delimiter = self.layout.delimiter + if i == 1 or not delimiter or string.match(delimiter, "[.!?]%s*$") then + ir:capitalize_first_term() + end + end + end + + -- util.debug(irs) + + local citation_delimiter = self.layout.delimiter + local citation_stream = {} + + local context = Context:new() + context.engine = engine + context.style = engine.style + context.area = self + context.in_bibliography = false + context.locale = engine:get_locale(engine.lang) + context.name_inheritance = self.name_inheritance + context.format = output_format + + local previous_ir + for i, ir in ipairs(irs) do + local cite_prefix = citation_items[i].prefix + local cite_suffix = citation_items[i].suffix + if not ir.collapse_suppressed then + local ir_inlines = ir:flatten(output_format) + if #ir_inlines > 0 then + -- Make sure ir_inlines has outputs contents. + -- collapse_AuthorCollapseNoDateSorted.txt + if previous_ir then + if previous_ir.own_delimiter then + table.insert(citation_stream, PlainText:new(previous_ir.own_delimiter)) + else + local left_most_str + if cite_prefix then + left_most_str = cite_prefix[1]:get_left_most_string() + end + if citation_delimiter and not (cite_prefix and util.startswith(left_most_str, ",")) then + table.insert(citation_stream, PlainText:new(citation_delimiter)) + end + end + end + + if cite_prefix then + table.insert(citation_stream, Micro:new(cite_prefix)) + end + + -- util.debug(ir) + util.extend(citation_stream, ir_inlines) + previous_ir = ir + + if cite_suffix then + table.insert(citation_stream, Micro:new(cite_suffix)) + end + end + end + end + + local has_printed_form = true + if #citation_items == 0 then + -- bugreports_AuthorOnlyFail.txt + citation_stream = {PlainText:new("[NO_PRINTED_FORM]")} + has_printed_form = false + elseif #citation_stream == 0 then + -- date_DateNoDateNoTest.txt + has_printed_form = false + citation_stream = {PlainText:new("[CSL STYLE ERROR: reference with no printed form.]")} + elseif #citation_stream == 1 and citation_stream[1].value == "[NO_PRINTED_FORM]" then + has_printed_form = false + end + + local author_only_mode = (properties.mode == "author-only" or + (#citation_items >= 1 and citation_items[1]["author-only"])) + if has_printed_form and context.area.layout.affixes and not author_only_mode then + local affixes = context.area.layout.affixes + if affixes.prefix then + table.insert(citation_stream, 1, PlainText:new(affixes.prefix)) + end + if affixes.suffix then + table.insert(citation_stream, PlainText:new(affixes.suffix)) + end + end + + if has_printed_form and context.area.layout.formatting then + citation_stream = {Formatted:new(citation_stream, context.area.layout.formatting)} + end + + if properties.mode == "composite" then + local author_ir + if irs[1] then + author_ir = irs[1].author_ir + end + if author_ir then + local infix = properties.infix + if infix then + if string.match(infix, "^%w") then + -- discretionary_SingleNarrativeCitation.txt + infix = " " .. infix + end + if string.match(infix, "%w$") then + infix = infix .. " " + end + if infix == "" then + -- discretionary_AuthorOnlySuppressLocator.txt + infix = " " + end + for i, inline in ipairs(InlineElement:parse(infix, context)) do + table.insert(citation_stream, i, inline) + end + else + table.insert(citation_stream, 1, PlainText:new(" ")) + end + + local author_inlines = author_ir:flatten(output_format) + for i, inline in ipairs(author_inlines) do + table.insert(citation_stream, i, inline) + end + end + end + + local str = output_format:output(citation_stream, context) + str = util.strip(str) + + return str +end + +function Citation:sorted_citation_items(items, engine) + local citation_sort = self.sort + if not citation_sort then + return items + end + + local state = IrState:new() + local context = Context:new() + context.engine = engine + context.style = engine.style + context.area = self + context.in_bibliography = false + context.locale = engine:get_locale(engine.lang) + context.name_inheritance = self.name_inheritance + context.format = SortStringFormat:new() + -- context.id = id + context.cite = nil + -- context.reference = self:get_item(id) + + items = citation_sort:sort(items, state, context) + return items +end + +function Citation:build_fully_disambiguated_ir(cite_item, output_format, engine, properties) + local cite_ir = self:build_ambiguous_ir(cite_item, output_format, engine) + -- util.debug(cite_ir) + cite_ir = self:apply_disambiguate_add_givenname(cite_ir, engine) + cite_ir = self:apply_disambiguate_add_names(cite_ir, engine) + cite_ir = self:apply_disambiguate_conditionals(cite_ir, engine) + cite_ir = self:apply_disambiguate_add_year_suffix(cite_ir, engine) + + return cite_ir +end + +function Citation:build_ambiguous_ir(cite_item, output_format, engine) + local state = IrState:new(engine.style) + local context = Context:new() + context.engine = engine + context.style = engine.style + context.area = self + context.locale = engine:get_locale(engine.lang) + context.name_inheritance = self.name_inheritance + context.format = output_format + context.id = cite_item.id + context.cite = cite_item + -- context.reference = self:get_item(cite_item.id) + context.reference = engine.registry.registry[cite_item.id] + + local ir + if context.reference then + ir = self:build_ir(engine, state, context) + else + ir = Rendered:new({Formatted:new({PlainText:new(cite_item.id)}, {["font-weight"] = "bold"})}, self) + end + + ir.cite_item = cite_item + ir.reference = context.reference + ir.ir_index = #engine.disam_irs + 1 + table.insert(engine.disam_irs, ir) + ir.is_ambiguous = false + ir.disam_level = 0 + + -- Formattings like font-style are ignored for disambiguation. + local disam_format = DisamStringFormat:new() + local inlines = ir:flatten(disam_format) + local disam_str = disam_format:output(inlines, context) + ir.disam_str = disam_str + + if not engine.cite_irs_by_output[disam_str] then + engine.cite_irs_by_output[disam_str] = {} + end + + for ir_index, ir_ in pairs(engine.cite_irs_by_output[disam_str]) do + if ir_.cite_item.id ~= cite_item.id then + ir.is_ambiguous = true + break + end + end + engine.cite_irs_by_output[disam_str][ir.ir_index] = ir + + return ir +end + +function Citation:build_ir(engine, state, context) + if not self.layout then + util.error("Missing citation layout.") + end + return self.layout:build_ir(engine, state, context) +end + +function Citation:apply_disambiguate_add_givenname(cite_ir, engine) + if self.disambiguate_add_givenname then + -- util.debug("disambiguate_add_givenname: " .. cite_ir.cite_item.id) + + local gn_disam_rule = self.givenname_disambiguation_rule + if gn_disam_rule == "all-names" or gn_disam_rule == "all-names-with-initials" then + cite_ir = self:apply_disambiguate_add_givenname_all_names(cite_ir, engine) + elseif gn_disam_rule == "primary-name" or gn_disam_rule == "primary-name-with-initials" then + cite_ir = self:apply_disambiguate_add_givenname_primary_name(cite_ir, engine) + elseif gn_disam_rule == "by-cite" then + cite_ir = self:apply_disambiguate_add_givenname_by_cite(cite_ir, engine) + end + end + return cite_ir +end + +-- TODO: reorganize this code +function Citation:apply_disambiguate_add_givenname_all_names(cite_ir, engine) + -- util.debug("disambiguate_add_givenname_all_names: " .. cite_ir.cite_item.id) + if not cite_ir.person_name_irs or #cite_ir.person_name_irs == 0 then + return cite_ir + end + + -- util.debug(cite_ir.disam_str) + + for _, person_name_ir in ipairs(cite_ir.person_name_irs) do + local name_output = person_name_ir.name_output + -- util.debug(name_output) + + if not person_name_ir.person_name_index then + person_name_ir.person_name_index = #engine.person_names + 1 + table.insert(engine.person_names, person_name_ir) + end + + if not engine.person_names_by_output[name_output] then + engine.person_names_by_output[name_output] = {} + end + engine.person_names_by_output[name_output][person_name_ir.person_name_index] = person_name_ir + + local ambiguous_name_irs = {} + local ambiguous_same_output_irs = {} + + for _, pn_ir in pairs(engine.person_names_by_output[person_name_ir.name_output]) do + if pn_ir.full_name ~= person_name_ir.full_name then + table.insert(ambiguous_name_irs, pn_ir) + end + if pn_ir.name_output == person_name_ir.name_output then + table.insert(ambiguous_same_output_irs, pn_ir) + end + end + + -- util.debug(person_name_ir.name_output) + -- util.debug(person_name_ir.full_name) + -- util.debug(#ambiguous_name_irs) + -- util.debug(person_name_ir.disam_variants_index) + -- util.debug(person_name_ir.disam_variants) + + while person_name_ir.disam_variants_index < #person_name_ir.disam_variants do + if #ambiguous_name_irs == 0 then + break + end + + for _, pn_ir in ipairs(ambiguous_same_output_irs) do + -- expand one name + if pn_ir.disam_variants_index < #pn_ir.disam_variants then + pn_ir.disam_variants_index = pn_ir.disam_variants_index + 1 + pn_ir.name_output = pn_ir.disam_variants[pn_ir.disam_variants_index] + pn_ir.inlines = pn_ir.disam_inlines[pn_ir.name_output] + + if not engine.person_names_by_output[pn_ir.name_output] then + engine.person_names_by_output[pn_ir.name_output] = {} + end + engine.person_names_by_output[pn_ir.name_output][pn_ir.person_name_index] = pn_ir + end + end + + -- util.debug(person_name_ir.name_output) + + -- update ambiguous_name_irs and ambiguous_same_output_irs + ambiguous_name_irs = {} + ambiguous_same_output_irs = {} + for _, pn_ir in pairs(engine.person_names_by_output[person_name_ir.name_output]) do + if pn_ir.full_name ~= person_name_ir.full_name then + -- util.debug(pn_ir.full_name .. ": " .. pn_ir.name_output) + table.insert(ambiguous_name_irs, pn_ir) + end + if pn_ir.name_output == person_name_ir.name_output then + table.insert(ambiguous_same_output_irs, pn_ir) + end + end + + end + end + + -- update cite_ir output + local disam_format = DisamStringFormat:new() + local inlines = cite_ir:flatten(disam_format) + local disam_str = disam_format:output(inlines, nil) + cite_ir.disam_str = disam_str + if not engine.cite_irs_by_output[disam_str] then + engine.cite_irs_by_output[disam_str] = {} + end + engine.cite_irs_by_output[disam_str][cite_ir.ir_index] = cite_ir + + -- update ambiguous_cite_irs and ambiguous_same_output_irs + local ambiguous_cite_irs = {} + for ir_index, ir_ in pairs(engine.cite_irs_by_output[cite_ir.disam_str]) do + -- util.debug(ir_.cite_item.id) + if ir_.cite_item.id ~= cite_ir.cite_item.id then + table.insert(ambiguous_cite_irs, ir_) + end + end + if #ambiguous_cite_irs == 0 then + cite_ir.is_ambiguous = false + end + + return cite_ir +end + +function Citation:apply_disambiguate_add_givenname_primary_name(cite_ir, engine) + if not cite_ir.person_name_irs or #cite_ir.person_name_irs == 0 then + return cite_ir + end + local person_name_ir = cite_ir.person_name_irs[1] + local name_output = person_name_ir.name_output + -- util.debug(name_output) + + if not person_name_ir.person_name_index then + person_name_ir.person_name_index = #engine.person_names + 1 + table.insert(engine.person_names, person_name_ir) + end + if not engine.person_names_by_output[name_output] then + engine.person_names_by_output[name_output] = {} + end + engine.person_names_by_output[name_output][person_name_ir.person_name_index] = person_name_ir + + local ambiguous_name_irs = {} + local ambiguous_same_output_irs = {} + + for _, pn_ir in pairs(engine.person_names_by_output[person_name_ir.name_output]) do + if pn_ir.full_name ~= person_name_ir.full_name then + table.insert(ambiguous_name_irs, pn_ir) + end + if pn_ir.name_output == person_name_ir.name_output then + table.insert(ambiguous_same_output_irs, pn_ir) + end + end + + for _, name_variant in ipairs(person_name_ir.disam_variants) do + if #ambiguous_name_irs == 0 then + break + end + + for _, pn_ir in ipairs(ambiguous_same_output_irs) do + -- expand one name + if pn_ir.disam_variants_index < #pn_ir.disam_variants then + pn_ir.disam_variants_index = pn_ir.disam_variants_index + 1 + pn_ir.name_output = pn_ir.disam_variants[pn_ir.disam_variants_index] + pn_ir.inlines = pn_ir.disam_inlines[pn_ir.name_output] + + if not engine.person_names_by_output[pn_ir.name_output] then + engine.person_names_by_output[pn_ir.name_output] = {} + end + engine.person_names_by_output[pn_ir.name_output][person_name_ir.person_name_index] = person_name_ir + end + end + + -- update ambiguous_name_irs and ambiguous_same_output_irs + ambiguous_name_irs = {} + ambiguous_same_output_irs = {} + for _, pn_ir in pairs(engine.person_names_by_output[person_name_ir.name_output]) do + if pn_ir.full_name ~= person_name_ir.full_name then + table.insert(ambiguous_name_irs, pn_ir) + end + if pn_ir.name_output == person_name_ir.name_output then + table.insert(ambiguous_same_output_irs, pn_ir) + end + end + end + + return cite_ir +end + +function Citation:apply_disambiguate_add_givenname_by_cite(cite_ir, engine) + if not cite_ir.is_ambiguous then + return cite_ir + end + if not cite_ir.person_name_irs or #cite_ir.person_name_irs == 0 then + return cite_ir + end + + -- for _, ir_ in ipairs(engine.disam_irs) do + -- util.debug(ir_.cite_item.id) + -- util.debug(ir_.disam_str) + -- end + + local disam_format = DisamStringFormat:new() + + local ambiguous_cite_irs = {} + local ambiguous_same_output_irs = {} + + for ir_index, ir_ in pairs(engine.cite_irs_by_output[cite_ir.disam_str]) do + if ir_.cite_item.id ~= cite_ir.cite_item.id then + table.insert(ambiguous_cite_irs, ir_) + end + if ir_.disam_str == cite_ir.disam_str then + table.insert(ambiguous_same_output_irs, ir_) + end + end + + for i, person_name_ir in ipairs(cite_ir.person_name_irs) do + -- util.debug(person_name_ir.name_output) + -- util.debug(person_name_ir.disam_variants) + if #ambiguous_cite_irs == 0 then + cite_ir.is_ambiguous = false + break + end + + -- util.debug(person_name_ir.disam_variants) + while person_name_ir.disam_variants_index < #person_name_ir.disam_variants do + -- util.debug(person_name_ir.name_output) + + local is_different_name = false + for _, ir_ in ipairs(ambiguous_cite_irs) do + if ir_.person_name_irs[i] then + if ir_.person_name_irs[i].full_name ~= person_name_ir.full_name then + -- util.debug(ir_.cite_item.id) + is_different_name = true + break + end + end + end + -- util.debug(is_different_name) + if not is_different_name then + break + end + + for _, ir_ in ipairs(ambiguous_same_output_irs) do + -- util.debug(ir_.cite_item.id) + local person_name_ir_ = ir_.person_name_irs[i] + if person_name_ir_ then + if person_name_ir_.disam_variants_index < #person_name_ir_.disam_variants then + person_name_ir_.disam_variants_index = person_name_ir_.disam_variants_index + 1 + local disam_variant = person_name_ir_.disam_variants[person_name_ir_.disam_variants_index] + person_name_ir_.name_output = disam_variant + -- util.debug(disam_variant) + person_name_ir_.inlines = person_name_ir_.disam_inlines[disam_variant] + -- Update cite ir output + local inlines = ir_:flatten(disam_format) + local disam_str = disam_format:output(inlines, nil) + ir_.disam_str = disam_str + if not engine.cite_irs_by_output[disam_str] then + engine.cite_irs_by_output[disam_str] = {} + end + engine.cite_irs_by_output[disam_str][ir_.ir_index] = ir_ + end + end + end + + -- update ambiguous_cite_irs and ambiguous_same_output_irs + ambiguous_cite_irs = {} + ambiguous_same_output_irs = {} + for ir_index, ir_ in pairs(engine.cite_irs_by_output[cite_ir.disam_str]) do + -- util.debug(ir_.cite_item.id) + if ir_.cite_item.id ~= cite_ir.cite_item.id then + table.insert(ambiguous_cite_irs, ir_) + end + if ir_.disam_str == cite_ir.disam_str then + table.insert(ambiguous_same_output_irs, ir_) + end + end + + -- util.debug(#ambiguous_cite_irs) + + if #ambiguous_cite_irs == 0 then + cite_ir.is_ambiguous = false + return cite_ir + end + + end + end + + return cite_ir +end + +local function find_first_name_ir(ir) + if ir._type == "NameIr" then + return ir + end + if ir.children then + for _, child in ipairs(ir.children) do + local name_ir = find_first_name_ir(child) + if name_ir then + return name_ir + end + end + end + return nil +end + +function Citation:apply_disambiguate_add_names(cite_ir, engine) + if not self.disambiguate_add_names then + return cite_ir + end + + if not cite_ir.name_ir then + cite_ir.name_ir = find_first_name_ir(cite_ir) + end + local name_ir = cite_ir.name_ir + + if not cite_ir.is_ambiguous then + return cite_ir + end + + if not name_ir or not name_ir.et_al_abbreviation then + return cite_ir + end + + -- util.debug("disambiguate_add_names: " .. cite_ir.cite_item.id) + + if name_ir then + -- util.debug(cite_ir.disam_str) + -- util.debug(cite_ir.name_ir.full_name_str) + -- util.debug(cite_ir.is_ambiguous) + end + + local disam_format = DisamStringFormat:new() + + while cite_ir.is_ambiguous do + if #cite_ir.name_ir.hidden_name_irs == 0 then + break + end + + local ambiguous_cite_irs = {} + local ambiguous_same_output_irs = {} + for _, ir_ in pairs(engine.cite_irs_by_output[cite_ir.disam_str]) do + if ir_.cite_item.id ~= cite_ir.cite_item.id then + table.insert(ambiguous_cite_irs, ir_) + end + if ir_.disam_str == cite_ir.disam_str then + table.insert(ambiguous_same_output_irs, ir_) + end + end + + -- util.debug(#ambiguous_same_output_irs) + if #ambiguous_cite_irs == 0 then + cite_ir.is_ambiguous = false + break + end + + -- check if the cite can be (fully) disambiguated by adding names + local can_be_disambuguated = false + for _, ir_ in ipairs(ambiguous_cite_irs) do + if ir_.name_ir.full_name_str ~= cite_ir.name_ir.full_name_str then + can_be_disambuguated = true + break + end + end + -- util.debug(can_be_disambuguated) + if not can_be_disambuguated then + break + end + + for _, ir_ in ipairs(ambiguous_same_output_irs) do + local added_person_name_ir = ir_.name_ir.name_inheritance:expand_one_name(ir_.name_ir) + if added_person_name_ir then + -- util.debug("Updated: " .. ir_.cite_item.id) + table.insert(ir_.person_name_irs, added_person_name_ir) + + if not added_person_name_ir.person_name_index then + added_person_name_ir.person_name_index = #engine.person_names + 1 + table.insert(engine.person_names, added_person_name_ir) + end + local name_output = added_person_name_ir.name_output + if not engine.person_names_by_output[name_output] then + engine.person_names_by_output[name_output] = {} + end + engine.person_names_by_output[name_output][added_person_name_ir.person_name_index] = added_person_name_ir + + -- Update ir output + local inlines = ir_:flatten(disam_format) + local disam_str = disam_format:output(inlines, nil) + -- util.debug(disam_str) + ir_.disam_str = disam_str + if not engine.cite_irs_by_output[disam_str] then + engine.cite_irs_by_output[disam_str] = {} + end + engine.cite_irs_by_output[disam_str][ir_.ir_index] = ir_ + end + end + + -- util.debug("disambiguate_add_givenname") + + if self.disambiguate_add_givenname then + local gn_disam_rule = self.givenname_disambiguation_rule + if gn_disam_rule == "all-names" or gn_disam_rule == "all-names-with-initials" then + cite_ir = self:apply_disambiguate_add_givenname_all_names(cite_ir, engine) + elseif gn_disam_rule == "by-cite" then + cite_ir = self:apply_disambiguate_add_givenname_by_cite(cite_ir, engine) + end + end + + cite_ir.is_ambiguous = self:check_ambiguity(cite_ir, engine) + + -- for _, ir_ in ipairs(engine.disam_irs) do + -- util.debug(ir_.cite_item.id .. ": " .. ir_.disam_str) + -- end + + end + + + return cite_ir +end + +function Citation:collect_irs_with_disambiguate_branch(ir) + local irs_with_disambiguate_branch = {} + if ir.children then + for i, child_ir in ipairs(ir.children) do + if child_ir.disambiguate_branch_ir then + table.insert(irs_with_disambiguate_branch, child_ir) + elseif child_ir.children then + util.extend(irs_with_disambiguate_branch, + self:collect_irs_with_disambiguate_branch(child_ir)) + end + end + end + return irs_with_disambiguate_branch +end + +function Citation:apply_disambiguate_conditionals(cite_ir, engine) + -- util.debug(cite_ir) + + cite_ir.irs_with_disambiguate_branch = self:collect_irs_with_disambiguate_branch(cite_ir) + + local disam_format = DisamStringFormat:new() + + while cite_ir.is_ambiguous do + if #cite_ir.irs_with_disambiguate_branch == 0 then + break + end + + -- util.debug(cite_ir.cite_item.id) + -- util.debug(cite_ir.disam_str) + + -- update ambiguous_same_output_irs + local ambiguous_same_output_irs = {} + for _, ir_ in pairs(engine.cite_irs_by_output[cite_ir.disam_str]) do + if ir_.disam_str == cite_ir.disam_str then + table.insert(ambiguous_same_output_irs, ir_) + end + end + + for _, ir_ in ipairs(ambiguous_same_output_irs) do + if #ir_.irs_with_disambiguate_branch > 0 then + -- Disambiguation is incremental + -- disambiguate_IncrementalExtraText.txt + local condition_ir = ir_.irs_with_disambiguate_branch[1] + condition_ir.children[1] = condition_ir.disambiguate_branch_ir + condition_ir.group_var = condition_ir.disambiguate_branch_ir.group_var + table.remove(ir_.irs_with_disambiguate_branch, 1) + -- disambiguate_DisambiguateTrueReflectedInBibliography.txt + ir_.reference.disambiguate = true + + -- Update ir output + local inlines = ir_:flatten(disam_format) + local disam_str = disam_format:output(inlines, nil) + -- util.debug("update: " .. ir_.cite_item.id .. ": " .. disam_str) + ir_.disam_str = disam_str + if not engine.cite_irs_by_output[disam_str] then + engine.cite_irs_by_output[disam_str] = {} + end + engine.cite_irs_by_output[disam_str][ir_.ir_index] = ir_ + end + end + + cite_ir.is_ambiguous = self:check_ambiguity(cite_ir, engine) + -- util.debug(cite_ir.is_ambiguous) + -- for _, ir_ in ipairs(engine.disam_irs) do + -- util.debug(ir_.cite_item.id .. ": " .. ir_.disam_str) + -- end + + end + return cite_ir +end + +function Citation:check_ambiguity(cite_ir, engine) + for _, ir_ in pairs(engine.cite_irs_by_output[cite_ir.disam_str]) do + if ir_.cite_item.id ~= cite_ir.cite_item.id then + return true + end + end + return false +end + +function Citation:get_same_output_irs(cite_ir, engine) + local ambiguous_same_output_irs = {} + for _, ir_ in pairs(engine.cite_irs_by_output[cite_ir.disam_str]) do + if ir_.disam_str == cite_ir.disam_str then + table.insert(ambiguous_same_output_irs, ir_) + end + end + return ambiguous_same_output_irs +end + +function Citation:apply_disambiguate_add_year_suffix(cite_ir, engine) + if not cite_ir.is_ambiguous or not self.disambiguate_add_year_suffix then + return cite_ir + end + + local same_output_irs = self:get_same_output_irs(cite_ir, engine) + + table.sort(same_output_irs, function (a, b) + -- return a.ir_index < b.ir_index + return a.reference["citation-number"] < b.reference["citation-number"] + end) + + local year_suffix_number = 0 + -- util.debug(cite_ir) + + local disam_format = DisamStringFormat:new() + + for _, ir_ in ipairs(same_output_irs) do + ir_.reference.year_suffix_number = nil + end + + for _, ir_ in ipairs(same_output_irs) do + -- print(ir_.cite_item.id) + -- print(ir_.reference) + if not ir_.reference.year_suffix_number then + year_suffix_number = year_suffix_number + 1 + ir_.reference.year_suffix_number = year_suffix_number + ir_.reference["year-suffix"] = self:render_year_suffix(year_suffix_number) + end + + if not ir_.year_suffix_irs then + ir_.year_suffix_irs = ir_:collect_year_suffix_irs() + if #ir_.year_suffix_irs == 0 then + -- By default, the year-suffix is appended the first year rendered through cs:date + local year_ir = ir_:find_first_year_ir() + -- util.debug(year_ir) + if year_ir then + local year_suffix_ir = YearSuffix:new({}, self) + table.insert(year_ir.children, year_suffix_ir) + table.insert(ir_.year_suffix_irs, year_suffix_ir) + end + end + end + + for _, year_suffix_ir in ipairs(ir_.year_suffix_irs) do + year_suffix_ir.inlines = {PlainText:new(ir_.reference["year-suffix"])} + year_suffix_ir.year_suffix_number = ir_.reference.year_suffix_number + year_suffix_ir.group_var = "important" + end + + local inlines = ir_:flatten(disam_format) + local disam_str = disam_format:output(inlines, nil) + -- util.debug("update: " .. ir_.cite_item.id .. ": " .. disam_str) + ir_.disam_str = disam_str + if not engine.cite_irs_by_output[disam_str] then + engine.cite_irs_by_output[disam_str] = {} + end + engine.cite_irs_by_output[disam_str][ir_.ir_index] = ir_ + + end + + cite_ir.is_ambiguous = false + + return cite_ir +end + +function Citation:render_year_suffix(year_suffix_number) + if year_suffix_number <= 0 then + return nil + end + local year_suffix = "" + while year_suffix_number > 0 do + local i = (year_suffix_number - 1) % 26 + year_suffix = string.char(i + 97) .. year_suffix + year_suffix_number = (year_suffix_number - 1) // 26 + end + -- util.debug(year_suffix) + return year_suffix +end + +local function find_first(ir, check) + if check(ir) then + return ir + end + if ir.children then + for _, child in ipairs(ir.children) do + local target_ir = find_first(child, check) + if target_ir then + return target_ir + end + end + end + return nil +end + +-- Find the first rendering element and it should be produced by and names element +local function find_first_names_ir(ir) + if ir.first_names_ir then + return ir.first_names_ir + end + + local first_rendering_ir = find_first(ir, function (ir_) + return (ir_._element == "text" + or ir_._element == "date" + or ir_._element == "number" + or ir_._element == "names" + or ir_._element == "label") + and ir_.group_var ~= "missing" + end) + local first_names_ir + if first_rendering_ir and first_rendering_ir._element == "names" then + first_names_ir = first_rendering_ir + end + if first_names_ir then + local disam_format = DisamStringFormat:new() + local inlines = first_names_ir:flatten(disam_format) + first_names_ir.disam_str = disam_format:output(inlines, nil) + end + ir.first_names_ir = first_names_ir + return first_names_ir + +end + +function Citation:_apply_special_citation_form(irs, properties, output_format, engine) + if properties.mode then + if properties.mode == "author-only" then + for _, ir in ipairs(irs) do + self:_apply_citation_mode_author_only(ir) + end + elseif properties.mode == "suppress-author" then + -- suppress-author mode does not work in note style + -- discretionary_FirstReferenceNumberWithIntext.txt + if engine.style.class ~= "note" then + for _, ir in ipairs(irs) do + self:_apply_suppress_author(ir) + end + end + + elseif properties.mode == "composite" then + self:_apply_composite(irs[1], output_format, engine) + + end + + else + for _, ir in ipairs(irs) do + if ir.cite_item["author-only"] then + self:_apply_cite_author_only(ir) + elseif ir.cite_item["suppress-author"] then + self:_apply_suppress_author(ir) + end + end + end +end + +function Citation:_apply_citation_mode_author_only(ir) + -- Used in pr + local author_ir = find_first_names_ir(ir) + + if author_ir then + remove_name_formatting(author_ir) + ir.children = {author_ir} + else + ir.children = {Rendered:new({PlainText:new("[NO_PRINTED_FORM]")}, self)} + end + return ir +end + +-- Citation flags with makeCitationCluster +-- In contrast to Citation flags with processCitationCluster, this funciton +-- looks for the first rendering element instead of names element. +-- See discretionary_AuthorOnly.txt +function Citation:_apply_cite_author_only(ir) + local author_ir = find_first(ir, function (ir_) + return (ir_._element == "text" + or ir_._element == "date" + or ir_._element == "number" + or ir_._element == "names" + or ir_._element == "label") + and ir_.group_var ~= "missing" + end) + + if author_ir then + remove_name_formatting(author_ir) + ir.children = {author_ir} + else + ir.children = {Rendered:new({PlainText:new("[NO_PRINTED_FORM]")}, self)} + end + return ir +end + +function Citation:_apply_suppress_author(ir) + local author_ir = find_first_names_ir(ir) + if author_ir then + -- util.debug(author_ir) + author_ir.collapse_suppressed = true + end + return ir +end + +function Citation:_apply_composite(ir, output_format, engine) + -- local first_names_ir = find_first_names_ir(ir) + + local first_names_ir = find_first_names_ir(ir) + if first_names_ir then + -- util.debug(first_names_ir) + first_names_ir.collapse_suppressed = true + end + + local author_ir + if engine.style.intext then + local properties = {mode = "author-only"} + author_ir = engine.style.intext:build_fully_disambiguated_ir(ir.cite_item, output_format, engine, properties) + elseif first_names_ir then + author_ir = first_names_ir + end + + if author_ir then + remove_name_formatting(author_ir) + ir.author_ir = author_ir + else + ir.author_ir = Rendered:new({PlainText:new("[NO_PRINTED_FORM]")}, self) + end + + return ir +end + +function Citation:group_cites(irs) + local disam_format = DisamStringFormat:new() + for _, ir in ipairs(irs) do + local first_names_ir = ir.first_names_ir + if not first_names_ir then + first_names_ir = find_first(ir, function (ir_) + return ir_._element == "names" and ir_.group_var ~= "missing" + end) + if first_names_ir then + local inlines = first_names_ir:flatten(disam_format) + first_names_ir.disam_str = disam_format:output(inlines, nil) + end + ir.first_names_ir = first_names_ir + end + end + + local irs_by_name = {} + local name_list = {} + + for _, ir in ipairs(irs) do + local name_str = "" + if ir.first_names_ir then + name_str = ir.first_names_ir.disam_str + end + if not irs_by_name[name_str] then + irs_by_name[name_str] = {} + table.insert(name_list, name_str) + end + table.insert(irs_by_name[name_str], ir) + end + + local grouped = {} + for _, name_str in ipairs(name_list) do + local irs_with_same_name = irs_by_name[name_str] + for i, ir in ipairs(irs_with_same_name) do + if i < #irs_with_same_name then + ir.own_delimiter = self.cite_group_delimiter + end + table.insert(grouped, ir) + end + end + return grouped +end + +function Citation:collapse_cites(irs) + if self.collapse == "citation-number" then + self:collapse_cites_by_citation_number(irs) + elseif self.collapse == "year" then + self:collapse_cites_by_year(irs) + elseif self.collapse == "year-suffix" then + self:collapse_cites_by_year_suffix(irs) + elseif self.collapse == "year-suffix-ranged" then + self:collapse_cites_by_year_suffix_ranged(irs) + end +end + +function Citation:collapse_cites_by_citation_number(irs) + local cite_groups = {} + local current_group = {} + local previous_citation_number + for i, ir in ipairs(irs) do + local citation_number + local only_citation_number_ir = self:get_only_citation_number(ir) + if only_citation_number_ir then + -- Other irs like locators are not rendered. + -- collapse_CitationNumberRangesWithAffixesGrouped.txt + citation_number = only_citation_number_ir.citation_number + end + if i == 1 then + table.insert(current_group, ir) + elseif citation_number and previous_citation_number and + previous_citation_number + 1 == citation_number then + table.insert(current_group, ir) + else + table.insert(cite_groups, current_group) + current_group = {ir} + end + previous_citation_number = citation_number + end + table.insert(cite_groups, current_group) + + for _, cite_group in ipairs(cite_groups) do + if #cite_group >= 3 then + cite_group[1].own_delimiter = util.unicode["en dash"] + for i = 2, #cite_group - 1 do + cite_group[i].collapse_suppressed = true + end + cite_group[#cite_group].own_delimiter = self.after_collapse_delimiter + end + end +end + +function Citation:get_only_citation_number(ir) + if ir.citation_number then + return ir + end + if not ir.children then + return nil + end + local only_citation_number_ir + for _, child in ipairs(ir.children) do + if child.group_var ~= "missing" then + local citation_number_ir = self:get_only_citation_number(child) + if citation_number_ir then + if only_citation_number_ir then + return nil + else + only_citation_number_ir = citation_number_ir + end + else + return false + end + end + end + return only_citation_number_ir +end + +function Citation:collapse_cites_by_year(irs) + local cite_groups = {{}} + local previous_name_str + for i, ir in ipairs(irs) do + local name_str + if ir.first_names_ir then + name_str = ir.first_names_ir.disam_str + end + if i == 1 then + table.insert(cite_groups[#cite_groups], ir) + elseif name_str and name_str == previous_name_str then + -- ir.first_names_ir was set in the cite grouping stage + -- TODO: and not previous cite suffix + table.insert(cite_groups[#cite_groups], ir) + else + table.insert(cite_groups, {ir}) + end + previous_name_str = name_str + end + + for _, cite_group in ipairs(cite_groups) do + if #cite_group > 1 then + for i, cite_ir in ipairs(cite_group) do + if i > 1 and cite_ir.first_names_ir then + cite_ir.first_names_ir.collapse_suppressed = true + end + if i == #cite_group then + cite_ir.own_delimiter = self.after_collapse_delimiter + elseif i < #cite_group then + -- The delimiter depends on the citation > sort. + -- https://github.com/citation-style-language/test-suite/issues/39#issuecomment-687901688 + if cite_ir.cite_item.locator then + -- Special hack for + cite_ir.own_delimiter = self.after_collapse_delimiter + elseif self.cite_grouping then + if self.sort then + cite_ir.own_delimiter = self.cite_group_delimiter + else + cite_ir.own_delimiter = self.layout.delimiter + end + else + if self.sort then + cite_ir.own_delimiter = self.cite_group_delimiter + else + -- disambiguate_YearCollapseWithInstitution.txt + -- disambiguate_InitializeWithButNoDisambiguation.txt ? + cite_ir.own_delimiter = self.layout.delimiter + end + end + end + end + end + end +end + +local function find_rendered_year_suffix(ir) + if ir._type == "YearSuffix" then + return ir + end + if ir.children then + for _, child in ipairs(ir.children) do + if child.group_var ~= "missing" then + local year_suffix = find_rendered_year_suffix(child) + if year_suffix then + return year_suffix + end + end + end + end + return nil +end + +function Citation:collapse_cites_by_year_suffix(irs) + self:collapse_cites_by_year(irs) + -- Group by disam_str + -- The year-suffix is ommitted in DisamStringFormat + local cite_groups = {{}} + local previous_ir + local previous_year_suffix + for i, ir in ipairs(irs) do + local year_suffix = find_rendered_year_suffix(ir) + ir.rendered_year_suffix_ir = year_suffix + if i == 1 then + table.insert(cite_groups[#cite_groups], ir) + elseif year_suffix and previous_ir.disam_str == ir.disam_str and previous_year_suffix then + -- TODO: and not previous cite suffix + table.insert(cite_groups[#cite_groups], ir) + else + table.insert(cite_groups, {ir}) + end + previous_ir = ir + previous_year_suffix = year_suffix + end + + for _, cite_group in ipairs(cite_groups) do + if #cite_group > 1 then + for i, cite_ir in ipairs(cite_group) do + if i > 1 then + -- cite_ir.children = {cite_ir.rendered_year_suffix_ir} + -- Set the collapse_suppressed flag rather than removing the child irs. + -- This leaves the disamb ir structure unchanged. + self:suppress_ir_except_child(cite_ir, cite_ir.rendered_year_suffix_ir) + end + if i < #cite_group then + if self.cite_grouping then + -- In the current citeproc-js impplementation, explicitly set + -- cite-group-delimiter takes precedence over year-suffix-delimiter. + -- May be changed in the future. + -- https://github.com/citation-style-language/test-suite/issues/50 + cite_ir.own_delimiter = self.cite_group_delimiter + else + cite_ir.own_delimiter = self.year_suffix_delimiter + end + elseif i == #cite_group then + cite_ir.own_delimiter = self.after_collapse_delimiter + end + end + end + end +end + +function Citation:suppress_ir_except_child(ir, target) + if ir == target then + ir.collapse_suppressed = false + return false + end + ir.collapse_suppressed = true + if ir.children then + for _, child in ipairs(ir.children) do + if child.group_var ~= "missing" and not child.collapse_suppressed then + if not self:suppress_ir_except_child(child, target) then + ir.collapse_suppressed = false + end + end + end + end + return ir.collapse_suppressed +end + +function Citation:collapse_cites_by_year_suffix_ranged(irs) + self:collapse_cites_by_year_suffix(irs) + -- Group by disam_str + local cite_groups = {{}} + local previous_ir + local previous_year_suffix + for i, ir in ipairs(irs) do + local year_suffix_ir = find_rendered_year_suffix(ir) + ir.rendered_year_suffix_ir = year_suffix_ir + if i == 1 then + table.insert(cite_groups[#cite_groups], ir) + elseif year_suffix_ir and previous_ir.disam_str == ir.disam_str and previous_year_suffix and + year_suffix_ir.year_suffix_number == previous_year_suffix.year_suffix_number + 1 then + -- TODO: and not previous cite suffix + table.insert(cite_groups[#cite_groups], ir) + else + table.insert(cite_groups, {ir}) + end + previous_ir = ir + previous_year_suffix = year_suffix_ir + end + + for _, cite_group in ipairs(cite_groups) do + if #cite_group > 2 then + for i, cite_ir in ipairs(cite_group) do + if i == 1 then + cite_ir.own_delimiter = util.unicode["en dash"] + elseif i < #cite_group then + cite_ir.collapse_suppressed = true + end + end + end + end +end + + +local InText = Citation:derive("intext", { + givenname_disambiguation_rule = "by-cite", + cite_group_delimiter = ", ", + near_note_distance = 5, +}) + + +citation_module.Citation = Citation +citation_module.InText = InText + + +return citation_module diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-node-date.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-node-date.lua index 7ec1f439b83..f95be07607e 100644 --- a/Master/texmf-dist/scripts/citation-style-language/citeproc-node-date.lua +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-node-date.lua @@ -354,9 +354,9 @@ function DatePart:build_ir(single_date, engine, state, context, suppressed_affix local inlines = {PlainText:new(text)} local output_format = context.format - if not context.is_english then - print(debug.traceback()) - end + -- if not context.is_english then + -- print(debug.traceback()) + -- end local is_english = context:is_english() output_format:apply_text_case(inlines, self.text_case, is_english) inlines = output_format:with_format(inlines, self.formatting) diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-node-label.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-node-label.lua index 27cafa2f189..8dc64f3c941 100644 --- a/Master/texmf-dist/scripts/citation-style-language/citeproc-node-label.lua +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-node-label.lua @@ -70,7 +70,9 @@ function Label:_is_variable_plural(variable, context) return #variable > 1 elseif variable_type == "number" then if util.startswith(variable, "number-of-") then - return tonumber(value) > 1 + -- Issue #27: "number-of-pages": "91–129" + value = string.match(tostring(value), "%d+") + return value and tonumber(value) > 1 else value = tostring(value) -- label_CollapsedPageNumberPluralDetection.txt diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-node-locale.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-node-locale.lua index 0e8cdd07873..c9d9ed42592 100644 --- a/Master/texmf-dist/scripts/citation-style-language/citeproc-node-locale.lua +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-node-locale.lua @@ -96,7 +96,9 @@ function Locale:get_simple_term(name, form, plural) for _, fallback_form in ipairs(self.form_fallbacks[form]) do local key = name if form ~= "long" then - if not key then print(debug.traceback()) end + -- if not key then + -- print(debug.traceback()) + -- end key = key .. "/form-" .. fallback_form end local term = self.terms[key] diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-node-names.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-node-names.lua index 749f571bd24..f0eb95b1e2d 100644 --- a/Master/texmf-dist/scripts/citation-style-language/citeproc-node-names.lua +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-node-names.lua @@ -441,9 +441,6 @@ function Name:build_ir(variable, et_al, label, engine, state, context) ir.group_var = "important" end - ir.formatting = self.formatting - ir.affixes = self.affixes - irs = {ir} if label then @@ -478,6 +475,10 @@ function Name:build_person_name_ir(name, is_first, context) local inlines = self:render_person_name(name, is_first, is_latin, is_inverted, context) local person_name_ir = PersonNameIr:new(inlines, self) + -- discretionary_ExampleSeveralAuthorsWithIntext.txt + person_name_ir.formatting = self.formatting + person_name_ir.affixes = self.affixes + person_name_ir.is_inverted = is_inverted local output_format = SortStringFormat:new() @@ -1041,9 +1042,10 @@ function NamePart:format_text_case(text, context) local output_format = context.format local inlines = InlineElement:parse(text, context) local is_english = context:is_english() - if not output_format then - print(debug.traceback()) - end + -- if not output_format then + -- print(debug.traceback()) + -- assert(output_format) + -- end output_format:apply_text_case(inlines, self.text_case, is_english) inlines = output_format:with_format(inlines, self.formatting) diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-node-style.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-node-style.lua index 5c387bd6c8f..f963ed4e814 100644 --- a/Master/texmf-dist/scripts/citation-style-language/citeproc-node-style.lua +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-node-style.lua @@ -33,9 +33,17 @@ function Style:new() end function Style:parse(xml_str) - local csl_xml = dom.parse(xml_str) - if not csl_xml then - error("Failed to parse CSL style.") + -- The parsing error is not caught by busted in some situcations and thus it's processed here. + -- discretionary_CitationNumberAuthorOnlyThenSuppressAuthor.txt + local status, csl_xml = pcall(function () return dom.parse(xml_str) end) + if not status or not csl_xml then + if csl_xml then + local error_message = string.match(csl_xml, "^.-: (.*)$") + util.error("CSL parsing error: " .. util.rstrip(error_message)) + else + util.error("CSL parsing error") + end + return nil end local style_node = csl_xml:get_path("style")[1] if not csl_xml then @@ -44,33 +52,6 @@ function Style:parse(xml_str) return Style:from_node(style_node) end -local function make_name_inheritance(name, node) - name:set_attribute(node, "and") - name:set_attribute(node, "delimiter-precedes-et-al") - name:set_attribute(node, "delimiter-precedes-last") - name:set_number_attribute(node, "et-al-min") - name:set_number_attribute(node, "et-al-use-first") - name:set_number_attribute(node, "et-al-subsequent-min") - name:set_number_attribute(node, "et-al-subsequent-use-first") - name:set_bool_attribute(node, "et-al-use-last") - name:set_bool_attribute(node, "initialize") - name:set_attribute(node, "initialize-with") - name:set_attribute(node, "name-as-sort-order") - name:set_attribute(node, "sort-separator") - local delimiter = node:get_attribute("name-delimiter") - if delimiter then - name.delimiter = delimiter - end - local form = node:get_attribute("name-form") - if form then - name.form = form - end - local names_delimiter = node:get_attribute("names-delimiter") - if names_delimiter then - name.names_delimiter = names_delimiter - end -end - function Style:from_node(node) local o = Style:new() @@ -87,7 +68,7 @@ function Style:from_node(node) -- Inheritable Name Options -- https://docs.citationstyles.org/en/stable/specification.html#inheritable-name-options o.name_inheritance = require("citeproc-node-names").Name:new() - make_name_inheritance(o.name_inheritance, node) + Element.make_name_inheritance(o.name_inheritance, node) if o.page_range_format == "chicago" then if o.version < "1.1" then @@ -111,6 +92,8 @@ function Style:from_node(node) o.citation = child elseif element_name == "bibliography" then o.bibliography = child + elseif element_name == "intext" then + o.intext = child elseif element_name == "macro" then o.macros[child.name] = child elseif element_name == "locale" then @@ -197,328 +180,8 @@ function Info:from_node(node) end -local Citation = Element:derive("citation", { - givenname_disambiguation_rule = "by-cite", - -- https://github.com/citation-style-language/schema/issues/338 - -- The cite_group_delimiter may be changed to inherit the delimiter in citaion > layout. - cite_group_delimiter = ", ", - near_note_distance = 5, -}) - -function Citation:from_node(node, style) - - local o = Citation:new() - o.children = {} - - o:process_children_nodes(node) - - -- o.layouts = nil -- CSL-M extension - - for _, child in ipairs(o.children) do - local element_name = child.element_name - if element_name == "layout" then - o.layout = child - elseif element_name == "sort" then - o.sort = child - end - end - - -- Disambiguation - o:set_bool_attribute(node, "disambiguate-add-givenname") - o:set_attribute(node, "givenname-disambiguation-rule") - o:set_bool_attribute(node, "disambiguate-add-names") - o:set_bool_attribute(node, "disambiguate-add-year-suffix") - - -- Cite Grouping - o:set_attribute(node, "cite-group-delimiter") - -- In the current citeproc-js implementation and test suite, - -- cite grouping is activated by setting the cite-group-delimiter - -- attribute or the collapse attributes on cs:citation. - -- It may be changed to an independent procedure. - -- https://github.com/citation-style-language/schema/issues/338 - if node:get_attribute("cite-group-delimiter") then - o.cite_grouping = true - else - o.cite_grouping = false - end - - - -- Cite Collapsing - o:set_attribute(node, "collapse") - o:set_attribute(node, "year-suffix-delimiter") - if not o.year_suffix_delimiter then - o.year_suffix_delimiter = o.layout.delimiter - end - o:set_attribute(node, "after-collapse-delimiter") - if not o.after_collapse_delimiter then - o.after_collapse_delimiter = o.layout.delimiter - end - - -- Note Distance - o:set_number_attribute(node, "near-note-distance") - - local name_inheritance = require("citeproc-node-names").Name:new() - for key, value in pairs(style.name_inheritance) do - if value ~= nil then - name_inheritance[key] = value - end - end - make_name_inheritance(name_inheritance, node) - o.name_inheritance = name_inheritance - - -- update_mode = "plain" or "numeric" or "position" (or "both"?) - - return o -end - -function Citation:build_ir(engine, state, context) - if not self.layout then - util.error("Missing citation layout.") - end - return self.layout:build_ir(engine, state, context) -end - - -local Bibliography = Element:derive("bibliography", { - hanging_indent = false, - line_spacing = 1, - entry_spacing = 1, - subsequent_author_substitute_rule = "complete-all" -}) - -function Bibliography:from_node(node, style) - local o = Bibliography:new() - o.children = {} - - o:process_children_nodes(node) - - -- o.layouts = nil -- CSL-M extension - - for _, child in ipairs(o.children) do - local element_name = child.element_name - if element_name == "layout" then - o.layout = child - elseif element_name == "sort" then - o.sort = child - end - end - - -- Whitespace - o:set_bool_attribute(node, "hanging-indent") - o:set_attribute(node, "second-field-align") - o:set_number_attribute(node, "line-spacing") - o:set_number_attribute(node, "entry-spacing") - - -- Reference Grouping - o:set_attribute(node, "subsequent-author-substitute") - o:set_attribute(node, "subsequent-author-substitute-rule") - - local name_inheritance = require("citeproc-node-names").Name:new() - for key, value in pairs(style.name_inheritance) do - if value ~= nil then - name_inheritance[key] = value - end - end - make_name_inheritance(name_inheritance, node) - o.name_inheritance = name_inheritance - - return o -end - -function Bibliography:build_ir(engine, state, context) - if not self.layout then - util.error("Missing bibliography layout.") - end - local ir = self.layout:build_ir(engine, state, context) - -- util.debug(ir) - if self.second_field_align == "flush" and #ir.children >= 2 then - ir.children[1].display = "left-margin" - local right_inline_ir = SeqIr:new(util.slice(ir.children, 2), self) - right_inline_ir.display = "right-inline" - if ir.affixes then - right_inline_ir.affixes = ir.affixes - right_inline_ir.formatting = ir.formatting - ir.affixes = nil - ir.formatting = nil - end - ir.children = {ir.children[1], right_inline_ir} - end - - if self.subsequent_author_substitute then - self:substitute_subsequent_authors(engine, ir) - end - - if not ir then - ir = Rendered:new(PlainText:new("[CSL STYLE ERROR: reference with no printed form.]"), self) - end - return ir -end - -function Bibliography:substitute_subsequent_authors(engine, ir) - ir.first_name_ir = self:find_first_name_ir(ir) -- should be a SeqIr wiht _element = "names" - if not ir.first_name_ir then - engine.previous_bib_names_ir = nil - return - end - if self.subsequent_author_substitute_rule == "complete-all" then - self:substitute_subsequent_authors_complete_all(engine, ir) - elseif self.subsequent_author_substitute_rule == "complete-each" then - self:substitute_subsequent_authors_complete_each(engine, ir) - elseif self.subsequent_author_substitute_rule == "partial-each" then - self:substitute_subsequent_authors_partial_each(engine, ir) - elseif self.subsequent_author_substitute_rule == "partial-first" then - self:substitute_subsequent_authors_partial_first(engine, ir) - end - engine.previous_bib_names_ir = ir.first_name_ir -end - -function Bibliography:find_first_name_ir(ir) - if ir._type == "NameIr" then - return ir - elseif ir.children then - for _, child_ir in ipairs(ir.children) do - local first_name_ir = self:find_first_name_ir(child_ir) - if first_name_ir then - return first_name_ir - end - end - end - return nil -end - -function Bibliography:substitute_subsequent_authors_complete_all(engine, ir) - local bib_names_str = "" - - if #ir.first_name_ir.person_name_irs > 0 then - for _, person_name_ir in ipairs(ir.first_name_ir.person_name_irs) do - if bib_names_str ~= "" then - bib_names_str = bib_names_str .. " " - end - local name_variants = person_name_ir.disam_variants - bib_names_str = bib_names_str .. name_variants[#name_variants] - end - else - -- In case of a <text variable="title"/> in <substitute> - local disam_format = DisamStringFormat:new() - local inlines = ir.first_name_ir:flatten(disam_format) - bib_names_str = disam_format:output(inlines) - end - ir.first_name_ir.bib_names_str = bib_names_str - - if engine.previous_bib_names_ir and - engine.previous_bib_names_ir.bib_names_str == bib_names_str then - local text = self.subsequent_author_substitute - if text == "" then - ir.first_name_ir.children = {} - ir.first_name_ir.group_var = "missing" - else - -- the output of label is not substituted - -- util.debug(ir.first_name_ir) - ir.first_name_ir.children = {Rendered:new({PlainText:new(text)}, self)} - end - end -end - -function Bibliography:substitute_subsequent_authors_complete_each(engine, ir) - local bib_names_str = "" - - if #ir.first_name_ir.person_name_irs > 0 then - for _, person_name_ir in ipairs(ir.first_name_ir.person_name_irs) do - if bib_names_str ~= "" then - bib_names_str = bib_names_str .. " " - end - local name_variants = person_name_ir.disam_variants - bib_names_str = bib_names_str .. name_variants[#name_variants] - end - else - -- In case of a <text variable="title"/> in <substitute> - local disam_format = DisamStringFormat:new() - local inlines = ir.first_name_ir:flatten(disam_format) - bib_names_str = disam_format:output(inlines) - end - ir.first_name_ir.bib_names_str = bib_names_str - - if engine.previous_bib_names_ir and - engine.previous_bib_names_ir.bib_names_str == bib_names_str then - local text = self.subsequent_author_substitute - if #ir.first_name_ir.person_name_irs > 0 then - for _, person_name_ir in ipairs(ir.first_name_ir.person_name_irs) do - person_name_ir.inlines = {PlainText:new(text)} - end - else - -- In case of a <text variable="title"/> in <substitute> - if text == "" then - ir.first_name_ir.children = {} - ir.first_name_ir.group_var = "missing" - else - ir.first_name_ir.children = {Rendered:new({PlainText:new(text)}, self)} - end - end - end -end - -function Bibliography:substitute_subsequent_authors_partial_each(engine, ir) - local bib_names_str = "" - - if #ir.first_name_ir.person_name_irs > 0 then - if engine.previous_bib_names_ir then - for i, person_name_ir in ipairs(ir.first_name_ir.person_name_irs) do - local prev_name_ir = engine.previous_bib_names_ir.person_names[i] - if prev_name_ir then - local prev_name_variants = prev_name_ir.disam_variants - local prev_full_name_str = prev_name_variants[#prev_name_variants] - local name_variants = person_name_ir.disam_variants - local full_name_str = name_variants[#name_variants] - if prev_full_name_str == full_name_str then - local text = self.subsequent_author_substitute - person_name_ir.inlines = {PlainText:new(text)} - else - break - end - end - end - end - else - -- In case of a <text variable="title"/> in <substitute> - local disam_format = DisamStringFormat:new() - local inlines = ir.first_name_ir:flatten(disam_format) - bib_names_str = disam_format:output(inlines) - ir.first_name_ir.bib_names_str = bib_names_str - if engine.previous_bib_names_ir and - engine.previous_bib_names_ir.bib_names_str == bib_names_str then - local text = self.subsequent_author_substitute - if text == "" then - ir.first_name_ir.children = {} - ir.first_name_ir.group_var = "missing" - else - ir.first_name_ir.children = {Rendered:new({PlainText:new(text)}, self)} - end - end - end -end - -function Bibliography:substitute_subsequent_authors_partial_first(engine, ir) -end - -local Macro = Element:derive("macro") - -function Macro:from_node(node) - local o = Macro:new() - o.children = {} - o:set_attribute(node, "name") - o:process_children_nodes(node) - return o -end - -function Macro:build_ir(engine, state, context) - local ir = self:build_group_ir(engine, state, context) - return ir -end - style_module.Style = Style -style_module.Citation = Citation -style_module.Bibliography = Bibliography return style_module diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-node-text.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-node-text.lua index fae35350139..c7275b39beb 100644 --- a/Master/texmf-dist/scripts/citation-style-language/citeproc-node-text.lua +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-node-text.lua @@ -90,10 +90,12 @@ function Text:build_variable_ir(engine, state, context) end local inlines - if (variable == "URL" and engine.opt.url_link) or - (variable == "DOI" and engine.opt.doi_link) or - (variable == "PMID" and engine.opt.doi_link) or - (variable == "PMID" and engine.opt.doi_link) then + -- if not engine.opt then + -- print(debug.traceback()) + -- end + -- if engine.opt.wrap_url_and_doi and (variable == "URL" or variable == "DOI" or + -- variable == "PMID" or variable == "PMID") then + if variable == "URL" or variable == "DOI" or variable == "PMID" or variable == "PMID" then inlines = self:render_linked(engine, state, context, variable, text) else inlines = self:render_text_inlines(text, context) @@ -116,14 +118,13 @@ end function Text:render_linked(engine, state, context, variable, text) local href + local url_prefix = false -- The prefix is used as part of the URL. if variable == "URL" then href = text - elseif self.affixes and self.affixes.prefix then - if string.match(self.affixes.prefix, "https?://") then - text = self.affixes.prefix .. text - self.affixes.prefix = nil - href = text - end + elseif self.affixes and self.affixes.prefix and string.match(self.affixes.prefix, "https?://") then + text = self.affixes.prefix .. text + href = text + url_prefix = true elseif variable == "DOI" then href = "https://doi.org/" .. text elseif variable == "PMID" then @@ -131,6 +132,7 @@ function Text:render_linked(engine, state, context, variable, text) elseif variable == "PMCID" then href = "https://www.ncbi.nlm.nih.gov/pmc/articles/" .. text end + local inlines = {Linked:new(text, href)} local output_format = context.format local localized_quotes = nil @@ -139,6 +141,9 @@ function Text:render_linked(engine, state, context, variable, text) end inlines = output_format:with_format(inlines, self.formatting) inlines = output_format:affixed_quoted(inlines, self.affixes, localized_quotes) + if url_prefix then + table.remove(inlines, 1) + end return output_format:with_display(inlines, self.display) end diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-nodes.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-nodes.lua index 69e87739b4a..de01cc09d87 100644 --- a/Master/texmf-dist/scripts/citation-style-language/citeproc-nodes.lua +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-nodes.lua @@ -5,6 +5,9 @@ -- local style = require("citeproc-node-style") +local citation = require("citeproc-node-citation") +local citation = require("citeproc-node-citation") +local bibliography = require("citeproc-node-bibliography") local locale = require("citeproc-node-locale") local layout = require("citeproc-node-layout") local text = require("citeproc-node-text") @@ -18,8 +21,9 @@ local sort = require("citeproc-node-sort") local nodes = { ["style"] = style.Style, - ["citation"] = style.Citation, - ["bibliography"] = style.Bibliography, + ["citation"] = citation.Citation, + ["intext"] = citation.Intext, + ["bibliography"] = bibliography.Bibliography, ["locale"] = locale.Locale, ["term"] = locale.Term, ["layout"] = layout.Layout, diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-output.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-output.lua index 97cc53fc4a0..d3ef1d5ede0 100644 --- a/Master/texmf-dist/scripts/citation-style-language/citeproc-output.lua +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-output.lua @@ -110,6 +110,36 @@ function Quoted:new(inlines, localized_quotes) end +local Code = InlineElement:derive("Code") + +function Code:new(value) + local o = InlineElement.new(self) + o.value = value + setmetatable(o, self) + return o +end + + +local MathML = InlineElement:derive("MathML") + +function MathML:new(value) + local o = InlineElement.new(self) + o.value = value + setmetatable(o, self) + return o +end + + +local MathTeX = InlineElement:derive("MathTeX") + +function MathTeX:new(value) + local o = InlineElement.new(self) + o.value = value + setmetatable(o, self) + return o +end + + local NoCase = InlineElement:derive("NoCase") function NoCase:new(inlines) @@ -152,11 +182,95 @@ function Div:new(inlines, display) end -function InlineElement:parse(str, context) - -- Return a list of inlines - if type(str) ~= "string" then - print(debug.traceback()) +function InlineElement:parse(text, context) + local text_type = type(text) + local inlines + if text_type == "table" then + -- CSL rich text + inlines = self:parse_csl_rich_text(text) + elseif text_type == "string" then + -- String with HTML-like formatting tags + inlines = self:parse_html_tags(text, context) + elseif text_type == "number" then + inlines = {PlainText:new(tostring(text))} + else + util.error("Invalid text type") + end + return inlines +end + +function InlineElement:parse_csl_rich_text(text) + -- Example: [ + -- "A title with a", + -- { + -- "quote": "quoted string." + -- } + -- ] + local inlines = {} + + local text_type = type(text) + if text_type == "string" then + table.insert(inlines, PlainText:new(text)) + elseif text_type == "table" then + for _, subtext in ipairs(text) do + local subtext_type = type(subtext) + local inline + if subtext_type == "string" then + inline = PlainText:new(subtext) + elseif subtext_type == "table" then + local format + local content + for format_, content_ in pairs(subtext) do + format = format_ + content = content_ + end + if format == "bold" then + inline = Formatted:new(self:parse_csl_rich_text(content), {["font-weight"] = "bold"}) + elseif format == "code" then + if type(content) ~= "string" then + util.error("Invalid rich text content.") + end + inline = Code:new(content) + elseif format == "italic" then + inline = Formatted:new(self:parse_csl_rich_text(content), {["font-style"] = "italic"}) + elseif format == "math-ml" then + if type(content) ~= "string" then + util.error("Invalid rich text content.") + end + inline = Code:new(content) + elseif format == "math-tex" then + if type(content) ~= "string" then + util.error("Invalid rich text content.") + end + inline = Code:new(content) + elseif format == "preserve" then + inline = NoCase:new(self:parse_csl_rich_text(content)) + elseif format == "quote" then + inline = Quoted:new(self:parse_csl_rich_text(content)) + elseif format == "sc" then + inline = Formatted:new(self:parse_csl_rich_text(content), {["font-variant"] = "small-caps"}) + elseif format == "strike" then + inline = Formatted:new(self:parse_csl_rich_text(content), {["strike-through"] = true}) + elseif format == "sub" then + inline = Formatted:new(self:parse_csl_rich_text(content), {["font-variant"] = "small-caps"}) + elseif format == "sup" then + inline = Formatted:new(self:parse_csl_rich_text(content), {["font-variant"] = "small-caps"}) + end + end + table.insert(inlines, inline) + end + else + util.error("Invalid text type") end + + return inlines +end + +function InlineElement:parse_html_tags(str, context) + -- Return a list of inlines + -- if type(str) ~= "string" then + -- print(debug.traceback()) + -- end local html_str = "<div>" .. str .. "</div>" local ok, html = pcall(dom.parse, html_str) local inlines @@ -219,7 +333,12 @@ function InlineElement:parse_quotes(inlines, context) local quote_stack = {} local text_stack = {{}} - local localized_quotes = context:get_localized_quotes() + local localized_quotes + if context then + localized_quotes = context:get_localized_quotes() + else + localized_quotes = LocalizedQuotes:new() + end for _, fragment in ipairs(quote_fragments) do local top_text_list = text_stack[#text_stack] @@ -287,7 +406,7 @@ function InlineElement:parse_quotes(inlines, context) local elements = text_stack[1] if #text_stack > 1 then - assert(#text_stack == #quote_stack + 1) + -- assert(#text_stack == #quote_stack + 1) for i, quote in ipairs(quote_stack) do if quote == "'" then quote = util.unicode["apostrophe"] @@ -428,8 +547,10 @@ function InlineElement:capitalize_first_term() -- util.debug(self) if self._type == "PlainText" then self.value = util.capitalize(self.value) - elseif self.inlines[1] then + elseif self._type ~= "Code" and self._type ~= "MathML" and self._type ~= "MathTeX" then + if self.inlines[1] then self.inlines[1]:capitalize_first_term() + end end end @@ -461,9 +582,9 @@ function OutputFormat:flatten_ir(ir) end function OutputFormat:flatten_seq_ir(ir) - if not ir.children then - print(debug.traceback()) - end + -- if not ir.children then + -- print(debug.traceback()) + -- end if #ir.children == 0 then return {} end @@ -563,6 +684,9 @@ function OutputFormat:apply_text_case_inner(inlines, text_case, seen_one, is_upp seen_one = seen_one or string_contains_word(inline.value) elseif inline._type == "NoCase" or inline._type == "NoDecor" or + inline._type == "Code" or + inline._type == "MathML" or + inline._type == "MathTeX" or (inline._type == "Formatted" and inline.formatting["font-variant"] == "small-caps") or (inline._type == "Formatted" and inline.formatting["vertical-align"] == "sup") or (inline._type == "Formatted" and inline.formatting["vertical-align"] == "sub") then @@ -813,6 +937,11 @@ function OutputFormat:flip_flop(inlines, state) end end + elseif inline._type == "Code" or + inline._type == "MathML" or + inline._type == "MathTeX" then + return + elseif inline.inlines then -- Div, ... self:flip_flop(inline.inlines, state) end @@ -869,6 +998,11 @@ function OutputFormat:flip_flop_micro_inlines(inlines, state) end end + elseif inline._type == "Code" or + inline._type == "MathML" or + inline._type == "MathTeX" then + return + elseif inline.inlines then -- Div, ... self:flip_flop_micro_inlines(inline.inlines, state) end @@ -876,14 +1010,11 @@ function OutputFormat:flip_flop_micro_inlines(inlines, state) end local function find_left(inline) - if not inline then - print(debug.traceback()) - end if inline._type == "PlainText" then return inline -- elseif inline._type == "Micro" then -- return nil - elseif inline.inlines and inline._type~="Quoted" then + elseif inline.inlines and #inline.inlines > 0 and inline._type~="Quoted" then return find_left(inline.inlines[1]) else return nil @@ -1158,6 +1289,15 @@ function Markup:write_inline(inline, context) elseif inline._type == "Linked" then return self:write_link(inline, context) + elseif inline._type == "Code" then + return self:write_code(inline.inlines) + + elseif inline._type == "MathML" then + return self:write_mathml(inline.inlines) + + elseif inline._type == "MathTeX" then + return self:write_math_tex(inline.inlines) + elseif inline._type == "NoCase" or inline._type == "NoDecor" then return self:write_inlines(inline.inlines) @@ -1286,12 +1426,28 @@ end function LatexWriter:write_link(inline, context) if inline.href == inline.value then + -- URL return string.format("\\url{%s}", inline.value) + elseif context.engine.opt.wrap_url_and_doi then + return string.format("\\href{%s}{%s}", inline.href, self:write_escaped(inline.value, context)) else - return string.format("\\href{%s}{%s}", inline.href, inline.value) + return self:write_escaped(inline.value, context) end end +function LatexWriter:write_code(inline, context) + return inline.value +end + +function LatexWriter:write_mathml(inline, context) + util.error("MathML is not supported in LaTeX output.") + return "" +end + +function LatexWriter:write_math_tex(inline, context) + return string.format("$%s$", inline.value) +end + local HtmlWriter = Markup:new() @@ -1371,7 +1527,25 @@ function HtmlWriter:write_display(inline, context) end function HtmlWriter:write_link(inline, context) - return string.format('<a href="%s">%s</a>', inline.href, inline.value) + local content = self:write_escaped(inline.value, context) + if context.engine.opt.wrap_url_and_doi then + local href = self:write_escaped(inline.href, context) + return string.format('<a href="%s">%s</a>', href, content) + else + return content + end +end + +function HtmlWriter:write_code(inline, context) + return string.format("<code>%s</code>", inline.value) +end + +function HtmlWriter:write_mathml(inline, context) + return string.format('<math xmlns="http://www.w3.org/1998/Math/MathML">%s</math>', inline.value) +end + +function HtmlWriter:write_math_tex(inline, context) + return string.format("<code>$%s$</code>", self:write_escaped(inline.value, context)) end @@ -1432,9 +1606,9 @@ function DisamStringFormat:flatten_ir(ir) end function DisamStringFormat:flatten_seq_ir(ir) - if not ir.children then - print(debug.traceback()) - end + -- if not ir.children then + -- print(debug.traceback()) + -- end if #ir.children == 0 then return {} end @@ -1455,6 +1629,18 @@ function DisamStringFormat:flatten_seq_ir(ir) end +function InlineElement.has_space(inlines) + local str = DisamStringFormat:output(inlines) + if not str then + return false + end + if string.match(util.strip(str), "%s") then + return true + else + return false + end +end + output_module.move_in_puncts = { ["."] = true, ["!"] = true, diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc-util.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc-util.lua index 71221e6bec9..1602511d8ee 100644 --- a/Master/texmf-dist/scripts/citation-style-language/citeproc-util.lua +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc-util.lua @@ -11,6 +11,21 @@ local inspect -- only load it when debugging local util = {} +-- Deep copy +function util.deep_copy(obj) + local res + if type(obj) == "table" then + res = {} + for key, value in pairs(obj) do + res[key] = util.deep_copy(value) + end + else + res = obj + end + return res +end + +-- Shallow copy function util.clone(obj) if type(obj) == "table" then local res = {} @@ -56,7 +71,7 @@ function util.to_list(str) end function util.to_ordinal (n) - assert(type(n) == "number") + -- assert(type(n) == "number") local last_digit = n % 10 if last_digit == 1 and n ~= 11 then return tostring(n) .. "st" @@ -237,9 +252,9 @@ end -- Python list.extend() function util.extend(first, second) - if not second then - print(debug.traceback()) - end + -- if not second then + -- print(debug.traceback()) + -- end local l = #first for i, element in ipairs(second) do first[l + i] = element @@ -262,17 +277,17 @@ end function util.lstrip (str) if not str then - return nil + error("Invalid input") end - local res = string.gsub(str, "^%s+", "") + local res = string.gsub(str, "^%s*", "") return res end function util.rstrip (str) if not str then - return nil + error("Invalid input") end - local res = string.gsub(str, "%s+$", "") + local res = string.gsub(str, "%s*$", "") return res end @@ -280,16 +295,16 @@ function util.strip (str) return util.lstrip(util.rstrip(str)) end -function util.startswith (str, prefix) - if not str then - return false +function util.startswith(str, prefix) + if not str or type(str) ~= "string" then + print(debug.traceback()) end return string.sub(str, 1, #prefix) == prefix end -function util.endswith (str, suffix) - if not str then - return false +function util.endswith(str, suffix) + if not str or type(str) ~= "string" then + print(debug.traceback()) end return string.sub(str, -#suffix) == suffix end @@ -307,7 +322,7 @@ function util.is_numeric (str) if not string.match(w, "^%a*%d+%a*$") and not string.match(w, "^[MDCLXVI]+$") and not string.match(w, "^[mdclxvi]+$") then - -- Roman number withou validation + -- Roman number without validation return false end end @@ -828,7 +843,7 @@ function util.has_cjk_char(s) end function util.convert_roman (number) - assert(type(number) == "number") + -- assert(type(number) == "number") local output = {} for _, tuple in ipairs(util.roman_numerals) do local letter, value = table.unpack(tuple) @@ -979,9 +994,9 @@ util.superscripts = { -- File IO function util.read_file(path) - if not path then - print(debug.traceback()) - end + -- if not path then + -- print(debug.traceback()) + -- end local file = io.open(path, "r") if not file then -- util.error(string.format('Cannot read file "%s".', path)) diff --git a/Master/texmf-dist/scripts/citation-style-language/citeproc.lua b/Master/texmf-dist/scripts/citation-style-language/citeproc.lua index a4b04245305..b67b5b6477a 100755 --- a/Master/texmf-dist/scripts/citation-style-language/citeproc.lua +++ b/Master/texmf-dist/scripts/citation-style-language/citeproc.lua @@ -9,7 +9,7 @@ local citeproc = {} local engine = require("citeproc-engine") local util = require("citeproc-util") -citeproc.__VERSION__ = "0.2.2" +citeproc.__VERSION__ = "0.3.0" citeproc.new = engine.CiteProc.new citeproc.util = util |