From 3f9a15c967aa152f78c4c48524f8e68eae879b5e Mon Sep 17 00:00:00 2001 From: Omikhleia Date: Thu, 2 Jan 2025 19:08:40 +0100 Subject: [PATCH] feat(packages): Support page ranges in indexer --- packages/indexer/init.lua | 80 ++++++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 9 deletions(-) diff --git a/packages/indexer/init.lua b/packages/indexer/init.lua index 5a31ac33c..c3d63c2ab 100644 --- a/packages/indexer/init.lua +++ b/packages/indexer/init.lua @@ -3,9 +3,31 @@ local base = require("packages.base") local package = pl.class(base) package._name = "indexer" -function package.buildIndex (class) -- Called from the class, not as a package method +local function isNotSamePage (p1, p2) + if not p1 then + return true + end + return p1.display ~= p2.display or p1.value ~= p2.value +end + +local function groupPageRanges(pages) + local ret = {} + for _, page in ipairs(pages) do + if #ret == 0 + or ret[#ret][#ret[#ret]].display ~= page.display + or ret[#ret][#ret[#ret]].value + 1 ~= page.value + then + table.insert(ret, { page }) + else + table.insert(ret[#ret], page) + end + end + return ret +end + +function package.buildIndex () local nodes = SILE.scratch.info.thispage.index - local thisPage = class.packages.counters:formatCounter(SILE.scratch.counters.folio) + local pageno = pl.tablex.copy(SILE.scratch.counters.folio) if not nodes then return end @@ -13,18 +35,24 @@ function package.buildIndex (class) -- Called from the class, not as a package m if not SILE.scratch.index[node.index] then SILE.scratch.index[node.index] = {} end - local thisIndex = SILE.scratch.index[node.index] - if not thisIndex[node.label] then - thisIndex[node.label] = {} + local index = SILE.scratch.index[node.index] + if not index[node.label] then + index[node.label] = {} end - if not #thisIndex[node.label] or (thisIndex[node.label])[#thisIndex[node.label]] ~= thisPage then - table.insert(thisIndex[node.label], thisPage) + local pages = index[node.label] + if not #pages or isNotSamePage(pages[#pages], pageno) then + table.insert(pages, pageno) end end end -function package:_init () +function package:_init (options) base._init(self) + self.config = pl.tablex.merge({ + ["page-range-format"] = "expanded", + ["page-range-delimiter"] = "–", + ["page-delimiter"] = ", " + }, options, true) self:loadPackage("infonode") self.class:registerHook("endpage", self.buildIndex) if not SILE.scratch.index then @@ -32,6 +60,31 @@ function package:_init () end end +function package:formatPageRanges (pages) + local ranges = {} + for _, range in ipairs(groupPageRanges(pages)) do + if #range == 1 then + table.insert(ranges, self.class.packages.counters:formatCounter(range[1])) + else + table.insert(ranges, + self.class.packages.counters:formatCounter(range[1]) + .. self.config['page-range-delimiter'] + .. self.class.packages.counters:formatCounter(range[#range])) + end + end + return table.concat(ranges, self.config['page-delimiter']) +end + +function package:formatPages (pages) + if self.config['page-range-format'] ~= 'none' then + return self:formatPageRanges(pages) + end + local ret = pl.tablex.map(function (page) + return self.class.packages.counters:formatCounter(page) + end, pages) + return table.concat(ret, self.config['page-delimiter']) +end + function package:registerCommands () self:registerCommand("indexentry", function (options, content) if not options.label then @@ -64,7 +117,7 @@ function package:registerCommands () SU.collatedSort(sortedIndex) SILE.call("bigskip") for _, k in ipairs(sortedIndex) do - local pageno = table.concat(index[k], ", ") + local pageno = self:formatPages(index[k]) SILE.call("index:item", { pageno = pageno }, { k }) end end) @@ -85,6 +138,15 @@ end package.documentation = [[ \begin{document} An index is essentially the same thing as a table of contents, but sorted. + +The package accepts several configuration options: +\begin{itemize} +\item{\autodoc:parameter{page-range-format}: The format of the page range. +Possible values are \autodoc:parameter{expanded} (default), \autodoc:parameter{none}.} +\item{\autodoc:parameter{page-range-delimiter}: The delimiter between the start and end of a page range.} +\item{\autodoc:parameter{page-delimiter}: The delimiter between pages.} +\end{itemize} + This package provides the \autodoc:command{\indexentry} command, which can be called as either \autodoc:command{\indexentry[label=]} or \autodoc:command{\indexentry{}} (so that it can be called from a macro). Index entries are collated at the end of each page, and the command \autodoc:command{\printindex} will deposit them in a list. The entry can be styled using the \autodoc:command{\index:item} command.