diff --git a/packages/indexer/init.lua b/packages/indexer/init.lua index 49ddad548..d8e35e2f7 100644 --- a/packages/indexer/init.lua +++ b/packages/indexer/init.lua @@ -3,19 +3,30 @@ local base = require("packages.base") local package = pl.class(base) package._name = "indexer" -local function isNotSamePage (p1, p2) +if not SILE.scratch.pdf_destination_counter then + SILE.scratch.pdf_destination_counter = 1 +end + +-- Check if page p2 is not the same as previous page p1. +-- @tparam table p1 A page counter value or nil (if no previous page yet). +-- @tparam table p2 A page counter value. +-- @treturn boolean True if p2 is not the same as p1. +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) +-- Group pages into ranges of consecutive pages. +-- @tparam table pages A list of pages with pageno and link fields. +-- @treturn table A list of ranges, each containing a list of pages. +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 + or ret[#ret][#ret[#ret]].pageno.display ~= page.pageno.display + or ret[#ret][#ret[#ret]].pageno.value + 1 ~= page.pageno.value then table.insert(ret, { page }) else @@ -25,6 +36,33 @@ local function groupPageRanges(pages) return ret end +-- Wrap content in a link if a destination is provided. +-- @tparam string dest The destination name. +-- @tparam string page The page number. +-- @treturn table The content AST, possibly wrapped in a link. +local function _linkWrapper (dest, page) + if dest and SILE.Commands["pdf:link"] then + return SU.ast.createCommand("pdf:link", { dest = dest }, page) + end + return page +end + +-- Add a delimiter between elements of a table. +-- @tparam table t A list of elements. +-- @tparam string sep The delimiter. +-- @treturn table A new list with the delimiter inserted between elements. +local function _addDelimiter (t, sep) + local ret = {} + for i = 1, #t - 1 do + table.insert(ret, t[i]) + table.insert(ret, sep) + end + if #t > 0 then + table.insert(ret, t[#t]) + end + return ret +end + function package.buildIndex () local nodes = SILE.scratch.info.thispage.index local pageno = pl.tablex.copy(SILE.scratch.counters.folio) @@ -40,8 +78,8 @@ function package.buildIndex () index[node.label] = {} end local pages = index[node.label] - if not #pages or isNotSamePage(pages[#pages], pageno) then - table.insert(pages, pageno) + if not #pages or _isNotSamePage(pages[#pages], pageno) then + table.insert(pages, { pageno = pageno, link = node.link }) end end end @@ -62,17 +100,18 @@ end function package:formatPageRanges (pages) local ranges = {} - for _, range in ipairs(groupPageRanges(pages)) do + for _, range in ipairs(_groupPageRanges(pages)) do if #range == 1 then - table.insert(ranges, self.class.packages.counters:formatCounter(range[1])) + table.insert(ranges, _linkWrapper(range[1].link, self.class.packages.counters:formatCounter(range[1].pageno))) else - table.insert(ranges, - self.class.packages.counters:formatCounter(range[1]) - .. self.config['page-range-delimiter'] - .. self.class.packages.counters:formatCounter(range[#range])) + table.insert(ranges, { + _linkWrapper(range[1].link, self.class.packages.counters:formatCounter(range[1].pageno)), + self.config['page-range-delimiter'], + _linkWrapper(range[#range].link, self.class.packages.counters:formatCounter(range[#range].pageno)) + }) end end - return table.concat(ranges, self.config['page-delimiter']) + return _addDelimiter(ranges, self.config['page-delimiter']) end function package:formatPages (pages) @@ -80,9 +119,9 @@ function package:formatPages (pages) return self:formatPageRanges(pages) end local ret = pl.tablex.map(function (page) - return self.class.packages.counters:formatCounter(page) + return _linkWrapper(page.link, self.class.packages.counters:formatCounter(page.pageno)) end, pages) - return table.concat(ret, self.config['page-delimiter']) + return _addDelimiter(ret, self.config['page-delimiter']) end function package:registerCommands () @@ -102,8 +141,21 @@ function package:registerCommands () if not options.index then options.index = "main" end - SILE.call("info", { category = "index", value = { index = options.index, label = options.label } }) - end) + local dest + if SILE.Commands["pdf:destination"] then + dest = "dest" .. tostring(SILE.scratch.pdf_destination_counter) + SILE.call("pdf:destination", { name = dest }) + SILE.scratch.pdf_destination_counter = SILE.scratch.pdf_destination_counter + 1 + end + SILE.call("info", { + category = "index", + value = { + index = options.index, + label = options.label, + link = dest + } + }) + end, "Add an entry to the index") self:registerCommand("printindex", function (options, _) if not options.index then @@ -120,19 +172,20 @@ function package:registerCommands () local pageno = self:formatPages(index[k]) SILE.call("index:item", { pageno = pageno }, { k }) end - end) + end, "Print the index") self:registerCommand("index:item", function (options, content) + -- Unconventional: options.pageno is an AST SILE.settings:temporarily(function () SILE.settings:set("typesetter.parfillskip", SILE.types.node.glue()) SILE.settings:set("current.parindent", SILE.types.node.glue()) SILE.call("code", {}, content) -- Ideally, leaders SILE.call("hss") - SILE.typesetter:typeset(options.pageno) + SILE.process(options.pageno) SILE.call("smallskip") end) - end) + end, "Output an index item") end package.documentation = [[