diff options
Diffstat (limited to 'tex')
-rw-r--r-- | tex/context/base/mkii/cont-new.mkii | 2 | ||||
-rw-r--r-- | tex/context/base/mkii/context.mkii | 2 | ||||
-rw-r--r-- | tex/context/base/mkiv/cont-new.mkiv | 2 | ||||
-rw-r--r-- | tex/context/base/mkiv/context.mkiv | 2 | ||||
-rw-r--r-- | tex/context/base/mkiv/status-files.pdf | bin | 24746 -> 0 bytes | |||
-rw-r--r-- | tex/context/base/mkiv/status-lua.pdf | bin | 257141 -> 0 bytes | |||
-rw-r--r-- | tex/context/base/mkiv/strc-flt.mkvi | 41 | ||||
-rw-r--r-- | tex/context/base/mkiv/strc-num.lua | 5 | ||||
-rw-r--r-- | tex/context/base/mkxl/cont-new.mkxl | 2 | ||||
-rw-r--r-- | tex/context/base/mkxl/context.mkxl | 2 | ||||
-rw-r--r-- | tex/context/base/mkxl/math-ini.mkxl | 25 | ||||
-rw-r--r-- | tex/context/base/mkxl/pack-com.mkxl | 2 | ||||
-rw-r--r-- | tex/context/base/mkxl/strc-flt.mklx | 41 | ||||
-rw-r--r-- | tex/context/base/mkxl/strc-ref.lmt | 2919 | ||||
-rw-r--r-- | tex/context/base/mkxl/strc-ref.mklx | 45 | ||||
-rw-r--r-- | tex/context/base/mkxl/strc-rsc.lmt | 180 | ||||
-rw-r--r-- | tex/generic/context/luatex/luatex-fonts-merged.lua | 2 |
17 files changed, 3218 insertions, 54 deletions
diff --git a/tex/context/base/mkii/cont-new.mkii b/tex/context/base/mkii/cont-new.mkii index e0f571a21..35bbc0049 100644 --- a/tex/context/base/mkii/cont-new.mkii +++ b/tex/context/base/mkii/cont-new.mkii @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\newcontextversion{2021.11.24 19:45} +\newcontextversion{2021.11.26 10:41} %D This file is loaded at runtime, thereby providing an %D excellent place for hacks, patches, extensions and new diff --git a/tex/context/base/mkii/context.mkii b/tex/context/base/mkii/context.mkii index 1d4ad08ca..ea4de3fc8 100644 --- a/tex/context/base/mkii/context.mkii +++ b/tex/context/base/mkii/context.mkii @@ -20,7 +20,7 @@ %D your styles an modules. \edef\contextformat {\jobname} -\edef\contextversion{2021.11.24 19:45} +\edef\contextversion{2021.11.26 10:41} %D For those who want to use this: diff --git a/tex/context/base/mkiv/cont-new.mkiv b/tex/context/base/mkiv/cont-new.mkiv index d958478c6..a7daf8181 100644 --- a/tex/context/base/mkiv/cont-new.mkiv +++ b/tex/context/base/mkiv/cont-new.mkiv @@ -13,7 +13,7 @@ % \normalend % uncomment this to get the real base runtime -\newcontextversion{2021.11.24 19:45} +\newcontextversion{2021.11.26 10:41} %D This file is loaded at runtime, thereby providing an excellent place for hacks, %D patches, extensions and new features. There can be local overloads in cont-loc diff --git a/tex/context/base/mkiv/context.mkiv b/tex/context/base/mkiv/context.mkiv index cb8389083..99180f380 100644 --- a/tex/context/base/mkiv/context.mkiv +++ b/tex/context/base/mkiv/context.mkiv @@ -49,7 +49,7 @@ %D {YYYY.MM.DD HH:MM} format. \edef\contextformat {\jobname} -\edef\contextversion{2021.11.24 19:45} +\edef\contextversion{2021.11.26 10:41} %D Kind of special: diff --git a/tex/context/base/mkiv/status-files.pdf b/tex/context/base/mkiv/status-files.pdf Binary files differdeleted file mode 100644 index d880990ea..000000000 --- a/tex/context/base/mkiv/status-files.pdf +++ /dev/null diff --git a/tex/context/base/mkiv/status-lua.pdf b/tex/context/base/mkiv/status-lua.pdf Binary files differdeleted file mode 100644 index 732a6ddb9..000000000 --- a/tex/context/base/mkiv/status-lua.pdf +++ /dev/null diff --git a/tex/context/base/mkiv/strc-flt.mkvi b/tex/context/base/mkiv/strc-flt.mkvi index d9fd359c6..464b6ab52 100644 --- a/tex/context/base/mkiv/strc-flt.mkvi +++ b/tex/context/base/mkiv/strc-flt.mkvi @@ -1111,6 +1111,35 @@ \def\strc_floats_group_index {\numexpr\clf_listgroupindex{\currentfloat}{\currentfloatgroup}\relax} +%D A lightweight subnumber feature: +%D +%D \starttyping +%D \startplacefigure [location=none] +%D \startsubfloatnumbering +%D \startfloatcombination [nx=3] +%D \startplacefigure [title=Left] \externalfigure \stopplacefigure +%D \startplacefigure [title=Middle] \externalfigure \stopplacefigure +%D \startplacefigure [title=Right] \externalfigure \stopplacefigure +%D \stopfloatcombination +%D \stopsubfloatnumbering +%D \stopplacefigure +%D \stoptyping + +\glet\currentsubfloatcounter\empty + +\unexpanded\def\startsubfloatnumbering + {\glet\currentsubfloatcounter\s!unknown} + +\unexpanded\def\stopsubfloatnumbering + {\strc_counters_reset_sub\currentsubfloatcounter\plustwo + \glet\currentsubfloatcounter\empty} + +\defineconversionset[subfloats][number,characters] + +\setupcaptions + %[figure] + [\c!numberconversionset=subfloats] + \def\strc_floats_place_packaged_boxes {\expandafter\strc_floats_place_packaged_boxes_indeed\expandafter{\m_strc_floats_saved_userdata}} @@ -1127,7 +1156,17 @@ \ifx\currentfloatgroup\empty % independent \iftrialtypesetting\strc_counters_save\currentfloatcounter\fi - \strc_counters_increment\currentfloatcounter + \ifx\empty\currentsubfloatcounter + \strc_counters_increment\currentfloatcounter + \strc_counters_reset_sub\currentfloatcounter\plustwo + \else + \ifcase\strc_counters_raw_sub\currentfloatcounter\plustwo\relax + \strc_counters_increment\currentfloatcounter + \strc_counters_reset_sub\currentfloatcounter\plustwo + \fi + \strc_counters_increment_sub\currentfloatcounter\plustwo + \glet\currentsubfloatcounter\currentfloatcounter + \fi \else\ifcase\strc_floats_group_index % first in group \iftrialtypesetting\strc_counters_save\currentfloatcounter\fi diff --git a/tex/context/base/mkiv/strc-num.lua b/tex/context/base/mkiv/strc-num.lua index 25e575a56..5d00fcca9 100644 --- a/tex/context/base/mkiv/strc-num.lua +++ b/tex/context/base/mkiv/strc-num.lua @@ -357,8 +357,9 @@ end local function reset(name,n) local cd = counterdata[name] if cd then - for i=n or 1,#cd.data do - local d = cd.data[i] + local data = cd.data + for i=n or 1,#data do + local d = data[i] savevalue(name,i) local number = d.start or 0 d.number = number diff --git a/tex/context/base/mkxl/cont-new.mkxl b/tex/context/base/mkxl/cont-new.mkxl index b4fe6aca0..29f5d2db7 100644 --- a/tex/context/base/mkxl/cont-new.mkxl +++ b/tex/context/base/mkxl/cont-new.mkxl @@ -13,7 +13,7 @@ % \normalend % uncomment this to get the real base runtime -\newcontextversion{2021.11.24 19:45} +\newcontextversion{2021.11.26 10:41} %D This file is loaded at runtime, thereby providing an excellent place for hacks, %D patches, extensions and new features. There can be local overloads in cont-loc diff --git a/tex/context/base/mkxl/context.mkxl b/tex/context/base/mkxl/context.mkxl index 92fdea6ed..f93975914 100644 --- a/tex/context/base/mkxl/context.mkxl +++ b/tex/context/base/mkxl/context.mkxl @@ -29,7 +29,7 @@ %D {YYYY.MM.DD HH:MM} format. \immutable\edef\contextformat {\jobname} -\immutable\edef\contextversion{2021.11.24 19:45} +\immutable\edef\contextversion{2021.11.26 10:41} %overloadmode 1 % check frozen / warning %overloadmode 2 % check frozen / error diff --git a/tex/context/base/mkxl/math-ini.mkxl b/tex/context/base/mkxl/math-ini.mkxl index a5f4ac529..3404e25f3 100644 --- a/tex/context/base/mkxl/math-ini.mkxl +++ b/tex/context/base/mkxl/math-ini.mkxl @@ -490,21 +490,22 @@ \permanent\protected\def\mathupright {\setmathattribute\s!regular\s!tf\setmathfontstylealternate\s!tf} \permanent\protected\def\mathitalic {\setmathattribute\s!regular\s!it\setmathfontstylealternate\s!it} -\permanent\protected\def\mathscript {\setmathalphabet \s!script \setmathfontstylealternate\s!script} -\permanent\protected\def\mathfraktur {\setmathalphabet \s!fraktur \setmathfontstylealternate\s!fraktur} -\permanent\protected\def\mathblackboard{\setmathalphabet \s!blackboard \setmathfontstylealternate\s!blackboard} -\permanent\protected\def\mathrm {\setmathattribute\s!rm\s!tf \setmathfontstylealternate\s!tf} -\permanent\protected\def\mathss {\setmathattribute\s!ss\s!tf \setmathfontstylealternate\s!tf} -\permanent\protected\def\mathtt {\setmathattribute\s!tt\s!tf \setmathfontstylealternate\s!tf} +\permanent\protected\def\mathscript {\setmathalphabet \s!script \setmathfontstylealternate\s!script} +\permanent\protected\def\mathfraktur {\setmathalphabet \s!fraktur \setmathfontstylealternate\s!fraktur} +\permanent\protected\def\mathblackboard{\setmathalphabet \s!blackboard\setmathfontstylealternate\s!blackboard} -\permanent\protected\def\mathtf {\setmathfontstyle\s!tf \setmathfontstylealternate\s!tf} -\permanent\protected\def\mathsl {\setmathfontstyle\s!it \setmathfontstylealternate\s!it} % no sl -\permanent\protected\def\mathit {\setmathfontstyle\s!it \setmathfontstylealternate\s!it} +\permanent\protected\def\mathrm {\setmathattribute\s!rm\s!tf\setmathfontstylealternate\s!tf} +\permanent\protected\def\mathss {\setmathattribute\s!ss\s!tf\setmathfontstylealternate\s!tf} +\permanent\protected\def\mathtt {\setmathattribute\s!tt\s!tf\setmathfontstylealternate\s!tf} -\permanent\protected\def\mathbf {\setmathfontstyle\s!bf \setmathfontstylealternate\s!bf} -\permanent\protected\def\mathbs {\setmathfontstyle\s!bi \setmathfontstylealternate\s!bi} % no sl -\permanent\protected\def\mathbi {\setmathfontstyle\s!bi \setmathfontstylealternate\s!bi} +\permanent\protected\def\mathtf {\setmathfontstyle\s!tf\setmathfontstylealternate\s!tf} +\permanent\protected\def\mathsl {\setmathfontstyle\s!it\setmathfontstylealternate\s!it} % no sl +\permanent\protected\def\mathit {\setmathfontstyle\s!it\setmathfontstylealternate\s!it} + +\permanent\protected\def\mathbf {\setmathfontstyle\s!bf\setmathfontstylealternate\s!bf} +\permanent\protected\def\mathbs {\setmathfontstyle\s!bi\setmathfontstylealternate\s!bi} % no sl +\permanent\protected\def\mathbi {\setmathfontstyle\s!bi\setmathfontstylealternate\s!bi} \aliased\let\mathdefault\mathitalic diff --git a/tex/context/base/mkxl/pack-com.mkxl b/tex/context/base/mkxl/pack-com.mkxl index 21f99ffc2..1b932895e 100644 --- a/tex/context/base/mkxl/pack-com.mkxl +++ b/tex/context/base/mkxl/pack-com.mkxl @@ -559,7 +559,7 @@ {\pack_combinations_float_hack_b{\recurselevel}}% \to\scratchtoks}% brrr \let\pack_combinations_check_x_y\pack_combinations_float_check_x_y - \expanded{\startcombination[#1]\the\scratchtoks}\stopcombination + \normalexpanded{\startcombination[#1]\the\scratchtoks}\stopcombination \resetlocalfloats \egroup} diff --git a/tex/context/base/mkxl/strc-flt.mklx b/tex/context/base/mkxl/strc-flt.mklx index 2559e897b..94d06fd92 100644 --- a/tex/context/base/mkxl/strc-flt.mklx +++ b/tex/context/base/mkxl/strc-flt.mklx @@ -1092,6 +1092,35 @@ \def\strc_floats_group_index {\numexpr\clf_listgroupindex{\currentfloat}{\currentfloatgroup}\relax} +%D A lightweight subnumber feature: +%D +%D \starttyping +%D \startplacefigure [location=none] +%D \startsubfloatnumbering +%D \startfloatcombination [nx=3] +%D \startplacefigure [title=Left] \externalfigure \stopplacefigure +%D \startplacefigure [title=Middle] \externalfigure \stopplacefigure +%D \startplacefigure [title=Right] \externalfigure \stopplacefigure +%D \stopfloatcombination +%D \stopsubfloatnumbering +%D \stopplacefigure +%D \stoptyping + +\glet\currentsubfloatcounter\empty + +\permanent\protected\def\startsubfloatnumbering + {\glet\currentsubfloatcounter\s!unknown} + +\permanent\protected\def\stopsubfloatnumbering + {\strc_counters_reset_sub\currentsubfloatcounter\plustwo + \glet\currentsubfloatcounter\empty} + +\defineconversionset[subfloats][number,characters] + +\setupcaptions + %[figure] + [\c!numberconversionset=subfloats] + \def\strc_floats_place_packaged_boxes {\expandafter\strc_floats_place_packaged_boxes_indeed\expandafter{\m_strc_floats_saved_userdata}} @@ -1109,7 +1138,17 @@ \orelse\ifempty\currentfloatgroup % independent \iftrialtypesetting\strc_counters_save\currentfloatcounter\fi - \strc_counters_increment\currentfloatcounter + \ifempty\currentsubfloatcounter + \strc_counters_increment\currentfloatcounter + \strc_counters_reset_sub\currentfloatcounter\plustwo + \else + \ifcase\strc_counters_raw_sub\currentfloatcounter\plustwo\relax + \strc_counters_increment\currentfloatcounter + \strc_counters_reset_sub\currentfloatcounter\plustwo + \fi + \strc_counters_increment_sub\currentfloatcounter\plustwo + \glet\currentsubfloatcounter\currentfloatcounter + \fi \orelse\ifcase\strc_floats_group_index % first in group \iftrialtypesetting\strc_counters_save\currentfloatcounter\fi diff --git a/tex/context/base/mkxl/strc-ref.lmt b/tex/context/base/mkxl/strc-ref.lmt new file mode 100644 index 000000000..807305ce7 --- /dev/null +++ b/tex/context/base/mkxl/strc-ref.lmt @@ -0,0 +1,2919 @@ +if not modules then modules = { } end modules ['strc-ref'] = { + version = 1.001, + comment = "companion to strc-ref.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- beware, this is a first step in the rewrite (just getting rid of +-- the tuo file); later all access and parsing will also move to lua + +-- the useddata and pagedata names might change +-- todo: pack exported data + +-- todo: autoload components when ::: + +local format, gmatch, match, strip = string.format, string.gmatch, string.match, string.strip +local floor = math.floor +local rawget, tonumber, type, next = rawget, tonumber, type, next +local lpegmatch = lpeg.match +local insert, remove, copytable = table.insert, table.remove, table.copy +local formatters = string.formatters +local P, Cs, lpegmatch = lpeg.P, lpeg.Cs, lpeg.match + +local allocate = utilities.storage.allocate +local mark = utilities.storage.mark +local setmetatableindex = table.setmetatableindex + +local trace_referencing = false trackers.register("structures.referencing", function(v) trace_referencing = v end) +local trace_analyzing = false trackers.register("structures.referencing.analyzing", function(v) trace_analyzing = v end) +local trace_identifying = false trackers.register("structures.referencing.identifying", function(v) trace_identifying = v end) +local trace_importing = false trackers.register("structures.referencing.importing", function(v) trace_importing = v end) +local trace_empty = false trackers.register("structures.referencing.empty", function(v) trace_empty = v end) + +local check_duplicates = true + +directives.register("structures.referencing.checkduplicates", function(v) check_duplicates = v end) + +local report_references = logs.reporter("references") +local report_identifying = logs.reporter("references","identifying") +local report_importing = logs.reporter("references","importing") +local report_empty = logs.reporter("references","empty") +local report = report_references + +local variables = interfaces.variables +local v_page = variables.page +local v_auto = variables.auto +local v_yes = variables.yes +local v_name = variables.name + +local context = context +local commands = commands +local implement = interfaces.implement + +local ctx_latelua = context.latelua + +local texgetcount = tex.getcount +local texsetcount = tex.setcount +local texconditionals = tex.conditionals + +local getexpansion = token.getexpansion + +local productcomponent = resolvers.jobs.productcomponent +local justacomponent = resolvers.jobs.justacomponent + +local settings_to_array = utilities.parsers.settings_to_array +local settings_to_table = utilities.parsers.settings_to_array_obey_fences +local process_settings = utilities.parsers.process_stripped_settings +local unsetvalue = attributes.unsetvalue + +local structures = structures +local helpers = structures.helpers +local sections = structures.sections +local references = structures.references +local lists = structures.lists +local counters = structures.counters + +local jobpositions = job.positions +local getpos = jobpositions.getpos + +-- some might become local + +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() +local executers = allocate() +local handlers = allocate() +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 +references.executers = executers +references.handlers = handlers +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 +local prefixsplitter = references.prefixsplitter +local componentsplitter = references.componentsplitter + +local currentreference = nil + +local txtcatcodes = catcodes.numbers.txtcatcodes -- or just use "txtcatcodes" + +local context = context + +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_convertnumber = context.convertnumber +local ctx_emptyreference = context.emptyreference + +storage.register("structures/references/defined", references.defined, "structures.references.defined") + +local initializers = { } +local finalizers = { } +local somefound = false -- so we don't report missing when we have a fresh start + +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 + +local function initializer() -- can we use a tobesaved as metatable for collected? + tobesaved = references.tobesaved + collected = references.collected + 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 + somefound = next(collected) +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) + +local maxreferred = 1 +local nofreferred = 0 + +local function initializer() -- can we use a tobesaved as metatable for collected? + tobereferred = references.tobereferred + referred = references.referred + nofreferred = #referred +end + +-- no longer done 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 +-- a combination of a binary search and traverse backwards. A previous implementation +-- always did a traverse and was pretty slow on a large number of links (given that this +-- methods was used). It took me about a day to locate this as a bottleneck in processing +-- a 2500 page interactive document with 60 links per page. In that case, traversing +-- thousands of slots per link then brings processing to a grinding halt (especially when +-- there are no slots at all, which is the case in a first run). + +local sparsetobereferred = { } + +local function finalizer() + local lastr, lasti + local n = 0 + for i=1,maxreferred do + local r = tobereferred[i] + if not lastr then + lastr = r + lasti = i + elseif r ~= lastr then + n = n + 1 + sparsetobereferred[n] = { lastr, lasti } + lastr = r + lasti = i + end + end + if lastr then + n = n + 1 + sparsetobereferred[n] = { lastr, lasti } + end +end + +job.register('structures.references.referred', sparsetobereferred, initializer, finalizer) + +local function referredpage(n) + local max = nofreferred + if max > 0 then + -- find match + local min = 1 + while true do + local mid = floor((min+max)/2) + local r = referred[mid] + local m = r[2] + if n == m then + return r[1] + elseif n > m then + min = mid + 1 + else + max = mid - 1 + end + if min > max then + break + end + end + -- find first previous + for i=min,1,-1 do + local r = referred[i] + if r and r[2] < n then + return r[1] + end + end + end + -- fallback + return texgetcount("realpageno") +end + +references.referredpage = referredpage + +function references.registerpage(n) -- called in the backend code + if not tobereferred[n] then + if n > maxreferred then + maxreferred = n + end + tobereferred[n] = texgetcount("realpageno") + end +end + +-- todo: delay split till later as in destinations we split anyway + +local orders, lastorder = { }, 0 + +local function setnextorder(kind,name) + lastorder = 0 + if kind and name then + local ok = orders[kind] + if not ok then + ok = { } + orders[kind] = ok + end + lastorder = (ok[name] or 0) + 1 + ok[name] = lastorder + end + texsetcount("global","locationorder",lastorder) +end + + +local function setnextinternal(kind,name) + setnextorder(kind,name) -- always incremented with internal + local n = texgetcount("locationcount") + 1 + texsetcount("global","locationcount",n) + return n +end + +local function currentorder(kind,name) + return orders[kind] and orders[kind][name] or lastorder +end + +local function setcomponent(data) + -- we might consider doing this at the tex end, just like prefix + local component = productcomponent() + if component then + 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 + +references.setnextorder = setnextorder +references.setnextinternal = setnextinternal +references.currentorder = currentorder +references.setcomponent = setcomponent + +-- implement { +-- name = "setnextreferenceorder", +-- actions = setnextorder, +-- arguments = "2 strings", +-- } +-- +-- implement { +-- name = "currentreferenceorder", +-- actions = { currentorder, context }, +-- arguments = "2 strings", +-- } + +implement { + name = "setnextinternalreferences", + public = true, + protected = true, + actions = setnextinternal, + arguments = "2 arguments", +} + +implement { + name = "currentreferenceorder", + public = true, + protected = true, + actions = { currentorder, context }, + arguments = "2 arguments", +} + +local reported = setmetatableindex("table") + +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 + local function action(ref) + if ref == "" then + -- skip + elseif check_duplicates and pd[ref] then + if not prefix then + prefix = "" + end + if not reported[prefix][ref] then + if prefix ~= "" then + report_references("redundant reference %a in namespace %a",ref,prefix) + else + report_references("redundant reference %a",ref) + end + reported[prefix][ref] = true + end + else + n = n + 1 + pd[ref] = data + local r = data.references + ctx_dofinishreference(prefix or "",ref or "",r and r.internal or 0) + -- ctx_latelua(function() structures.references.enhance(prefix or ref,ref or "") end) + end + end + 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 function synchronizepage(reference) -- non public helper + reference.realpage = texgetcount("realpageno") + if jobpositions.used() then + reference.x, reference.y = getpos() + end +end + +references.synchronizepage = synchronizepage + +local function enhancereference(specification) + local prefix = specification.prefix + if prefix then + local entry = tobesaved[prefix] + if entry then + entry = entry[specification.tag] + if entry then + synchronizepage(entry.references) + else + -- normally a bug + end + else + -- normally a bug + end + else + -- normally a bug + end +end + +references.enhance = enhancereference + +-- implement { +-- name = "enhancereference", +-- arguments = "2 strings", +-- actions = function(prefix,tag) +-- enhancereference { prefix = prefix, tag = tag } +-- end, +-- } + +implement { + name = "deferredenhancereference", + arguments = "2 strings", + protected = true, + actions = function(prefix,tag) + ctx_latelua { action = enhancereference, prefix = prefix, tag = tag } + end, +} + +-- -- -- related to strc-ini.lua -- -- -- + +-- no metatable here .. better be sparse + +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 + end + end +end + +references.registerinitializer(function() register_from_lists(lists.collected,derived) end) + +-- 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 + 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 + if internal > 0 then + data.usedprefix = prefix + end + end + end + end + end +end + +references.tracedpages = table.setmetatableindex(allocate(),function(t,k) + if collectbypage then + collectbypage(t) + collectbypage = nil + end + return rawget(t,k) +end) + +-- urls + +local urls = references.urls or { } +references.urls = urls +local urldata = urls.data or { } +urls.data = urldata + +local p_untexurl = Cs ( ( + P("\\")/"" * (P("%")/"%%" + P(1)) + + P(" ")/"%%20" + + P(1) +)^1 ) + +function urls.untex(url) + return lpegmatch(p_untexurl,url) or url +end + +function urls.define(name,url,file,description) + if name and name ~= "" then + -- url = lpegmatch(replacer,url) + urldata[name] = { url or "", file or "", description or url or file or ""} + end +end + +function urls.get(name) + local u = urldata[name] + if u then + local url, file = u[1], u[2] + if file and file ~= "" then + return formatters["%s/%s"](url,file) + else + return url + end + end +end + +function urls.found(name) + return urldata[name] +end + +local function geturl(name) + local url = urls.get(name) + if url and url ~= "" then + ctx_pushcatcodes(txtcatcodes) + context(url) + ctx_popcatcodes() + end +end + +implement { + name = "doifelseurldefined", + actions = { urls.found, commands.doifelse }, + arguments = "string" +} + +implement { + name = "useurl", + actions = urls.define, + arguments = "4 strings", +} + +implement { + name = "geturl", + actions = geturl, + arguments = "string", +} + +-- files + +local files = references.files or { } +references.files = files +local filedata = files.data or { } +files.data = filedata + +function files.define(name,file,description) + if name and name ~= "" then + filedata[name] = { file or "", description or file or "" } + end +end + +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 files.found(name) + return filedata[name] +end + +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 = "3 strings" +} + +implement { + name = "getfile", + actions = getfile, + arguments = "string" +} + +-- helpers + +function references.checkedfile(whatever) -- return whatever if not resolved + if whatever then + local w = filedata[whatever] + if w then + return w[1] + else + return whatever + end + end +end + +function references.checkedurl(whatever) -- return whatever if not resolved + if whatever then + local w = urldata[whatever] + if w then + local u, f = w[1], w[2] + if f and f ~= "" then + return u .. "/" .. f + else + return u + end + else + return whatever + end + end +end + +function references.checkedfileorurl(whatever,default) -- return nil, nil if not resolved + if whatever then + local w = filedata[whatever] + if w then + return w[1], nil + else + local w = urldata[whatever] + if w then + local u, f = w[1], w[2] + if f and f ~= "" then + return nil, u .. "/" .. f + else + return nil, u + end + end + end + end + return default +end + +-- programs + +local programs = references.programs or { } +references.programs = programs +local programdata = programs.data or { } +programs.data = programdata + +function programs.define(name,file,description) + if name and name ~= "" then + programdata[name] = { file or "", description or file or ""} + end +end + +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 = programdata[whatever] + if w then + return w[1] + else + return whatever + end + end +end + +implement { + name = "defineprogram", + actions = programs.define, + arguments = "3 strings", +} + +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.from(name) + local u = urldata[name] + if u then + local url, file, description = u[1], u[2], u[3] + if description ~= "" then + return description + -- ok + elseif file and file ~= "" then + return url .. "/" .. file + else + return url + end + else + local f = filedata[name] + if f then + local file, description = f[1], f[2] + if description ~= "" then + return description + else + return file + end + end + end +end + +local function from(name) + local u = urldata[name] + if u then + local url, file, description = u[1], u[2], u[3] + if description ~= "" then + ctx_dofromurldescription(description) + -- ok + elseif file and file ~= "" then + ctx_dofromurlliteral(url .. "/" .. file) + else + ctx_dofromurlliteral(url) + end + else + local f = filedata[name] + if f then + local file, description = f[1], f[2] + if description ~= "" then + ctx_dofromfiledescription(description) + else + 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] = list +end + +function references.reset(prefix,reference) + local d = defined[prefix] + if d then + d[reference] = nil + end +end + +implement { + name = "definereference", + actions = references.define, + arguments = "3 strings", +} + +implement { + name = "resetreference", + actions = references.reset, + arguments = "2 strings", +} + +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 + 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) + local r = settings_to_table(reference) -- maybe option to honor () [] + for i=1,#r do + local ri = r[i] + local d = defined[prefix][ri] or defined[""][ri] + if d then + d = getexpansion(d) + resolve(prefix,d,nil,set) + else + local var = splitreference(ri) + if var then + var.reference = ri + local vo, vi = var.outer, var.inner + -- we catch this here .. it's a way to pass references with commas + if vi == "name" then + local arguments = var.arguments + if arguments then + vi = arguments + var.inner = arguments + var.reference = arguments + var.arguments = nil + end + elseif var.special == "name" then + local operation = var.operation + if operation then + vi = operation + var.inner = operation + var.reference = operation + var.operation = nil + var.special = nil + end + end + -- end of catch + if not vo and vi then + -- to be checked + d = defined[prefix][vi] or defined[""][vi] + -- + if d then + d = getexpansion(d) + resolve(prefix,d,var.arguments,set) -- args can be nil + else + if args then var.arguments = args end + set[#set+1] = var + end + else + if args then var.arguments = args end + set[#set+1] = var + end + else + -- report_references("funny pattern %a",ri) + end + end + end + return set + else + return { } + end +end + +-- prefix == "" is valid prefix which saves multistep lookup + +references.currentset = nil + +local externals = { } + +-- we have prefixes but also components: +-- +-- : prefix +-- :: always external +-- ::: internal (for products) or external (for components) + +local function loadexternalreferences(name,utilitydata) + local struc = utilitydata.structures + if struc then + local external = struc.references.collected -- direct references + local lists = struc.lists.collected -- indirect references (derived) + local pages = struc.pages.collected -- pagenumber data + -- a bit weird one, as we don't have the externals in the collected + for prefix, set in next, external do + if prefix == "" then + prefix = name -- this can clash! + end + for reference, data in next, set do + if trace_importing then + report_importing("registering %a reference, kind %a, name %a, prefix %a, reference %a", + "external","regular",name,prefix,reference) + end + local section = reference.section + local realpage = reference.realpage + if section then + reference.sectiondata = lists[section] + end + if realpage then + reference.pagedata = pages[realpage] + end + end + end + for i=1,#lists do + local entry = lists[i] + local metadata = entry.metadata + local references = entry.references + if metadata and references then + local reference = references.reference + if reference and reference ~= "" then + local kind = metadata.kind + local realpage = references.realpage + if kind and realpage then + references.pagedata = pages[realpage] + local prefix = references.prefix or "" + if prefix == "" then + prefix = name -- this can clash! + end + local target = external[prefix] + if not target then + target = { } + external[prefix] = target + end + -- for s in gmatch(reference,"%s*([^,]+)") do + -- if trace_importing then + -- report_importing("registering %s reference, kind %a, name %a, prefix %a, reference %a", + -- "external",kind,name,prefix,s) + -- end + -- target[s] = target[s] or entry + -- end + local function action(s) + if trace_importing then + report_importing("registering %s reference, kind %a, name %a, prefix %a, reference %a", + "external",kind,name,prefix,s) + end + target[s] = target[s] or entry + end + process_settings(reference,action) + end + end + end + end + externals[name] = external + return external + end +end + +local externalfiles = { } + +setmetatableindex(externalfiles, function(t,k) + local v = filedata[k] + if not v then + v = { k, k } + end + externalfiles[k] = v + return v +end) + +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 + local utilitydata = job.loadother(fullname) + if utilitydata then + local external = loadexternalreferences(k,utilitydata) + t[k] = external or false + return external + end + end + t[k] = false + return false +end) + +local productdata = allocate { + productreferences = { }, + componentreferences = { }, + components = { }, +} + +references.productdata = productdata + +local function loadproductreferences(productname,componentname,utilitydata) + local struc = utilitydata.structures + if struc then + local productreferences = struc.references.collected -- direct references + local lists = struc.lists.collected -- indirect references (derived) + local pages = struc.pages.collected -- pagenumber data + -- we use indirect tables to save room but as they are eventually + -- just references we resolve them to data here (the mechanisms + -- that use this data check for indirectness) + for prefix, set in next, productreferences do + for reference, data in next, set do + if trace_importing then + report_importing("registering %s reference, kind %a, name %a, prefix %a, reference %a", + "product","regular",productname,prefix,reference) + end + local section = reference.section + local realpage = reference.realpage + if section then + reference.sectiondata = lists[section] + end + if realpage then + reference.pagedata = pages[realpage] + end + end + end + -- + local componentreferences = { } + for i=1,#lists do + local entry = lists[i] + local metadata = entry.metadata + local references = entry.references + if metadata and references then + local reference = references.reference + if reference and reference ~= "" then + local kind = metadata.kind + local realpage = references.realpage + if kind and realpage then + references.pagedata = pages[realpage] + local prefix = references.prefix or "" + local component = references.component + local ctarget, ptarget + if not component or component == componentname then + -- skip + else + -- one level up + local external = componentreferences[component] + if not external then + external = { } + componentreferences[component] = external + end + if component == prefix then + prefix = "" + end + ctarget = external[prefix] + if not ctarget then + ctarget = { } + external[prefix] = ctarget + end + end + ptarget = productreferences[prefix] + if not ptarget then + ptarget = { } + productreferences[prefix] = ptarget + end + local function action(s) + 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 + process_settings(reference,action) + end + end + end + end + productdata.productreferences = productreferences -- not yet used + productdata.componentreferences = componentreferences + end +end + +local function loadproductvariables(product,component,utilitydata) + local struc = utilitydata.structures + if struc then + local lists = struc.lists and struc.lists.collected + if lists then + local pages = struc.pages and struc.pages.collected + for i=1,#lists do + local li = lists[i] + if li.metadata.kind == "section" and li.references.component == component then + local firstsection = li + if firstsection.numberdata then + local numbers = firstsection.numberdata.numbers + if numbers then + if trace_importing then + report_importing("initializing section number to %:t",numbers) + end + productdata.firstsection = firstsection + structures.documents.preset(numbers) + end + end + if pages and firstsection.references then + local firstpage = pages[firstsection.references.realpage] + local number = firstpage and firstpage.number + if number then + if trace_importing then + report_importing("initializing page number to %a",number) + end + productdata.firstpage = firstpage + counters.set("userpage",1,number) + end + end + break + end + end + end + end +end + +local function componentlist(tree,target) + local branches = tree and tree.branches + if branches then + for i=1,#branches do + local branch = branches[i] + local type = branch.type + if type == "component" then + if target then + target[#target+1] = branch.name + else + target = { branch.name } + end + elseif type == "product" or type == "component" then + target = componentlist(branch,target) + end + end + end + return target +end + +local function loadproductcomponents(product,component,utilitydata) + local job = utilitydata.job + productdata.components = componentlist(job and job.structure and job.structure.collected) or { } +end + +references.registerinitializer(function(tobesaved,collected) + -- not that much related to tobesaved or collected + productdata.components = componentlist(job.structure.collected) or { } +end) + +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 + local fullname = file.replacesuffix(product,"tuc") + if lfs.isfile(fullname) then -- todo: use other locator + local utilitydata = job.loadother(fullname) + if utilitydata then + if trace_importing then + report_importing("loading references for component %a of product %a from %a",component,product,fullname) + end + loadproductvariables (product,component,utilitydata) + loadproductreferences(product,component,utilitydata) + loadproductcomponents(product,component,utilitydata) + end + end + end +end + +references.productdata = productdata + +local useproduct = commands.useproduct + +if useproduct then + + local function newuseproduct(product) + useproduct(product) + if texconditionals.autocrossfilereferences then + local component = justacomponent() + if component then + if trace_referencing or trace_importing then + report_references("loading presets for component %a of product %a",component,product) + end + references.loadpresets(product,component) + end + end + end + + implement { + name = "useproduct", + actions = newuseproduct, + arguments = "string", + overload = true, + } + +end + +-- productdata.firstsection.numberdata.numbers +-- productdata.firstpage.number + +local function report_identify_special(set,var,i,type) + local reference = set.reference + local prefix = set.prefix or "" + local special = var.special + local error = var.error + local kind = var.kind + if error then + report_identifying("type %a, reference %a, index %a, prefix %a, special %a, error %a",type,reference,i,prefix,special,error) + else + report_identifying("type %a, reference %a, index %a, prefix %a, special %a, kind %a",type,reference,i,prefix,special,kind) + end +end + +local function report_identify_arguments(set,var,i,type) + local reference = set.reference + local prefix = set.prefix or "" + local arguments = var.arguments + local error = var.error + local kind = var.kind + if error then + report_identifying("type %a, reference %a, index %a, prefix %a, arguments %a, error %a",type,reference,i,prefix,arguments,error) + else + report_identifying("type %a, reference %a, index %a, prefix %a, arguments %a, kind %a",type,reference,i,prefix,arguments,kind) + end +end + +local function report_identify_outer(set,var,i,type) + local reference = set.reference + local prefix = set.prefix or "" + local outer = var.outer + local error = var.error + local kind = var.kind + if outer then + if error then + report_identifying("type %a, reference %a, index %a, prefix %a, outer %a, error %a",type,reference,i,prefix,outer,error) + else + report_identifying("type %a, reference %a, index %a, prefix %a, outer %a, kind %a",type,reference,i,prefix,outer,kind) + end + else + if error then + report_identifying("type %a, reference %a, index %a, prefix %a, error %a",type,reference,i,prefix,error) + else + report_identifying("type %a, reference %a, index %a, prefix %a, kind %a",type,reference,i,prefix,kind) + end + end +end + +local function identify_special(set,var,i) + local special = var.special + local s = specials[special] + if s then + local outer = var.outer + local operation = var.operation + local arguments = var.arguments + if outer then + if operation then + -- special(outer::operation) + var.kind = "special outer with operation" + else + -- special() + var.kind = "special outer" + end + var.f = outer + elseif operation then + if arguments then + -- special(operation{argument,argument}) + var.kind = "special operation with arguments" + else + -- special(operation) + var.kind = "special operation" + end + else + -- special() + var.kind = "special" + end + if trace_identifying then + report_identify_special(set,var,i,"1a") + end + else + var.error = "unknown special" + end + return var +end + +local function identify_arguments(set,var,i) + local s = specials[var.inner] + if s then + -- inner{argument} + var.kind = "special operation with arguments" + else + var.error = "unknown inner or special" + end + if trace_identifying then + report_identify_arguments(set,var,i,"3a") + end + return var +end + +-- 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 + -- the next test is a safeguard when references are auto loaded from outer + if not inner or inner == "" then + return false + end + 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 + local i = collected[""] + if i then + i = i[splitinner] + if i then + return finish_inner(var,"",i) + end + end + end + local i = collected[splitprefix] + if i then + i = i[splitinner] + if i then + return finish_inner(var,splitprefix,i) + end + end + 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 + local i = derived[""] + if i then + i = i[splitinner] + if i then + return finish_inner(var,"",i) + end + end + end + local i = derived[splitprefix] + if i then + i = i[splitinner] + if i then + return finish_inner(var,splitprefix,i) + end + end + end + end + -- we now ignore the split prefix and treat the whole inner as a potential + -- reference into the global list + local i = collected[prefix] + if i then + i = i[inner] + if i then + return finish_inner(var,prefix,i) + end + 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 + 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.p = "" + var.i = i + var.r = (i.references and i.references.realpage) or (i.pagedata and i.pagedata.realpage) or 1 + else + var.error = "unknown inner or special" + end + end + return var +end + +local function identify_outer(set,var,i) + local outer = var.outer + local inner = var.inner + local external = externals[outer] + if external then + local v = identify_inner(set,var,"",external) + if v then + v.kind = "outer with inner" + set.external = true + if trace_identifying then + report_identify_outer(set,v,i,"2a") + end + return v + end +-- weird too (we really need to check how this table is build + local v = identify_inner(set,var,var.outer,external) + if v then + v.kind = "outer with inner" + set.external = true + if trace_identifying then + report_identify_outer(set,v,i,"2c") + end + return v + end +-- + -- somewhat rubish: we use outer as first step in the externals table so it makes no + -- sense to have it as prefix so the next could be an option + local external = external[""] + if external 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 + report_identify_outer(set,v,i,"2b") + end + return v + end + end + end + local external = productdata.componentreferences[outer] + if external then + local v = identify_inner(set,var,"",external) + if v then + v.kind = "outer with inner" + set.external = true + if trace_identifying then + report_identify_outer(set,v,i,"2c") + end + return v + end + end + local external = productdata.productreferences[outer] + if external then + local vi = external[inner] + if vi then + var.kind = "outer with inner" + var.i = vi + set.external = true + if trace_identifying then + report_identify_outer(set,var,i,"2d") + end + return var + end + end + -- the rest + local special = var.special + 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" + else + -- outer::inner + var.kind = "outer with inner" + end + var.i = inner + var.f = outer + if type(inner) == "table" then + -- can this really happen? + var.r = (inner.references and inner.references.realpage) or (inner.pagedata and inner.pagedata.realpage) or 1 + else + var.r = 1 + end + if trace_identifying then + report_identify_outer(set,var,i,"2e") + end + elseif special then + local s = specials[special] + if s then + if operation then + if arguments then + -- outer::special(operation{argument,argument}) + var.kind = "outer with special and operation and arguments" + else + -- outer::special(operation) + var.kind = "outer with special and operation" + end + else + -- outer::special() + var.kind = "outer with special" + end + var.f = outer + else + var.error = "unknown outer with special" + end + if trace_identifying then + report_identify_outer(set,var,i,"2f") + end + else + -- outer:: + var.kind = "outer" + var.f = outer + if trace_identifying then + report_identify_outer(set,var,i,"2g") + end + end + 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 + + -- 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 + + -- nest we look at each component (but we can omit the already consulted one + + local jobstructure = job.structure + local components = jobstructure and jobstructure.components + if components then + 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 c=1,#components do + local component = components[c] + local data = componentreferences[component] + if data then + local d = data[""] + local vi = d and d[inner] + if vi then + var.outer = component + var.i = vi + var.kind = "outer with inner" + set.external = true + if trace_identifying then + report_identify_outer(set,var,i,"4d") + end + return var + end + end + end + end + local component, inner = lpegmatch(componentsplitter,inner) + if component then + local data = componentreferences and componentreferences[component] + if data then + local d = data[""] + local vi = d and d[inner] + if vi then + var.inner = inner + var.outer = component + var.i = vi + var.kind = "outer with inner" + set.external = true + if trace_identifying then + report_identify_outer(set,var,i,"4e") + end + return var + end + end + local data = productreferences and productreferences[component] + if data then + local vi = data[inner] + if vi then + var.inner = inner + var.outer = component + var.i = vi + var.kind = "outer with inner" + set.external = true + if trace_identifying then + report_identify_outer(set,var,i,"4f") + end + return var + end + end + end + var.error = "unknown inner" + else + var.error = "no inner" + end + if trace_identifying then + report_identify_outer(set,var,i,"4g") + 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 + 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 + return var +end + +local function identify_outer_component(set,var,i) + local component = var.component + local inner = var.inner + local data = productdata.componentreferences[component] + if data then + local d = data[""] + local vi = d and d[inner] + if vi then + var.inner = inner + var.outer = component + var.i = vi + var.kind = "outer with inner" + set.external = true + if trace_identifying then + report_identify_outer(set,var,i,"6a") + end + return var + end + end + local data = productdata.productreferences[component] + if data then + local vi = data[inner] + if vi then + var.inner = inner + var.outer = component + var.i = vi + var.kind = "outer with inner" + set.external = true + if trace_identifying then + report_identify_outer(set,var,i,"6b") + end + return var + end + end + var.error = "unknown component" + if trace_identifying then + report_identify_outer(set,var,i,"6c") + end + return var +end + +local nofidentified = 0 + +local function identify(prefix,reference) + if not reference then + prefix, reference = "", prefix + end + local set = resolve(prefix,reference) + local bug = false + nofidentified = nofidentified + 1 + set.n = nofidentified + for i=1,#set do + local var = set[i] + 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) + elseif var.arguments then + var = identify_arguments(set,var,i) + elseif not var.component then + var = identify_inner_or_outer(set,var,i) + elseif productcomponent() then + var = identify_inner_component(set,var,i) + else + var = identify_outer_component(set,var,i) + end + set[i] = var + bug = bug or var.error + end + references.currentset = mark(set) -- mark, else in api doc + if trace_analyzing then + report_references(table.serialize(set,reference)) + end + return set, bug +end + +references.identify = identify + +local unknowns, nofunknowns, f_valid = { }, 0, formatters["[%s][%s]"] + +function references.valid(prefix,reference,specification) + local set, bug = identify(prefix,reference) + local unknown = bug or #set == 0 + if unknown then + currentreference = nil -- will go away + local str = f_valid(prefix,reference) + local u = unknowns[str] + if not u then + if somefound then + interfaces.showmessage("references",1,str) -- 1 = unknown, 4 = illegal + end + unknowns[str] = 1 + nofunknowns = nofunknowns + 1 + else + unknowns[str] = u + 1 + end + else + 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 + +implement { + name = "doifelsereference", + actions = { references.valid, commands.doifelse }, + arguments = { + "string", + "string", + { + { "highlight", "boolean" }, + { "newwindow", "boolean" }, + { "layer" }, + } + } +} + +logs.registerfinalactions(function() + if nofunknowns > 0 then + statistics.register("cross referencing", function() + return format("%s identified, %s unknown",nofidentified,nofunknowns) + end) + local sortedhash = table.sortedhash + logs.startfilelogging(report,"missing references") + for k, v in table.sortedhash(unknowns) do + report("%4i %s",v,k) + end + logs.stopfilelogging() + if logs.loggingerrors() then + logs.starterrorlogging(report,"missing references") + for k, v in table.sortedhash(unknowns) do + report("%4i %s",v,k) + end + logs.stoperrorlogging() + end + end +end) + +-- 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 outermethod = v_auto -- only page|auto now +local defaultinnermethod = defaultinnermethod +local defaultoutermethod = defaultoutermethod +references.innermethod = innermethod -- don't mess with this one directly +references.outermethod = outermethod -- don't mess with this one directly + +function references.setlinkmethod(inner,outer) + if not outer and type(inner) == "string" then + local m = settings_to_array(inner) + inner = m[1] + outer = m[2] or v_auto + end + if toboolean(inner) or inner == v_page or inner == v_yes then + innermethod = v_page + elseif inner == v_name then + innermethod = v_name + else + innermethod = v_auto + end + if toboolean(outer) or outer == v_page or outer == v_yes then + outermethod = v_page + elseif inner == v_name then + outermethod = v_name + else + outermethod = v_auto + end + references.innermethod = innermethod + references.outermethod = outermethod + function references.setlinkmethod() + report_references("link method is already set and frozen: inner %a, outer %a",innermethod,outermethod) + end +end + +implement { + name = "setreferencelinkmethod", + actions = references.setlinkmethod, + arguments = "string", + -- onlyonce = true +} + +function references.getinnermethod() + return innermethod or defaultinnermethod +end + +function references.getoutermethod() + return outermethod or defaultoutermethod +end + +directives.register("references.linkmethod", function(v) -- page auto + references.setlinkmethod(v) +end) + +-- we can call setinternalreference with an already known internal or with +-- a reference/prefix specification + +local destinationattributes = { } + +local function setinternalreference(specification) + local internal = specification.internal + local destination = unsetvalue + if innermethod == v_auto or innermethod == v_name then + local t = { } -- maybe add to current (now only used for tracing) + local tn = 0 + local reference = specification.reference + local view = specification.view + if reference then + local prefix = specification.prefix + if prefix and prefix ~= "" then + local prefix = prefix .. ":" -- watch out, : here + local function action(ref) + tn = tn + 1 + t[tn] = prefix .. ref + end + process_settings(reference,action) + else + local function action(ref) + tn = tn + 1 + t[tn] = ref + end + process_settings(reference,action) + end + end + -- 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 + if innermethod ~= v_name then -- innermethod == v_auto + -- we don't want too many #1 #2 #3 etc + tn = tn + 1 + t[tn] = internal -- when number it's internal + end + if not view then + local i = references.internals[internal] + if i then + view = i.references.view + end + end + end + destination = references.mark(t,nil,nil,view) -- returns an attribute + end + if internal then -- new + destinationattributes[internal] = destination + end + texsetcount("lastdestinationattribute",destination) + return destination +end + +local function getinternalreference(internal) + return destinationattributes[internal] or 0 +end + +references.setinternalreference = setinternalreference +references.getinternalreference = getinternalreference + +implement { + name = "setinternalreference", + actions = setinternalreference, + arguments = { + { + { "prefix" }, + { "reference" }, + { "internal", "integer" }, + { "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 = rdat.prefix, + reference = rdat.reference, + internal = rdat.internal, + view = rdat.view + } or unsetvalue + end + end + texsetcount("lastdestinationattribute",attr) + return attr +end + +implement { + name = "setdestinationattribute", + 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] + local i = l and l.references.internal + return i and destinationattributes[i] or 0 +end + +function references.getinternalcachedlistreference(n) -- n points into list (todo: registers) + local l = lists.cached[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" +} + +implement { + name = "getinternalcachedlistreference", + actions = { references.getinternalcachedlistreference, context }, + arguments = "integer" +} + + +-- + +function references.getcurrentmetadata(tag) + local data = currentreference and currentreference.i + return data and data.metadata and data.metadata[tag] +end + +implement { + name = "getcurrentreferencemetadata", + actions = { references.getcurrentmetadata, context }, + arguments = "string", +} + +local function currentmetadata(tag) + local data = currentreference and currentreference.i + return data and data.metadata and data.metadata[tag] +end + +references.currentmetadata = currentmetadata + +local function getcurrentprefixspec(default) + local data = currentreference and currentreference.i + local metadata = data and data.metadata + return + metadata and metadata.kind or "?", + metadata and metadata.name or "?", + default or "?" +end + +references.getcurrentprefixspec = getcurrentprefixspec + +-- 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", +} + +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) + 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 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,prefixspec,numberspec) + elseif trace_referencing then + report_references("name %a, kind %a, using generic filter",name,kind) + end + elseif trace_referencing then + report_references("name %a, unknown kind",name) + end + end + elseif name == "realpage" then + context(0) + elseif trace_referencing then + report_references("name %a, no reference",name) + end +end + +local function filterreferencedefault() + return filterreference("default",getcurrentprefixspec("default")) +end + +references.filter = filterreference +references.filterdefault = filterreferencedefault + +implement { + name = "filterreference", + actions = filterreference, + arguments = "string", +} + +implement { + name = "filterdefaultreference", + actions = filterreference, + arguments = { + "string", -- 'default' + { { "*" } }, -- prefixspec + { { "*" } }, -- numberspec + } +} + +function genericfilters.title(data) + if data then + local titledata = data.titledata or data.useddata + if titledata then + helpers.title(titledata.reference or titledata.title or "?",data.metadata) + end + end +end + +function genericfilters.text(data) + if data then + local entries = data.entries or data.useddata + if entries then + helpers.title(entries.text or "?",data.metadata) + end + end +end + +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",numberspec,numberdata) + else + local useddata = data.useddata + if useddata and useddata.number then + context(useddata.number) + end + end + end +end + +genericfilters.default = genericfilters.text + +function genericfilters.page(data,prefixspec,pagespec) + local pagedata = data.pagedata + if pagedata then + local number = pagedata.number + local conversion = pagedata.conversion + if not number then + -- error + elseif conversion then + ctx_convertnumber(conversion,number) + else + context(number) + end + else + helpers.prefixpage(data,prefixspec,pagespec) + end +end + +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 genericfilters + filter = filter and (filter[name] or filter.unknown) + if filter then + filter(data,name) + return + end + end + local namedata = userdata and userdata[name] + if namedata then + context(namedata) + end + end +end + +function textfilters.title(data) + helpers.title(data.entries.text or "?",data.metadata) +end + +-- no longer considered useful: +-- +-- function filters.text.number(data) +-- helpers.title(data.entries.text or "?",data.metadata) +-- end + +function textfilters.page(data,prefixspec,pagespec) + helpers.prefixpage(data,prefixspec,pagespec) +end + +fullfilters.title = textfilters.title +fullfilters.page = textfilters.page + +function sectionfilters.number(data,what,prefixspec) + if data then + local numberdata = data.numberdata + if not numberdata then + local useddata = data.useddata + if useddata and useddata.number then + context(useddata.number) + end + elseif numberdata.hidenumber then + local references = data.references + if trace_empty then + report_empty("reference %a has a hidden number",references.reference) + ctx_emptyreference() -- maybe an option + end + else + sections.typesetnumber(numberdata,"number",prefixspec,numberdata) + end + end +end + +sectionfilters.title = genericfilters.title +sectionfilters.page = genericfilters.page +sectionfilters.default = sectionfilters.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 = genericfilters.number } -- not copy as it might be extended differently + t[k] = v + return v +end) + +-- function references.sectiontitle(n) +-- helpers.sectiontitle(lists.collected[tonumber(n) or 0]) +-- end + +-- function references.sectionnumber(n) +-- helpers.sectionnumber(lists.collected[tonumber(n) or 0]) +-- end + +-- function references.sectionpage(n,prefixspec,pagespec) +-- helpers.prefixedpage(lists.collected[tonumber(n) or 0],prefixspec,pagespec) +-- end + +-- analyze + +references.testrunners = references.testrunners or { } +references.testspecials = references.testspecials or { } + +local runners = references.testrunners +local specials = references.testspecials + +-- We need to prevent ending up in the 'relative location' analyzer as it is +-- 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,actions,position,spread) + local p = tonumber(page) + if not p then + return 0 + 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 + else + return 1 -- same + end +end + +local function setreferencerealpage(actions) + if not actions then + actions = references.currentset + end + if type(actions) == "table" then + local realpage = actions.realpage + if realpage then + return realpage + end + local nofactions = #actions + if nofactions > 0 then + for i=1,nofactions do + local a = actions[i] + local what = runners[a.kind] + if what then + what = what(a,actions) -- needs documentation + end + end + realpage = actions.realpage + if realpage then + return realpage + end + end + actions.realpage = 0 + end + return 0 +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,position,spread) + if not actions then + actions = references.currentset + end + if not actions then + actions = { realpage = 0, pagestate = 0 } + elseif actions.pagestate then + -- already done + else + local realpage = actions.realpage or setreferencerealpage(actions) + if realpage == 0 then + actions.pagestate = 0 + elseif actions.external then + actions.pagestate = 0 + else + actions.pagestate = checkedpagestate(actions.n,realpage,actions,position,spread) + end + end + return actions +end + +local function referencepagestate(position,detail,spread) + local actions = references.currentset + if not actions then + return 0 + else + local pagestate = actions.pagestate + for i=1,#actions do + local a = actions[i] + if a.outer then + pagestate = 0 + actions.pagestate = pagestate + break + end + end + if not pagestate then + references.analyze(actions,position,spread) -- delayed unless explicitly asked for + pagestate = actions.pagestate + end + if detail then + return pagestate + elseif pagestate == 4 then + return 2 -- compatible + elseif pagestate == 5 then + return 3 -- compatible + else + return pagestate + end + end +end + +implement { + name = "referencepagestate", + actions = { referencepagestate, context }, + arguments = "string" +} + +implement { + name = "referencepagedetail", + actions = { referencepagestate, context }, + arguments = { "string", "boolean", "boolean" } +} + +-- local function referencerealpage() +-- local actions = references.currentset +-- return not actions and 0 or actions.realpage or setreferencerealpage(actions) +-- end +-- +-- implement { +-- name = "referencerealpage", +-- actions = { referencerealpage, context }, +-- -- arguments = "string" -- hm, weird +-- } + +implement { + name = "askedreference", + public = true, + protected = true, + actions = function() + local actions = references.currentset + if actions then + context("[p=%s,r=%s]",actions.prefix or "",actions.reference) + end + end +} + +implement { + name = "referencerealpage", + actions = function() + local actions = references.currentset + context(not actions and 0 or actions.realpage or setreferencerealpage(actions)) + end +} + +local function referencepos(key) + local actions = references.currentset + local i = actions[1].i -- brrr + local v = 0 + if i then + local a = i.references + if a then + v = a[key] or 0 + end + end + return v +end + +implement { name = "referenceposx", actions = function() context("%p",referencepos("x")) end } +implement { name = "referenceposy", actions = function() context("%p",referencepos("y")) end } + +implement { + name = "referencecolumn", + actions = function() + local actions = references.currentset + local column = 1 + if actions then + column = jobpositions.columnofpos(actions.realpage or setreferencerealpage(actions),referencepos("x")) + end + context(column or 1) + end +} + +local plist, nofrealpages + +local function realpageofpage(p) -- the last one counts ! + if not plist then + local pages = structures.pages.collected + nofrealpages = #pages + plist = { } + for rp=1,nofrealpages do + local page = pages[rp] + if page then + plist[page.number] = rp + end + end + references.nofrealpages = nofrealpages + end + return plist[p] +end + +references.realpageofpage = realpageofpage + +function references.checkedrealpage(r) + if not plist then + realpageofpage(r) -- just initialize + end + if not r then + return texgetcount("realpageno") + elseif r < 1 then + return 1 + elseif r > nofrealpages then + return nofrealpages + else + return r + end +end + +-- use local ? + +local pages = allocate { + [variables.firstpage] = function() return counters.record("realpage")["first"] end, + [variables.previouspage] = function() return counters.record("realpage")["previous"] end, + [variables.nextpage] = function() return counters.record("realpage")["next"] end, + [variables.lastpage] = function() return counters.record("realpage")["last"] end, + + [variables.firstsubpage] = function() return counters.record("subpage" )["first"] end, + [variables.previoussubpage] = function() return counters.record("subpage" )["previous"] end, + [variables.nextsubpage] = function() return counters.record("subpage" )["next"] end, + [variables.lastsubpage] = function() return counters.record("subpage" )["last"] end, + + [variables.forward] = function() return counters.record("realpage")["forward"] end, + [variables.backward] = function() return counters.record("realpage")["backward"] end, +} + +references.pages = pages + +-- maybe some day i will merge this in the backend code with a testmode (so each +-- runner then implements a branch) + +runners["inner"] = function(var,actions) + local r = var.r + if r then + actions.realpage = r + end +end + +runners["special"] = function(var,actions) + local handler = specials[var.special] + return handler and handler(var,actions) +end + +runners["special operation"] = runners["special"] +runners["special operation with arguments"] = runners["special"] + +function specials.internal(var,actions) + local v = internals[tonumber(var.operation)] + local r = v and v.references + if r then + local p = r.realpage + if p then +-- setmetatableindex(actions,r) + actions.realpage = p + actions.view = r.view + end + end +end + +specials.i = specials.internal + +function specials.page(var,actions) + local o = var.operation + local p = pages[o] + if type(p) == "function" then + p = p() + else + p = tonumber(realpageofpage(tonumber(o))) + end + if p then + var.r = p + actions.realpage = actions.realpage or p -- first wins + end +end + +function specials.realpage(var,actions) + local p = tonumber(var.operation) + if p then + var.r = p + actions.realpage = actions.realpage or p -- first wins + end +end + +function specials.userpage(var,actions) + local p = tonumber(realpageofpage(var.operation)) + if p then + var.r = p + actions.realpage = actions.realpage or p -- first wins + end +end + +function specials.deltapage(var,actions) + local p = tonumber(var.operation) + if p then + p = references.checkedrealpage(p + texgetcount("realpageno")) + var.r = p + actions.realpage = actions.realpage or p -- first wins + end +end + +function specials.section(var,actions) + local sectionname = var.arguments + local destination = var.operation + local internal = structures.sections.internalreference(sectionname,destination) + if internal then + var.special = "internal" + var.operation = internal + var.arguments = nil + specials.internal(var,actions) + end +end + +-- 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) + +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: + +function references.export(usedname) end +function references.import(usedname) end +function references.load (usedname) end + +implement { name = "exportreferences", actions =references.export } + +-- better done here .... we don't insert/remove, just use a pointer + +local prefixstack = { "" } +local prefixlevel = 1 + +local function pushreferenceprefix(prefix) + prefixlevel = prefixlevel + 1 + prefixstack[prefixlevel] = prefix + return prefix +end + +local function popreferenceprefix() + prefixlevel = prefixlevel - 1 + if prefixlevel > 0 then + 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 +} diff --git a/tex/context/base/mkxl/strc-ref.mklx b/tex/context/base/mkxl/strc-ref.mklx index f335764bf..cbc4142da 100644 --- a/tex/context/base/mkxl/strc-ref.mklx +++ b/tex/context/base/mkxl/strc-ref.mklx @@ -27,8 +27,8 @@ \writestatus{loading}{ConTeXt Structure Macros / Cross Referencing} -\registerctxluafile{strc-rsc}{} -\registerctxluafile{strc-ref}{} +\registerctxluafile{strc-rsc}{autosuffix} +\registerctxluafile{strc-ref}{autosuffix} \registerctxluafile{node-ref}{autosuffix,optimize} \unprotect @@ -85,7 +85,6 @@ %mutable\let\currentreferencetitle \empty \mutable\let\currentreferenceuserdata \empty - \newif\ifreferencing \referencingtrue \appendtoks @@ -164,7 +163,6 @@ \prewordbreak % to be tested: \removeunwantedspaces\permithyphenation \fi} - \protected\def\strc_references_set_named_reference {\ifreferencing \expandafter\strc_references_set_named_reference_indeed @@ -580,8 +578,6 @@ \mutable\let\currentsubtextreference \empty \mutable\let\currentsubsubtextreference\empty -\newcount\referencehastexstate % set in backend - % referencepagestate: % % 0 = no page ref, 1=same page, 2=before, 3=after @@ -692,17 +688,8 @@ \newconditional\gotonewwindow \setfalse\gotonewwindow -\permanent\def\expandtexincurrentreference % will happen in lua some time - {\ifcase\referencehastexstate\else\clf_expandcurrentreference\fi} - -\permanent\def\expandreferenceoperation#tag#content{\clf_setreferenceoperation#tag{#content}} -\permanent\def\expandreferencearguments#tag#content{\clf_setreferencearguments#tag{#content}} - -\permanent\def\doifelsereferencefound#label#yes#nop% - {\clf_doifelsereference{\referenceprefix}{#label}{\extrareferencearguments}% - {\expandtexincurrentreference - #yes}% - {#nop}} +\permanent\def\doifelsereferencefound#label% + {\clf_doifelsereference{\referenceprefix}{#label}{\extrareferencearguments}} \aliased\let\doifreferencefoundelse\doifelsereferencefound @@ -826,11 +813,13 @@ \permanent\def\setnextinternalreference {\global\advance\locationcount\plusone} -\permanent\def\setnextinternalreferences#kind#name% plural - {\clf_setnextinternalreference{#kind}{#name}} - -\permanent\def\getinternalorderreference#kind#name% - {\clf_currentreferenceorder{#kind}{#name}} +% defined at the lua end +% +% \permanent\def\setnextinternalreferences#kind#name% plural +% {\clf_setnextinternalreference{#kind}{#name}} +% +% \permanent\def\getinternalorderreference#kind#name% +% {\clf_currentreferenceorder{#kind}{#name}} \permanent\def\thisissomeinternal#kind#name% only for old time sake, will go away {\begingroup @@ -1543,8 +1532,7 @@ \c_attr_reference\attributeunsetvalue \iflocation \clf_doifelsereference{\referenceprefix}{#label}{\extrareferencearguments}% - {\expandtexincurrentreference - \clf_injectcurrentreferencehtdp + {\clf_injectcurrentreferencehtdp \ht\strutbox \dp\strutbox \relax @@ -1601,8 +1589,7 @@ {\endgroup} \protected\def\strc_references_start_goto_yes - {\expandtexincurrentreference - \clf_injectcurrentreferencehtdp + {\clf_injectcurrentreferencehtdp \ht\strutbox \dp\strutbox \relax @@ -1624,8 +1611,7 @@ \c_attr_reference\attributeunsetvalue \iflocation \clf_doifelsereference{\referenceprefix}{#label}{\extrareferencearguments}% - {\expandtexincurrentreference - \clf_injectcurrentreferencehtdp + {\clf_injectcurrentreferencehtdp \dimexpr\interactionparameter\c!height\relax \dimexpr\interactionparameter\c!depth \relax \relax @@ -1710,8 +1696,7 @@ \c_attr_reference\attributeunsetvalue \iflocation \clf_doifelsereference{\referenceprefix}{#label}{\extrareferencearguments}% - {\expandtexincurrentreference - \clf_injectcurrentreference + {\clf_injectcurrentreference \setlocationattributes \global\lastsavedreferenceattribute\lastreferenceattribute \dostarttagged\t!link\empty diff --git a/tex/context/base/mkxl/strc-rsc.lmt b/tex/context/base/mkxl/strc-rsc.lmt new file mode 100644 index 000000000..e725ebf71 --- /dev/null +++ b/tex/context/base/mkxl/strc-rsc.lmt @@ -0,0 +1,180 @@ +if not modules then modules = { } end modules ['strc-rsc'] = { + version = 1.001, + comment = "companion to strc-ref.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- The scanner is in a separate module so that we can test without too +-- many dependencies. + +-- The scanner accepts nested outer, but we don't care too much, maybe +-- some day we will have both but currently the innermost wins. + +local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns +local lpegP, lpegS, lpegCs, lpegCt, lpegCf, lpegCc, lpegC, lpegCg = lpeg.P, lpeg.S, lpeg.Cs, lpeg.Ct, lpeg.Cf, lpeg.Cc, lpeg.C, lpeg.Cg +local find = string.find + +local spaces = lpegP(" ")^0 +local lparent = lpegP("(") +local rparent = lpegP(")") +local lbrace = lpegP("{") +local rbrace = lpegP("}") +local tcolon = lpegP(":::") -- component or outer +local dcolon = lpegP("::") -- outer +local scolon = lpegP(":") -- prefix +local backslash = lpegP("\\") + + lparent = spaces * lparent * spaces + rparent = spaces * rparent * spaces + lbrace = spaces * lbrace * spaces + rbrace = spaces * rbrace * spaces + tcolon = spaces * tcolon * spaces + dcolon = spaces * dcolon * spaces + +local endofall = spaces * lpegP(-1) + +----- o_token = 1 - rparent - rbrace - lparent - lbrace -- can be made more efficient +----- a_token = 1 - rbrace +local s_token = 1 - lparent - lbrace +local i_token = 1 - lparent - lbrace - endofall +local f_token = 1 - lparent - lbrace - dcolon +local c_token = 1 - lparent - lbrace - tcolon + +-- experimental + +local o_token = lpegpatterns.nestedparents + + (1 - rparent - lbrace) +local a_token = lpegpatterns.nestedbraces + + (1 - rbrace) +local q_token = lpegpatterns.unsingle + + lpegpatterns.undouble + +local component = lpegCg(lpegCc("component") * lpegCs(c_token^1)) +local outer = lpegCg(lpegCc("outer") * lpegCs(f_token^1)) +----- operation = lpegCg(lpegCc("operation") * lpegCs(o_token^1)) +local operation = lpegCg(lpegCc("operation") * lpegCs(q_token + o_token^1)) +local arguments = lpegCg(lpegCc("arguments") * lpegCs(q_token + a_token^0)) +local special = lpegCg(lpegCc("special") * lpegCs(s_token^1)) +local inner = lpegCg(lpegCc("inner") * lpegCs(i_token^1)) + + arguments = (lbrace * arguments * rbrace)^-1 + component = component * tcolon + outer = outer * dcolon + operation = outer^-1 * operation -- special case: page(file::1) and file::page(1) + inner = inner * arguments + special = special * lparent * (operation * arguments)^-1 * rparent + +local referencesplitter = spaces + * lpegCf (lpegCt("") * (component + outer)^-1 * (special + inner)^-1 * endofall, rawset) + +local prefixsplitter = lpegCs(lpegP((1-scolon)^1 * scolon)) + * #-scolon + * lpegCs(lpegP(1)^1) + +local componentsplitter = lpegCs(lpegP((1-scolon)^1)) + * scolon * #-scolon + * lpegCs(lpegP(1)^1) + +prefixsplitter = componentsplitter + +local function splitreference(str) + if str and str ~= "" then + return lpegmatch(referencesplitter,str) + end +end + +local function splitprefix(str) + return lpegmatch(prefixsplitter,str) +end + +local function splitcomponent(str) + return lpegmatch(componentsplitter,str) +end + +-- register in the right namespace + +structures = structures or { } +structures.references = structures.references or { } +local references = structures.references + +references.referencesplitter = referencesplitter +references.splitreference = splitreference +references.prefixsplitter = prefixsplitter +references.splitprefix = splitprefix +references.componentsplitter = componentsplitter +references.splitcomponent = splitcomponent + +-- test code: + +-- inspect(splitreference([[component:::inner]])) +-- inspect(splitprefix([[component:::inner]])) +-- inspect(splitprefix([[component:inner]])) + +-- inspect(splitreference([[name(foo)]])) +-- inspect(splitreference([[name{foo}]])) +-- inspect(splitreference([[xx::name(foo, bar and me)]])) + +-- inspect(splitreference([[ ]])) +-- inspect(splitreference([[ inner ]])) +-- inspect(splitreference([[ special ( operation { argument, argument } ) ]])) +-- inspect(splitreference([[ special ( operation { argument } ) ]])) +-- inspect(splitreference([[ special ( operation { argument, \argument } ) ]])) +-- inspect(splitreference([[ special ( operation { \argument } ) ]])) +-- inspect(splitreference([[ special ( operation ) ]])) +-- inspect(splitreference([[ special ( \operation ) ]])) +-- inspect(splitreference([[ special ( o\peration ) ]])) +-- inspect(splitreference([[ special ( ) ]])) +-- inspect(splitreference([[ inner { argument } ]])) +-- inspect(splitreference([[ inner { \argument } ]])) +-- inspect(splitreference([[ inner { ar\gument } ]])) +-- inspect(splitreference([[inner{a\rgument}]])) +-- inspect(splitreference([[ inner { argument, argument } ]])) +-- inspect(splitreference([[ inner { argument, \argument } ]])) -- fails: bug in lpeg? +-- inspect(splitreference([[ inner { \argument, \argument } ]])) +-- inspect(splitreference([[ outer :: ]])) +-- inspect(splitreference([[ outer :: inner]])) +-- inspect(splitreference([[ outer :: special (operation { argument,argument } ) ]])) +-- inspect(splitreference([[ outer :: special (operation { } )]])) +-- inspect(splitreference([[ outer :: special ( operation { argument, \argument } ) ]])) +-- inspect(splitreference([[ outer :: special ( operation ) ]])) +-- inspect(splitreference([[ outer :: special ( \operation ) ]])) +-- inspect(splitreference([[ outer :: special ( ) ]])) +-- inspect(splitreference([[ outer :: inner { argument } ]])) +-- inspect(splitreference([[ special ( outer :: operation ) ]])) + +-- inspect(splitreference([[inner(foo,bar)]])) + +-- inspect(splitreference([[]])) +-- inspect(splitreference([[inner]])) +-- inspect(splitreference([[special(operation{argument,argument})]])) +-- inspect(splitreference([[special(operation)]])) +-- inspect(splitreference([[special(\operation)]])) +-- inspect(splitreference([[special()]])) +-- inspect(splitreference([[inner{argument}]])) +-- inspect(splitreference([[inner{\argument}]])) +-- inspect(splitreference([[outer::]])) +-- inspect(splitreference([[outer::inner]])) +-- inspect(splitreference([[outer::special(operation{argument,argument})]])) +-- inspect(splitreference([[outer::special(operation{argument,\argument})]])) +-- inspect(splitreference([[outer::special(operation)]])) +-- inspect(splitreference([[outer::special(\operation)]])) +-- inspect(splitreference([[outer::special()]])) +-- inspect(splitreference([[outer::inner{argument}]])) +-- inspect(splitreference([[special(outer::operation)]])) + +-- inspect(splitreference([[special(operation)]])) +-- inspect(splitreference([[special(operation(whatever))]])) +-- inspect(splitreference([[special(operation{argument,argument{whatever}})]])) +-- inspect(splitreference([[special(operation{argument{whatever}})]])) + +-- inspect(splitreference([[special("operation(")]])) +-- inspect(splitreference([[special("operation(whatever")]])) +-- inspect(splitreference([[special(operation{"argument,argument{whatever"})]])) +-- inspect(splitreference([[special(operation{"argument{whatever"})]])) + +-- inspect(splitreference([[url(http://a,b.c)]])) +-- inspect(splitcomponent([[url(http://a,b.c)]])) +-- inspect(splitcomponent([[url(http://a.b.c)]])) + diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua index 211277fa7..56f05f2d9 100644 --- a/tex/generic/context/luatex/luatex-fonts-merged.lua +++ b/tex/generic/context/luatex/luatex-fonts-merged.lua @@ -1,6 +1,6 @@ -- merged file : c:/data/develop/context/sources/luatex-fonts-merged.lua -- parent file : c:/data/develop/context/sources/luatex-fonts.lua --- merge date : 2021-11-24 19:45 +-- merge date : 2021-11-26 10:41 do -- begin closure to overcome local limits and interference |