strc-bkm.lua /size: 17 Kb    last modification: 2020-07-01 14:35
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
strc-bkm
'
]
=
{
2
version
=
0
.
200
,
3
comment
=
"
companion to strc-bkm.mkiv
"
,
4
author
=
"
Hans Hagen, PRAGMA-ADE, Hasselt NL
"
,
5
copyright
=
"
PRAGMA ADE / ConTeXt Development Team
"
,
6
license
=
"
see context related readme files
"
7
}
8 9
-- Future version will support adding arbitrary bookmarks with
10
-- associated complex actions (rather trivial to implement).
11 12
-- this should become proper separated backend code
13 14
-- we should hook the placement into everystoptext ... needs checking
15 16
-- todo: make an lpeg for stripped
17 18
local
next
,
type
=
next
,
type
19
local
gsub
,
lower
=
string
.
gsub
,
string
.
lower
20
local
concat
=
table
.
concat
21
local
settings_to_hash
=
utilities
.
parsers
.
settings_to_hash
22 23
local
trace_bookmarks
=
false
trackers
.
register
(
"
references.bookmarks
"
,
function
(
v
)
trace_bookmarks
=
v
end
)
24
local
report_bookmarks
=
logs
.
reporter
(
"
structure
"
,
"
bookmarks
"
)
25 26
local
structures
=
structures
27 28
structures
.
bookmarks
=
structures
.
bookmarks
or
{
}
29 30
local
bookmarks
=
structures
.
bookmarks
31
local
sections
=
structures
.
sections
32
local
lists
=
structures
.
lists
33
local
levelmap
=
sections
.
levelmap
34
local
variables
=
interfaces
.
variables
35
local
implement
=
interfaces
.
implement
36
local
codeinjections
=
backends
.
codeinjections
37 38
bookmarks
.
method
=
"
internal
"
-- or "page"
39 40
local
names
=
{
}
41
local
opened
=
{
}
42
local
forced
=
{
}
43
local
numbered
=
{
}
44 45
function
bookmarks
.
setopened
(
key
,
value
)
46
if
value
=
=
nil
then
47
value
=
true
48
end
49
if
type
(
key
)
=
=
"
table
"
then
50
for
i
=
1
,
#
key
do
51
opened
[
key
[
i
]
]
=
value
52
end
53
else
54
opened
[
key
]
=
value
55
end
56
end
57 58
function
bookmarks
.
register
(
settings
)
59
local
force
=
settings
.
force
=
=
variables
.
yes
60
local
number
=
settings
.
number
=
=
variables
.
yes
61
local
allopen
=
settings
.
opened
=
=
variables
.
all
62
for
k
,
v
in
next
,
settings_to_hash
(
settings
.
names
or
"
"
)
do
63
names
[
k
]
=
true
64
if
force
then
65
forced
[
k
]
=
true
66
if
allopen
then
67
opened
[
k
]
=
true
68
end
69
end
70
if
number
then
71
numbered
[
k
]
=
true
72
end
73
end
74
if
not
allopen
then
75
for
k
,
v
in
next
,
settings_to_hash
(
settings
.
opened
or
"
"
)
do
76
opened
[
k
]
=
true
77
end
78
end
79
end
80 81
function
bookmarks
.
overload
(
name
,
text
)
82
local
l
,
ls
=
lists
.
tobesaved
,
nil
83
if
#
l
=
=
0
then
84
-- no entries
85
elseif
name
=
=
"
"
then
86
ls
=
l
[
#
l
]
87
else
88
for
i
=
#
l
,
0
,
-1
do
89
local
li
=
l
[
i
]
90
local
metadata
=
li
.
metadata
91
if
metadata
and
not
metadata
.
nolist
and
metadata
.
name
=
=
name
then
92
ls
=
li
93
break
94
end
95
end
96
end
97
if
ls
then
98
local
titledata
=
ls
.
titledata
99
if
titledata
then
100
titledata
.
bookmark
=
text
101
end
102
end
103
-- last resort
104
-- context.writetolist({name},text,"")
105
end
106 107
local
function
stripped
(
str
)
-- kind of generic
108
str
=
gsub
(
str
,
"
\\([A-Z]+)
"
,
"
%1
"
)
-- \LOGO
109
str
=
gsub
(
str
,
"
\\
"
,
"
"
)
-- \
110
str
=
gsub
(
str
,
"
\\([A-Za-z]+) *{(.-)}
"
,
"
%2
"
)
-- \bla{...}
111
str
=
gsub
(
str
,
"
+
"
,
"
"
)
-- spaces
112
return
str
113
end
114 115
-- todo: collect specs and collect later i.e. multiple places
116 117
local
numberspec
=
{
}
118 119
function
bookmarks
.
setup
(
spec
)
120
-- table.merge(numberspec,spec)
121
for
k
,
v
in
next
,
spec
do
122
numberspec
[
k
]
=
v
123
end
124
end
125 126
function
bookmarks
.
place
(
)
127
if
next
(
names
)
then
128
local
levels
=
{
}
129
local
noflevels
=
0
130
local
lastlevel
=
1
131
local
nofblocks
=
#
lists
.
sectionblocks
-- always >= 1
132
local
showblocktitle
=
toboolean
(
numberspec
.
showblocktitle
,
true
)
133
-- local allsections = sections.collected
134
local
allblocks
=
sections
.
sectionblockdata
135
for
i
=
1
,
nofblocks
do
136
local
block
=
lists
.
sectionblocks
[
i
]
137
local
blockdone
=
nofblocks
=
=
1
138
local
list
=
lists
.
filter
{
139
names
=
names
,
140
criterium
=
block
.
.
"
:all
"
,
141
forced
=
forced
,
142
}
143
for
i
=
1
,
#
list
do
144
local
li
=
list
[
i
]
145
local
metadata
=
li
.
metadata
146
local
name
=
metadata
.
name
147
if
not
metadata
.
nolist
or
forced
[
name
]
then
-- and levelmap[name] then
148
local
titledata
=
li
.
titledata
149
--
150
if
not
titledata
then
151
local
userdata
=
li
.
userdata
152
if
userdata
then
153
local
first
=
userdata
.
first
154
local
second
=
userdata
.
second
155
if
first
then
156
if
second
then
157
titledata
=
{
title
=
first
.
.
"
"
.
.
second
}
158
else
159
titledata
=
{
title
=
first
}
160
end
161
elseif
second
then
162
titledata
=
{
title
=
second
}
163
else
164
-- ignoring (command and so)
165
end
166
end
167
end
168
--
169
if
titledata
then
170
if
not
blockdone
then
171
if
showblocktitle
then
172
-- add block entry
173
local
blockdata
=
allblocks
[
block
]
174
local
references
=
li
.
references
175
noflevels
=
noflevels
+
1
176
levels
[
noflevels
]
=
{
177
level
=
1
,
-- toplevel
178
title
=
stripped
(
blockdata
.
bookmark
~
=
"
"
and
blockdata
.
bookmark
or
block
)
,
179
reference
=
references
,
180
opened
=
allopen
or
opened
[
name
]
,
-- same as first entry
181
realpage
=
references
and
references
.
realpage
or
0
,
-- handy for later
182
usedpage
=
true
,
183
}
184
end
185
blockdone
=
true
186
end
187
local
structural
=
levelmap
[
name
]
188
lastlevel
=
structural
or
lastlevel
189
if
nofblocks
>
1
then
190
-- we have a block so increase the level
191
lastlevel
=
lastlevel
+
1
192
end
193
local
title
=
titledata
.
bookmark
194
if
not
title
or
title
=
=
"
"
then
195
-- We could typeset the title and then convert it.
196
-- if not structural then
197
-- title = titledata.title or "?")
198
-- else
199
title
=
titledata
.
title
or
"
?
"
200
-- end
201
end
202
-- if numbered[name] then
203
-- local sectiondata = allsections[li.references.section]
204
-- if sectiondata then
205
-- local numberdata = li.numberdata
206
-- if numberdata and not numberdata.hidenumber then
207
-- -- we could typeset the number and convert it
208
-- local number = sections.typesetnumber(sectiondata,"direct",numberspec,sectiondata)
209
-- if number and #number > 0 then
210
-- title = concat(number) .. " " .. title
211
-- end
212
-- end
213
-- end
214
-- end
215
if
numbered
[
name
]
then
216
local
numberdata
=
li
.
numberdata
217
if
numberdata
and
not
numberdata
.
hidenumber
then
218
-- we could typeset the number and convert it
219
local
number
=
sections
.
typesetnumber
(
numberdata
,
"
direct
"
,
numberspec
,
numberdata
)
220
if
number
and
#
number
>
0
then
221
title
=
concat
(
number
)
.
.
"
"
.
.
title
222
end
223
end
224
end
225
noflevels
=
noflevels
+
1
226
local
references
=
li
.
references
227
levels
[
noflevels
]
=
{
228
level
=
lastlevel
,
229
title
=
stripped
(
title
)
,
-- can be replaced by converter
230
reference
=
references
,
-- has internal and realpage
231
opened
=
allopen
or
opened
[
name
]
,
232
realpage
=
references
and
references
.
realpage
or
0
,
-- handy for later
233
usedpage
=
true
,
234
structural
=
structural
,
235
name
=
name
,
236
}
237
end
238
end
239
end
240
end
241
-- inspect(levels)
242
bookmarks
.
finalize
(
levels
)
243
function
bookmarks
.
place
(
)
end
-- prevent second run
244
end
245
end
246 247
function
bookmarks
.
flatten
(
levels
)
248
if
not
levels
then
249
-- a plugin messed up
250
return
{
}
251
end
252
-- This function promotes leading structurelements with a higher level
253
-- to the next lower level. Such situations are the result of lack of
254
-- structure: a subject preceding a chapter in a sectionblock. So, the
255
-- following code runs over section blocks as well. (bookmarks-007.tex)
256
local
noflevels
=
#
levels
257
if
noflevels
>
1
then
258
local
function
showthem
(
)
259
for
i
=
1
,
noflevels
do
260
local
level
=
levels
[
i
]
261
-- if level.structural then
262
-- report_bookmarks("%i > %s > %s",level.level,level.reference.block,level.title)
263
-- else
264
report_bookmarks
(
"
%i > %s > %s > %s
"
,
level
.
level
,
level
.
reference
.
block
,
level
.
name
,
level
.
title
)
265
-- end
266
end
267
end
268
if
trace_bookmarks
then
269
report_bookmarks
(
"
checking structure
"
)
270
showthem
(
)
271
end
272
local
skip
=
false
273
local
done
=
0
274
local
start
=
1
275
local
one
=
levels
[
1
]
276
local
first
=
one
.
level
277
local
block
=
one
.
reference
.
block
278
for
i
=
2
,
noflevels
do
279
local
current
=
levels
[
i
]
280
local
new
=
current
.
level
281
local
reference
=
current
.
reference
282
local
newblock
=
type
(
reference
)
=
=
"
table
"
and
current
.
reference
.
block
or
block
283
if
newblock
~
=
block
then
284
first
=
new
285
block
=
newblock
286
start
=
i
287
skip
=
false
288
elseif
skip
then
289
-- go on
290
elseif
new
>
first
then
291
skip
=
true
292
elseif
new
<
first
then
293
for
j
=
start
,
i
-1
do
294
local
previous
=
levels
[
j
]
295
local
old
=
previous
.
level
296
previous
.
level
=
new
297
if
trace_bookmarks
then
298
report_bookmarks
(
"
promoting entry %a from level %a to %a: %s
"
,
j
,
old
,
new
,
previous
.
title
)
299
end
300
done
=
done
+
1
301
end
302
skip
=
true
303
end
304
end
305
if
trace_bookmarks
then
306
if
done
>
0
then
307
report_bookmarks
(
"
%a entries promoted
"
)
308
showthem
(
)
309
else
310
report_bookmarks
(
"
nothing promoted
"
)
311
end
312
end
313
end
314
return
levels
315
end
316 317
local
extras
=
{
}
318
local
lists
=
{
}
319
local
names
=
{
}
320 321
bookmarks
.
extras
=
extras
322 323
local
function
cleanname
(
name
)
324
return
lower
(
file
.
basename
(
name
)
)
325
end
326 327
function
extras
.
register
(
name
,
levels
)
328
if
name
and
levels
then
329
name
=
cleanname
(
name
)
330
local
found
=
names
[
name
]
331
if
found
then
332
lists
[
found
]
.
levels
=
levels
333
else
334
lists
[
#
lists
+
1
]
=
{
335
name
=
name
,
336
levels
=
levels
,
337
}
338
names
[
name
]
=
#
lists
339
end
340
end
341
end
342 343
function
extras
.
get
(
name
)
344
if
name
then
345
local
found
=
names
[
cleanname
(
name
)
]
346
if
found
then
347
return
lists
[
found
]
.
levels
348
end
349
else
350
return
lists
351
end
352
end
353 354
function
extras
.
reset
(
name
)
355
local
l
,
n
=
{
}
,
{
}
356
if
name
then
357
name
=
cleanname
(
name
)
358
for
i
=
1
,
#
lists
do
359
local
li
=
lists
[
i
]
360
local
ln
=
li
.
name
361
if
name
=
=
ln
then
362
-- skip
363
else
364
local
m
=
#
l
+
1
365
l
[
m
]
=
li
366
n
[
ln
]
=
m
367
end
368
end
369
end
370
lists
,
names
=
l
,
n
371
end
372 373
local
function
checklists
(
)
374
for
i
=
1
,
#
lists
do
375
local
levels
=
lists
[
i
]
.
levels
376
for
j
=
1
,
#
levels
do
377
local
entry
=
levels
[
j
]
378
local
pageindex
=
entry
.
pageindex
379
if
pageindex
then
380
entry
.
reference
=
figures
.
getrealpage
(
pageindex
)
381
entry
.
pageindex
=
nil
382
end
383
end
384
end
385
end
386 387
function
extras
.
tosections
(
levels
)
388
local
sections
=
{
}
389
local
noflists
=
#
lists
390
for
i
=
1
,
noflists
do
391
local
levels
=
lists
[
i
]
.
levels
392
local
data
=
{
}
393
sections
[
i
]
=
data
394
for
j
=
1
,
#
levels
do
395
local
entry
=
levels
[
j
]
396
if
entry
.
usedpage
then
397
local
section
=
entry
.
section
398
local
d
=
data
[
section
]
399
if
d
then
400
d
[
#
d
+
1
]
=
entry
401
else
402
data
[
section
]
=
{
entry
}
403
end
404
end
405
end
406
end
407
return
sections
408
end
409 410
function
extras
.
mergesections
(
levels
,
sections
)
411
if
not
sections
or
#
sections
=
=
0
then
412
return
levels
413
elseif
not
levels
then
414
return
{
}
415
else
416
local
merge
=
{
}
417
local
noflists
=
#
lists
418
if
#
levels
=
=
0
then
419
local
level
=
0
420
local
section
=
0
421
for
i
=
1
,
noflists
do
422
local
entries
=
sections
[
i
]
[
0
]
423
if
entries
then
424
for
i
=
1
,
#
entries
do
425
local
entry
=
entries
[
i
]
426
merge
[
#
merge
+
1
]
=
entry
427
entry
.
level
=
entry
.
level
+
level
428
end
429
end
430
end
431
else
432
for
j
=
1
,
#
levels
do
433
local
entry
=
levels
[
j
]
434
merge
[
#
merge
+
1
]
=
entry
435
local
section
=
entry
.
reference
.
section
436
local
level
=
entry
.
level
437
entry
.
section
=
section
-- for tracing
438
for
i
=
1
,
noflists
do
439
local
entries
=
sections
[
i
]
[
section
]
440
if
entries
then
441
for
i
=
1
,
#
entries
do
442
local
entry
=
entries
[
i
]
443
merge
[
#
merge
+
1
]
=
entry
444
entry
.
level
=
entry
.
level
+
level
445
end
446
end
447
end
448
end
449
end
450
return
merge
451
end
452
end
453 454
function
bookmarks
.
merge
(
levels
,
mode
)
455
return
extras
.
mergesections
(
levels
,
extras
.
tosections
(
)
)
456
end
457 458
local
sequencers
=
utilities
.
sequencers
459
local
appendgroup
=
sequencers
.
appendgroup
460
local
appendaction
=
sequencers
.
appendaction
461 462
local
bookmarkactions
=
sequencers
.
new
{
463
arguments
=
"
levels,method
"
,
464
returnvalues
=
"
levels
"
,
465
results
=
"
levels
"
,
466
}
467 468
appendgroup
(
bookmarkactions
,
"
before
"
)
-- user
469
appendgroup
(
bookmarkactions
,
"
system
"
)
-- private
470
appendgroup
(
bookmarkactions
,
"
after
"
)
-- user
471 472
appendaction
(
bookmarkactions
,
"
system
"
,
bookmarks
.
flatten
)
473
appendaction
(
bookmarkactions
,
"
system
"
,
bookmarks
.
merge
)
474 475
function
bookmarks
.
finalize
(
levels
)
476
local
method
=
bookmarks
.
method
or
"
internal
"
477
checklists
(
)
-- so that plugins have the adapted page number
478
levels
=
bookmarkactions
.
runner
(
levels
,
method
)
479
if
levels
and
#
levels
>
0
then
480
-- normally this is not needed
481
local
purged
=
{
}
482
for
i
=
1
,
#
levels
do
483
local
l
=
levels
[
i
]
484
if
l
.
usedpage
~
=
false
then
485
purged
[
#
purged
+
1
]
=
l
486
end
487
end
488
--
489
codeinjections
.
addbookmarks
(
purged
,
method
)
490
else
491
-- maybe a plugin messed up
492
end
493
end
494 495
function
bookmarks
.
installhandler
(
what
,
where
,
func
)
496
if
not
func
then
497
where
,
func
=
"
after
"
,
where
498
end
499
if
where
=
=
"
before
"
or
where
=
=
"
after
"
then
500
sequencers
.
appendaction
(
bookmarkactions
,
where
,
func
)
501
else
502
report_tex
(
"
installing bookmark %a handlers in %a is not possible
"
,
what
,
tostring
(
where
)
)
503
end
504
end
505 506
-- interface
507 508
implement
{
509
name
=
"
setupbookmarks
"
,
510
actions
=
bookmarks
.
setup
,
511
arguments
=
{
512
{
513
{
"
separatorset
"
}
,
514
{
"
conversionset
"
}
,
515
{
"
starter
"
}
,
516
{
"
stopper
"
}
,
517
{
"
segments
"
}
,
518
{
"
showblocktitle
"
}
,
519
}
520
}
521
}
522 523
implement
{
524
name
=
"
registerbookmark
"
,
525
actions
=
bookmarks
.
register
,
526
arguments
=
{
527
{
528
{
"
names
"
}
,
529
{
"
opened
"
}
,
530
{
"
force
"
}
,
531
{
"
number
"
}
,
532
}
533
}
534
}
535 536
implement
{
537
name
=
"
overloadbookmark
"
,
538
actions
=
bookmarks
.
overload
,
539
arguments
=
"
2 strings
"
,
540
}
541