diff options
author | Hans Hagen <pragma@wxs.nl> | 2021-11-26 11:03:54 +0100 |
---|---|---|
committer | Context Git Mirror Bot <phg@phi-gamma.net> | 2021-11-26 11:03:54 +0100 |
commit | 05a83ce931821be77947a811e5f433c4f1dfa29c (patch) | |
tree | 83dc7116d313813ee302d21e8f48e459e33eabda | |
parent | 81b32baedc58b7485c16d29a09f4e97a279c98e8 (diff) | |
download | context-05a83ce931821be77947a811e5f433c4f1dfa29c.tar.gz |
2021-11-26 10:43:00
23 files changed, 3237 insertions, 71 deletions
diff --git a/doc/context/documents/general/manuals/luametatex.pdf b/doc/context/documents/general/manuals/luametatex.pdf Binary files differindex 822146908..49347ca50 100644 --- a/doc/context/documents/general/manuals/luametatex.pdf +++ b/doc/context/documents/general/manuals/luametatex.pdf diff --git a/doc/context/sources/general/manuals/luametatex/luametatex-building.tex b/doc/context/sources/general/manuals/luametatex/luametatex-building.tex index 61b11b18d..357e2ed41 100644 --- a/doc/context/sources/general/manuals/luametatex/luametatex-building.tex +++ b/doc/context/sources/general/manuals/luametatex/luametatex-building.tex @@ -73,7 +73,7 @@ column. If there are three columns, the first two will store inserts. Then when the last column is dealt with \prm {insertstoring} can be set to 2 and that will signal the builder that we will inject the inserts. In both cases, the value of this register will be set to zero so that it doesn't influence further -processing. You can use \prm {ifinsert} to check if am insert box is void. More +processing. You can use \prm {ifinsert} to check if an insert box is void. More details about these (probably experimental for a while) features can be found in documents that come with \CONTEXT. @@ -118,14 +118,14 @@ The \LUAMETATEX\ engine has some tracing built in that is enabled by setting \pr \startsection[title={Adjusts}] The \prm {vadjust} primitive injects something in the vertical list after the -lien where it ends up. In \PDFTEX\ the \type {pre} keyword was added so that one +line where it ends up. In \PDFTEX\ the \type {pre} keyword was added so that one could force something before a previous line (actually this was something that we needed in \CONTEXT\ \MKII). The \LUAMETATEX\ engine also supports the \type {post} keyword. We support a few more keywords: \type {before} will prepend the adjustment to the already given one, and \type {after} will append it. The \type {index} keyword -expects an integer and relate that to the current adjustment. This index is +expects an integer and relates that to the current adjustment. This index is passed to an (optional) callback when the adjustment is finally moved to the vertical list. That move is actually delayed because like inserts and marks these (vertical) adjustments can migrate to the \quote {outer} vertical level. @@ -158,7 +158,7 @@ and inserts bubble up to the outer level. \LL \stoptabulate -If you want to migrate marks and inserts you need to set al these flags. Migrated +If you want to migrate marks and inserts you need to set all these flags. Migrated marks and inserts end up as post|-|box properties and will be handled in the page builder as such. At the \LUA\ end you can add pre- and post|-|box material too. @@ -166,9 +166,9 @@ builder as such. At the \LUA\ end you can add pre- and post|-|box material too. \startsection[title={Pages}] -The page builder can triggered by (for instance) a penalty but you can also use -\prm {pageboundary}. This will trigger the page builder but not leave anything -behind. +The page builder can be triggered by (for instance) a penalty but you can also +use \prm {pageboundary}. This will trigger the page builder but not leave +anything behind. \stopsection @@ -177,10 +177,10 @@ behind. As far as I know the \OMEGA/\ALEPH\ local box mechanism is mostly in those engines in order to support repetitive quotes. In \LUATEX\ this mechanism has been made more robust and in \LUAMETATEX\ it became more tightly integrated in -the paragraph properties. In order for it to be a more generic useful feature it -got more features. For instance it is a bit painful to manage with respect to +the paragraph properties. In order for it to be more generic and useful, it got +more features. For instance it is a bit painful to manage with respect to grouping (which is a reason why it's not that much used). The most interesting -property is that the dimensions are taking into account when a paragraph is +property is that the dimensions are taken into account when a paragraph is broken into lines. There are three commands: \prm {localleftbox}, \prm {localrightbox} and the @@ -190,7 +190,7 @@ used the index but this was a cheap extra signal so we keep it). These commands take optional keywords. The \type {index} keyword has to be followed by an integer. This index determines the order which doesn't introduce a -significant compatibility issue: local boxes are hardly used and originally have +significant compatibility issue: local boxes are hardly used and originally had only one instance. The \type {par} keyword forces the box to be added to the current paragraph head. @@ -202,7 +202,7 @@ The \type {local} keyword tells this mechanism not to update the registers that keep these boxes. In that case a next paragraph will start fresh. The \type {keep} option will do the opposite and retain the box after a group ends. -There commands: \prm {localleftboxbox}, \prm {localrightboxbox} and \prm +The commands: \prm {localleftboxbox}, \prm {localrightboxbox} and \prm {localmiddleboxbox} return a copy of the current related register content. \stopsection diff --git a/doc/context/sources/general/manuals/luametatex/luametatex-enhancements.tex b/doc/context/sources/general/manuals/luametatex/luametatex-enhancements.tex index 9a03654d8..121ffe216 100644 --- a/doc/context/sources/general/manuals/luametatex/luametatex-enhancements.tex +++ b/doc/context/sources/general/manuals/luametatex/luametatex-enhancements.tex @@ -115,7 +115,7 @@ understand it (apart from maybe a few real dirty low level helpers that are also dirty because of performance reasons). Just take this into account when reading on. And yes, there are still a few possibilities I want to explore \unknown\ some might -show up temporary so don't be surprises. I'm also aware that some new features can +show up temporarily so don't be surprised. I'm also aware that some new features can have bugs or side effects that didn't show up in \CONTEXT, which after all is the benchmark and environment in which all this evolves. @@ -2568,7 +2568,7 @@ introduce incompatibilities. The \prm {numexpression} and \prm {dimexpression} primitives are equivalent but offer more. The first one operates in the integer domain and the second one -assumes scaled values. Often the second one can act like the first when one +assumes scaled values. Often the second one can act like the first when serialized with \prm {number} in front. This is because when \TEX\ sees a symbolic reference to an integer or dimension it can treat them as it likes. @@ -2602,7 +2602,7 @@ alternatives so that we can get around catcode issues. \LL \stoptabulate -Here are some things that \prm {numexpr} is not suitable: +Here are some things that \prm {numexpr} is not suitable for: \starttyping \scratchcounter = \numexpression diff --git a/doc/context/sources/general/manuals/luametatex/luametatex-fonts.tex b/doc/context/sources/general/manuals/luametatex/luametatex-fonts.tex index 83336ffd1..4e85a1b20 100644 --- a/doc/context/sources/general/manuals/luametatex/luametatex-fonts.tex +++ b/doc/context/sources/general/manuals/luametatex/luametatex-fonts.tex @@ -125,6 +125,7 @@ has no ligatures and kerns and is normally not processed at all. \NC \type {kerns} \NC table \NC kerning information \NC\NR \NC \type {ligatures} \NC table \NC ligaturing information \NC\NR \NC \type {mathkern} \NC table \NC math cut-in specifications \NC\NR +\NC \type {smaller} \NC number \NC the next smaller math size character \NC\NR \LL \stoptabulate diff --git a/doc/context/sources/general/manuals/luametatex/luametatex-math.tex b/doc/context/sources/general/manuals/luametatex/luametatex-math.tex index af34af92b..30ce11c3b 100644 --- a/doc/context/sources/general/manuals/luametatex/luametatex-math.tex +++ b/doc/context/sources/general/manuals/luametatex/luametatex-math.tex @@ -28,7 +28,7 @@ You might be surprised that we don't use all these new control features in reason for adding so much is that I decided it made more sense to be complete now than gradually add more and more. At some point we should be able to say \quote {This is it}. Also, when looking at these features, you need to keep in mind that -when it comes to math \LATEX\ is the dominant macro package and it never needed +when it comes to math, \LATEX\ is the dominant macro package and it never needed these engine features, so most are probably just here for exploration purposes. \stopsection diff --git a/doc/context/sources/general/manuals/luametatex/luametatex-modifications.tex b/doc/context/sources/general/manuals/luametatex/luametatex-modifications.tex index cc08bc9c6..083fa732a 100644 --- a/doc/context/sources/general/manuals/luametatex/luametatex-modifications.tex +++ b/doc/context/sources/general/manuals/luametatex/luametatex-modifications.tex @@ -251,7 +251,8 @@ don't expect \LUAMETATEX\ to be compatible. related primitives \prm {lpcode}, \prm {rpcode}, \prm {efcode}, \prm {leftmarginkern}, \prm {rightmarginkern} are promoted to core primitives. The two commands \prm {protrudechars} and \prm {adjustspacing} control these - processes. + processes. The protrusion and kern related primitives are now dimensions + while expansion is still one of these 1000 based scales. \stopitem \startitem 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 |