if not modules then modules = { } end modules ['strc-doc'] = { version = 1.001, comment = "companion to strc-doc.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } -- todo: associate counter with head -- we need to better split the lua/tex end -- we need to freeze and document this module -- keep this as is: -- -- in section titles by default a zero aborts, so there we need: sectionset=bagger with \definestructureprefixset [bagger] [section-2,section-4] [] -- in lists however zero's are ignored, so there numbersegments=2:4 gives result local next, type, tonumber, select = next, type, tonumber, select local find, match = string.find, string.match local concat, fastcopy, insert, remove = table.concat, table.fastcopy, table.insert, table.remove local sortedhash, sortedkeys = table.sortedhash, table.sortedkeys local max, min = math.max, math.min local allocate, mark, accesstable = utilities.storage.allocate, utilities.storage.mark, utilities.tables.accesstable local setmetatableindex = table.setmetatableindex local lpegmatch, P, C = lpeg.match, lpeg.P, lpeg.C local catcodenumbers = catcodes.numbers local ctxcatcodes = catcodenumbers.ctxcatcodes local variables = interfaces.variables local implement = interfaces.implement local v_last = variables.last local v_first = variables.first local v_previous = variables.previous local v_next = variables.next local v_auto = variables.auto local v_strict = variables.strict local v_all = variables.all local v_positive = variables.positive local v_current = variables.current local texgetcount = tex.getcount local trace_sectioning = false trackers.register("structures.sectioning", function(v) trace_sectioning = v end) local trace_details = false trackers.register("structures.details", function(v) trace_details = v end) local report_structure = logs.reporter("structure","sectioning") local report_used = logs.reporter("structure") local context = context local commands = commands local ctx_doifelse = commands.doifelse local structures = structures local helpers = structures.helpers local documents = structures.documents local sections = structures.sections local lists = structures.lists local counters = structures.counters local sets = structures.sets local tags = structures.tags local processors = typesetters.processors local applyprocessor = processors.apply local startapplyprocessor = processors.startapply local stopapplyprocessor = processors.stopapply local strippedprocessor = processors.stripped local convertnumber = converters.convert local ctx_convertnumber = context.convertnumber local ctx_sprint = context.sprint local ctx_finalizeauto = context.finalizeautostructurelevel -- -- -- document -- -- -- local data -- the current state function documents.initialize() data = allocate { -- whole data is marked numbers = { }, forced = { }, ownnumbers = { }, status = { }, checkers = { }, depth = 0, blocks = { }, block = "", } documents.data = data end function documents.reset() data.numbers = { } data.forced = { } data.ownnumbers = { } data.status = { } -- data.checkers = { } data.depth = 0 end documents.initialize() -- -- -- components -- -- -- function documents.preset(numbers) local nofnumbers = #numbers local ownnumbers = { } data.numbers = numbers data.ownnumbers = ownnumbers data.depth = nofnumbers for i=1,nofnumbers do ownnumbers[i] = "" end sections.setnumber(nofnumbers,"-1") end -- -- -- sections -- -- -- -- This is just a quick way to have access to prefixes and the numbers (section entry in a ref) -- is not the list entry. An alternative is to use the list index of the last numbered section. In -- that case we should check a buse of the current structure. local collected = allocate() local tobesaved = allocate() sections.collected = collected sections.tobesaved = tobesaved -- We have to save this mostly redundant list because we can have (rare) -- cases with own numbers that don't end up in the list so we get out of -- sync when we use (*). local function initializer() collected = sections.collected tobesaved = sections.tobesaved end job.register('structures.sections.collected', tobesaved, initializer) local registered = sections.registered or allocate() sections.registered = registered storage.register("structures/sections/registered", registered, "structures.sections.registered") local function update(name,level,section) for k, v in next, registered do if k ~= name and v.coupling == name then report_structure("updating section level %a to level of %a",k,name) context.doredefinehead(k,name) update(k,level,section) end end end function sections.register(name,specification) registered[name] = specification local level = specification.level local section = specification.section update(name,level,section) end function sections.currentid() return #tobesaved end local lastsaved = 0 function sections.save(sectiondata) local sectiondata = helpers.simplify(sectiondata) -- maybe done earlier local numberdata = sectiondata.numberdata local ntobesaved = #tobesaved if not numberdata or sectiondata.metadata.nolist then -- stay else ntobesaved = ntobesaved + 1 tobesaved[ntobesaved] = numberdata if not collected[ntobesaved] then collected[ntobesaved] = numberdata end end lastsaved = ntobesaved return ntobesaved end function sections.currentsectionindex() return lastsaved -- only for special controlled situations end -- See comment above (*). We cannot use the following space optimization: -- -- function sections.load() -- setmetatableindex(collected,nil) -- local lists = lists.collected -- for i=1,#lists do -- local list = lists[i] -- local metadata = list.metadata -- if metadata and metadata.kind == "section" and not metadata.nolist then -- local numberdata = list.numberdata -- if numberdata then -- collected[#collected+1] = numberdata -- end -- end -- end -- sections.load = functions.dummy -- end -- -- table.setmetatableindex(collected, function(t,i) -- sections.load() -- return collected[i] or { } -- end) sections.verbose = true local sectionblockdata = sections.sectionblockdata or { } sections.sectionblockdata = sectionblockdata local levelmap = sections.levelmap or { } sections.levelmap = levelmap levelmap.block = -1 storage.register("structures/sections/levelmap", sections.levelmap, "structures.sections.levelmap") function sections.setlevel(name,level) -- level can be number or parent (=string) local l = tonumber(level) if not l then l = levelmap[level] end if l and l > 0 then levelmap[name] = l else -- error end end function sections.getlevel(name) return levelmap[name] or 0 end table.setmetatableindex(sectionblockdata,"table") function sections.setblock(name,settings) local block = name or data.block or "unknown" -- can be used to set the default data.block = block sectionblockdata[block] = settings return block end local jobvariables = job.variables local pushed_order = { } local pushed_done = { } jobvariables.tobesaved.sectionblockorder = pushed_order -- function sections.order() -- return jobvariables.collected.sectionblockorder or pushed_order -- so we have a first pass list too -- end function sections.setinitialblock(default) local order = jobvariables.collected.sectionblockorder or pushed_order local name = #order > 0 and order[1] or default or "bodypart" context.setsectionblock { name } -- interfaces.setmacro("currentsectionblock",name) -- sections.setblock(name,{}) end function sections.pushblock(name,settings) counters.check(0) -- we assume sane usage of \page between blocks local block = name or data.block insert(data.blocks,block) data.block = block sectionblockdata[block] = settings documents.reset() if not pushed_done[name] then pushed_done[name] = true local nofpushed = #pushed_order + 1 pushed_order[nofpushed] = name end return block end function sections.popblock() local block = remove(data.blocks) or data.block data.block = block documents.reset() return block end local function getcurrentblock() return data.block or data.blocks[#data.blocks] or "unknown" end sections.currentblock = getcurrentblock function sections.currentlevel() return data.depth end function sections.getcurrentlevel() context(data.depth) end local saveset = { } -- experiment, see sections/tricky-001.tex function sections.setentry(given) -- old number local numbers = data.numbers -- local metadata = given.metadata local numberdata = given.numberdata local references = given.references local directives = given.directives local userdata = given.userdata if not metadata then metadata = { } given.metadata = metadata end if not numberdata then numberdata = { } given.numberdata = numberdata end if not references then references = { } given.references = references end local ownnumbers = data.ownnumbers local forced = data.forced local status = data.status local olddepth = data.depth local givenname = metadata.name local mappedlevel = levelmap[givenname] local newdepth = tonumber(mappedlevel or (olddepth > 0 and olddepth) or 1) -- hm, levelmap only works for section-* local resetset = directives and directives.resetset or "" -- local resetter = sets.getall("structure:resets",data.block,resetset) -- a trick to permit userdata to overload title, ownnumber and reference -- normally these are passed as argument but nowadays we provide several -- interfaces (we need this because we want to be compatible) if trace_details then report_structure("name %a, mapped level %a, old depth %a, new depth %a, reset set %a", givenname,mappedlevel,olddepth,newdepth,resetset) end if userdata then -- kind of obsolete as we can pass them directly anyway ... NEEDS CHECKING ! if userdata.reference and userdata.reference ~= "" then given.metadata.reference = userdata.reference ; userdata.reference = nil end if userdata.ownnumber and userdata.ownnumber ~= "" then given.numberdata.ownnumber = userdata.ownnumber ; userdata.ownnumber = nil end if userdata.title and userdata.title ~= "" then given.titledata.title = userdata.title ; userdata.title = nil end if userdata.bookmark and userdata.bookmark ~= "" then given.titledata.bookmark = userdata.bookmark ; userdata.bookmark = nil end if userdata.label and userdata.label ~= "" then given.titledata.label = userdata.label ; userdata.label = nil end end -- so far for the trick if saveset then saveset[newdepth] = (resetset ~= "" and resetset) or saveset[newdepth] or "" end if newdepth > olddepth then for i=olddepth+1,newdepth do local s = tonumber(sets.get("structure:resets",data.block,saveset and saveset[i] or resetset,i)) if trace_details then report_structure("new depth %s, old depth %s, reset set %a, reset value %a, current %a",olddepth,newdepth,resetset,s,numbers[i]) end if not s or s == 0 then numbers[i] = numbers[i] or 0 ownnumbers[i] = ownnumbers[i] or "" else numbers[i] = s - 1 ownnumbers[i] = "" end status[i] = { } end elseif newdepth < olddepth then for i=olddepth,newdepth+1,-1 do local s = tonumber(sets.get("structure:resets",data.block,saveset and saveset[i] or resetset,i)) if trace_details then report_structure("new depth %s, old depth %s, reset set %a, reset value %a, current %a",olddepth,newdepth,resetset,s,numbers[i]) end if not s or s == 0 then numbers[i] = numbers[i] or 0 ownnumbers[i] = ownnumbers[i] or "" else numbers[i] = s - 1 ownnumbers[i] = "" end status[i] = nil end end counters.check(newdepth) ownnumbers[newdepth] = numberdata.ownnumber or "" numberdata.ownnumber = nil data.depth = newdepth -- new number olddepth = newdepth if metadata.increment then local oldn = numbers[newdepth] or 0 local newn = 0 local fd = forced[newdepth] if fd then if fd[1] == "add" then newn = oldn + fd[2] + 1 else newn = fd[2] + 1 end if newn < 0 then newn = 1 -- maybe zero is nicer end forced[newdepth] = nil if trace_details then report_structure("old depth %a, new depth %a, old n %a, new n %a, forced %t",olddepth,newdepth,oldn,newn,fd) end else newn = oldn + 1 if trace_details then report_structure("old depth %a, new depth %a, old n %a, new n %a, increment",olddepth,newdepth,oldn,newn) end end numbers[newdepth] = newn end status[newdepth] = given or { } for k, v in next, data.checkers do if v[1] == newdepth and v[2] then v[2](k) end end numberdata.numbers = { unpack(numbers,1,newdepth) } if not numberdata.block then numberdata.block = getcurrentblock() -- also in references end if #ownnumbers > 0 then numberdata.ownnumbers = fastcopy(ownnumbers) -- { unpack(ownnumbers) } end if trace_details then report_structure("name %a, numbers % a, own numbers % a",givenname,numberdata.numbers,numberdata.ownnumbers) end if not references.block then references.block = getcurrentblock() -- also in numberdata end local tag = references.tag or tags.getid(metadata.kind,metadata.name) if tag and tag ~= "" and tag ~= "?" then references.tag = tag end local setcomponent = structures.references.setcomponent if setcomponent then setcomponent(given) -- might move to the tex end end references.section = sections.save(given) -- given.numberdata = nil end function sections.reportstructure() if sections.verbose then local numbers = data.numbers local ownnumbers = data.ownnumbers local status = data.status local depth = data.depth local d = status[depth] local o = concat(ownnumbers,".",1,depth) local n = (numbers and concat(numbers,".",1,min(depth,#numbers))) or 0 local t = d.titledata.title local l = t or "" local t = (l ~= "" and l) or t or "[no title]" local m = d.metadata.name if o and not find(o,"^%.*$") then report_structure("%s @ level %i : (%s) %s -> %s",m,depth,n,o,t) elseif d.directives and d.directives.hidenumber then report_structure("%s @ level %i : (%s) -> %s",m,depth,n,t) else report_structure("%s @ level %i : %s -> %s",m,depth,n,t) end end end -- function sections.setnumber(depth,n) -- local forced, depth, new = data.forced, depth or data.depth, tonumber(n) or 0 -- if type(n) == "string" then -- if find(n,"^[%+%-]") then -- forced[depth] = { "add", new } -- else -- forced[depth] = { "set", new } -- end -- else -- forced[depth] = { "set", new } -- end -- end function sections.setnumber(depth,n) data.forced[depth or data.depth] = { type(n) == "string" and find(n,"^[%+%-]") and "add" or "set", tonumber(n) or 0 } end function sections.numberatdepth(depth) return data.numbers[tonumber(depth) or sections.getlevel(depth) or 0] or 0 end function sections.numbers() return data.numbers end function sections.matchingtilldepth(depth,numbers,parentnumbers) local dn = parentnumbers or data.numbers local ok = false for i=1,depth do if dn[i] == numbers[i] then ok = true else return false end end return ok end function sections.getnumber(depth) -- redefined later ... context(data.numbers[depth] or 0) end function sections.set(key,value) data.status[data.depth][key] = value -- may be nil for a reset end function sections.cct() local metadata = data.status[data.depth].metadata context(metadata and metadata.catcodes or ctxcatcodes) end -- this one will become: return catcode, d (etc) -- function sections.structuredata(depth,key,default,honorcatcodetable) -- todo: spec table and then also depth -- if depth then -- depth = levelmap[depth] or tonumber(depth) -- end -- if not depth or depth == 0 then -- depth = data.depth -- end -- local data = data.status[depth] -- local d -- if data then -- if find(key,".",1,true) then -- d = accesstable(key,data) -- else -- d = data.titledata -- d = d and d[key] -- end -- end -- if d and type(d) ~= "table" then -- if honorcatcodetable == true or honorcatcodetable == v_auto then -- local metadata = data.metadata -- local catcodes = metadata and metadata.catcodes -- if catcodes then -- ctx_sprint(catcodes,d) -- else -- context(d) -- end -- elseif not honorcatcodetable or honorcatcodetable == "" then -- context(d) -- else -- local catcodes = catcodenumbers[honorcatcodetable] -- if catcodes then -- ctx_sprint(catcodes,d) -- else -- context(d) -- end -- end -- elseif default then -- context(default) -- end -- end function sections.structuredata(depth,key,default,honorcatcodetable) -- todo: spec table and then also depth local detail = false if type(depth) == "string" then depth, detail = string.splitup(depth,":") end if depth then depth = levelmap[depth] or tonumber(depth) end if not depth or depth == 0 then depth = data.depth end local useddata if detail == "+" then useddata = structures.lists.collected[#structures.lists.tobesaved+1] else useddata = data.status[depth] end local d if useddata then if find(key,".",1,true) then d = accesstable(key,useddata) else d = useddata.titledata d = d and d[key] end end if d and type(d) ~= "table" then if honorcatcodetable == true or honorcatcodetable == v_auto then local metadata = useddata.metadata local catcodes = metadata and metadata.catcodes if catcodes then ctx_sprint(catcodes,d) else context(d) end elseif not honorcatcodetable or honorcatcodetable == "" then context(d) else local catcodes = catcodenumbers[honorcatcodetable] if catcodes then ctx_sprint(catcodes,d) else context(d) end end elseif default then context(default) end end -- function sections.userdata(depth,key,default) -- if depth then -- depth = levelmap[depth] or tonumber(depth) -- end -- if not depth or depth == 0 then -- depth = data.depth -- end -- if depth > 0 then -- local userdata = data.status[depth] -- userdata = userdata and userdata.userdata -- userdata = (userdata and userdata[key]) or default -- if userdata then -- context(userdata) -- end -- end -- end function sections.userdata(depth,key,default) local detail = false if type(depth) == "string" then depth, detail = string.splitup(depth,":") end if depth then depth = levelmap[depth] end if not depth then depth = tonumber(depth) end if not depth or depth == 0 then depth = data.depth end local userdata if detail == "+" then userdata = structures.lists.collected[#structures.lists.tobesaved+1] if userdata then userdata = userdata.userdata end elseif depth > 0 then userdata = data.status[depth] userdata = userdata and userdata.userdata end userdata = (userdata and userdata[key]) or default if userdata then context(userdata) end end function sections.setchecker(name,level,command) -- hm, checkers are not saved data.checkers[name] = (name and command and level >= 0 and { level, command }) or nil end function sections.current() return data.status[data.depth] end local function depthnumber(n) local depth = data.depth if not n or n == 0 then n = depth elseif n < 0 then n = depth + n end return data.numbers[n] or 0 end sections.depthnumber = depthnumber function sections.autodepth(numbers) for i=#numbers,1,-1 do if numbers[i] ~= 0 then return i end end return 0 end -- function structures.currentsectionnumber() -- brr, namespace wrong local sc = sections.current() return sc and sc.numberdata end -- \dorecurse{3} { -- \chapter{Blabla} \subsection{bla 1 1} \subsection{bla 1 2} -- \section{bla 2} \subsection{bla 2 1} \subsection{bla 2 2} -- } -- sign=all => also zero and negative -- sign=positive => also zero -- sign=hang => llap sign -- this can be a local function local function process(index,numbers,ownnumbers,criterium,separatorset,conversion,conversionset,entry,result,preceding,done,language) -- todo: too much (100 steps) local number = numbers and (numbers[index] or 0) local ownnumber = ownnumbers and ownnumbers[index] or "" if number > criterium or (ownnumber ~= "") then local block = (entry.block ~= "" and entry.block) or sections.currentblock() -- added if preceding then local separator = sets.get("structure:separators",block,separatorset,preceding,".") if separator then if result then result[#result+1] = strippedprocessor(separator) else applyprocessor(separator) end end preceding = false end if result then if ownnumber ~= "" then result[#result+1] = ownnumber elseif conversion and conversion ~= "" then -- traditional (e.g. used in itemgroups) .. inherited! result[#result+1] = convertnumber(conversion,number,language) else local theconversion = sets.get("structure:conversions",block,conversionset,index,"numbers") result[#result+1] = convertnumber(theconversion,number,language) end else if ownnumber ~= "" then applyprocessor(ownnumber) elseif conversion and conversion ~= "" then -- traditional (e.g. used in itemgroups) ctx_convertnumber(conversion,number) else local theconversion = sets.get("structure:conversions",block,conversionset,index,"numbers") local data = startapplyprocessor(theconversion) ctx_convertnumber(data or "numbers",number) stopapplyprocessor() end end return index, true else return preceding or false, done end end -- kind : section number prefix function sections.typesetnumber(entry,kind,...) -- -- Maybe the hiding becomes an option .. after all this test was there -- for a reason, but for now we have this: -- -- if entry and entry.hidenumber ~= true then if entry then local separatorset = "" local conversionset = "" local conversion = "" local groupsuffix = "" local stopper = "" local starter = "" local connector = "" local set = "" local segments = "" local criterium = "" local language = "" for d=1,select("#",...) do local data = select(d,...) -- can be multiple parametersets if data then if separatorset == "" then separatorset = data.separatorset or "" end if conversionset == "" then conversionset = data.conversionset or "" end if conversion == "" then conversion = data.conversion or "" end if groupsuffix == "" then groupsuffix = data.groupsuffix or "" end if stopper == "" then stopper = data.stopper or "" end if starter == "" then starter = data.starter or "" end if connector == "" then connector = data.connector or "" end if set == "" then set = data.set or "" end if segments == "" then segments = data.segments or "" end if criterium == "" then criterium = data.criterium or "" end if language == "" then language = data.language or "" end end end if separatorset == "" then separatorset = "default" end if conversionset == "" then conversionset = "default" end -- not used if conversion == "" then conversion = nil end if groupsuffix == "" then groupsuffix = nil end if stopper == "" then stopper = nil end if starter == "" then starter = nil end if connector == "" then connector = nil end if set == "" then set = "default" end if segments == "" then segments = nil end if language == "" then language = nil end -- if criterium == v_strict then criterium = 0 elseif criterium == v_positive then criterium = -1 elseif criterium == v_all then criterium = -1000000 else criterium = 0 end -- local firstprefix = 0 local lastprefix = 16 -- too much, could max found level if segments == v_current then firstprefix = data.depth lastprefix = firstprefix elseif segments then local f, l = match(tostring(segments),"^(.-):(.+)$") if l == "*" or l == v_all then l = 100 -- new end if f and l then -- 0:100, chapter:subsubsection firstprefix = tonumber(f) or sections.getlevel(f) or 0 lastprefix = tonumber(l) or sections.getlevel(l) or 100 else -- 3, section local fl = tonumber(segments) or sections.getlevel(segments) -- generalize if fl then firstprefix = fl lastprefix = fl end end end -- local numbers = entry.numbers local ownnumbers = entry.ownnumbers if numbers then local done = false local preceding = false -- local result = kind == "direct" and { } if result then connector = false end -- local prefixlist = set and sets.getall("structure:prefixes","",set) -- "" == block if starter then if result then result[#result+1] = strippedprocessor(starter) else applyprocessor(starter) end end -- inspect(entry) if prefixlist and (kind == "section" or kind == "prefix" or kind == "direct") then -- find valid set (problem: for sectionnumber we should pass the level) -- no holes local b = 1 local e = #prefixlist local bb = 0 local ee = 0 -- find last valid number -- print("index >>",b,e) -- inspect(prefixlist) for k=e,b,-1 do local prefix = prefixlist[k] local index = sections.getlevel(prefix) or k if index >= firstprefix and index <= lastprefix then local number = numbers and numbers[index] if number then local ownnumber = ownnumbers and ownnumbers[index] or "" if number > 0 or (ownnumber ~= "") then break else e = k -1 end end end end -- find valid range for k=b,e do local prefix = prefixlist[k] local index = sections.getlevel(prefix) or k if index >= firstprefix and index <= lastprefix then local number = numbers and numbers[index] if number then local ownnumber = ownnumbers and ownnumbers[index] or "" if number > 0 or (ownnumber ~= "") then if bb == 0 then bb = k end ee = k elseif criterium >= 0 then bb = 0 ee = 0 end else break end end end -- print valid range for k=bb,ee do local prefix = prefixlist[k] local index = sections.getlevel(prefix) or k if index >= firstprefix and index <= lastprefix then preceding, done = process(index,numbers,ownnumbers,criterium,separatorset,conversion,conversionset,entry,result,preceding,done,language) end end else -- also holes check for index=firstprefix,lastprefix do preceding, done = process(index,numbers,ownnumbers,criterium,separatorset,conversion,conversionset,entry,result,preceding,done,language) end end -- if done then if connector and kind == 'prefix' then if result then -- can't happen as we're in 'direct' else applyprocessor(connector) end else if groupsuffix and kind ~= "prefix" then if result then result[#result+1] = strippedprocessor(groupsuffix) else applyprocessor(groupsuffix) end end if stopper then if result then result[#result+1] = strippedprocessor(stopper) else applyprocessor(stopper) end end end end return result -- a table ! else -- report_structure("error: no numbers") end end end function sections.title() local sc = sections.current() if sc then helpers.title(sc.titledata.title,sc.metadata) end end function sections.findnumber(depth,what) -- needs checking (looks wrong and slow too) local data = data.status[depth or data.depth] if not data then return end local references = data.references if not references then return end local index = references.section local collected = sections.collected local sectiondata = collected[index] if sectiondata and sectiondata.hidenumber ~= true then -- can be nil local quit = what == v_previous or what == v_next if what == v_first or what == v_previous then for i=index-1,1,-1 do local s = collected[i] if s then local n = s.numbers if #n == depth and n[depth] and n[depth] ~= 0 then sectiondata = s if quit then break end elseif #n < depth then break end end end elseif what == v_last or what == v_next then for i=index+1,#collected do local s = collected[i] if s then local n = s.numbers if #n == depth and n[depth] and n[depth] ~= 0 then sectiondata = s if quit then break end elseif #n < depth then break end end end end return sectiondata end end function sections.finddata(depth,what) local data = data.status[depth or data.depth] if not data then return end local references = data.references if not references then return end local index = references.listindex if not index then return end local collected = structures.lists.collected local quit = what == v_previous or what == v_next if what == v_first or what == v_previous then for i=index-1,1,-1 do local s = collected[i] if not s then break elseif s.metadata.kind == "section" then -- maybe check on name local n = s.numberdata.numbers if #n == depth and n[depth] and n[depth] ~= 0 then data = s if quit then break end elseif #n < depth then break end end end elseif what == v_last or what == v_next then for i=index+1,#collected do local s = collected[i] if not s then break elseif s.metadata.kind == "section" then -- maybe check on name local n = s.numberdata.numbers if #n == depth and n[depth] and n[depth] ~= 0 then data = s if quit then break end elseif #n < depth then break end end end end return data end function sections.internalreference(sectionname,what) -- to be used in pagebuilder (no marks used) local r = type(sectionname) == "number" and sectionname or registered[sectionname] if r then local data = sections.finddata(r.level,what) return data and data.references and data.references.internal end end function sections.fullnumber(depth,what) local sectiondata = sections.findnumber(depth,what) if sectiondata then sections.typesetnumber(sectiondata,'section',sectiondata) end end function sections.getnumber(depth,what) -- redefined here local sectiondata = sections.findnumber(depth,what) local askednumber = 0 if sectiondata then local numbers = sectiondata.numbers if numbers then askednumber = numbers[depth] or 0 end end context(askednumber) end -- maybe handy function sections.showstructure() local tobesaved = structures.lists.tobesaved if not tobesaved then return end local levels = setmetatableindex("table") local names = setmetatableindex("table") report_used() report_used("sections") for i=1,#tobesaved do local si = tobesaved[i] local md = si.metadata if md and md.kind == "section" then local level = md.level local name = md.name local numbers = si.numberdata.numbers local title = si.titledata.title report_used(" %i : %-10s %-20s %s",level,concat(numbers,"."),name,title) levels[level][name] = true names[name][level] = true end end report_used() report_used("levels") for level, list in sortedhash(levels) do report_used(" %s : % t",level,sortedkeys(list)) end report_used() report_used("names") for name, list in sortedhash(names) do report_used(" %-10s : % t",name,sortedkeys(list)) end report_used() end -- experimental local levels = { } local function autonextstructurelevel(level) if level > #levels then for i=#levels+1,level do levels[i] = false end else for i=level,#levels do if levels[i] then ctx_finalizeauto() levels[i] = false end end end levels[level] = true end local function autofinishstructurelevels() for i=1,#levels do if levels[i] then ctx_finalizeauto() end end levels = { } end implement { name = "autonextstructurelevel", actions = autonextstructurelevel, arguments = "integer", } implement { name = "autofinishstructurelevels", actions = autofinishstructurelevels, } -- interface (some are actually already commands, like sections.fullnumber) implement { name = "depthnumber", actions = { depthnumber, context }, arguments = "integer", } implement { name = "structurenumber", actions = sections.fullnumber } implement { name = "structuretitle", actions = sections.title } implement { name = "structurevariable", actions = sections.structuredata, arguments = { false, "string" } } implement { name = "structureuservariable", actions = sections.userdata, arguments = { false, "string" } } implement { name = "structurecatcodedget", actions = sections.structuredata, arguments = { false, "string", false, true } } implement { name = "structuregivencatcodedget", actions = sections.structuredata, arguments = { false, "string", false, "integer" } } implement { name = "structureautocatcodedget", actions = sections.structuredata, arguments = { false, "string", false, "string" } } implement { name = "namedstructurevariable", actions = sections.structuredata, arguments = "2 strings" } implement { name = "namedstructureuservariable", actions = sections.userdata, arguments = "2 strings" } implement { name = "setstructurelevel", actions = sections.setlevel, arguments = "2 strings" } implement { name = "getstructurelevel", actions = sections.getcurrentlevel, arguments = "string" } implement { name = "setstructurenumber", actions = sections.setnumber, arguments = { "integer", "string" } } -- string as we support +- implement { name = "getstructurenumber", actions = sections.getnumber, arguments = "integer" } implement { name = "getsomestructurenumber", actions = sections.getnumber, arguments = { "integer", "string" } } implement { name = "getfullstructurenumber", actions = sections.fullnumber, arguments = "integer" } implement { name = "getsomefullstructurenumber", actions = sections.fullnumber, arguments = { "integer", "string" } } implement { name = "getspecificstructuretitle", actions = sections.structuredata, arguments = { "string", "'titledata.title'",false,"string" } } implement { name = "reportstructure", actions = sections.reportstructure } implement { name = "showstructure", actions = sections.showstructure } implement { name = "registersection", actions = sections.register, arguments = { "string", { { "coupling" }, { "section" }, { "level", "integer" }, { "parent" }, } } } implement { name = "setsectionentry", actions = sections.setentry, arguments = { { { "references", { { "internal", "integer" }, { "block" }, { "backreference" }, { "prefix" }, { "reference" }, } }, { "directives", { { "resetset" } } }, { "metadata", { { "kind" }, { "name" }, { "catcodes", "integer" }, { "coding" }, { "xmlroot" }, { "xmlsetup" }, { "nolist", "boolean" }, { "increment" }, } }, { "titledata", { { "label" }, { "title" }, { "bookmark" }, { "marking" }, { "list" }, { "reference" }, } }, { "numberdata", { { "block" }, { "hidenumber", "boolean" }, { "separatorset" }, { "conversionset" }, { "conversion" }, { "starter" }, { "stopper" }, { "set" }, { "segments" }, { "ownnumber" }, { "language" }, { "criterium" }, }, }, { "userdata" }, } } } -- os.exit() implement { name = "setsectionblock", actions = sections.setblock, arguments = { "string", { { "bookmark" } } } } implement { name = "setinitialsectionblock", actions = sections.setinitialblock, arguments = "string", -- onlyonce = true, } implement { name = "pushsectionblock", actions = sections.pushblock, arguments = { "string", { { "bookmark" } } } } implement { name = "popsectionblock", actions = sections.popblock, } interfaces.implement { name = "doifelsefirstsectionpage", arguments = "1 argument", public = true, protected = true, actions = function(name) local found = false -- local list = structures.lists.collected local list = lists.collected if list then local realpage = texgetcount("realpageno") for i=1,#list do local listdata = list[i] local metadata = listdata.metadata if metadata and metadata.kind == "section" and metadata.name == name then -- local current = structures.documents.data.status[metadata.level] local current = data.status[metadata.level] if current and current.references.internal == listdata.references.internal then found = listdata.references.realpage == realpage break end end end end ctx_doifelse(found) end, } -- could be faster (in huge lists) -- local firstpages = table.setmetatableindex(function(t,name) -- -- local list = structures.lists.collected -- local list = lists.collected -- local pages = { } -- if list then -- for i=1,#list do -- local listdata = list[i] -- local metadata = listdata.metadata -- if metadata and metadata.kind == "section" and metadata.name == name then -- local references = listdata.references -- if references then -- pages[references.internal] = listdata -- end -- end -- end -- end -- t[name] = pages -- return pages -- end) -- -- interfaces.implement { -- name = "doifelsefirstsectionpage", -- arguments = "1 argument", -- public = true, -- protected = true, -- actions = function(name) -- local found = firstpages[name] -- if found then -- local level = structures.sections.levelmap[name] -- if level then -- -- local current = structures.documents.data.status[level] -- local current = data.status[level] -- if current then -- local realpage = texgetcount("realpageno") -- found = found[current.references.internal] -- found = found and found.references.realpage == realpage -- else -- found = false -- end -- else -- found = false -- end -- end -- ctx_doifelse(found) -- end, -- }