summaryrefslogtreecommitdiff
path: root/tex/context/base/mkxl/strc-lst.lmt
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/mkxl/strc-lst.lmt')
-rw-r--r--tex/context/base/mkxl/strc-lst.lmt1512
1 files changed, 1512 insertions, 0 deletions
diff --git a/tex/context/base/mkxl/strc-lst.lmt b/tex/context/base/mkxl/strc-lst.lmt
new file mode 100644
index 000000000..467d916d8
--- /dev/null
+++ b/tex/context/base/mkxl/strc-lst.lmt
@@ -0,0 +1,1512 @@
+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_product = variables.product
+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
+
+-- maybe:
+--
+-- 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 split = references.splitreference(reference)
+-- if split then
+-- local prefix = split and split.outer
+-- local rest = split and split.inner
+-- if prefix and rest then
+-- local d = references.derived
+-- local r = (d[prefix] and d[prefix][rest]) or (d[""] and d[""][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
+-- end
+-- 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
+
+filters[v_product] = function(specification)
+ local reference = specification.reference
+ if reference and reference ~= "" then
+-- local utilitydata = job.loadother(reference,true)
+ local fullname = file.replacesuffix(reference,"tuc")
+ if lfs.isfile(fullname) then
+ local utilitydata = job.loadother(fullname)
+ if utilitydata then
+ local collected = utilitydata.structures.lists.collected or { }
+ setmetatableindex(collected,{ external = reference })
+ return collected
+ end
+ end
+ end
+ return { }
+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.external(n)
+ return lists.result.external or ""
+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 = "listexternal", actions = { lists.external, context }, arguments = "integer" }
+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