diff options
Diffstat (limited to 'tex/context/base/strc-ref.lua')
-rw-r--r-- | tex/context/base/strc-ref.lua | 1576 |
1 files changed, 1039 insertions, 537 deletions
diff --git a/tex/context/base/strc-ref.lua b/tex/context/base/strc-ref.lua index 938af1ad7..2a1d0dd59 100644 --- a/tex/context/base/strc-ref.lua +++ b/tex/context/base/strc-ref.lua @@ -16,7 +16,7 @@ if not modules then modules = { } end modules ['strc-ref'] = { local format, find, gmatch, match, strip = string.format, string.find, string.gmatch, string.match, string.strip local floor = math.floor -local rawget, tonumber = rawget, tonumber +local rawget, tonumber, type = rawget, tonumber, type local lpegmatch = lpeg.match local insert, remove, copytable = table.insert, table.remove, table.copy local formatters = string.formatters @@ -44,19 +44,21 @@ local report_importing = logs.reporter("references","importing") local report_empty = logs.reporter("references","empty") local variables = interfaces.variables -local constants = interfaces.constants +local v_default = variables.default +local v_url = variables.url +local v_file = variables.file +local v_unknown = variables.unknown +local v_page = variables.page +local v_auto = variables.auto + local context = context local commands = commands +local implement = interfaces.implement local texgetcount = tex.getcount local texsetcount = tex.setcount local texconditionals = tex.conditionals -local v_default = variables.default -local v_url = variables.url -local v_file = variables.file -local v_unknown = variables.unknown -local v_yes = variables.yes local productcomponent = resolvers.jobs.productcomponent local justacomponent = resolvers.jobs.justacomponent @@ -75,6 +77,8 @@ local references = structures.references local lists = structures.lists local counters = structures.counters +local jobpositions = job.positions + -- some might become local references.defined = references.defined or allocate() @@ -82,6 +86,7 @@ references.defined = references.defined or allocate() local defined = references.defined local derived = allocate() local specials = allocate() +local functions = allocate() local runners = allocate() local internals = allocate() local filters = allocate() @@ -91,9 +96,13 @@ local tobesaved = allocate() local collected = allocate() local tobereferred = allocate() local referred = allocate() +local usedinternals = allocate() +local flaginternals = allocate() +local usedviews = allocate() references.derived = derived references.specials = specials +references.functions = functions references.runners = runners references.internals = internals references.filters = filters @@ -103,6 +112,9 @@ references.tobesaved = tobesaved references.collected = collected references.tobereferred = tobereferred references.referred = referred +references.usedinternals = usedinternals +references.flaginternals = flaginternals +references.usedviews = usedviews local splitreference = references.splitreference local splitprefix = references.splitcomponent -- replaces: references.splitprefix @@ -111,6 +123,22 @@ local componentsplitter = references.componentsplitter local currentreference = nil +local txtcatcodes = catcodes.numbers.txtcatcodes -- or just use "txtcatcodes" +local context_delayed = context.delayed + +local ctx_pushcatcodes = context.pushcatcodes +local ctx_popcatcodes = context.popcatcodes +local ctx_dofinishreference = context.dofinishreference +local ctx_dofromurldescription = context.dofromurldescription +local ctx_dofromurlliteral = context.dofromurlliteral +local ctx_dofromfiledescription = context.dofromfiledescription +local ctx_dofromfileliteral = context.dofromfileliteral +local ctx_expandreferenceoperation = context.expandreferenceoperation +local ctx_expandreferencearguments = context.expandreferencearguments +local ctx_getreferencestructureprefix = context.getreferencestructureprefix +local ctx_convertnumber = context.convertnumber +local ctx_emptyreference = context.emptyreference + storage.register("structures/references/defined", references.defined, "structures.references.defined") local initializers = { } @@ -119,6 +147,7 @@ local finalizers = { } function references.registerinitializer(func) -- we could use a token register instead initializers[#initializers+1] = func end + function references.registerfinalizer(func) -- we could use a token register instead finalizers[#finalizers+1] = func end @@ -129,12 +158,32 @@ local function initializer() -- can we use a tobesaved as metatable for collecte for i=1,#initializers do initializers[i](tobesaved,collected) end + for prefix, list in next, collected do + for tag, data in next, list do + local r = data.references + local i = r.internal + if i then + internals[i] = data + usedinternals[i] = r.used + end + end + end end local function finalizer() for i=1,#finalizers do finalizers[i](tobesaved) end + for prefix, list in next, tobesaved do + for tag, data in next, list do + local r = data.references + local i = r.internal + local f = flaginternals[i] + if f then + r.used = usedviews[i] or true + end + end + end end job.register('structures.references.collected', tobesaved, initializer, finalizer) @@ -148,6 +197,38 @@ local function initializer() -- can we use a tobesaved as metatable for collecte nofreferred = #referred end +-- no longer fone this way + +-- references.resolvers = references.resolvers or { } +-- local resolvers = references.resolvers +-- +-- function resolvers.section(var) +-- local vi = lists.collected[var.i[2]] +-- if vi then +-- var.i = vi +-- var.r = (vi.references and vi.references.realpage) or (vi.pagedata and vi.pagedata.realpage) or 1 +-- else +-- var.i = nil +-- var.r = 1 +-- end +-- end +-- +-- resolvers.float = resolvers.section +-- resolvers.description = resolvers.section +-- resolvers.formula = resolvers.section +-- resolvers.note = resolvers.section +-- +-- function resolvers.reference(var) +-- local vi = var.i[2] +-- if vi then +-- var.i = vi +-- var.r = (vi.references and vi.references.realpage) or (vi.pagedata and vi.pagedata.realpage) or 1 +-- else +-- var.i = nil +-- var.r = 1 +-- end +-- end + -- We make the array sparse (maybe a finalizer should optionally return a table) because -- there can be quite some page links involved. We only store one action number per page -- which is normally good enough for what we want (e.g. see above/below) and we do @@ -215,8 +296,6 @@ local function referredpage(n) return texgetcount("realpageno") end --- setmetatableindex(referred,function(t,k) return referredpage(k) end ) - references.referredpage = referredpage function references.registerpage(n) -- called in the backend code @@ -246,16 +325,15 @@ local function setnextorder(kind,name) texsetcount("global","locationorder",lastorder) end -references.setnextorder = setnextorder -function references.setnextinternal(kind,name) +local function setnextinternal(kind,name) setnextorder(kind,name) -- always incremented with internal local n = texgetcount("locationcount") + 1 texsetcount("global","locationcount",n) return n end -function references.currentorder(kind,name) +local function currentorder(kind,name) return orders[kind] and orders[kind][name] or lastorder end @@ -266,43 +344,52 @@ local function setcomponent(data) local references = data and data.references if references then references.component = component + if references.prefix == component then + references.prefix = nil + end end return component end -- but for the moment we do it here (experiment) end -commands.setnextinternalreference = references.setnextinternal +references.setnextorder = setnextorder +references.setnextinternal = setnextinternal +references.currentorder = currentorder +references.setcomponent = setcomponent -function commands.currentreferenceorder(kind,name) - context(references.currentorder(kind,name)) -end +implement { + name = "setnextreferenceorder", + actions = setnextorder, + arguments = { "string", "string" } +} -references.setcomponent = setcomponent +implement { + name = "setnextinternalreference", + actions = setnextinternal, + arguments = { "string", "string" } +} + +implement { + name = "currentreferenceorder", + actions = { currentorder, context }, + arguments = { "string", "string" } +} -function references.set(kind,prefix,tag,data) --- setcomponent(data) - local pd = tobesaved[prefix] -- nicer is a metatable +function references.set(data) + local references = data.references + local reference = references.reference + if not reference or reference == "" then + -- report_references("invalid reference") -- harmless + return 0 + end + local prefix = references.prefix or "" + local pd = tobesaved[prefix] -- nicer is a metatable if not pd then pd = { } tobesaved[prefix] = pd end local n = 0 - -- for ref in gmatch(tag,"[^,]+") do - -- if ref ~= "" then - -- if check_duplicates and pd[ref] then - -- if prefix and prefix ~= "" then - -- report_references("redundant reference %a in namespace %a",ref,prefix) - -- else - -- report_references("redundant reference %a",ref) - -- end - -- else - -- n = n + 1 - -- pd[ref] = data - -- context.dofinishsomereference(kind,prefix,ref) - -- end - -- end - -- end local function action(ref) if ref == "" then -- skip @@ -315,145 +402,201 @@ function references.set(kind,prefix,tag,data) else n = n + 1 pd[ref] = data - context.dofinishsomereference(kind,prefix,ref) + local r = data.references + ctx_dofinishreference(prefix or "",ref or "",r and r.internal or 0) end end - process_settings(tag,action) + process_settings(reference,action) return n > 0 end +-- function references.enhance(prefix,tag) +-- local l = tobesaved[prefix][tag] +-- if l then +-- l.references.realpage = texgetcount("realpageno") +-- end +-- end + +local getpos = function() getpos = backends.codeinjections.getpos return getpos () end + +local function synchronizepage(reference) -- non public helper + reference.realpage = texgetcount("realpageno") + if jobpositions.used then + reference.x, reference.y = getpos() + end +end + +references.synchronizepage = synchronizepage + function references.enhance(prefix,tag) local l = tobesaved[prefix][tag] if l then - l.references.realpage = texgetcount("realpageno") + synchronizepage(l.references) end end -commands.enhancereference = references.enhance +implement { + name = "enhancereference", + actions = references.enhance, + arguments = { "string", "string" } +} -- -- -- related to strc-ini.lua -- -- -- -references.resolvers = references.resolvers or { } -local resolvers = references.resolvers +-- no metatable here .. better be sparse -local function getfromlist(var) - local vi = var.i - if vi then - vi = vi[3] or lists.collected[vi[2]] - if vi then - local r = vi.references and vi.references - if r then - r = r.realpage - end - if not r then - r = vi.pagedata and vi.pagedata - if r then - r = r.realpage +local function register_from_lists(collected,derived,pages,sections) + local derived_g = derived[""] -- global + local derived_p = nil + local derived_c = nil + local prefix = nil + local component = nil + local entry = nil + if not derived_g then + derived_g = { } + derived[""] = derived_g + end + local function action(s) + if trace_referencing then + report_references("list entry %a provides %a reference %a on realpage %a",i,kind,s,realpage) + end + if derived_p and not derived_p[s] then + derived_p[s] = entry + end + if derived_c and not derived_c[s] then + derived_c[s] = entry + end + if not derived_g[s] then + derived_g[s] = entry -- first wins + end + end + for i=1,#collected do + entry = collected[i] + local metadata = entry.metadata + if metadata then + local kind = metadata.kind -- why this check + if kind then + local references = entry.references + if references then + local reference = references.reference + if reference and reference ~= "" then + local realpage = references.realpage + if realpage then + prefix = references.prefix + component = references.component + if prefix and prefix ~= "" then + derived_p = derived[prefix] + if not derived_p then + derived_p = { } + derived[prefix] = derived_p + end + end + if component and component ~= "" and component ~= prefix then + derived_c = derived[component] + if not derived_c then + derived_c = { } + derived[component] = derived_c + end + end + process_settings(reference,action) + end + end end end - var.i = vi - var.r = r or 1 - else - var.i = nil - var.r = 1 end - else - var.i = nil - var.r = 1 end + -- inspect(derived) end --- resolvers.section = getfromlist --- resolvers.float = getfromlist --- resolvers.description = getfromlist --- resolvers.formula = getfromlist --- resolvers.note = getfromlist - -setmetatableindex(resolvers,function(t,k) - local v = getfromlist - resolvers[k] = v - return v -end) - -function resolvers.reference(var) - local vi = var.i[2] -- check - if vi then - var.i = vi - var.r = (vi.references and vi.references.realpage) or (vi.pagedata and vi.pagedata.realpage) or 1 - else - var.i = nil - var.r = 1 - end -end +references.registerinitializer(function() register_from_lists(lists.collected,derived) end) -local function register_from_lists(collected,derived,pages,sections) - local g = derived[""] if not g then g = { } derived[""] = g end -- global - for i=1,#collected do - local entry = collected[i] - local m, r = entry.metadata, entry.references - if m and r then - local reference = r.reference or "" - local prefix = r.referenceprefix or "" - local component = r.component and r.component or "" - if reference ~= "" then - local kind, realpage = m.kind, r.realpage - if kind and realpage then - local d = derived[prefix] - if not d then - d = { } - derived[prefix] = d +-- tracing + +local function collectbypage(tracedpages) + -- lists + do + local collected = structures.lists.collected + local data = nil + local function action(reference) + local prefix = data.prefix + local component = data.component + local realpage = data.realpage + if realpage then + local pagelist = rawget(tracedpages,realpage) + local internal = data.internal or 0 + local prefix = (prefix ~= "" and prefix) or (component ~= "" and component) or "" + local pagedata = { prefix, reference, internal } + if pagelist then + pagelist[#pagelist+1] = pagedata + else + tracedpages[realpage] = { pagedata } + end + if internal > 0 then + data.usedprefix = prefix + end + end + end + for i=1,#collected do + local entry = collected[i] + local metadata = entry.metadata + if metadata and metadata.kind then + data = entry.references + if data then + local reference = data.reference + if reference and reference ~= "" then + process_settings(reference,action) end - local c = derived[component] - if not c then - c = { } - derived[component] = c + end + end + end + end + -- references + do + for prefix, list in next, collected do + for reference, entry in next, list do + local data = entry.references + if data then + local realpage = data.realpage + local internal = data.internal or 0 + local pagelist = rawget(tracedpages,realpage) + local pagedata = { prefix, reference, internal } + if pagelist then + pagelist[#pagelist+1] = pagedata + else + tracedpages[realpage] = { pagedata } end - local t = { kind, i, entry } - -- for s in gmatch(reference,"%s*([^,]+)") do - -- if trace_referencing then - -- report_references("list entry %a provides %a reference %a on realpage %a",i,kind,s,realpage) - -- end - -- c[s] = c[s] or t -- share them - -- d[s] = d[s] or t -- share them - -- g[s] = g[s] or t -- first wins - -- end - local function action(s) - if trace_referencing then - report_references("list entry %a provides %a reference %a on realpage %a",i,kind,s,realpage) - end - c[s] = c[s] or t -- share them - d[s] = d[s] or t -- share them - g[s] = g[s] or t -- first wins + if internal > 0 then + data.usedprefix = prefix end - process_settings(reference,action) end end end end --- inspect(derived) end -references.registerinitializer(function() register_from_lists(lists.collected,derived) end) +references.tracedpages = table.setmetatableindex(allocate(),function(t,k) + if collectbypage then + collectbypage(t) + collectbypage = nil + end + return rawget(t,k) +end) -- urls -references.urls = references.urls or { } -references.urls.data = references.urls.data or { } - -local urls = references.urls.data +local urls = references.urls or { } +references.urls = urls +local urldata = urls.data or { } +urls.data = urldata -function references.urls.define(name,url,file,description) +function urls.define(name,url,file,description) if name and name ~= "" then - urls[name] = { url or "", file or "", description or url or file or ""} + urldata[name] = { url or "", file or "", description or url or file or ""} end end -local pushcatcodes = context.pushcatcodes -local popcatcodes = context.popcatcodes -local txtcatcodes = catcodes.numbers.txtcatcodes -- or just use "txtcatcodes" - -function references.urls.get(name) - local u = urls[name] +function urls.get(name) + local u = urldata[name] if u then local url, file = u[1], u[2] if file and file ~= "" then @@ -464,59 +607,93 @@ function references.urls.get(name) end end -function commands.geturl(name) - local url = references.urls.get(name) +function urls.found(name) + return urldata[name] +end + +local function geturl(name) + local url = urls.get(name) if url and url ~= "" then - pushcatcodes(txtcatcodes) + ctx_pushcatcodes(txtcatcodes) context(url) - popcatcodes() + ctx_popcatcodes() end end --- function commands.gethyphenatedurl(name,...) --- local url = references.urls.get(name) --- if url and url ~= "" then --- hyphenatedurl(url,...) --- end --- end +implement { + name = "doifelseurldefined", + actions = { urls.found, commands.doifelse }, + arguments = "string" +} -function commands.doifurldefinedelse(name) - commands.doifelse(urls[name]) -end +implement { + name = "useurl", + actions = urls.define, + arguments = { "string", "string", "string", "string" } +} -commands.useurl= references.urls.define +implement { + name = "geturl", + actions = geturl, + arguments = "string", +} -- files -references.files = references.files or { } -references.files.data = references.files.data or { } - -local files = references.files.data +local files = references.files or { } +references.files = files +local filedata = files.data or { } +files.data = filedata -function references.files.define(name,file,description) +function files.define(name,file,description) if name and name ~= "" then - files[name] = { file or "", description or file or "" } + filedata[name] = { file or "", description or file or "" } end end -function references.files.get(name,method,space) -- method: none, before, after, both, space: yes/no - local f = files[name] +function files.get(name,method,space) -- method: none, before, after, both, space: yes/no + local f = filedata[name] if f then context(f[1]) end end -function commands.doiffiledefinedelse(name) - commands.doifelse(files[name]) +function files.found(name) + return filedata[name] end -commands.usefile= references.files.define +local function getfile(name) + local fil = files.get(name) + if fil and fil ~= "" then + ctx_pushcatcodes(txtcatcodes) + context(fil) + ctx_popcatcodes() + end +end + +implement { + name = "doifelsefiledefined", + actions = { files.found, commands.doifelse }, + arguments = "string" +} + +implement { + name = "usefile", + actions = files.define, + arguments = { "string", "string", "string" } +} + +implement { + name = "getfile", + actions = getfile, + arguments = "string" +} -- helpers function references.checkedfile(whatever) -- return whatever if not resolved if whatever then - local w = files[whatever] + local w = filedata[whatever] if w then return w[1] else @@ -527,7 +704,7 @@ end function references.checkedurl(whatever) -- return whatever if not resolved if whatever then - local w = urls[whatever] + local w = urldata[whatever] if w then local u, f = w[1], w[2] if f and f ~= "" then @@ -543,11 +720,11 @@ end function references.checkedfileorurl(whatever,default) -- return nil, nil if not resolved if whatever then - local w = files[whatever] + local w = filedata[whatever] if w then return w[1], nil else - local w = urls[whatever] + local w = urldata[whatever] if w then local u, f = w[1], w[2] if f and f ~= "" then @@ -563,25 +740,25 @@ end -- programs -references.programs = references.programs or { } -references.programs.data = references.programs.data or { } +local programs = references.programs or { } +references.programs = programs +local programdata = programs.data or { } +programs.data = programdata -local programs = references.programs.data - -function references.programs.define(name,file,description) +function programs.define(name,file,description) if name and name ~= "" then - programs[name] = { file or "", description or file or ""} + programdata[name] = { file or "", description or file or ""} end end -function references.programs.get(name) - local f = programs[name] +function programs.get(name) + local f = programdata[name] return f and f[1] end function references.checkedprogram(whatever) -- return whatever if not resolved if whatever then - local w = programs[whatever] + local w = programdata[whatever] if w then return w[1] else @@ -590,23 +767,33 @@ function references.checkedprogram(whatever) -- return whatever if not resolved end end -commands.defineprogram = references.programs.define +implement { + name = "defineprogram", + actions = programs.define, + arguments = { "string", "string", "string" } +} -function commands.getprogram(name) - local f = programs[name] - if f then - context(f[1]) +local function getprogram(name) + local p = programdata[name] + if p then + context(p[1]) end end +implement { + name = "getprogram", + actions = getprogram, + arguments = "string" +} + -- shared by urls and files -function references.whatfrom(name) - context((urls[name] and v_url) or (files[name] and v_file) or v_unknown) -end +-- function references.whatfrom(name) +-- context((urldata[name] and v_url) or (filedata[name] and v_file) or v_unknown) +-- end function references.from(name) - local u = urls[name] + local u = urldata[name] if u then local url, file, description = u[1], u[2], u[3] if description ~= "" then @@ -618,7 +805,7 @@ function references.from(name) return url end else - local f = files[name] + local f = filedata[name] if f then local file, description = f[1], f[2] if description ~= "" then @@ -630,34 +817,40 @@ function references.from(name) end end -function commands.from(name) - local u = urls[name] +local function from(name) + local u = urldata[name] if u then local url, file, description = u[1], u[2], u[3] if description ~= "" then - context.dofromurldescription(description) + ctx_dofromurldescription(description) -- ok elseif file and file ~= "" then - context.dofromurlliteral(url .. "/" .. file) + ctx_dofromurlliteral(url .. "/" .. file) else - context.dofromurlliteral(url) + ctx_dofromurlliteral(url) end else - local f = files[name] + local f = filedata[name] if f then local file, description = f[1], f[2] if description ~= "" then - context.dofromfiledescription(description) + ctx_dofromfiledescription(description) else - context.dofromfileliteral(file) + ctx_dofromfileliteral(file) end end end end +implement { + name = "from", + actions = from, + arguments = "string" +} + function references.define(prefix,reference,list) local d = defined[prefix] if not d then d = { } defined[prefix] = d end - d[reference] = { "defined", list } + d[reference] = list end function references.reset(prefix,reference) @@ -667,44 +860,34 @@ function references.reset(prefix,reference) end end -commands.definereference = references.define -commands.resetreference = references.reset - --- \primaryreferencefoundaction --- \secondaryreferencefoundaction --- \referenceunknownaction - --- t.special t.operation t.arguments t.outer t.inner +implement { + name = "definereference", + actions = references.define, + arguments = { "string", "string", "string" } +} --- to what extend do we check the non prefixed variant +implement { + name = "resetreference", + actions = references.reset, + arguments = { "string", "string" } +} -local strict = false +setmetatableindex(defined,"table") local function resolve(prefix,reference,args,set) -- we start with prefix,reference if reference and reference ~= "" then if not set then set = { prefix = prefix, reference = reference } else - set.reference = set.reference or reference - set.prefix = set.prefix or prefix + if not set.reference then set.reference = reference end + if not set.prefix then set.prefix = prefix end end local r = settings_to_array(reference) for i=1,#r do local ri = r[i] - local d - if strict then - d = defined[prefix] or defined[""] - d = d and d[ri] - else - d = defined[prefix] - d = d and d[ri] - if not d then - d = defined[""] - d = d and d[ri] - end - end + local d = defined[prefix][ri] or defined[""][ri] if d then - resolve(prefix,d[2],nil,set) + resolve(prefix,d,nil,set) else local var = splitreference(ri) if var then @@ -712,20 +895,10 @@ local function resolve(prefix,reference,args,set) -- we start with prefix,refere local vo, vi = var.outer, var.inner if not vo and vi then -- to be checked - if strict then - d = defined[prefix] or defined[""] - d = d and d[vi] - else - d = defined[prefix] - d = d and d[vi] - if not d then - d = defined[""] - d = d and d[vi] - end - end + d = defined[prefix][vi] or defined[""][vi] -- if d then - resolve(prefix,d[2],var.arguments,set) -- args can be nil + resolve(prefix,d,var.arguments,set) -- args can be nil else if args then var.arguments = args end set[#set+1] = var @@ -752,35 +925,47 @@ end references.currentset = nil -function commands.setreferenceoperation(k,v) +local function setreferenceoperation(k,v) references.currentset[k].operation = v end -function commands.setreferencearguments(k,v) +local function setreferencearguments(k,v) references.currentset[k].arguments = v end -local expandreferenceoperation = context.expandreferenceoperation -local expandreferencearguments = context.expandreferencearguments - function references.expandcurrent() -- todo: two booleans: o_has_tex& a_has_tex local currentset = references.currentset if currentset and currentset.has_tex then for i=1,#currentset do local ci = currentset[i] local operation = ci.operation - if operation and find(operation,"\\") then -- if o_has_tex then - expandreferenceoperation(i,operation) + if operation and find(operation,"\\",1,true) then -- if o_has_tex then + ctx_expandreferenceoperation(i,operation) end local arguments = ci.arguments - if arguments and find(arguments,"\\") then -- if a_has_tex then - expandreferencearguments(i,arguments) + if arguments and find(arguments,"\\",1,true) then -- if a_has_tex then + ctx_expandreferencearguments(i,arguments) end end end end -commands.expandcurrentreference = references.expandcurrent -- for the moment the same +implement { + name = "expandcurrentreference", + actions = references.expandcurrent +} + +implement { + name = "setreferenceoperation", + actions = setreferenceoperation, + arguments = { "integer", "string" } +} + +implement { + name = "setreferencearguments", + actions = setreferencearguments, + arguments = { "integer", "string" } +} local externals = { } @@ -824,7 +1009,7 @@ local function loadexternalreferences(name,utilitydata) local realpage = references.realpage if kind and realpage then references.pagedata = pages[realpage] - local prefix = references.referenceprefix or "" + local prefix = references.prefix or "" local target = external[prefix] if not target then target = { } @@ -856,8 +1041,8 @@ end local externalfiles = { } -table.setmetatableindex(externalfiles, function(t,k) - local v = files[k] +setmetatableindex(externalfiles, function(t,k) + local v = filedata[k] if not v then v = { k, k } end @@ -865,7 +1050,7 @@ table.setmetatableindex(externalfiles, function(t,k) return v end) -table.setmetatableindex(externals,function(t,k) -- either or not automatically +setmetatableindex(externals, function(t,k) -- either or not automatically local filename = externalfiles[k][1] -- filename local fullname = file.replacesuffix(filename,"tuc") if lfs.isfile(fullname) then -- todo: use other locator @@ -926,7 +1111,7 @@ local function loadproductreferences(productname,componentname,utilitydata) local realpage = references.realpage if kind and realpage then references.pagedata = pages[realpage] - local prefix = references.referenceprefix or "" + local prefix = references.prefix or "" local component = references.component local ctarget, ptarget if not component or component == componentname then @@ -952,22 +1137,6 @@ local function loadproductreferences(productname,componentname,utilitydata) ptarget = { } productreferences[prefix] = ptarget end - -- for s in gmatch(reference,"%s*([^,]+)") do - -- if ptarget then - -- if trace_importing then - -- report_importing("registering %s reference, kind %a, name %a, prefix %a, reference %a", - -- "product",kind,productname,prefix,s) - -- end - -- ptarget[s] = ptarget[s] or entry - -- end - -- if ctarget then - -- if trace_importing then - -- report_importing("registering %s reference, kind %a, name %a, prefix %a, referenc %a", - -- "component",kind,productname,prefix,s) - -- end - -- ctarget[s] = ctarget[s] or entry - -- end - -- end local function action(s) if ptarget then if trace_importing then @@ -1062,7 +1231,7 @@ references.registerinitializer(function(tobesaved,collected) productdata.components = componentlist(job.structure.collected) or { } end) -function structures.references.loadpresets(product,component) -- we can consider a special components hash +function references.loadpresets(product,component) -- we can consider a special components hash if product and component and product~= "" and component ~= "" and not productdata.product then -- maybe: productdata.filename ~= filename productdata.product = product productdata.component = component @@ -1082,13 +1251,13 @@ function structures.references.loadpresets(product,component) -- we can consider end end -structures.references.productdata = productdata +references.productdata = productdata local useproduct = commands.useproduct if useproduct then - function commands.useproduct(product) + local function newuseproduct(product) useproduct(product) if texconditionals.autocrossfilereferences then local component = justacomponent() @@ -1096,11 +1265,18 @@ if useproduct then if trace_referencing or trace_importing then report_references("loading presets for component %a of product %a",component,product) end - structures.references.loadpresets(product,component) + references.loadpresets(product,component) end end end + implement { + name = "useproduct", + actions = newuseproduct, + arguments = "string", + overload = true, + } + end -- productdata.firstsection.numberdata.numbers @@ -1194,7 +1370,7 @@ local function identify_arguments(set,var,i) local s = specials[var.inner] if s then -- inner{argument} - var.kind = "special with arguments" + var.kind = "special operation with arguments" else var.error = "unknown inner or special" end @@ -1204,114 +1380,105 @@ local function identify_arguments(set,var,i) return var end -local function identify_inner(set,var,prefix,collected,derived,tobesaved) +-- needs checking: if we don't do too much (redundant) checking now +-- inner ... we could move the prefix logic into the parser so that we have 'm for each entry +-- foo:bar -> foo == prefix (first we try the global one) +-- -:bar -> ignore prefix + +local function finish_inner(var,p,i) + var.kind = "inner" + var.i = i + var.p = p + var.r = (i.references and i.references.realpage) or (i.pagedata and i.pagedata.realpage) or 1 + return var +end + +local function identify_inner(set,var,prefix,collected,derived) local inner = var.inner - local outer = var.outer - -- inner ... we could move the prefix logic into the parser so that we have 'm for each entry - -- foo:bar -> foo == prefix (first we try the global one) - -- -:bar -> ignore prefix - local p, i = prefix, nil - local splitprefix, splitinner -- the next test is a safeguard when references are auto loaded from outer - if inner then - splitprefix, splitinner = lpegmatch(prefixsplitter,inner) + if not inner or inner == "" then + return false end - -- these are taken from other anonymous references + local splitprefix, splitinner = lpegmatch(prefixsplitter,inner) if splitprefix and splitinner then + -- we check for a prefix:reference instance in the regular set of collected + -- references; a special case is -: which forces a lookup in the global list if splitprefix == "-" then - i = collected[""] - i = i and i[splitinner] + local i = collected[""] if i then - p = "" - end - else - i = collected[splitprefix] - i = i and i[splitinner] - if i then - p = splitprefix + i = i[splitinner] + if i then + return finish_inner(var,"",i) + end end end - end - -- todo: strict here - if not i then - i = collected[prefix] - i = i and i[inner] - if i then - p = prefix - end - end - if not i and prefix ~= "" then - i = collected[""] - i = i and i[inner] + local i = collected[splitprefix] if i then - p = "" + i = i[splitinner] + if i then + return finish_inner(var,splitprefix,i) + end end - end - if i then - var.i = { "reference", i } - resolvers.reference(var) - var.kind = "inner" - var.p = p - elseif derived then - -- these are taken from other data structures (like lists) - if splitprefix and splitinner then + if derived then + -- next we look for a reference in the regular set of collected references + -- using the prefix that is active at this moment (so we overload the given + -- these are taken from other data structures (like lists) if splitprefix == "-" then - i = derived[""] - i = i and i[splitinner] + local i = derived[""] if i then - p = "" + i = i[splitinner] + if i then + return finish_inner(var,"",i) + end end - else - i = derived[splitprefix] - i = i and i[splitinner] + end + local i = derived[splitprefix] + if i then + i = i[splitinner] if i then - p = splitprefix + return finish_inner(var,splitprefix,i) end end end - if not i then - i = derived[prefix] - i = i and i[inner] - if i then - p = prefix - end + end + -- we now ignore the split prefix and treat the whole inner as a potential + -- referenice into the global list + local i = collected[prefix] + if i then + i = i[inner] + if i then + return finish_inner(var,prefix,i) end - if not i and prefix ~= "" then - i = derived[""] - i = i and i[inner] + end + if not i and derived then + -- and if not found we look in the derived references + local i = derived[prefix] + if i then + i = i[inner] if i then - p = "" + return finish_inner(var,prefix,i) end end + end + return false +end + +local function unprefixed_inner(set,var,prefix,collected,derived,tobesaved) + local inner = var.inner + local s = specials[inner] + if s then + var.kind = "special" + else + local i = (collected and collected[""] and collected[""][inner]) or + (derived and derived [""] and derived [""][inner]) or + (tobesaved and tobesaved[""] and tobesaved[""][inner]) if i then var.kind = "inner" - var.i = i - var.p = p - local ri = resolvers[i[1]] - if ri then - ri(var) - else - -- can't happen as we catch it with a metatable now - report_references("unknown inner resolver for %a",i[1]) - end + var.p = "" + var.i = i + var.r = (i.references and i.references.realpage) or (i.pagedata and i.pagedata.realpage) or 1 else - -- no prefixes here - local s = specials[inner] - if s then - var.kind = "special" - else - i = (collected and collected[""] and collected[""][inner]) or - (derived and derived [""] and derived [""][inner]) or - (tobesaved and tobesaved[""] and tobesaved[""][inner]) - if i then - var.kind = "inner" - var.i = { "reference", i } - resolvers.reference(var) - var.p = "" - else - var.error = "unknown inner or special" - end - end + var.error = "unknown inner or special" end end return var @@ -1322,9 +1489,8 @@ local function identify_outer(set,var,i) local inner = var.inner local external = externals[outer] if external then - local v = copytable(var) - v = identify_inner(set,v,nil,external) - if v.i and not v.error then + local v = identify_inner(set,var,nil,external) + if v then v.kind = "outer with inner" set.external = true if trace_identifying then @@ -1332,9 +1498,8 @@ local function identify_outer(set,var,i) end return v end - v = copytable(var) - local v = identify_inner(set,v,v.outer,external) - if v.i and not v.error then + local v = identify_inner(set,var,var.outer,external) + if v then v.kind = "outer with inner" set.external = true if trace_identifying then @@ -1345,8 +1510,8 @@ local function identify_outer(set,var,i) end local external = productdata.componentreferences[outer] if external then - local v = identify_inner(set,copytable(var),nil,external) - if v.i and not v.error then + local v = identify_inner(set,var,nil,external) + if v then v.kind = "outer with inner" set.external = true if trace_identifying then @@ -1373,6 +1538,8 @@ local function identify_outer(set,var,i) local arguments = var.arguments local operation = var.operation if inner then + -- tricky: in this case we can only use views when we're sure that all inners + -- are flushed in the outer document so that should become an option if arguments then -- outer::inner{argument} var.kind = "outer with inner with arguments" @@ -1380,9 +1547,9 @@ local function identify_outer(set,var,i) -- outer::inner var.kind = "outer with inner" end - var.i = { "reference", inner } - resolvers.reference(var) + var.i = inner var.f = outer + var.r = (inner.references and inner.references.realpage) or (inner.pagedata and inner.pagedata.realpage) or 1 if trace_identifying then report_identify_outer(set,var,i,"2e") end @@ -1419,57 +1586,62 @@ local function identify_outer(set,var,i) return var end +-- todo: avoid copy + local function identify_inner_or_outer(set,var,i) -- here we fall back on product data local inner = var.inner if inner and inner ~= "" then - local v = identify_inner(set,copytable(var),set.prefix,collected,derived,tobesaved) - if v.i and not v.error then - v.kind = "inner" -- check this + + -- first we look up in collected and derived using the current prefix + + local prefix = set.prefix + + local v = identify_inner(set,var,set.prefix,collected,derived) + if v then if trace_identifying then report_identify_outer(set,v,i,"4a") end return v end - -- these get auto prefixes but are loaded in the document so they are - -- internal .. we also set the realpage (for samepage analysis) + -- nest we look at each component (but we can omit the already consulted one local components = job.structure.components if components then - for i=1,#components do - local component = components[i] - local data = collected[component] - local vi = data and data[inner] - if vi then --- var = copytable(var) --- var.kind = "inner" --- var.i = vi --- var.p = component --- runners.inner(var.r = vi.references.realpage --- if trace_identifying then --- report_identify_outer(set,var,i,"4x") --- end --- return var -local v = identify_inner(set,copytable(var),component,collected) -- is copy needed ? -if v.i and not v.error then - v.kind = "inner" - if trace_identifying then - report_identify_outer(set,var,i,"4x") - end - return v -end + for c=1,#components do + local component = components[c] + if component ~= prefix then + local v = identify_inner(set,var,component,collected,derived) + if v then + if trace_identifying then + report_identify_outer(set,var,i,"4b") + end + return v + end end end end + -- as a last resort we will consult the global lists + + local v = unprefixed_inner(set,var,"",collected,derived,tobesaved) + if v then + if trace_identifying then + report_identify_outer(set,v,i,"4c") + end + return v + end + + -- not it gets bad ... we need to look in external files ... keep in mind that + -- we can best use explicit references for this ... we might issue a warning + local componentreferences = productdata.componentreferences local productreferences = productdata.productreferences local components = productdata.components if components and componentreferences then - -- for component, data in next, productdata.componentreferences do -- better do this in order of processing: - for i=1,#components do - local component = components[i] + for c=1,#components do + local component = components[c] local data = componentreferences[component] if data then local d = data[""] @@ -1480,7 +1652,7 @@ end var.kind = "outer with inner" set.external = true if trace_identifying then - report_identify_outer(set,var,i,"4b") + report_identify_outer(set,var,i,"4d") end return var end @@ -1500,7 +1672,7 @@ end var.kind = "outer with inner" set.external = true if trace_identifying then - report_identify_outer(set,var,i,"4c") + report_identify_outer(set,var,i,"4e") end return var end @@ -1515,7 +1687,7 @@ end var.kind = "outer with inner" set.external = true if trace_identifying then - report_identify_outer(set,var,i,"4d") + report_identify_outer(set,var,i,"4f") end return var end @@ -1526,30 +1698,18 @@ end var.error = "no inner" end if trace_identifying then - report_identify_outer(set,var,i,"4e") + report_identify_outer(set,var,i,"4g") end return var end --- local function identify_inner_or_outer(set,var,i) --- -- we might consider first checking with a prefix prepended and then without --- -- which is better for fig:oeps --- local var = do_identify_inner_or_outer(set,var,i) --- if var.error then --- local prefix = set.prefix --- if prefix and prefix ~= "" then --- var.inner = prefix .. ':' .. var.inner --- var.error = nil --- return do_identify_inner_or_outer(set,var,i) --- end --- end --- return var --- end - local function identify_inner_component(set,var,i) -- we're in a product (maybe ignore when same as component) local component = var.component - identify_inner(set,var,component,collected,derived,tobesaved) + local v = identify_inner(set,var,component,collected,derived) + if not v then + var.error = "unknown inner in component" + end if trace_identifying then report_identify_outer(set,var,i,"5a") end @@ -1611,7 +1771,11 @@ local function identify(prefix,reference) set.n = nofidentified for i=1,#set do local var = set[i] - if var.special then + local spe = var.special + local fnc = functions[spe] + if fnc then + var = fnc(var) or { error = "invalid special function" } + elseif spe then var = identify_special(set,var,i) elseif var.outer then var = identify_outer(set,var,i) @@ -1638,7 +1802,7 @@ references.identify = identify local unknowns, nofunknowns, f_valid = { }, 0, formatters["[%s][%s]"] -function references.valid(prefix,reference,highlight,newwindow,layer) +function references.valid(prefix,reference,specification) local set, bug = identify(prefix,reference) local unknown = bug or #set == 0 if unknown then @@ -1653,16 +1817,28 @@ function references.valid(prefix,reference,highlight,newwindow,layer) unknowns[str] = u + 1 end else - set.highlight, set.newwindow, set.layer = highlight, newwindow, layer + set.highlight = specification.highlight + set.newwindow = specification.newwindow + set.layer = specification.layer currentreference = set[1] end -- we can do the expansion here which saves a call return not unknown end -function commands.doifelsereference(prefix,reference,highlight,newwindow,layer) - commands.doifelse(references.valid(prefix,reference,highlight,newwindow,layer)) -end +implement { + name = "doifelsereference", + actions = { references.valid, commands.doifelse }, + arguments = { + "string", + "string", + { + { "highlight", "boolean" }, + { "newwindow", "boolean" }, + { "layer" }, + } + } +} function references.reportproblems() -- might become local if nofunknowns > 0 then @@ -1685,92 +1861,199 @@ end luatex.registerstopactions(references.reportproblems) -local innermethod = "names" +-- The auto method will try to avoid named internals in a clever way which +-- can make files smaller without sacrificing external references. Some of +-- the housekeeping happens the backend side. + +local innermethod = v_auto -- only page|auto now +local defaultinnermethod = defaultinnermethod +references.innermethod = innermethod -- don't mess with this one directly function references.setinnermethod(m) - if m then - if m == "page" or m == "mixed" or m == "names" then - innermethod = m - elseif m == true or m == v_yes then - innermethod = "page" - end + if toboolean(m) or m == v_page then + innermethod = v_page + else + innermethod = v_auto end + references.innermethod = innermethod function references.setinnermethod() report_references("inner method is already set and frozen to %a",innermethod) end end +implement { + name = "setinnerreferencemethod", + actions = references.setinnermethod, + arguments = "string", + onlyonce = true +} + function references.getinnermethod() - return innermethod or "names" + return innermethod or defaultinnermethod end -directives.register("references.linkmethod", function(v) -- page mixed names +directives.register("references.linkmethod", function(v) -- page auto references.setinnermethod(v) end) -- this is inconsistent -function references.setinternalreference(prefix,tag,internal,view) -- needs checking - if innermethod == "page" then - return unsetvalue - else - local t, tn = { }, 0 -- maybe add to current - if tag then +local destinationattributes = { } + +local function setinternalreference(specification) + local internal = specification.internal + local destination = unsetvalue + if innermethod == v_auto then + local t, tn = { }, 0 -- maybe add to current (now only used for tracing) + local reference = specification.reference + if reference then + local prefix = specification.prefix if prefix and prefix ~= "" then prefix = prefix .. ":" -- watch out, : here - -- for ref in gmatch(tag,"[^,]+") do - -- tn = tn + 1 - -- t[tn] = prefix .. ref - -- end local function action(ref) tn = tn + 1 t[tn] = prefix .. ref end - process_settings(tag,action) + process_settings(reference,action) else - -- for ref in gmatch(tag,"[^,]+") do - -- tn = tn + 1 - -- t[tn] = ref - -- end local function action(ref) tn = tn + 1 t[tn] = ref end - process_settings(tag,action) + process_settings(reference,action) end end - if internal and innermethod == "names" then -- mixed or page + -- ugly .. later we decide to ignore it when we have a real one + -- but for testing we might want to see them all + if internal then tn = tn + 1 - t[tn] = "aut:" .. internal + t[tn] = internal -- when number it's internal end - local destination = references.mark(t,nil,nil,view) -- returns an attribute - texsetcount("lastdestinationattribute",destination) - return destination + destination = references.mark(t,nil,nil,specification.view) -- returns an attribute end + if internal then -- new + destinationattributes[internal] = destination + end + texsetcount("lastdestinationattribute",destination) + return destination end -function references.setandgetattribute(kind,prefix,tag,data,view) -- maybe do internal automatically here - local attr = references.set(kind,prefix,tag,data) and references.setinternalreference(prefix,tag,nil,view) or unsetvalue - texsetcount("lastdestinationattribute",attr) - return attr +local function getinternalreference(internal) + return destinationattributes[internal] or 0 end -commands.setreferenceattribute = references.setandgetattribute +references.setinternalreference = setinternalreference +references.getinternalreference = getinternalreference -function references.getinternalreference(n) -- n points into list (todo: registers) - local l = lists.collected[n] - return l and l.references.internal or n -end +implement { + name = "setinternalreference", + actions = setinternalreference, + arguments = { + { + { "prefix" }, + { "reference" }, + { "internal", "integer" }, + { "view" } + } + } +} -function commands.setinternalreference(prefix,tag,internal,view) -- needs checking - context(references.setinternalreference(prefix,tag,internal,view)) +-- implement { +-- name = "getinternalreference", +-- actions = { getinternalreference, context }, +-- arguments = "integer", +-- } + +function references.setandgetattribute(data) -- maybe do internal automatically here + local attr = unsetvalue + local mdat = data.metadata + local rdat = data.references + if mdat and rdat then + if not rdat.section then + rdat.section = structures.sections.currentid() + end + local ndat = data.numberdata + if ndat then + local numbers = ndat.numbers + if type(numbers) == "string" then + ndat.numbers = counters.compact(numbers,nil,true) + end + data.numberdata = helpers.simplify(ndat) + end + local pdat = data.prefixdata + if pdat then + data.prefixdata = helpers.simplify(pdat) + end + local udat = data.userdata + if type(udat) == "string" then + data.userdata = helpers.touserdata(udat) + end + if not rdat.block then + rdat.block = structures.sections.currentblock() + end + local done = references.set(data) -- we had kind i.e .item -> full + if done then + attr = setinternalreference { + prefix = prefix, + reference = tag, + internal = rdat.internal, + view = rdat.view + } or unsetvalue + end + end + texsetcount("lastdestinationattribute",attr) + return attr end -function commands.getinternalreference(n) -- this will also be a texcount +implement { + name = "setreferenceattribute", + actions = references.setandgetattribute, + arguments = { + { + { + "references", { + { "internal", "integer" }, + { "block" }, + { "view" }, + { "prefix" }, + { "reference" }, + }, + }, + { + "metadata", { + { "kind" }, + { "xmlroot" }, + { "catcodes", "integer" }, + }, + }, + { + "prefixdata", { "*" } + }, + { + "numberdata", { "*" } + }, + { + "entries", { "*" } + }, + { + "userdata" + } + } + } +} + +function references.getinternallistreference(n) -- n points into list (todo: registers) local l = lists.collected[n] - context(l and l.references.internal or n) + local i = l and l.references.internal + return i and destinationattributes[i] or 0 end +implement { + name = "getinternallistreference", + actions = { references.getinternallistreference, context }, + arguments = "integer" +} + -- function references.getcurrentmetadata(tag) @@ -1778,12 +2061,11 @@ function references.getcurrentmetadata(tag) return data and data.metadata and data.metadata[tag] end -function commands.getcurrentreferencemetadata(tag) - local data = references.getcurrentmetadata(tag) - if data then - context(data) - end -end +implement { + name = "getcurrentreferencemetadata", + actions = { references.getcurrentmetadata, context }, + arguments = "string", +} local function currentmetadata(tag) local data = currentreference and currentreference.i @@ -1793,32 +2075,58 @@ end references.currentmetadata = currentmetadata local function getcurrentprefixspec(default) - -- todo: message - return currentmetadata("kind") or "?", currentmetadata("name") or "?", default or "?" + local data = currentreference and currentreference.i + local metadata = data and data.metadata + return + metatadata and metadata.kind or "?", + metatadata and metadata.name or "?", + default or "?" end references.getcurrentprefixspec = getcurrentprefixspec -function commands.getcurrentprefixspec(default) - context.getreferencestructureprefix(getcurrentprefixspec(default)) -end +-- implement { +-- name = "getcurrentprefixspec", +-- actions = { getcurrentprefixspec, context }, -- returns 3 arguments +-- arguments = "string", +-- } + +implement { + name = "getcurrentprefixspec", + actions = function(tag) + context("{%s}{%s}{%s}",getcurrentprefixspec(tag)) + end, + arguments = "string", +} -function references.filter(name,...) -- number page title ... +local genericfilters = { } +local userfilters = { } +local textfilters = { } +local fullfilters = { } +local sectionfilters = { } + +filters.generic = genericfilters +filters.user = userfilters +filters.text = textfilters +filters.full = fullfilters +filters.section = sectionfilters + +local function filterreference(name,prefixspec,numberspec) -- number page title ... local data = currentreference and currentreference.i -- maybe we should take realpage from here if data then if name == "realpage" then local cs = references.analyze() -- normally already analyzed but also sets state - context(tonumber(cs.realpage) or 0) -- todo, return and in command namespace + context(tonumber(cs.realpage) or 0) else -- assumes data is table local kind = type(data) == "table" and data.metadata and data.metadata.kind if kind then - local filter = filters[kind] or filters.generic - filter = filter and (filter[name] or filter.unknown or filters.generic[name] or filters.generic.unknown) + local filter = filters[kind] or genericfilters + filter = filter and (filter[name] or filter.unknown or genericfilters[name] or genericfilters.unknown) if filter then if trace_referencing then report_references("name %a, kind %a, using dedicated filter",name,kind) end - filter(data,name,...) + filter(data,name,prefixspec,numberspec) elseif trace_referencing then report_references("name %a, kind %a, using generic filter",name,kind) end @@ -1833,18 +2141,30 @@ function references.filter(name,...) -- number page title ... end end -function references.filterdefault() - return references.filter("default",getcurrentprefixspec(v_default)) +local function filterreferencedefault() + return filterreference("default",getcurrentprefixspec("default")) end -function commands.currentreferencedefault(tag) - if not tag then tag = "default" end - references.filter(tag,context.delayed(getcurrentprefixspec(tag))) -end +references.filter = filterreference +references.filterdefault = filterreferencedefault + +implement { + name = "filterreference", + actions = filterreference, + arguments = "string", +} -filters.generic = { } +implement { + name = "filterdefaultreference", + actions = filterreference, + arguments = { + "string", -- 'default' + { { "*" } }, -- prefixspec + { { "*" } }, -- numberspec + } +} -function filters.generic.title(data) +function genericfilters.title(data) if data then local titledata = data.titledata or data.useddata if titledata then @@ -1853,7 +2173,7 @@ function filters.generic.title(data) end end -function filters.generic.text(data) +function genericfilters.text(data) if data then local entries = data.entries or data.useddata if entries then @@ -1862,12 +2182,12 @@ function filters.generic.text(data) end end -function filters.generic.number(data,what,prefixspec) -- todo: spec and then no stopper +function genericfilters.number(data,what,prefixspec,numberspec) if data then numberdata = lists.reordered(data) -- data.numberdata if numberdata then helpers.prefix(data,prefixspec) - sections.typesetnumber(numberdata,"number",numberdata) + sections.typesetnumber(numberdata,"number",numberspec,numberdata) else local useddata = data.useddata if useddata and useddata.number then @@ -1877,16 +2197,16 @@ function filters.generic.number(data,what,prefixspec) -- todo: spec and then no end end -filters.generic.default = filters.generic.text +genericfilters.default = genericfilters.text -function filters.generic.page(data,prefixspec,pagespec) +function genericfilters.page(data,prefixspec,pagespec) local pagedata = data.pagedata if pagedata then local number, conversion = pagedata.number, pagedata.conversion if not number then -- error elseif conversion then - context.convertnumber(conversion,number) + ctx_convertnumber(conversion,number) else context(number) end @@ -1895,14 +2215,12 @@ function filters.generic.page(data,prefixspec,pagespec) end end -filters.user = { } - -function filters.user.unknown(data,name) +function userfilters.unknown(data,name) if data then local userdata = data.userdata local userkind = userdata and userdata.kind if userkind then - local filter = filters[userkind] or filters.generic + local filter = filters[userkind] or genericfilters filter = filter and (filter[name] or filter.unknown) if filter then filter(data,name) @@ -1916,9 +2234,7 @@ function filters.user.unknown(data,name) end end -filters.text = { } - -function filters.text.title(data) +function textfilters.title(data) helpers.title(data.entries.text or "?",data.metadata) end @@ -1928,18 +2244,14 @@ end -- helpers.title(data.entries.text or "?",data.metadata) -- end -function filters.text.page(data,prefixspec,pagespec) +function textfilters.page(data,prefixspec,pagespec) helpers.prefixpage(data,prefixspec,pagespec) end -filters.full = { } - -filters.full.title = filters.text.title -filters.full.page = filters.text.page - -filters.section = { } +fullfilters.title = textfilters.title +fullfilters.page = textfilters.page -function filters.section.number(data,what,prefixspec) +function sectionfilters.number(data,what,prefixspec) if data then local numberdata = data.numberdata if not numberdata then @@ -1951,7 +2263,7 @@ function filters.section.number(data,what,prefixspec) local references = data.references if trace_empty then report_empty("reference %a has a hidden number",references.reference) - context.emptyreference() -- maybe an option + ctx_emptyreference() -- maybe an option end else sections.typesetnumber(numberdata,"number",prefixspec,numberdata) @@ -1959,18 +2271,18 @@ function filters.section.number(data,what,prefixspec) end end -filters.section.title = filters.generic.title -filters.section.page = filters.generic.page -filters.section.default = filters.section.number +sectionfilters.title = genericfilters.title +sectionfilters.page = genericfilters.page +sectionfilters.default = sectionfilters.number --- filters.note = { default = filters.generic.number } --- filters.formula = { default = filters.generic.number } --- filters.float = { default = filters.generic.number } --- filters.description = { default = filters.generic.number } --- filters.item = { default = filters.generic.number } +-- filters.note = { default = genericfilters.number } +-- filters.formula = { default = genericfilters.number } +-- filters.float = { default = genericfilters.number } +-- filters.description = { default = genericfilters.number } +-- filters.item = { default = genericfilters.number } setmetatableindex(filters, function(t,k) -- beware, test with rawget - local v = { default = filters.generic.number } -- not copy as it might be extended differently + local v = { default = genericfilters.number } -- not copy as it might be extended differently t[k] = v return v end) @@ -1999,12 +2311,71 @@ local specials = references.testspecials -- pretty slow (progressively). In the pagebody one can best check the reference -- real page to determine if we need contrastlocation as that is more lightweight. -local function checkedpagestate(n,page) - local r = referredpage(n) +local function checkedpagestate(n,page,actions,position,spread) local p = tonumber(page) if not p then return 0 - elseif p > r then + end + if position and #actions > 0 then + local i = actions[1].i -- brrr + if i then + local a = i.references + if a then + local x = a.x + local y = a.y + if x and y then + local jp = jobpositions.collected[position] + if jp then + local px = jp.x + local py = jp.y + local pp = jp.p + if p == pp then + -- same page + if py > y then + return 5 -- above + elseif py < y then + return 4 -- below + elseif px > x then + return 4 -- below + elseif px < x then + return 5 -- above + else + return 1 -- same + end + elseif spread then + if pp % 2 == 0 then + -- left page + if pp > p then + return 2 -- before + elseif pp + 1 == p then +-- return 4 -- below (on right page) + return 5 -- above (on left page) + else + return 3 -- after + end + else + -- right page + if pp < p then + return 3 -- after + elseif pp - 1 == p then +-- return 5 -- above (on left page) + return 4 -- below (on right page) + else + return 2 -- before + end + end + elseif pp > p then + return 2 -- before + else + return 3 -- after + end + end + end + end + end + end + local r = referredpage(n) -- sort of obsolete + if p > r then return 3 -- after elseif p < r then return 2 -- before @@ -2043,11 +2414,13 @@ local function setreferencerealpage(actions) end end +references.setreferencerealpage = setreferencerealpage + -- we store some analysis data alongside the indexed array -- at this moment only the real reference page is analyzed -- normally such an analysis happens in the backend code -function references.analyze(actions) +function references.analyze(actions,position,spread) if not actions then actions = references.currentset end @@ -2062,32 +2435,56 @@ function references.analyze(actions) elseif actions.external then actions.pagestate = 0 else - actions.pagestate = checkedpagestate(actions.n,realpage) + actions.pagestate = checkedpagestate(actions.n,realpage,actions,position,spread) end end return actions end -function commands.referencepagestate(actions) - if not actions then - actions = references.currentset - end +local function referencepagestate(position,detail,spread) + local actions = references.currentset if not actions then - context(0) + return 0 else if not actions.pagestate then - references.analyze(actions) -- delayed unless explicitly asked for --- print("NO STATE",actions.reference,actions.pagestate) + references.analyze(actions,position,spread) -- delayed unless explicitly asked for + end + local pagestate = actions.pagestate + if detail then + return pagestate + elseif pagestate == 4 then + return 2 -- compatible + elseif pagestate == 5 then + return 3 -- compatible + else + return pagestate end - context(actions.pagestate) end end -function commands.referencerealpage(actions) +implement { + name = "referencepagestate", + actions = { referencepagestate, context }, + arguments = "string" +} + +implement { + name = "referencepagedetail", + actions = { referencepagestate, context }, + arguments = { "string", "boolean", "boolean" } +} + +local function referencerealpage(actions) actions = actions or references.currentset - context(not actions and 0 or actions.realpage or setreferencerealpage(actions)) + return not actions and 0 or actions.realpage or setreferencerealpage(actions) end +implement { + name = "referencerealpage", + actions = { referencerealpage, context }, + arguments = "string" +} + local plist, nofrealpages local function realpageofpage(p) -- the last one counts ! @@ -2164,7 +2561,7 @@ runners["special operation with arguments"] = runners["special"] -- check the validity. function specials.internal(var,actions) - local v = references.internals[tonumber(var.operation)] + local v = internals[tonumber(var.operation)] local r = v and v.references.realpage if r then actions.realpage = r @@ -2224,10 +2621,103 @@ function specials.section(var,actions) end end --- needs a better split ^^^ +-- experimental: + +local p_splitter = lpeg.splitat(":") +local p_lower = lpeg.patterns.utf8lower + +-- We can cache lowercased titles which saves a lot of time, but then +-- we can better have a global cache with weak keys. + +-- local lowercache = table.setmetatableindex(function(t,k) +-- local v = lpegmatch(p_lower,k) +-- t[k] = v +-- return v +-- end) -commands.filterreference = references.filter -commands.filterdefaultreference = references.filterdefault +local lowercache = false + +local function locate(list,askedkind,askedname,pattern) + local kinds = lists.kinds + local names = lists.names + if askedkind and not kinds[askedkind] then + return false + end + if askedname and not names[askedname] then + return false + end + for i=1,#list do + local entry = list[i] + local metadata = entry.metadata + if metadata then + local found = false + if askedname then + local name = metadata.name + if name then + found = name == askedname + end + elseif askedkind then + local kind = metadata.kind + if kind then + found = kind == askedkind + end + end + if found then + local titledata = entry.titledata + if titledata then + local title = titledata.title + if title then + if lowercache then + found = lpegmatch(pattern,lowercache[title]) + else + found = lpegmatch(pattern,lpegmatch(p_lower,title)) + end + if found then + return { + inner = pattern, + kind = "inner", + reference = pattern, + i = entry, + p = "", + r = entry.references.realpage, + } + end + end + end + end + end + end +end + +function functions.match(var,actions) + if not var.outer then + local operation = var.operation + if operation and operation ~= "" then + local operation = lpegmatch(p_lower,operation) + local list = lists.collected + local names = false + local kinds = false + local where, what = lpegmatch(p_splitter,operation) + if where and what then + local pattern = lpeg.finder(what) + return + locate(list,false,where,pattern) + or locate(list,where,false,pattern) + or { error = "no match" } + else + local pattern = lpeg.finder(operation) + -- todo: don't look at section and float in last pass + return + locate(list,"section",false,pattern) + or locate(list,"float",false,pattern) + or locate(list,false,false,pattern) + or { error = "no match" } + end + end + end +end + +-- needs a better split ^^^ -- done differently now: @@ -2235,24 +2725,36 @@ function references.export(usedname) end function references.import(usedname) end function references.load (usedname) end -commands.exportreferences = references.export +implement { name = "exportreferences", actions =references.export } -- better done here .... we don't insert/remove, just use a pointer local prefixstack = { "" } local prefixlevel = 1 -function commands.pushreferenceprefix(prefix) +local function pushreferenceprefix(prefix) prefixlevel = prefixlevel + 1 prefixstack[prefixlevel] = prefix - context(prefix) + return prefix end -function commands.popreferenceprefix() +local function popreferenceprefix() prefixlevel = prefixlevel - 1 if prefixlevel > 0 then - context(prefixstack[prefixlevel]) + return prefixstack[prefixlevel] else report_references("unable to pop referenceprefix") + return "" end end + +implement { + name = "pushreferenceprefix", + actions = { pushreferenceprefix, context }, -- we can use setmacro + arguments = "string", +} + +implement { + name = "popreferenceprefix", + actions = { popreferenceprefix, context }, -- we can use setmacro +} |