diff options
Diffstat (limited to 'tex/context/base/mkxl/strc-lst.lmt')
-rw-r--r-- | tex/context/base/mkxl/strc-lst.lmt | 1512 |
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 |