if not modules then modules = { } end modules ['strc-lst'] = { version = 1.001, comment = "companion to strc-lst.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } -- when all datastructures are stable a packer will be added which will -- bring down memory consumption a bit; we can use for instance a pagenumber, -- section, metadata cache (internal then has to move up one level) or a -- shared cache [we can use a fast and stupid serializer] -- todo: tag entry in list is crap -- -- move more to commands local tonumber, type, next = tonumber, type, next local concat, insert, remove, sort = table.concat, table.insert, table.remove, table.sort local lpegmatch = lpeg.match local setmetatableindex = table.setmetatableindex local sortedkeys = table.sortedkeys local settings_to_set = utilities.parsers.settings_to_set local allocate = utilities.storage.allocate local checked = utilities.storage.checked local trace_lists = false trackers.register("structures.lists", function(v) trace_lists = v end) local report_lists = logs.reporter("structure","lists") local context = context local commands = commands local implement = interfaces.implement local conditionals = tex.conditionals local ctx_latelua = context.latelua local cheat = true local structures = structures local lists = structures.lists local sections = structures.sections local helpers = structures.helpers local documents = structures.documents local tags = structures.tags local counters = structures.counters local references = structures.references local collected = allocate() local tobesaved = allocate() local cached = allocate() local pushed = allocate() local kinds = allocate() local names = allocate() lists.collected = collected lists.tobesaved = tobesaved lists.enhancers = lists.enhancers or { } -----.internals = allocate(lists.internals or { }) -- to be checked lists.ordered = allocate(lists.ordered or { }) -- to be checked lists.cached = cached lists.pushed = pushed lists.kinds = kinds lists.names = names local sorters = sorters local sortstripper = sorters.strip local sortsplitter = sorters.splitters.utf local sortcomparer = sorters.comparers.basic local sectionblocks = allocate() lists.sectionblocks = sectionblocks references.specials = references.specials or { } local matchingtilldepth = sections.matchingtilldepth local numberatdepth = sections.numberatdepth local getsectionlevel = sections.getlevel local typesetnumber = sections.typesetnumber local autosectiondepth = sections.autodepth local variables = interfaces.variables local v_all = variables.all local v_reference = variables.reference local v_title = variables.title local v_command = variables.command local v_text = variables.text local v_current = variables.current local v_previous = variables.previous local v_intro = variables.intro local v_here = variables.here local v_component = variables.component local v_reference = variables.reference local v_local = variables["local"] local v_default = variables.default local cheats = { [variables.fit] = true, [variables.tight] = true, } local function zerostrippedconcat(t,separator) local f = 1 local l = #t for i=f,l do if t[i] == 0 then f = f + 1 end end for i=l,f,-1 do if t[i] == 0 then l = l - 1 end end return concat(t,separator,f,l) end -- -- -- -- -- -- local function initializer() -- create a cross reference between internal references -- and list entries local collected = lists.collected local internals = checked(references.internals) local ordered = lists.ordered local usedinternals = references.usedinternals local blockdone = { } local lastblock = nil for i=1,#collected do local c = collected[i] local m = c.metadata local r = c.references if m then -- access by internal reference if r then local internal = r.internal if internal then internals[internal] = c usedinternals[internal] = r.used end local block = r.block if not block then -- shouldn't happen elseif lastblock == block then -- we're okay elseif lastblock then if blockdone[block] then report_lists("out of order sectionsblocks, maybe use \\setsectionblock") else blockdone[block] = true sectionblocks[#sectionblocks+1] = block end lastblock = block elseif not blockdone[block] then blockdone[block] = true sectionblocks[#sectionblocks+1] = block lastblock = block end end -- access by order in list local kind = m.kind local name = m.name if kind and name then local ok = ordered[kind] if ok then local on = ok[name] if on then on[#on+1] = c else ok[name] = { c } end else ordered[kind] = { [name] = { c } } end kinds[kind] = true names[name] = true elseif kind then kinds[kind] = true elseif name then names[name] = true end end if r then r.listindex = i -- handy to have end end end local function finalizer() local flaginternals = references.flaginternals local usedviews = references.usedviews for i=1,#tobesaved do local r = tobesaved[i].references if r then local i = r.internal local f = flaginternals[i] local v = usedviews[i] if cheat and v and cheats[v] then -- cheats check added, to be tested by RKB -- this permits runs=2 with interactivity r.view = v end if f then r.used = v or true end end end end job.register('structures.lists.collected', tobesaved, initializer, finalizer) local groupindices = setmetatableindex("table") function lists.groupindex(name,group) local groupindex = groupindices[name] return groupindex and groupindex[group] or 0 end -- we could use t (as hash key) in order to check for dup entries function lists.addto(t) -- maybe more more here (saves parsing at the tex end) local metadata = t.metadata local userdata = t.userdata local numberdata = t.numberdata if userdata and type(userdata) == "string" then t.userdata = helpers.touserdata(userdata) end if not metadata.level then metadata.level = structures.sections.currentlevel() -- this is not used so it will go away end -- -- if not conditionals.inlinelefttoright then -- metadata.idir = "r2l" -- end -- if not conditionals.displaylefttoright then -- metadata.ddir = "r2l" -- end -- if numberdata then local numbers = numberdata.numbers if type(numbers) == "string" then counters.compact(numberdata,numbers,numberdata.level) end end local group = numberdata and numberdata.group local name = metadata.name local kind = metadata.kind if not group then -- forget about it elseif group == "" then group, numberdata.group = nil, nil else local groupindex = groupindices[name][group] if groupindex then numberdata.numbers = cached[groupindex].numberdata.numbers end end local setcomponent = references.setcomponent if setcomponent then setcomponent(t) -- can be inlined end local r = t.references if r and not r.section then r.section = structures.sections.currentid() end local b = r and t.block if r and not b then local s = r.section if s then s = structures.sections.tobesaved[s] r.block = s and s.block or nil end end local i = r and r.internal or 0 -- brrr if r and kind and name then local tag = tags.getid(kind,name) if tag and tag ~= "?" then r.tag = tag -- todo: use internal ... is unique enough end end local p = pushed[i] if not p then p = #cached + 1 cached[p] = helpers.simplify(t) pushed[i] = p if r then r.listindex = p end end if group then groupindices[name][group] = p end if trace_lists then report_lists("added %a, internal %a",name,p) end return p end function lists.discard(n) n = tonumber(n) if not n then -- maybe an error message elseif n == #cached then cached[n] = nil n = n - 1 while n > 0 and cached[n] == false do cached[n] = nil -- collect garbage n = n - 1 end else cached[n] = false end end function lists.iscached(n) return cached[tonumber(n)] end -- this is the main pagenumber enhancer local enhanced = { } local synchronizepage = function(r) -- bah ... will move synchronizepage = references.synchronizepage return synchronizepage(r) end local function enhancelist(specification) local n = specification.n local l = cached[n] if not l then report_lists("enhancing %a, unknown internal",n) elseif enhanced[n] then if trace_lists then report_lists("enhancing %a, name %a, duplicate ignored",n,name) end else local metadata = l.metadata local references = l.references -- l.directives = nil -- might change -- save in the right order (happens at shipout) lists.tobesaved[#lists.tobesaved+1] = l -- default enhancer (cross referencing) synchronizepage(references) -- tags local kind = metadata.kind local name = metadata.name if trace_lists then report_lists("enhancing %a, name %a, page %a",n,name,references.realpage or 0) end -- if references then -- -- is this used ? -- local tag = tags.getid(kind,name) -- if tag and tag ~= "?" then -- references.tag = tag -- end -- end -- specific enhancer (kind of obsolete) local enhancer = kind and lists.enhancers[kind] if enhancer then enhancer(l) end -- enhanced[n] = true return l end end lists.enhance = enhancelist -- we can use level instead but we can also decide to remove level from the metadata local nesting = { } function lists.pushnesting(i) local parent = lists.result[i] local name = parent.metadata.name local numberdata = parent and parent.numberdata local numbers = numberdata and numberdata.numbers local number = numbers and numbers[getsectionlevel(name)] or 0 insert(nesting, { number = number, name = name, result = lists.result, parent = parent }) end function lists.popnesting() local old = remove(nesting) if old then lists.result = old.result else report_lists("nesting error") end end -- Historically we had blocks but in the mkiv approach that could as well be a level -- which would simplify things a bit. local splitter = lpeg.splitat(":") -- maybe also :: or have a block parameter local listsorters = { [v_command] = function(a,b) if a.metadata.kind == "command" or b.metadata.kind == "command" then return a.references.internal < b.references.internal else return a.references.order < b.references.order end end, [v_all] = function(a,b) return a.references.internal < b.references.internal end, [v_title] = function(a,b) local da = a.titledata local db = b.titledata if da and db then local ta = da.title local tb = db.title if ta and tb then local sa = da.split if not sa then sa = sortsplitter(sortstripper(ta)) da.split = sa end local sb = db.split if not sb then sb = sortsplitter(sortstripper(tb)) db.split = sb end return sortcomparer(da,db) == -1 end end return a.references.internal < b.references.internal end } -- was: names, criterium, number, collected, forced, nested, sortorder local filters = setmetatableindex(function(t,k) return t[v_default] end) local function filtercollected(specification) -- local names = specification.names or { } local criterium = specification.criterium or v_default local number = 0 -- specification.number local reference = specification.reference or "" local collected = specification.collected or lists.collected local forced = specification.forced or { } local nested = specification.nested or false local sortorder = specification.sortorder or specification.order -- local numbers = documents.data.numbers local depth = documents.data.depth local block = false -- all local wantedblock, wantedcriterium = lpegmatch(splitter,criterium) -- block:criterium if wantedblock == "" or wantedblock == v_all or wantedblock == v_text then criterium = wantedcriterium ~= "" and wantedcriterium or criterium elseif not wantedcriterium then block = documents.data.block else block = wantedblock criterium = wantedcriterium end if block == "" then block = false end if type(names) == "string" then names = settings_to_set(names) end local all = not next(names) or names[v_all] or false -- specification.names = names specification.criterium = criterium specification.number = 0 -- obsolete specification.reference = reference -- new specification.collected = collected specification.forced = forced -- todo: also on other branched, for the moment only needed for bookmarks specification.nested = nested specification.sortorder = sortorder specification.numbers = numbers specification.depth = depth specification.block = block specification.all = all -- if specification.atmost then criterium = v_text end -- if trace_lists then report_lists("filtering names %,t, criterium %a, block %a",sortedkeys(names), criterium, block or "*") end local result = filters[criterium](specification) if trace_lists then report_lists("criterium %a, block %a, found %a",specification.criterium, specification.block or "*", #result) end -- local levels = tonumber(specification.levels) if levels then local minlevel = 1000 local found = result local nofresult = #result for i=1,nofresult do local v = found[i] local l = v.metadata.level or 1 if l < minlevel then minlevel = l end end local maxlevel = minlevel + levels - 1 result = { } nofresult = 0 for i=1,#found do local v = found[i] local l = v.metadata.level or 1 if l >= minlevel and l <= maxlevel then nofresult = nofresult + 1 result[nofresult] = v end end end -- if sortorder then -- experiment local sorter = listsorters[sortorder] if sorter then if trace_lists then report_lists("sorting list using method %a",sortorder) end for i=1,#result do result[i].references.order = i end sort(result,sorter) end end -- return result end filters[v_intro] = function(specification) local collected = specification.collected local result = { } local nofresult = 0 local all = specification.all local names = specification.names for i=1,#collected do local v = collected[i] local metadata = v.metadata if metadata and (all or names[metadata.name or false]) then local r = v.references if r and r.section == 0 then nofresult = nofresult + 1 result[nofresult] = v end end end return result end filters[v_reference] = function(specification) local collected = specification.collected local result = { } local nofresult = 0 local names = specification.names local sections = sections.collected local reference = specification.reference if reference ~= "" then local prefix, rest = lpegmatch(references.prefixsplitter,reference) -- p::r local r = prefix and rest and references.derived[prefix][rest] or references.derived[""][reference] local s = r and r.numberdata -- table ref ! if s then local depth = getsectionlevel(r.metadata.name) local numbers = s.numbers for i=1,#collected do local v = collected[i] local r = v.references if r and (not block or not r.block or block == r.block) then local metadata = v.metadata if metadata and names[metadata.name or false] then local sectionnumber = (r.section == 0) or sections[r.section] if sectionnumber then if matchingtilldepth(depth,numbers,sectionnumber.numbers) then nofresult = nofresult + 1 result[nofresult] = v end end end end end else report_lists("unknown reference %a specified",reference) end else report_lists("no reference specified") end return result end filters[v_all] = function(specification) local collected = specification.collected local result = { } local nofresult = 0 local block = specification.block local all = specification.all local forced = specification.forced local names = specification.names local sections = sections.collected for i=1,#collected do local v = collected[i] local r = v.references if r and (not block or not r.block or block == r.block) then local metadata = v.metadata if metadata then local name = metadata.name or false local sectionnumber = (r.section == 0) or sections[r.section] if forced[name] or (sectionnumber and not metadata.nolist and (all or names[name])) then -- and not sectionnumber.hidenumber then nofresult = nofresult + 1 result[nofresult] = v end end end end return result end filters[v_text] = filters[v_all] filters[v_current] = function(specification) if specification.depth == 0 then specification.nested = false specification.criterium = v_intro return filters[v_intro](specification) end local collected = specification.collected local result = { } local nofresult = 0 local depth = specification.depth local block = specification.block local all = specification.all local names = specification.names local numbers = specification.numbers local sections = sections.collected for i=1,#collected do local v = collected[i] local r = v.references if r and (not block or not r.block or block == r.block) then local sectionnumber = sections[r.section] if sectionnumber then -- and not sectionnumber.hidenumber then local cnumbers = sectionnumber.numbers local metadata = v.metadata if cnumbers then if metadata and not metadata.nolist and (all or names[metadata.name or false]) and #cnumbers > depth then local ok = true for d=1,depth do local cnd = cnumbers[d] if not (cnd == 0 or cnd == numbers[d]) then ok = false break end end if ok then nofresult = nofresult + 1 result[nofresult] = v end end end end end end return result end filters[v_here] = function(specification) -- this is quite dirty ... as cnumbers is not sparse we can misuse #cnumbers if specification.depth == 0 then specification.nested = false specification.criterium = v_intro return filters[v_intro](specification) end local collected = specification.collected local result = { } local nofresult = 0 local depth = specification.depth local block = specification.block local all = specification.all local names = specification.names local numbers = specification.numbers local sections = sections.collected for i=1,#collected do local v = collected[i] local r = v.references if r then -- and (not block or not r.block or block == r.block) then local sectionnumber = sections[r.section] if sectionnumber then -- and not sectionnumber.hidenumber then local cnumbers = sectionnumber.numbers local metadata = v.metadata if cnumbers then if metadata and not metadata.nolist and (all or names[metadata.name or false]) and #cnumbers >= depth then local ok = true for d=1,depth do local cnd = cnumbers[d] if not (cnd == 0 or cnd == numbers[d]) then ok = false break end end if ok then nofresult = nofresult + 1 result[nofresult] = v end end end end end end return result end filters[v_previous] = function(specification) if specification.depth == 0 then specification.nested = false specification.criterium = v_intro return filters[v_intro](specification) end local collected = specification.collected local result = { } local nofresult = 0 local block = specification.block local all = specification.all local names = specification.names local numbers = specification.numbers local sections = sections.collected local depth = specification.depth for i=1,#collected do local v = collected[i] local r = v.references if r and (not block or not r.block or block == r.block) then local sectionnumber = sections[r.section] if sectionnumber then -- and not sectionnumber.hidenumber then local cnumbers = sectionnumber.numbers local metadata = v.metadata if cnumbers then if metadata and not metadata.nolist and (all or names[metadata.name or false]) and #cnumbers >= depth then local ok = true for d=1,depth-1 do local cnd = cnumbers[d] if not (cnd == 0 or cnd == numbers[d]) then ok = false break end end if ok then nofresult = nofresult + 1 result[nofresult] = v end end end end end end return result end filters[v_local] = function(specification) local numbers = specification.numbers local nested = nesting[#nesting] if nested then return filtercollected { names = specification.names, criterium = nested.name, collected = specification.collected, forced = specification.forced, nested = nested, sortorder = specification.sortorder, } else specification.criterium = autosectiondepth(numbers) == 0 and v_all or v_current specification.nested = false return filtercollected(specification) -- rechecks, so better (for determining all) end end filters[v_component] = function(specification) -- special case, no structure yet local collected = specification.collected local result = { } local nofresult = 0 local all = specification.all local names = specification.names local component = resolvers.jobs.currentcomponent() or "" if component ~= "" then for i=1,#collected do local v = collected[i] local r = v.references local m = v.metadata if r and r.component == component and (m and names[m.name] or all) then nofresult = nofresult + 1 result[nofresult] = v end end end return result end -- local number = tonumber(number) or numberatdepth(depth) or 0 -- if number > 0 then -- ... -- end filters[v_default] = function(specification) -- is named local collected = specification.collected local result = { } local nofresult = 0 ----- depth = specification.depth local block = specification.block local criterium = specification.criterium local all = specification.all local names = specification.names local numbers = specification.numbers local sections = sections.collected local reference = specification.reference local nested = specification.nested -- if reference then reference = tonumber(reference) end -- local depth = getsectionlevel(criterium) local pnumbers = nil local pblock = block local parent = nested and nested.parent -- if parent then pnumbers = parent.numberdata.numbers or pnumbers -- so local as well as nested pblock = parent.references.block or pblock if trace_lists then report_lists("filtering by block %a and section %a",pblock,criterium) end end -- for i=1,#collected do local v = collected[i] local r = v.references -- inspect(v) if r and (not block or not r.block or pblock == r.block) then local sectionnumber = sections[r.section] if sectionnumber then local metadata = v.metadata local cnumbers = sectionnumber.numbers if cnumbers then if all or names[metadata.name or false] then if reference then -- filter by number if reference == cnumbers[depth] then nofresult = nofresult + 1 result[nofresult] = v end else if #cnumbers >= depth and matchingtilldepth(depth,cnumbers,pnumbers) then nofresult = nofresult + 1 result[nofresult] = v end end end end end end end return result end -- names, criterium, number, collected, forced, nested, sortorder) -- names is hash or string lists.filter = filtercollected lists.result = { } function lists.getresult(r) return lists.result[r] end function lists.process(specification) local result = filtercollected(specification) local total = #result lists.result = result if total > 0 then local usedinternals = references.usedinternals local usedviews = references.usedviews local specials = settings_to_set(specification.extras or "") specials = next(specials) and specials or nil for i=1,total do local listentry = result[i] local metadata = listentry.metadata local numberdata = listentry.numberdata local references = listentry.references local special = specials and numberdata and specials[zerostrippedconcat(numberdata.numbers,".")] or "" if cheat and references then -- this permits runs=2 with interactivity local internal = references.internal usedinternals[internal] = true usedviews [internal] = references.view end context.strclistsentryprocess(metadata.name,metadata.kind,i,special) end end end function lists.analyze(specification) lists.result = filtercollected(specification) end function lists.userdata(name,r,tag) -- to tex (todo: xml) local result = lists.result[r] if result then local userdata = result.userdata local str = userdata and userdata[tag] if str then return str, result.metadata end end end function lists.uservalue(name,r,tag,default) -- to lua local str = lists.result[r] if str then str = str.userdata end if str then str = str[tag] end return str or default end function lists.size() return #lists.result end function lists.location(n) local l = lists.result[n] return l and l.references.internal or n end function lists.label(n,default) local l = lists.result[n] local t = l.titledata return t and t.label or default or "" end function lists.sectionnumber(name,n,spec) local data = lists.result[n] local sectiondata = sections.collected[data.references.section] -- hm, prefixnumber? typesetnumber(sectiondata,"prefix",spec,sectiondata) -- data happens to contain the spec too end -- some basics (todo: helpers for pages) function lists.title(name,n,tag) -- tag becomes obsolete local data = lists.result[n] if data then local titledata = data.titledata if titledata then helpers.title(titledata[tag] or titledata.list or titledata.title or "",data.metadata) end end end function lists.hastitledata(name,n,tag) local data = cached[tonumber(n)] if data then local titledata = data.titledata if titledata then return (titledata[tag] or titledata.title or "") ~= "" end end return false end function lists.haspagedata(name,n) local data = lists.result[n] if data then local references = data.references if references and references.realpage then -- or references.pagedata return true end end return false end function lists.hasnumberdata(name,n) local data = lists.result[n] if data then local numberdata = data.numberdata if numberdata and not numberdata.hidenumber then -- the hide number is true return true end end return false end function lists.rawnumber(n,name) local data = lists.result[n] if data then local numberdata = data.numberdata if numberdata then numberdata = numberdata.numbers return numberdata and numberdata[getsectionlevel(name)] or numberdata[name] or 0 end end return 0 end function lists.prefix(name,n,spec) helpers.prefix(lists.result[n],spec) end function lists.page(name,n,pagespec) helpers.page(lists.result[n],pagespec) end function lists.prefixedpage(name,n,prefixspec,pagespec) helpers.prefixpage(lists.result[n],prefixspec,pagespec) end function lists.realpage(name,n) local data = lists.result[n] if data then local references = data.references return references and references.realpage or 0 else return 0 end end -- numbers stored in entry.numberdata + entry.numberprefix function lists.number(name,n,spec) local data = lists.result[n] if data then local numberdata = data.numberdata if numberdata then typesetnumber(numberdata,"number",spec or false,numberdata or false) end end end function lists.prefixednumber(name,n,prefixspec,numberspec,forceddata) local data = lists.result[n] if data then helpers.prefix(data,prefixspec) local numberdata = data.numberdata or forceddata if numberdata then typesetnumber(numberdata,"number",numberspec or false,numberdata or false) end end end -- todo, do this in references namespace ordered instead (this is an experiment) -- -- also see lpdf-ano (maybe move this there) local splitter = lpeg.splitat(":") function references.specials.order(var,actions) -- references.specials ! local operation = var.operation if operation then local kind, name, n = lpegmatch(splitter,operation) local order = lists.ordered[kind] order = order and order[name] local v = order[tonumber(n)] local r = v and v.references.realpage if r then actions.realpage = r var.operation = r -- brrr, but test anyway return references.specials.page(var,actions) end end end -- interface (maybe strclistpush etc) if not lists.reordered then function lists.reordered(data) return data.numberdata end end implement { name = "pushlist", actions = lists.pushnesting, arguments = "integer" } implement { name = "poplist", actions = lists.popnesting } implement { name = "addtolist", actions = { lists.addto, context }, arguments = { { { "references", { { "internal", "integer" }, { "block" }, { "section", "integer" }, { "location" }, { "prefix" }, { "reference" }, { "view" }, { "order", "integer" }, } }, { "metadata", { { "kind" }, { "name" }, { "level", "integer" }, { "catcodes", "integer" }, { "coding" }, { "xmlroot" }, { "setup" }, } }, { "userdata" }, { "titledata", { { "label" }, { "title" }, { "bookmark" }, { "marking" }, { "list" }, { "reference" }, } }, { "prefixdata", { { "prefix" }, { "separatorset" }, { "conversionset" }, { "conversion" }, { "set" }, { "segments" }, { "connector" }, } }, { "numberdata", { { "level", "integer" }, { "numbers" }, { "groupsuffix" }, { "group" }, { "counter" }, { "separatorset" }, { "conversionset" }, { "conversion" }, { "starter" }, { "stopper" }, { "segments" }, } } } } } implement { name = "enhancelist", arguments = "integer", actions = function(n) enhancelist { n = n } end } implement { name = "deferredenhancelist", arguments = "integer", protected = true, -- for now, pre 1.09 actions = function(n) ctx_latelua { action = enhancelist, n = n } end, } implement { name = "processlist", actions = lists.process, arguments = { { { "names" }, { "criterium" }, { "reference" }, { "extras" }, { "order" }, { "levels" }, } } } implement { name = "analyzelist", actions = lists.analyze, arguments = { { { "names" }, { "criterium" }, { "reference" }, } } } implement { name = "listtitle", actions = lists.title, arguments = { "string", "integer" } } implement { name = "listprefixednumber", actions = lists.prefixednumber, arguments = { "string", "integer", { { "prefix" }, { "separatorset" }, { "conversionset" }, { "starter" }, { "stopper" }, { "set" }, { "segments" }, { "connector" }, }, { { "separatorset" }, { "conversionset" }, { "starter" }, { "stopper" }, { "segments" }, } } } implement { name = "listprefixedpage", actions = lists.prefixedpage, arguments = { "string", "integer", { { "separatorset" }, { "conversionset" }, { "set" }, { "segments" }, { "connector" }, }, { { "prefix" }, { "conversionset" }, { "starter" }, { "stopper" }, } } } implement { name = "listsize", actions = { lists.size, context } } implement { name = "listlocation", actions = { lists.location, context }, arguments = "integer" } implement { name = "listlabel", actions = { lists.label, context }, arguments = { "integer", "string" } } implement { name = "listrealpage", actions = { lists.realpage, context }, arguments = { "string", "integer" } } implement { name = "listgroupindex", actions = { lists.groupindex, context }, arguments = "2 strings", } implement { name = "currentsectiontolist", actions = { sections.current, lists.addto, context } } local function userdata(name,r,tag) local str, metadata = lists.userdata(name,r,tag) if str then -- local catcodes = metadata and metadata.catcodes -- if catcodes then -- context.sprint(catcodes,str) -- else -- context(str) -- end helpers.title(str,metadata) end end implement { name = "listuserdata", actions = userdata, arguments = { "string", "integer", "string" } } -- we could also set variables .. names will change (when this module is done) -- maybe strc_lists_savedtitle etc implement { name = "doifelselisthastitle", actions = { lists.hastitledata, commands.doifelse }, arguments = { "string", "integer" } } implement { name = "doifelselisthaspage", actions = { lists.haspagedata, commands.doifelse }, arguments = { "string", "integer" } } implement { name = "doifelselisthasnumber", actions = { lists.hasnumberdata, commands.doifelse }, arguments = { "string", "integer" } } implement { name = "doifelselisthasentry", actions = { lists.iscached, commands.doifelse }, arguments = "integer" } local function savedlisttitle(name,n,tag) local data = cached[tonumber(n)] if data then local titledata = data.titledata if titledata then helpers.title(titledata[tag] or titledata.title or "",data.metadata) end end end local function savedlistnumber(name,n) local data = cached[tonumber(n)] if data then local numberdata = data.numberdata if numberdata then typesetnumber(numberdata,"number",numberdata or false) end end end local function savedlistprefixednumber(name,n) local data = cached[tonumber(n)] if data then local numberdata = lists.reordered(data) if numberdata then helpers.prefix(data,data.prefixdata) typesetnumber(numberdata,"number",numberdata or false) end end end lists.savedlisttitle = savedlisttitle lists.savedlistnumber = savedlistnumber lists.savedlistprefixednumber = savedlistprefixednumber implement { name = "savedlistnumber", actions = savedlistnumber, arguments = { "string", "integer" } } implement { name = "savedlisttitle", actions = savedlisttitle, arguments = { "string", "integer" } } implement { name = "savedlistprefixednumber", actions = savedlistprefixednumber, arguments = { "string", "integer" } } implement { name = "discardfromlist", actions = lists.discard, arguments = "integer" } implement { name = "rawlistnumber", actions = { lists.rawnumber, context }, arguments = { "integer", "string" }, } -- new and experimental and therefore off by default lists.autoreorder = false -- true local function addlevel(t,k) local v = { } setmetatableindex(v,function(t,k) local v = { } t[k] = v return v end) t[k] = v return v end local internals = setmetatableindex({ }, function(t,k) local sublists = setmetatableindex({ },addlevel) local collected = lists.collected or { } for i=1,#collected do local entry = collected[i] local numberdata = entry.numberdata if numberdata then local metadata = entry.metadata if metadata then local references = entry.references if references then local kind = metadata.kind local name = numberdata.counter or metadata.name local internal = references.internal if kind and name and internal then local sublist = sublists[kind][name] sublist[#sublist + 1] = { internal, numberdata } end end end end end for k, v in next, sublists do for k, v in next, v do local tmp = { } for i=1,#v do tmp[i] = v[i] end sort(v,function(a,b) return a[1] < b[1] end) for i=1,#v do t[v[i][1]] = tmp[i][2] end end end setmetatableindex(t,nil) return t[k] end) function lists.reordered(entry) local numberdata = entry.numberdata if lists.autoreorder then if numberdata then local metadata = entry.metadata if metadata then local references = entry.references if references then local kind = metadata.kind local name = numberdata.counter or metadata.name local internal = references.internal if kind and name and internal then return internals[internal] or numberdata end end end end else function lists.reordered(entry) return entry.numberdata end end return numberdata end