WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 3e75e8b

Browse files
committed
feat(packages): Add filter option to the bibliography when cited=false
Some simple built-in filters are provided, allowing to filter the complete bibliography by entry type, keywords or issue date. N.B. Some code also refactored and notably in-code LDoc comments and annotations have been improved.
1 parent 520dc2d commit 3e75e8b

File tree

2 files changed

+172
-40
lines changed

2 files changed

+172
-40
lines changed

packages/bibtex/csl/processor.lua

Lines changed: 168 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
--- A processor for bibliographies.
1+
--- A CSL processor for bibliographies.
22
--
33
-- @copyright License: MIT (c) 2025 Omikhleia
44
--
@@ -23,13 +23,6 @@ local resolveFile = SILE and SILE.resolveFile or function (filename)
2323
end
2424

2525
-- Loaders for CSL locale and style files.
26-
-- They will look for a file in the following order:
27-
-- - First in csl/locales/ (resp. csl/styles/) wherever SILE looks at these from the working directory.
28-
-- This allows users to put their own CSL files in a simple place
29-
-- - Then in `packages/bibtex/csl/locales/` (ibid.)
30-
-- This allows users to put their own CSL files e.g. in a local copy of the package, following its structure
31-
-- - Then in `packages.bibtex.csl.locales.locales` (ibid.)
32-
-- This allows users to use CSL files from the (extended) Lua path, e.g. from a module.
3326
local function loadCslLocale (name)
3427
local filename = resolveFile("csl/locales/" .. name .. ".xml")
3528
or resolveFile("packages/bibtex/csl/locales/locales-" .. name .. ".xml")
@@ -59,6 +52,71 @@ local function loadCslStyle (name)
5952
return style
6053
end
6154

55+
local OP = {
56+
EQ = 0,
57+
IN = 1,
58+
}
59+
60+
local CSLVAR = {
61+
["type"] = OP.EQ,
62+
keyword = OP.IN,
63+
}
64+
local function isMatching (entry, var, val)
65+
if not entry[var] then
66+
return false
67+
end
68+
if CSLVAR[var] == OP.EQ then
69+
return entry[var] == val
70+
elseif CSLVAR[var] == OP.IN then
71+
return entry[var]:contains(val)
72+
end
73+
SU.error("Unsupported CSL variable " .. var .. " in filter")
74+
end
75+
local getIssuedYear = function (entry)
76+
local d = entry.issued
77+
if not d then
78+
return nil
79+
end
80+
if type(d) == "table" then -- range of dates
81+
if d.startdate then
82+
return d.startdate.year
83+
end
84+
if d.enddate then
85+
return d.enddate.year
86+
end
87+
end
88+
return d.year
89+
end
90+
local function builtinFilter(name)
91+
local year = name:match("^issued%-(%d+)$")
92+
if year then
93+
return function (entry)
94+
local issuedYear = getIssuedYear(entry)
95+
return issuedYear and issuedYear == year
96+
end
97+
end
98+
local year1, year2 = name:match("^issued%-(%d+)%-(%d+)$")
99+
if year1 and year2 then
100+
return function (entry)
101+
local issuedYear = getIssuedYear(entry)
102+
return issuedYear and tonumber(issuedYear) >= tonumber(year1) and tonumber(issuedYear) <= tonumber(year2)
103+
end
104+
end
105+
local varkey, val = name:match("^not%-([^-]+)%-(.+)$")
106+
if varkey and val then
107+
return function (entry)
108+
return not isMatching(entry, varkey, val)
109+
end
110+
end
111+
varkey, val = name:match("^([^-]+)%-(.+)$")
112+
if varkey and val then
113+
return function (entry)
114+
return isMatching(entry, varkey, val)
115+
end
116+
end
117+
SU.error("Unknown filter " .. f)
118+
end
119+
62120
-- CSL ENTRY PROXY (PSEUDO-CLASS)
63121

64122
local NIL_SENTINEL = {} -- Sentinel value to indicate that a field is overridden to nil
@@ -68,7 +126,7 @@ local NIL_SENTINEL = {} -- Sentinel value to indicate that a field is overridden
68126
-- so that we can for instance cache the CSL item and override some fields at some later
69127
-- processing time.
70128
-- @tparam table entry Orignal table to proxy
71-
-- @treturn table Proxy object wrapping the original entry
129+
-- @treturn table Proxy object wrapping the original entry
72130
local function CslEntry(entry)
73131
local proxy = {
74132
_entry = entry,
@@ -101,8 +159,13 @@ end
101159
local CslProcessor = pl.class()
102160

103161
--- (Constructor) Create a new CSL Bibliography manager.
162+
-- Usage example:
163+
--
164+
-- local biblio = CslProcessor()
165+
--
104166
-- @treturn CslProcessor New CSL Bibliography manager instance
105167
function CslProcessor:_init ()
168+
self._filter = {}
106169
self._data = {
107170
bib = {},
108171
cited = {
@@ -128,9 +191,26 @@ function CslProcessor:getCslEngine ()
128191
end
129192

130193
---- Set the bibliography style and locale for the CSL engine.
194+
-- Example usage:
195+
--
196+
-- biblio:setBibliographyStyle('chicago-author-date', 'en-US', {
197+
-- localizedPunctuation = false,
198+
-- italicExtension = true,
199+
-- mathExtension = true,
200+
-- })
201+
--
202+
-- Style and locale files are searched in the following order:
203+
--
204+
-- - First in `csl/locales/` (resp. `csl/styles/`) wherever SILE looks at these from the working directory.
205+
-- This allows users to put their own CSL files in a simple place
206+
-- - Then in `packages/bibtex/csl/locales/` (ibid.)
207+
-- This allows users to put their own CSL files e.g. in a local copy of the package, following its structure
208+
-- - Then in `packages.bibtex.csl.locales.locales` (ibid.)
209+
-- This allows users to use CSL files from the (extended) Lua path, e.g. from a module.
210+
--
131211
-- @tparam string stylename Name of the CSL style to use
132-
-- @tparam string lang Language code for the locale (e.g., "en-US")
133-
-- @tparam[opt] table options Additional options for the CSL engine
212+
-- @tparam string lang Language code for the locale (e.g., "en-US")
213+
-- @tparam[opt] table options Additional options for the CSL engine
134214
function CslProcessor:setBibliographyStyle (stylename, lang, options)
135215
options = options or {
136216
localizedPunctuation = false,
@@ -196,6 +276,8 @@ function CslProcessor:_getEntryForCite (key, warn_uncited)
196276
end
197277

198278
--- Retrieve a locator from an options table.
279+
-- Keys are expected to be locators like "page", "chapter", etc. as per CSL rules,
280+
-- Some some extra convenience abbreviations are also supported.
199281
-- @tparam table options Options (key-value pairs) that may contain a locator
200282
-- @treturn table Locator
201283
function CslProcessor:_getLocator (options)
@@ -219,7 +301,7 @@ end
219301

220302
--- Track the position of a citation acconrding to the CSL rules.
221303
-- @tparam string key Citation key
222-
-- @tparam {label=string, value=string}|nil locator Locator
304+
-- @tparam {label=string,value=string}|nil locator Locator
223305
-- @tparam boolean is_single Single or multiple citation
224306
-- @treturn string Position of the citation ("first", "subsequent", "ibid", "ibid-with-locator")
225307
function CslProcessor:_getCitePosition (key, locator, is_single)
@@ -276,15 +358,22 @@ function CslProcessor:_adapter (entry, citnum)
276358
return cslentry
277359
end
278360

361+
--- Load a bibliography file and parse it.
362+
-- @tparam string bibfile Path to the BibTeX file to load
363+
function CslProcessor:loadBibliography (bibfile)
364+
local bib = self._data.bib
365+
parseBibtex(bibfile, bib)
366+
end
367+
279368
--- Cite a sigle entry with optional locator.
280369
-- Usage example:
281-
-- ```lua
282-
-- local cite = biblio:cite({
283-
-- key = 'mykey1',
284-
-- page = "191-193",
285-
-- })
286-
-- ```
287-
-- @tparam {key=string, [string]=string} item Citation item with a key and optional locator
370+
--
371+
-- local cite = biblio:cite({
372+
-- key = 'mykey1',
373+
-- page = "191-193",
374+
-- })
375+
--
376+
-- @tparam {key=string,[string]=string} item Citation item with a key and optional locator
288377
-- @treturn string|nil Formatted citation string or nil if the entry is not found
289378
function CslProcessor:cite (item)
290379
local key = item.key
@@ -311,13 +400,14 @@ end
311400

312401
--- Cite multiple entries with optional locators.
313402
-- Usage example:
314-
-- ```lua
315-
-- local cites = biblio:cites({
316-
-- { key = 'mykey1', page = "191" },
317-
-- { key = 'mykey2', chapter = "2" },
318-
-- { key = 'mykey3' },
319-
-- })
320-
-- @tparam table items List of citation items, each with a key and optional locator
403+
--
404+
-- local cites = biblio:cites({
405+
-- { key = 'mykey1', page = "191" },
406+
-- { key = 'mykey2', chapter = "2" },
407+
-- { key = 'mykey3' },
408+
-- })
409+
--
410+
-- @tparam {key:string,[string]:string}[] items List of citation items, each with a key and optional locator
321411
-- @treturn string|nil Formatted citation string or nil if no entries are found
322412
function CslProcessor:cites (items)
323413
local is_single = #items == 1
@@ -343,10 +433,10 @@ function CslProcessor:cites (items)
343433
end
344434

345435
--- Retrieve a reference for a given key.
346-
-- This is used to get the full reference for an entry, e.g. for a bibliography.
347-
-- It will return the entry as a CSL item, with the citation number set.
436+
-- This is used to get the full reference for an entry, though this is more for debugging
437+
-- purposes than for actual bibliography processing.
348438
-- @tparam string key Citation key
349-
-- @treturn string[nul Formatted reference string or nil if the entry is not found
439+
-- @treturn string|nil Formatted reference string or nil if the entry is not found
350440
function CslProcessor:reference (key)
351441
local entry, citnum = self:_getEntryForCite(key, true) -- warn if not yet cited
352442
if entry then
@@ -357,11 +447,30 @@ function CslProcessor:reference (key)
357447
end
358448
end
359449

360-
--- Retrieve a bibliography of entries
361-
-- @tparam {cited=boolean} options Options for the bibliography
450+
--- Retrieve a bibliography of entries.
451+
-- Supported options are:
452+
--
453+
-- - `cited`: boolean, whether to include only cited entries (default: true)
454+
-- - `filter`: string, filters to apply to the entries, if cited is false.
455+
--
456+
-- The filter is a space-separated list of filter names.
457+
-- These can consist of named filters, or built-in filters.
458+
-- The list is understood as a logical AND, i.e. all filters must match.
459+
--
460+
-- Built-in filters are:
461+
--
462+
-- - `issued-Y`: entries issued in year Y
463+
-- - `issued-Y1-Y2`: entries issued between two years Y1 and Y2
464+
-- - `type-x`: entries of the given type (e.g. book, article-journal, etc.)
465+
-- - `not-type-x`: entries that are not of the given type
466+
-- - `keyword-x`: entries that have the given keyword
467+
-- - `not-keyword-x`: entries that do not have the given keyword
468+
--
469+
-- @tparam {cited=boolean,filter=string} options Options for the bibliography
362470
-- @treturn string Formatted bibliography string
363471
function CslProcessor:bibliography (options)
364472
local bib
473+
local filter = options.filter
365474
if SU.boolean(options.cited, true) then
366475
bib = {}
367476
for _, key in ipairs(self._data.cited.keys) do
@@ -397,7 +506,10 @@ function CslProcessor:bibliography (options)
397506
citnum = prevcite.citnum
398507
end
399508
local cslentry = self:_adapter(entry, citnum)
400-
table.insert(entries, cslentry)
509+
local isFiltered = not filter or self:applyFilter(cslentry, filter)
510+
if isFiltered then
511+
table.insert(entries, cslentry)
512+
end
401513
end
402514
end
403515
end
@@ -410,12 +522,26 @@ function CslProcessor:bibliography (options)
410522
return cite
411523
end
412524

413-
--- Load a bibliography file and parse it.
414-
-- @tparam string bibfile Path to the BibTeX file to load
415-
-- @treturn nil
416-
function CslProcessor:loadBibliography (bibfile)
417-
local bib = self._data.bib
418-
parseBibtex(bibfile, bib)
525+
--- Define a named filter for the CSL processor.
526+
-- @tparam string name Name of the filter
527+
-- @tparam function filterFn Function taking a CSL entry and returning true if the filter matches
528+
function CslProcessor:defineFilter(name, filterFn)
529+
self._filter[name] = filterFn
530+
end
531+
532+
--- Apply a filter to a CSL entry.
533+
-- It will check if the entry matches the filter by calling the filter function.
534+
-- @tparam CslEntry entry CSL entry to filter
535+
-- @tparam string names Names of the filters to apply (separated by spaces)
536+
function CslProcessor:applyFilter(entry, names)
537+
local filters = pl.stringx.split(names, " "):filter(function (s) return s ~= "" end)
538+
for _, f in ipairs(filters) do
539+
local filterFn = self._filter[f] or builtinFilter(f)
540+
if not filterFn(entry) then
541+
return false
542+
end
543+
end
544+
return true
419545
end
420546

421547
local bibTagsToHtml = {
@@ -431,10 +557,12 @@ local function biblink (url, _, class)
431557
end
432558

433559
--- Convert a formatted bibliography to HTML format.
434-
-- NOTE: Very naive implementation for now :)
560+
--
561+
-- **NOTE**: Very naive implementation for now. Private, may change in the future.
562+
--
435563
-- @tparam string out The bibliography output as a string
436564
-- @treturn string HTML formatted bibliography
437-
function CslProcessor:toHtml (out)
565+
function CslProcessor:_toHtml (out)
438566
-- Replace custom tags with HTML equivalents
439567
for tag, html in pairs(bibTagsToHtml) do
440568
local openTag = "<" .. tag .. ">"

packages/bibtex/init.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,8 +300,12 @@ Any other element triggers an error, and any text content is silently ignored.
300300
301301
To produce a bibliography of cited references, use \autodoc:command{\printbibliography}.
302302
After printing the bibliography, the list of cited entries will be cleared. This allows you to start fresh for subsequent uses (e.g., in a different chapter).
303+
303304
If you want to include all entries in the bibliography, not just those that have been cited, set the option \autodoc:parameter{cited} to false.
304305
306+
In that case, the \autodoc:parameter{filter} option can be used to filter the entries to be included in the bibliography.
307+
It accepts list of space-separated filters, such as \code{type-book} or \code{not-type-book}, or \code{keyword-foo} or \code{not-keyword-foo}, \code{year-2020} or \code{year-2023-2025}.
308+
305309
To produce a bibliographic reference, use \autodoc:command{\reference{<key>}}.
306310
Note that this command is not intended for actual use, but for testing purposes.
307311
It may be removed in the future.

0 commit comments

Comments
 (0)