Skip to content

Commit

Permalink
feat(packages): Support links to pages in the indexer
Browse files Browse the repository at this point in the history
  • Loading branch information
Omikhleia authored and Didier Willis committed Jan 5, 2025
1 parent a84d4ef commit e262509
Showing 1 changed file with 73 additions and 20 deletions.
93 changes: 73 additions & 20 deletions packages/indexer/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -62,27 +100,28 @@ 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)
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)
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 ()
Expand All @@ -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
Expand All @@ -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 = [[
Expand Down

0 comments on commit e262509

Please sign in to comment.