Skip to content

Commit 5953e59

Browse files
OmikhleiaDidier Willis
authored andcommitted
feat(packages): Support links to pages in the indexer
1 parent a84d4ef commit 5953e59

1 file changed

Lines changed: 75 additions & 20 deletions

File tree

packages/indexer/init.lua

Lines changed: 75 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,30 @@ local base = require("packages.base")
33
local package = pl.class(base)
44
package._name = "indexer"
55

6-
local function isNotSamePage (p1, p2)
6+
if not SILE.scratch.pdf_destination_counter then
7+
SILE.scratch.pdf_destination_counter = 1
8+
end
9+
10+
-- Check if page p2 is not the same as previous page p1.
11+
-- @tparam table p1 A page counter value or nil (if no previous page yet).
12+
-- @tparam table p2 A page counter value.
13+
-- @treturn boolean True if p2 is not the same as p1.
14+
local function _isNotSamePage (p1, p2)
715
if not p1 then
816
return true
917
end
1018
return p1.display ~= p2.display or p1.value ~= p2.value
1119
end
1220

13-
local function groupPageRanges(pages)
21+
-- Group pages into ranges of consecutive pages.
22+
-- @tparam table pages A list of pages with pageno and link fields.
23+
-- @treturn table A list of ranges, each containing a list of pages.
24+
local function _groupPageRanges(pages)
1425
local ret = {}
1526
for _, page in ipairs(pages) do
1627
if #ret == 0
17-
or ret[#ret][#ret[#ret]].display ~= page.display
18-
or ret[#ret][#ret[#ret]].value + 1 ~= page.value
28+
or ret[#ret][#ret[#ret]].pageno.display ~= page.pageno.display
29+
or ret[#ret][#ret[#ret]].pageno.value + 1 ~= page.pageno.value
1930
then
2031
table.insert(ret, { page })
2132
else
@@ -25,6 +36,33 @@ local function groupPageRanges(pages)
2536
return ret
2637
end
2738

39+
-- Wrap content in a link if a destination is provided.
40+
-- @tparam string dest The destination name.
41+
-- @tparam string page The page number.
42+
-- @treturn table The content AST, possibly wrapped in a link.
43+
local function _linkWrapper (dest, page)
44+
if dest and SILE.Commands["pdf:link"] then
45+
return SU.ast.createCommand("pdf:link", { dest = dest }, page)
46+
end
47+
return page
48+
end
49+
50+
-- Add a delimiter between elements of a table.
51+
-- @tparam table t A list of elements.
52+
-- @tparam string sep The delimiter.
53+
-- @treturn table A new list with the delimiter inserted between elements.
54+
local function _addDelimiter (t, sep)
55+
local ret = {}
56+
for i = 1, #t - 1 do
57+
table.insert(ret, t[i])
58+
table.insert(ret, sep)
59+
end
60+
if #t > 0 then
61+
table.insert(ret, t[#t])
62+
end
63+
return ret
64+
end
65+
2866
function package.buildIndex ()
2967
local nodes = SILE.scratch.info.thispage.index
3068
local pageno = pl.tablex.copy(SILE.scratch.counters.folio)
@@ -40,8 +78,8 @@ function package.buildIndex ()
4078
index[node.label] = {}
4179
end
4280
local pages = index[node.label]
43-
if not #pages or isNotSamePage(pages[#pages], pageno) then
44-
table.insert(pages, pageno)
81+
if not #pages or _isNotSamePage(pages[#pages], pageno) then
82+
table.insert(pages, { pageno = pageno, link = node.link })
4583
end
4684
end
4785
end
@@ -62,27 +100,28 @@ end
62100

63101
function package:formatPageRanges (pages)
64102
local ranges = {}
65-
for _, range in ipairs(groupPageRanges(pages)) do
103+
for _, range in ipairs(_groupPageRanges(pages)) do
66104
if #range == 1 then
67-
table.insert(ranges, self.class.packages.counters:formatCounter(range[1]))
105+
table.insert(ranges, _linkWrapper(range[1].link, self.class.packages.counters:formatCounter(range[1].pageno)))
68106
else
69-
table.insert(ranges,
70-
self.class.packages.counters:formatCounter(range[1])
71-
.. self.config['page-range-delimiter']
72-
.. self.class.packages.counters:formatCounter(range[#range]))
107+
table.insert(ranges, {
108+
_linkWrapper(range[1].link, self.class.packages.counters:formatCounter(range[1].pageno)),
109+
self.config['page-range-delimiter'],
110+
_linkWrapper(range[#range].link, self.class.packages.counters:formatCounter(range[#range].pageno))
111+
})
73112
end
74113
end
75-
return table.concat(ranges, self.config['page-delimiter'])
114+
return _addDelimiter(ranges, self.config['page-delimiter'])
76115
end
77116

78117
function package:formatPages (pages)
79118
if self.config['page-range-format'] ~= 'none' then
80119
return self:formatPageRanges(pages)
81120
end
82121
local ret = pl.tablex.map(function (page)
83-
return self.class.packages.counters:formatCounter(page)
122+
return _linkWrapper(page.link, self.class.packages.counters:formatCounter(page.pageno))
84123
end, pages)
85-
return table.concat(ret, self.config['page-delimiter'])
124+
return _addDelimiter(ret, self.config['page-delimiter'])
86125
end
87126

88127
function package:registerCommands ()
@@ -102,8 +141,21 @@ function package:registerCommands ()
102141
if not options.index then
103142
options.index = "main"
104143
end
105-
SILE.call("info", { category = "index", value = { index = options.index, label = options.label } })
106-
end)
144+
local dest
145+
if SILE.Commands["pdf:destination"] then
146+
dest = "dest" .. tostring(SILE.scratch.pdf_destination_counter)
147+
SILE.call("pdf:destination", { name = dest })
148+
SILE.scratch.pdf_destination_counter = SILE.scratch.pdf_destination_counter + 1
149+
end
150+
SILE.call("info", {
151+
category = "index",
152+
value = {
153+
index = options.index,
154+
label = options.label,
155+
link = dest
156+
}
157+
})
158+
end, "Add an entry to the index")
107159

108160
self:registerCommand("printindex", function (options, _)
109161
if not options.index then
@@ -120,19 +172,20 @@ function package:registerCommands ()
120172
local pageno = self:formatPages(index[k])
121173
SILE.call("index:item", { pageno = pageno }, { k })
122174
end
123-
end)
175+
end, "Print the index")
124176

125177
self:registerCommand("index:item", function (options, content)
178+
-- Unconventional: options.pageno is an AST
126179
SILE.settings:temporarily(function ()
127180
SILE.settings:set("typesetter.parfillskip", SILE.types.node.glue())
128181
SILE.settings:set("current.parindent", SILE.types.node.glue())
129182
SILE.call("code", {}, content)
130183
-- Ideally, leaders
131184
SILE.call("hss")
132-
SILE.typesetter:typeset(options.pageno)
185+
SILE.process(options.pageno)
133186
SILE.call("smallskip")
134187
end)
135-
end)
188+
end, "Output an index item")
136189
end
137190

138191
package.documentation = [[
@@ -153,6 +206,8 @@ The entry can be styled using the \autodoc:command{\index:item} command.
153206
154207
Multiple indexes are available and an index can be selected by passing the \autodoc:parameter{index=<name>} parameter to \autodoc:command{\indexentry} and \autodoc:command{\printindex}.
155208
209+
If the \autodoc:package{pdf} package is loaded, then pages in the index will be hyperlinked to the relevant references.
210+
156211
\end{document}
157212
]]
158213

0 commit comments

Comments
 (0)