1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
|
if not modules then modules = { } end modules ['strc-bkm'] = {
version = 0.200,
comment = "companion to strc-bkm.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright = "PRAGMA ADE / ConTeXt Development Team",
license = "see context related readme files"
}
-- Future version will support adding arbitrary bookmarks with
-- associated complex actions (rather trivial to implement).
-- this should become proper separated backend code
-- we should hook the placement into everystoptext ... needs checking
local format, concat, gsub = string.format, table.concat, string.gsub
local utfvalues = utf.values
local settings_to_hash = utilities.parsers.settings_to_hash
local codeinjections = backends.codeinjections
local trace_bookmarks = false trackers.register("references.bookmarks", function(v) trace_bookmarks = v end)
local report_bookmarks = logs.reporter("structure","bookmarks")
local structures = structures
structures.bookmarks = structures.bookmarks or { }
local bookmarks = structures.bookmarks
local sections = structures.sections
local lists = structures.lists
local levelmap = sections.levelmap
local variables = interfaces.variables
bookmarks.method = "internal" -- or "page"
local names, opened, forced, numbered = { }, { }, { }, { }
function bookmarks.register(settings)
local force = settings.force == variables.yes
local number = settings.number == variables.yes
local allopen = settings.opened == variables.all
for k, v in next, settings_to_hash(settings.names or "") do
names[k] = true
if force then
forced[k] = true
if allopen then
opened[k] = true
end
end
if number then
numbered[k] = true
end
end
if not allopen then
for k, v in next, settings_to_hash(settings.opened or "") do
opened[k] = true
end
end
end
function bookmarks.overload(name,text)
local l, ls = lists.tobesaved, nil
if #l == 0 then
-- no entries
elseif name == "" then
ls = l[#l]
else
for i=#l,0,-1 do
local li = l[i]
local metadata = li.metadata
if metadata and not metadata.nolist and metadata.name == name then
ls = li
break
end
end
end
if ls then
ls.titledata.bookmark = text
end
end
local function stripped(str) -- kind of generic
str = gsub(str,"\\([A-Z]+)","%1") -- \LOGO
str = gsub(str,"\\ "," ") -- \
str = gsub(str,"\\([A-Za-z]+) *{(.-)}","%1") -- \bla{...}
str = gsub(str," +"," ") -- spaces
return str
end
-- todo: collect specs and collect later i.e. multiple places
local numberspec = { }
function bookmarks.setup(spec)
-- table.merge(numberspec,spec)
for k, v in next, spec do
numberspec[k] = v
end
end
function bookmarks.place()
if next(names) then
local list = lists.filtercollected(names,"all",nil,lists.collected,forced)
if #list > 0 then
local levels, noflevels, lastlevel = { }, 0, 1
for i=1,#list do
local li = list[i]
local metadata = li.metadata
local name = metadata.name
if not metadata.nolist or forced[name] then -- and levelmap[name] then
local titledata = li.titledata
if titledata then
local structural = levelmap[name]
lastlevel = structural or lastlevel
local title = titledata.bookmark
if not title or title == "" then
-- We could typeset the title and then convert it.
if not structural then
-- placeholder, todo: bookmarklabel
title = name .. ": " .. (titledata.title or "?")
else
title = titledata.title or "?"
end
end
if numbered[name] then
local sectiondata = sections.collected[li.references.section]
local numberdata = li.numberdata
if sectiondata and numberdata and not numberdata.hidenumber then
-- we could typeset the number and convert it
title = concat(sections.typesetnumber(sectiondata,"direct",numberspec,sectiondata)) .. " " .. title
end
end
noflevels = noflevels + 1
levels[noflevels] = {
lastlevel,
stripped(title), -- can be replaced by converter
li.references, -- has internal and realpage
allopen or opened[name]
}
end
end
end
bookmarks.finalize(levels)
end
function bookmarks.place() end -- prevent second run
end
end
function bookmarks.flatten(levels)
-- This function promotes leading structurelements with a higher level
-- to the next lower level. Such situations are the result of lack of
-- structure: a subject preceding a chapter in a sectionblock. So, the
-- following code runs over section blocks as well. (bookmarks-007.tex)
local noflevels = #levels
if noflevels > 1 then
local skip, start, one = false, 1, levels[1]
local first, block = one[1], one[3].block
for i=2,noflevels do
local li = levels[i]
local new, newblock = li[1], li[3].block
if newblock ~= block then
first, block, start, skip = new, newblock, i, false
elseif skip then
-- go on
elseif new > first then
skip = true
elseif new < first then
for j=start,i-1 do
local lj = levels[j]
local old = lj[1]
lj[1] = new
if trace_bookmarks then
report_bookmarks("promoting entry %a from level %a to %a: %s",j,old,new,lj[2])
end
end
skip = true
end
end
end
end
function bookmarks.finalize(levels)
-- This function can be overloaded by an optional converter
-- that uses nodes.toutf on a typeset stream. This is something
-- that we will support when the main loop has become a coroutine.
codeinjections.addbookmarks(levels,bookmarks.method)
end
-- interface
commands.overloadbookmark = bookmarks.overload
commands.registerbookmark = bookmarks.register
commands.setupbookmarks = bookmarks.setup
|