1if not modules then modules = { } end modules ['strc-doc'] = {
2 version = 1.001,
3 comment = "companion to strc-doc.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
10
11
12
13
14
15
16
17
18local next, type, tonumber, select = next, type, tonumber, select
19local find, match = string.find, string.match
20local concat, fastcopy, insert, remove = table.concat, table.fastcopy, table.insert, table.remove
21local sortedhash, sortedkeys = table.sortedhash, table.sortedkeys
22local max, min = math.max, math.min
23local allocate, mark, accesstable = utilities.storage.allocate, utilities.storage.mark, utilities.tables.accesstable
24local setmetatableindex = table.setmetatableindex
25local lpegmatch, P, C = lpeg.match, lpeg.P, lpeg.C
26
27local catcodenumbers = catcodes.numbers
28local ctxcatcodes = catcodenumbers.ctxcatcodes
29local variables = interfaces.variables
30
31local implement = interfaces.implement
32
33local v_last = variables.last
34local v_first = variables.first
35local v_previous = variables.previous
36local v_next = variables.next
37local v_auto = variables.auto
38local v_strict = variables.strict
39local v_all = variables.all
40local v_positive = variables.positive
41local v_current = variables.current
42
43local texgetcount = tex.getcount
44
45local trace_sectioning = false trackers.register("structures.sectioning", function(v) trace_sectioning = v end)
46local trace_details = false trackers.register("structures.details", function(v) trace_details = v end)
47
48local report_structure = logs.reporter("structure","sectioning")
49local report_used = logs.reporter("structure")
50
51local context = context
52local commands = commands
53local ctx_doifelse = commands.doifelse
54
55local structures = structures
56local helpers = structures.helpers
57local documents = structures.documents
58local sections = structures.sections
59local lists = structures.lists
60local counters = structures.counters
61local sets = structures.sets
62local tags = structures.tags
63
64local processors = typesetters.processors
65local applyprocessor = processors.apply
66local startapplyprocessor = processors.startapply
67local stopapplyprocessor = processors.stopapply
68local strippedprocessor = processors.stripped
69
70local convertnumber = converters.convert
71
72local ctx_convertnumber = context.convertnumber
73local ctx_sprint = context.sprint
74local ctx_finalizeauto = context.finalizeautostructurelevel
75
76
77
78local data
79
80function documents.initialize()
81 data = allocate {
82 numbers = { },
83 forced = { },
84 ownnumbers = { },
85 status = { },
86 checkers = { },
87 depth = 0,
88 blocks = { },
89 block = "",
90 }
91 documents.data = data
92end
93
94function documents.reset()
95 data.numbers = { }
96 data.forced = { }
97 data.ownnumbers = { }
98 data.status = { }
99
100 data.depth = 0
101end
102
103documents.initialize()
104
105
106
107function documents.preset(numbers)
108 local nofnumbers = #numbers
109 local ownnumbers = { }
110 data.numbers = numbers
111 data.ownnumbers = ownnumbers
112 data.depth = nofnumbers
113 for i=1,nofnumbers do
114 ownnumbers[i] = ""
115 end
116 sections.setnumber(nofnumbers,"-1")
117end
118
119
120
121
122
123
124
125local collected = allocate()
126local tobesaved = allocate()
127
128sections.collected = collected
129sections.tobesaved = tobesaved
130
131
132
133
134
135local function initializer()
136 collected = sections.collected
137 tobesaved = sections.tobesaved
138end
139
140job.register('structures.sections.collected', tobesaved, initializer)
141
142local registered = sections.registered or allocate()
143sections.registered = registered
144
145storage.register("structures/sections/registered", registered, "structures.sections.registered")
146
147local function update(name,level,section)
148 for k, v in next, registered do
149 if k ~= name and v.coupling == name then
150 report_structure("updating section level %a to level of %a",k,name)
151 context.doredefinehead(k,name)
152 update(k,level,section)
153 end
154 end
155end
156
157function sections.register(name,specification)
158 registered[name] = specification
159 local level = specification.level
160 local section = specification.section
161 update(name,level,section)
162end
163
164function sections.currentid()
165 return #tobesaved
166end
167
168local lastsaved = 0
169
170function sections.save(sectiondata)
171 local sectiondata = helpers.simplify(sectiondata)
172 local numberdata = sectiondata.numberdata
173 local ntobesaved = #tobesaved
174 if not numberdata or sectiondata.metadata.nolist then
175
176 else
177 ntobesaved = ntobesaved + 1
178 tobesaved[ntobesaved] = numberdata
179 if not collected[ntobesaved] then
180 collected[ntobesaved] = numberdata
181 end
182 end
183 lastsaved = ntobesaved
184 return ntobesaved
185end
186
187function sections.currentsectionindex()
188 return lastsaved
189end
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214sections.verbose = true
215
216local sectionblockdata = sections.sectionblockdata or { }
217sections.sectionblockdata = sectionblockdata
218
219local levelmap = sections.levelmap or { }
220sections.levelmap = levelmap
221levelmap.block = -1
222
223storage.register("structures/sections/levelmap", sections.levelmap, "structures.sections.levelmap")
224
225function sections.setlevel(name,level)
226 local l = tonumber(level)
227 if not l then
228 l = levelmap[level]
229 end
230 if l and l > 0 then
231 levelmap[name] = l
232 else
233
234 end
235end
236
237function sections.getlevel(name)
238 return levelmap[name] or 0
239end
240
241table.setmetatableindex(sectionblockdata,"table")
242
243function sections.setblock(name,settings)
244 local block = name or data.block or "unknown"
245 data.block = block
246 sectionblockdata[block] = settings
247 return block
248end
249
250local jobvariables = job.variables
251local pushed_order = { }
252local pushed_done = { }
253
254jobvariables.tobesaved.sectionblockorder = pushed_order
255
256
257
258
259
260function sections.setinitialblock(default)
261 local order = jobvariables.collected.sectionblockorder or pushed_order
262 local name = #order > 0 and order[1] or default or "bodypart"
263 context.setsectionblock { name }
264
265
266end
267
268function sections.pushblock(name,settings)
269 counters.check(0)
270 local block = name or data.block
271 insert(data.blocks,block)
272 data.block = block
273 sectionblockdata[block] = settings
274 documents.reset()
275 if not pushed_done[name] then
276 pushed_done[name] = true
277 local nofpushed = #pushed_order + 1
278 pushed_order[nofpushed] = name
279 end
280 return block
281end
282
283function sections.popblock()
284 local block = remove(data.blocks) or data.block
285 data.block = block
286 documents.reset()
287 return block
288end
289
290local function getcurrentblock()
291 return data.block or data.blocks[#data.blocks] or "unknown"
292end
293
294sections.currentblock = getcurrentblock
295
296function sections.currentlevel()
297 return data.depth
298end
299
300function sections.getcurrentlevel()
301 context(data.depth)
302end
303
304local saveset = { }
305
306function sections.setentry(given)
307
308 local numbers = data.numbers
309
310 local metadata = given.metadata
311 local numberdata = given.numberdata
312 local references = given.references
313 local directives = given.directives
314 local userdata = given.userdata
315
316 if not metadata then
317 metadata = { }
318 given.metadata = metadata
319 end
320 if not numberdata then
321 numberdata = { }
322 given.numberdata = numberdata
323 end
324 if not references then
325 references = { }
326 given.references = references
327 end
328
329 local ownnumbers = data.ownnumbers
330 local forced = data.forced
331 local status = data.status
332 local olddepth = data.depth
333 local givenname = metadata.name
334 local mappedlevel = levelmap[givenname]
335 local newdepth = tonumber(mappedlevel or (olddepth > 0 and olddepth) or 1)
336 local resetset = directives and directives.resetset or ""
337
338
339
340
341 if trace_details then
342 report_structure("name %a, mapped level %a, old depth %a, new depth %a, reset set %a",
343 givenname,mappedlevel,olddepth,newdepth,resetset)
344 end
345 if userdata then
346
347 if userdata.reference and userdata.reference ~= "" then given.metadata.reference = userdata.reference ; userdata.reference = nil end
348 if userdata.ownnumber and userdata.ownnumber ~= "" then given.numberdata.ownnumber = userdata.ownnumber ; userdata.ownnumber = nil end
349 if userdata.title and userdata.title ~= "" then given.titledata.title = userdata.title ; userdata.title = nil end
350 if userdata.bookmark and userdata.bookmark ~= "" then given.titledata.bookmark = userdata.bookmark ; userdata.bookmark = nil end
351 if userdata.label and userdata.label ~= "" then given.titledata.label = userdata.label ; userdata.label = nil end
352 end
353
354 if saveset then
355 saveset[newdepth] = (resetset ~= "" and resetset) or saveset[newdepth] or ""
356 end
357 if newdepth > olddepth then
358 for i=olddepth+1,newdepth do
359 local s = tonumber(sets.get("structure:resets",data.block,saveset and saveset[i] or resetset,i))
360 if trace_details then
361 report_structure("new depth %s, old depth %s, reset set %a, reset value %a, current %a",olddepth,newdepth,resetset,s,numbers[i])
362 end
363 if not s or s == 0 then
364 numbers[i] = numbers[i] or 0
365 ownnumbers[i] = ownnumbers[i] or ""
366 else
367 numbers[i] = s - 1
368 ownnumbers[i] = ""
369 end
370 status[i] = { }
371 end
372 elseif newdepth < olddepth then
373 for i=olddepth,newdepth+1,-1 do
374 local s = tonumber(sets.get("structure:resets",data.block,saveset and saveset[i] or resetset,i))
375 if trace_details then
376 report_structure("new depth %s, old depth %s, reset set %a, reset value %a, current %a",olddepth,newdepth,resetset,s,numbers[i])
377 end
378 if not s or s == 0 then
379 numbers[i] = numbers[i] or 0
380 ownnumbers[i] = ownnumbers[i] or ""
381 else
382 numbers[i] = s - 1
383 ownnumbers[i] = ""
384 end
385 status[i] = nil
386 end
387 end
388 counters.check(newdepth)
389 ownnumbers[newdepth] = numberdata.ownnumber or ""
390 numberdata.ownnumber = nil
391 data.depth = newdepth
392
393 olddepth = newdepth
394 if metadata.increment then
395 local oldn = numbers[newdepth] or 0
396 local newn = 0
397 local fd = forced[newdepth]
398 if fd then
399 if fd[1] == "add" then
400 newn = oldn + fd[2] + 1
401 else
402 newn = fd[2] + 1
403 end
404 if newn < 0 then
405 newn = 1
406 end
407 forced[newdepth] = nil
408 if trace_details then
409 report_structure("old depth %a, new depth %a, old n %a, new n %a, forced %t",olddepth,newdepth,oldn,newn,fd)
410 end
411 else
412 newn = oldn + 1
413 if trace_details then
414 report_structure("old depth %a, new depth %a, old n %a, new n %a, increment",olddepth,newdepth,oldn,newn)
415 end
416 end
417 numbers[newdepth] = newn
418 end
419 status[newdepth] = given or { }
420 for k, v in next, data.checkers do
421 if v[1] == newdepth and v[2] then
422 v[2](k)
423 end
424 end
425 numberdata.numbers = { unpack(numbers,1,newdepth) }
426 if not numberdata.block then
427 numberdata.block = getcurrentblock()
428 end
429 if #ownnumbers > 0 then
430 numberdata.ownnumbers = fastcopy(ownnumbers)
431 end
432 if trace_details then
433 report_structure("name %a, numbers % a, own numbers % a",givenname,numberdata.numbers,numberdata.ownnumbers)
434 end
435 if not references.block then
436 references.block = getcurrentblock()
437 end
438 local tag = references.tag or tags.getid(metadata.kind,metadata.name)
439 if tag and tag ~= "" and tag ~= "?" then
440 references.tag = tag
441 end
442 local setcomponent = structures.references.setcomponent
443 if setcomponent then
444 setcomponent(given)
445 end
446 references.section = sections.save(given)
447
448end
449
450function sections.reportstructure()
451 if sections.verbose then
452 local depth = data.depth
453 local status = data.status
454 local d = status[depth]
455 if d then
456 local numbers = data.numbers
457 local ownnumbers = data.ownnumbers
458 local o = concat(ownnumbers,".",1,depth)
459 local n = (numbers and concat(numbers,".",1,min(depth,#numbers))) or 0
460 local t = d.titledata.title
461 local l = t or ""
462 local t = (l ~= "" and l) or t or "[no title]"
463 local m = d.metadata.name
464 if o and not find(o,"^%.*$") then
465 report_structure("%s @ level %i : (%s) %s -> %s",m,depth,n,o,t)
466 elseif d.directives and d.directives.hidenumber then
467 report_structure("%s @ level %i : (%s) -> %s",m,depth,n,t)
468 else
469 report_structure("%s @ level %i : %s -> %s",m,depth,n,t)
470 end
471 else
472 report_structure("invalid structure @ depth %S",depth)
473 end
474 end
475end
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490function sections.setnumber(depth,n)
491 data.forced[depth or data.depth] = {
492 type(n) == "string" and find(n,"^[%+%-]") and "add" or "set",
493 tonumber(n) or 0
494 }
495end
496
497function sections.numberatdepth(depth)
498 return data.numbers[tonumber(depth) or sections.getlevel(depth) or 0] or 0
499end
500
501function sections.numbers()
502 return data.numbers
503end
504
505function sections.matchingtilldepth(depth,numbers,parentnumbers)
506 local dn = parentnumbers or data.numbers
507 local ok = false
508 for i=1,depth do
509 if dn[i] == numbers[i] then
510 ok = true
511 else
512 return false
513 end
514 end
515 return ok
516end
517
518function sections.getnumber(depth)
519 context(data.numbers[depth] or 0)
520end
521
522function sections.set(key,value)
523 data.status[data.depth][key] = value
524end
525
526function sections.cct()
527 local metadata = data.status[data.depth].metadata
528 context(metadata and metadata.catcodes or ctxcatcodes)
529end
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574function sections.structuredata(depth,key,default,honorcatcodetable)
575 local detail = false
576 if type(depth) == "string" then
577 depth, detail = string.splitup(depth,":")
578 end
579 if depth then
580 depth = levelmap[depth] or tonumber(depth)
581 end
582 if not depth or depth == 0 then
583 depth = data.depth
584 end
585 local useddata
586 if detail == "+" then
587 useddata = structures.lists.collected[#structures.lists.tobesaved+1]
588 else
589 useddata = data.status[depth]
590 end
591 local d
592 if useddata then
593 if find(key,".",1,true) then
594 d = accesstable(key,useddata)
595 else
596 d = useddata.titledata
597 d = d and d[key]
598 end
599 end
600 if d and type(d) ~= "table" then
601 if honorcatcodetable == true or honorcatcodetable == v_auto then
602 local metadata = useddata.metadata
603 local catcodes = metadata and metadata.catcodes
604 if catcodes then
605 ctx_sprint(catcodes,d)
606 else
607 context(d)
608 end
609 elseif not honorcatcodetable or honorcatcodetable == "" then
610 context(d)
611 else
612 local catcodes = catcodenumbers[honorcatcodetable]
613 if catcodes then
614 ctx_sprint(catcodes,d)
615 else
616 context(d)
617 end
618 end
619 elseif default then
620 context(default)
621 end
622end
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641function sections.userdata(depth,key,default)
642 local detail = false
643 if type(depth) == "string" then
644 depth, detail = string.splitup(depth,":")
645 end
646 if depth then
647 depth = levelmap[depth]
648 end
649 if not depth then
650 depth = tonumber(depth)
651 end
652 if not depth or depth == 0 then
653 depth = data.depth
654 end
655 local userdata
656 if detail == "+" then
657 userdata = structures.lists.collected[#structures.lists.tobesaved+1]
658 if userdata then
659 userdata = userdata.userdata
660 end
661 elseif depth > 0 then
662 userdata = data.status[depth]
663 userdata = userdata and userdata.userdata
664 end
665 userdata = (userdata and userdata[key]) or default
666 if userdata then
667 context(userdata)
668 end
669end
670
671function sections.setchecker(name,level,command)
672 data.checkers[name] = (name and command and level >= 0 and { level, command }) or nil
673end
674
675function sections.current()
676 return data.status[data.depth]
677end
678
679local function depthnumber(n)
680 local depth = data.depth
681 if not n or n == 0 then
682 n = depth
683 elseif n < 0 then
684 n = depth + n
685 end
686 return data.numbers[n] or 0
687end
688
689sections.depthnumber = depthnumber
690
691function sections.autodepth(numbers)
692 for i=#numbers,1,-1 do
693 if numbers[i] ~= 0 then
694 return i
695 end
696 end
697 return 0
698end
699
700
701
702function structures.currentsectionnumber()
703 local sc = sections.current()
704 return sc and sc.numberdata
705end
706
707
708
709
710
711
712
713
714
715
716
717
718local function process(index,numbers,ownnumbers,criterium,separatorset,conversion,conversionset,entry,result,preceding,done,language)
719
720 local number = numbers and (numbers[index] or 0)
721 local ownnumber = ownnumbers and ownnumbers[index] or ""
722 if number > criterium or (ownnumber ~= "") then
723 local block = (entry.block ~= "" and entry.block) or sections.currentblock()
724 if preceding then
725 local separator = sets.get("structure:separators",block,separatorset,preceding,".")
726 if separator then
727 if result then
728 result[#result+1] = strippedprocessor(separator)
729 else
730 applyprocessor(separator)
731 end
732 end
733 preceding = false
734 end
735 if result then
736 if ownnumber ~= "" then
737 result[#result+1] = ownnumber
738 elseif conversion and conversion ~= "" then
739 result[#result+1] = convertnumber(conversion,number,language)
740 else
741 local theconversion = sets.get("structure:conversions",block,conversionset,index,"numbers")
742 result[#result+1] = convertnumber(theconversion,number,language)
743 end
744 else
745 if ownnumber ~= "" then
746 applyprocessor(ownnumber)
747 elseif conversion and conversion ~= "" then
748 ctx_convertnumber(conversion,number)
749 else
750 local theconversion = sets.get("structure:conversions",block,conversionset,index,"numbers")
751 local data = startapplyprocessor(theconversion)
752 ctx_convertnumber(data or "numbers",number)
753 stopapplyprocessor()
754 end
755 end
756 return index, true
757 else
758 return preceding or false, done
759 end
760end
761
762
763
764function sections.typesetnumber(entry,kind,...)
765
766
767
768
769
770 if entry then
771 local separatorset = ""
772 local conversionset = ""
773 local conversion = ""
774 local groupsuffix = ""
775 local stopper = ""
776 local starter = ""
777 local connector = ""
778 local set = ""
779 local segments = ""
780 local criterium = ""
781 local language = ""
782 for d=1,select("#",...) do
783 local data = select(d,...)
784 if data then
785 if separatorset == "" then separatorset = data.separatorset or "" end
786 if conversionset == "" then conversionset = data.conversionset or "" end
787 if conversion == "" then conversion = data.conversion or "" end
788 if groupsuffix == "" then groupsuffix = data.groupsuffix or "" end
789 if stopper == "" then stopper = data.stopper or "" end
790 if starter == "" then starter = data.starter or "" end
791 if connector == "" then connector = data.connector or "" end
792 if set == "" then set = data.set or "" end
793 if segments == "" then segments = data.segments or "" end
794 if criterium == "" then criterium = data.criterium or "" end
795 if language == "" then language = data.language or "" end
796 end
797 end
798 if separatorset == "" then separatorset = "default" end
799 if conversionset == "" then conversionset = "default" end
800 if conversion == "" then conversion = nil end
801 if groupsuffix == "" then groupsuffix = nil end
802 if stopper == "" then stopper = nil end
803 if starter == "" then starter = nil end
804 if connector == "" then connector = nil end
805 if set == "" then set = "default" end
806 if segments == "" then segments = nil end
807 if language == "" then language = nil end
808
809 if criterium == v_strict then
810 criterium = 0
811 elseif criterium == v_positive then
812 criterium = -1
813 elseif criterium == v_all then
814 criterium = -1000000
815 else
816 criterium = 0
817 end
818
819 local firstprefix = 0
820 local lastprefix = 16
821 if segments == v_current then
822 firstprefix = data.depth
823 lastprefix = firstprefix
824 elseif segments then
825 local f, l = match(tostring(segments),"^(.-):(.+)$")
826 if l == "*" or l == v_all then
827 l = 100
828 end
829 if f and l then
830
831 firstprefix = tonumber(f) or sections.getlevel(f) or 0
832 lastprefix = tonumber(l) or sections.getlevel(l) or 100
833 else
834
835 local fl = tonumber(segments) or sections.getlevel(segments)
836 if fl then
837 firstprefix = fl
838 lastprefix = fl
839 end
840 end
841 end
842
843 local numbers = entry.numbers
844 local ownnumbers = entry.ownnumbers
845 if numbers then
846 local done = false
847 local preceding = false
848
849 local result = kind == "direct" and { }
850 if result then
851 connector = false
852 end
853
854 local prefixlist = set and sets.getall("structure:prefixes","",set)
855 if starter then
856 if result then
857 result[#result+1] = strippedprocessor(starter)
858 else
859 applyprocessor(starter)
860 end
861 end
862
863 if prefixlist and (kind == "section" or kind == "prefix" or kind == "direct") then
864
865
866 local b = 1
867 local e = #prefixlist
868 local bb = 0
869 local ee = 0
870
871
872
873 for k=e,b,-1 do
874 local prefix = prefixlist[k]
875 local index = sections.getlevel(prefix) or k
876 if index >= firstprefix and index <= lastprefix then
877 local number = numbers and numbers[index]
878 if number then
879 local ownnumber = ownnumbers and ownnumbers[index] or ""
880 if number > 0 or (ownnumber ~= "") then
881 break
882 else
883 e = k -1
884 end
885 end
886 end
887 end
888
889 for k=b,e do
890 local prefix = prefixlist[k]
891 local index = sections.getlevel(prefix) or k
892 if index >= firstprefix and index <= lastprefix then
893 local number = numbers and numbers[index]
894 if number then
895 local ownnumber = ownnumbers and ownnumbers[index] or ""
896 if number > 0 or (ownnumber ~= "") then
897 if bb == 0 then
898 bb = k
899 end
900 ee = k
901 elseif criterium >= 0 then
902 bb = 0
903 ee = 0
904 end
905 else
906 break
907 end
908 end
909 end
910
911 for k=bb,ee do
912 local prefix = prefixlist[k]
913 local index = sections.getlevel(prefix) or k
914 if index >= firstprefix and index <= lastprefix then
915 preceding, done = process(index,numbers,ownnumbers,criterium,separatorset,conversion,conversionset,entry,result,preceding,done,language)
916 end
917 end
918 else
919
920 for index=firstprefix,lastprefix do
921 preceding, done = process(index,numbers,ownnumbers,criterium,separatorset,conversion,conversionset,entry,result,preceding,done,language)
922 end
923 end
924
925 if done then
926 if connector and kind == 'prefix' then
927 if result then
928
929 else
930 applyprocessor(connector)
931 end
932 else
933 if groupsuffix and kind ~= "prefix" then
934 if result then
935 result[#result+1] = strippedprocessor(groupsuffix)
936 else
937 applyprocessor(groupsuffix)
938 end
939 end
940 if stopper then
941 if result then
942 result[#result+1] = strippedprocessor(stopper)
943 else
944 applyprocessor(stopper)
945 end
946 end
947 end
948 end
949 return result
950 else
951
952 end
953 end
954end
955
956function sections.title()
957 local sc = sections.current()
958 if sc then
959 helpers.title(sc.titledata.title,sc.metadata)
960 end
961end
962
963function sections.findnumber(depth,what)
964 local data = data.status[depth or data.depth]
965 if not data then
966 return
967 end
968 local references = data.references
969 if not references then
970 return
971 end
972 local index = references.section
973 local collected = sections.collected
974 local sectiondata = collected[index]
975 if sectiondata and sectiondata.hidenumber ~= true then
976 local quit = what == v_previous or what == v_next
977 if what == v_first or what == v_previous then
978 for i=index-1,1,-1 do
979 local s = collected[i]
980 if s then
981 local n = s.numbers
982 if #n == depth and n[depth] and n[depth] ~= 0 then
983 sectiondata = s
984 if quit then
985 break
986 end
987 elseif #n < depth then
988 break
989 end
990 end
991 end
992 elseif what == v_last or what == v_next then
993 for i=index+1,#collected do
994 local s = collected[i]
995 if s then
996 local n = s.numbers
997 if #n == depth and n[depth] and n[depth] ~= 0 then
998 sectiondata = s
999 if quit then
1000 break
1001 end
1002 elseif #n < depth then
1003 break
1004 end
1005 end
1006 end
1007 end
1008 return sectiondata
1009 end
1010end
1011
1012function sections.finddata(depth,what)
1013 local data = data.status[depth or data.depth]
1014 if not data then
1015 return
1016 end
1017 local references = data.references
1018 if not references then
1019 return
1020 end
1021 local index = references.listindex
1022 if not index then
1023 return
1024 end
1025 local collected = structures.lists.collected
1026 local quit = what == v_previous or what == v_next
1027 if what == v_first or what == v_previous then
1028 for i=index-1,1,-1 do
1029 local s = collected[i]
1030 if not s then
1031 break
1032 elseif s.metadata.kind == "section" then
1033 local n = s.numberdata.numbers
1034 if #n == depth and n[depth] and n[depth] ~= 0 then
1035 data = s
1036 if quit then
1037 break
1038 end
1039 elseif #n < depth then
1040 break
1041 end
1042 end
1043 end
1044 elseif what == v_last or what == v_next then
1045 for i=index+1,#collected do
1046 local s = collected[i]
1047 if not s then
1048 break
1049 elseif s.metadata.kind == "section" then
1050 local n = s.numberdata.numbers
1051 if #n == depth and n[depth] and n[depth] ~= 0 then
1052 data = s
1053 if quit then
1054 break
1055 end
1056 elseif #n < depth then
1057 break
1058 end
1059 end
1060 end
1061 end
1062 return data
1063end
1064
1065function sections.internalreference(sectionname,what)
1066 local r = type(sectionname) == "number" and sectionname or registered[sectionname]
1067 if r then
1068 local data = sections.finddata(r.level,what)
1069 return data and data.references and data.references.internal
1070 end
1071end
1072
1073function sections.fullnumber(depth,what)
1074 local sectiondata = sections.findnumber(depth,what)
1075 if sectiondata then
1076 sections.typesetnumber(sectiondata,'section',sectiondata)
1077 end
1078end
1079
1080function sections.getnumber(depth,what)
1081 local sectiondata = sections.findnumber(depth,what)
1082 local askednumber = 0
1083 if sectiondata then
1084 local numbers = sectiondata.numbers
1085 if numbers then
1086 askednumber = numbers[depth] or 0
1087 end
1088 end
1089 context(askednumber)
1090end
1091
1092
1093
1094function sections.showstructure()
1095
1096 local tobesaved = structures.lists.tobesaved
1097
1098 if not tobesaved then
1099 return
1100 end
1101
1102 local levels = setmetatableindex("table")
1103 local names = setmetatableindex("table")
1104
1105 report_used()
1106 report_used("sections")
1107 for i=1,#tobesaved do
1108 local si = tobesaved[i]
1109 local md = si.metadata
1110 if md and md.kind == "section" then
1111 local level = md.level
1112 local name = md.name
1113 local numbers = si.numberdata.numbers
1114 local title = si.titledata.title
1115 report_used(" %i : %-10s %-20s %s",level,concat(numbers,"."),name,title)
1116 levels[level][name] = true
1117 names[name][level] = true
1118 end
1119 end
1120 report_used()
1121 report_used("levels")
1122 for level, list in sortedhash(levels) do
1123 report_used(" %s : % t",level,sortedkeys(list))
1124 end
1125 report_used()
1126 report_used("names")
1127 for name, list in sortedhash(names) do
1128 report_used(" %-10s : % t",name,sortedkeys(list))
1129 end
1130 report_used()
1131end
1132
1133
1134
1135local levels = { }
1136
1137local function autonextstructurelevel(level)
1138 if level > #levels then
1139 for i=#levels+1,level do
1140 levels[i] = false
1141 end
1142 else
1143 for i=level,#levels do
1144 if levels[i] then
1145 ctx_finalizeauto()
1146 levels[i] = false
1147 end
1148 end
1149 end
1150 levels[level] = true
1151end
1152
1153local function autofinishstructurelevels()
1154 for i=1,#levels do
1155 if levels[i] then
1156 ctx_finalizeauto()
1157 end
1158 end
1159 levels = { }
1160end
1161
1162implement {
1163 name = "autonextstructurelevel",
1164 actions = autonextstructurelevel,
1165 arguments = "integer",
1166}
1167
1168implement {
1169 name = "autofinishstructurelevels",
1170 actions = autofinishstructurelevels,
1171}
1172
1173
1174
1175implement {
1176 name = "depthnumber",
1177 actions = { depthnumber, context },
1178 arguments = "integer",
1179}
1180
1181implement { name = "structurenumber", actions = sections.fullnumber }
1182implement { name = "structuretitle", actions = sections.title }
1183
1184implement { name = "structurevariable", actions = sections.structuredata, arguments = { false, "string" } }
1185implement { name = "structureuservariable", actions = sections.userdata, arguments = { false, "string" } }
1186implement { name = "structurecatcodedget", actions = sections.structuredata, arguments = { false, "string", false, true } }
1187implement { name = "structuregivencatcodedget", actions = sections.structuredata, arguments = { false, "string", false, "integer" } }
1188implement { name = "structureautocatcodedget", actions = sections.structuredata, arguments = { false, "string", false, "string" } }
1189
1190implement { name = "namedstructurevariable", actions = sections.structuredata, arguments = "2 strings" }
1191implement { name = "namedstructureuservariable", actions = sections.userdata, arguments = "2 strings" }
1192
1193implement { name = "setstructurelevel", actions = sections.setlevel, arguments = "2 strings" }
1194implement { name = "getstructurelevel", actions = sections.getcurrentlevel, arguments = "string" }
1195implement { name = "setstructurenumber", actions = sections.setnumber, arguments = { "integer", "string" } }
1196implement { name = "getstructurenumber", actions = sections.getnumber, arguments = "integer" }
1197implement { name = "getsomestructurenumber", actions = sections.getnumber, arguments = { "integer", "string" } }
1198implement { name = "getfullstructurenumber", actions = sections.fullnumber, arguments = "integer" }
1199implement { name = "getsomefullstructurenumber", actions = sections.fullnumber, arguments = { "integer", "string" } }
1200implement { name = "getspecificstructuretitle", actions = sections.structuredata, arguments = { "string", "'titledata.title'",false,"string" } }
1201
1202implement { name = "reportstructure", actions = sections.reportstructure }
1203implement { name = "showstructure", actions = sections.showstructure }
1204
1205implement {
1206 name = "registersection",
1207 actions = sections.register,
1208 arguments = {
1209 "string",
1210 {
1211 { "coupling" },
1212 { "section" },
1213 { "level", "integer" },
1214 { "parent" },
1215 }
1216 }
1217}
1218
1219implement {
1220 name = "setsectionentry",
1221 actions = sections.setentry,
1222 arguments = {
1223 {
1224 { "references", {
1225 { "internal", "integer" },
1226 { "block" },
1227 { "backreference" },
1228 { "prefix" },
1229 { "reference" },
1230 }
1231 },
1232 { "directives", {
1233 { "resetset" }
1234 }
1235 },
1236 { "metadata", {
1237 { "kind" },
1238 { "name" },
1239 { "catcodes", "integer" },
1240 { "coding" },
1241 { "xmlroot" },
1242 { "xmlsetup" },
1243 { "nolist", "boolean" },
1244 { "increment" },
1245 }
1246 },
1247 { "titledata", {
1248 { "label" },
1249 { "title" },
1250 { "bookmark" },
1251 { "marking" },
1252 { "list" },
1253 { "reference" },
1254 }
1255 },
1256 { "numberdata", {
1257 { "block" },
1258 { "hidenumber", "boolean" },
1259 { "separatorset" },
1260 { "conversionset" },
1261 { "conversion" },
1262 { "starter" },
1263 { "stopper" },
1264 { "set" },
1265 { "segments" },
1266 { "ownnumber" },
1267 { "language" },
1268 { "criterium" },
1269 },
1270 },
1271 { "userdata" },
1272 }
1273 }
1274}
1275
1276
1277
1278implement {
1279 name = "setsectionblock",
1280 actions = sections.setblock,
1281 arguments = { "string", { { "bookmark" } } }
1282}
1283
1284implement {
1285 name = "setinitialsectionblock",
1286 actions = sections.setinitialblock,
1287 arguments = "string",
1288
1289}
1290
1291implement {
1292 name = "pushsectionblock",
1293 actions = sections.pushblock,
1294 arguments = { "string", { { "bookmark" } } }
1295}
1296
1297implement {
1298 name = "popsectionblock",
1299 actions = sections.popblock,
1300}
1301
1302interfaces.implement {
1303 name = "doifelsefirstsectionpage",
1304 arguments = "1 argument",
1305 public = true,
1306 protected = true,
1307 actions = function(name)
1308 local found = false
1309
1310 local list = lists.collected
1311 if list then
1312 local realpage = texgetcount("realpageno")
1313 for i=1,#list do
1314 local listdata = list[i]
1315 local metadata = listdata.metadata
1316 if metadata and metadata.kind == "section" and metadata.name == name then
1317
1318 local current = data.status[metadata.level]
1319 if current and current.references.internal == listdata.references.internal then
1320 found = listdata.references.realpage == realpage
1321 break
1322 end
1323 end
1324 end
1325 end
1326 ctx_doifelse(found)
1327 end,
1328}
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378 |