summaryrefslogtreecommitdiff
path: root/doc/context/sources/general/manuals/lowlevel/lowlevel-alignments.tex
diff options
context:
space:
mode:
Diffstat (limited to 'doc/context/sources/general/manuals/lowlevel/lowlevel-alignments.tex')
-rw-r--r--doc/context/sources/general/manuals/lowlevel/lowlevel-alignments.tex697
1 files changed, 697 insertions, 0 deletions
diff --git a/doc/context/sources/general/manuals/lowlevel/lowlevel-alignments.tex b/doc/context/sources/general/manuals/lowlevel/lowlevel-alignments.tex
new file mode 100644
index 000000000..c641e0d65
--- /dev/null
+++ b/doc/context/sources/general/manuals/lowlevel/lowlevel-alignments.tex
@@ -0,0 +1,697 @@
+% language=us runpath=texruns:manuals/lowlevel
+
+\startcomponent lowlevel-alignments
+
+\environment lowlevel-style
+
+\startdocument
+ [title=alignments,
+ color=middlegreen]
+
+\startsection[title=Introduction]
+
+\TEX\ has a couple of subsystems and alignments is one of them. This mechanism is
+used to construct tables or alike. Because alignments use low level primitives to
+set up and construct a table, and because such a setup can be rather extensive, in
+most cases users will rely on macros that hide this.
+
+\startbuffer
+\halign {
+ \alignmark\hss \aligntab
+ \hss\alignmark\hss \aligntab
+ \hss\alignmark \cr
+ 1.1 \aligntab 2,2 \aligntab 3=3 \cr
+ 11.11 \aligntab 22,22 \aligntab 33=33 \cr
+ 111.111 \aligntab 222,222 \aligntab 333=333 \cr
+}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+That one doesn't look too complex and comes out as:
+
+\blank\getbuffer\blank
+
+This is how the previous code comes out when we use one of the \CONTEXT\ table
+mechanism.
+
+\startbuffer
+\starttabulate[|l|c|r|]
+ \NC 1.1 \NC 2,2 \NC 3=3 \NC \NR
+ \NC 11.11 \NC 22,22 \NC 33=33 \NC \NR
+ \NC 111.111 \NC 222,222 \NC 333=333 \NC \NR
+\stoptabulate
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\blank\getbuffer\blank
+
+That one looks a bit different with respect to spaces, so let's go back to the
+low level variant:
+
+\startbuffer
+\halign {
+ \alignmark\hss \aligntab
+ \hss\alignmark\hss \aligntab
+ \hss\alignmark \cr
+ 1.1\aligntab 2,2\aligntab 3=3\cr
+ 11.11\aligntab 22,22\aligntab 33=33\cr
+ 111.111\aligntab 222,222\aligntab 333=333\cr
+}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+Here we don't have spaces in the content part and therefore also no spaces in the
+result:
+
+\blank\getbuffer\blank
+
+You can automate dealing with unwanted spacing:
+
+\startbuffer
+\halign {
+ \ignorespaces\alignmark\unskip\hss \aligntab
+ \hss\ignorespaces\alignmark\unskip\hss \aligntab
+ \hss\ignorespaces\alignmark\unskip \cr
+ 1.1 \aligntab 2,2 \aligntab 3=3 \cr
+ 11.11 \aligntab 22,22 \aligntab 33=33 \cr
+ 111.111 \aligntab 222,222 \aligntab 333=333 \cr
+}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+We get:
+
+\blank\getbuffer\blank
+
+By moving the space skipping and cleanup to the so called preamble we don't need
+to deal with it in the content part. We can also deal with inter|-|column spacing
+there:
+
+\startbuffer
+\halign {
+ \ignorespaces\alignmark\unskip\hss \tabskip 1em \aligntab
+ \hss\ignorespaces\alignmark\unskip\hss \tabskip 1em \aligntab
+ \hss\ignorespaces\alignmark\unskip \tabskip 0pt \cr
+ 1.1 \aligntab 2,2 \aligntab 3=3 \cr
+ 11.11 \aligntab 22,22 \aligntab 33=33 \cr
+ 111.111 \aligntab 222,222 \aligntab 333=333 \cr
+}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\blank\getbuffer\blank
+
+If for the moment we forget about spanning columns (\type {\span}) and locally
+ignoring preamble entries (\type {\omit}) these basic commands are not that
+complex to deal with. Here we use \type {\alignmark} but that is just a primitive
+that we use instead of \type {#} while \type {\aligntab} is the same as \type
+{&}, but using the characters instead also assumes that they have the catcode
+that relates to a parameter and alignment tab (and in \CONTEXT\ that is not the
+case). The \TEX book has plenty alignment examples so if you really want to learn
+about them, consult that must|-|have|-|book.
+
+\stopsection
+
+\startsection[title=Between the lines]
+
+The individual rows of a horizontal alignment are treated as lines. This means that,
+as we see in the previous section, the interline spacing is okay. However, that also
+means that when we mix the lines with rules, the normal \TEX\ habits kick in. Take
+this:
+
+\startbuffer
+\halign {
+ \ignorespaces\alignmark\unskip\hss \tabskip 1em \aligntab
+ \hss\ignorespaces\alignmark\unskip\hss \tabskip 1em \aligntab
+ \hss\ignorespaces\alignmark\unskip \tabskip 0pt \cr
+ \noalign{\hrule}
+ 1.1 \aligntab 2,2 \aligntab 3=3 \cr
+ \noalign{\hrule}
+ 11.11 \aligntab 22,22 \aligntab 33=33 \cr
+ \noalign{\hrule}
+ 111.111 \aligntab 222,222 \aligntab 333=333 \cr
+ \noalign{\hrule}
+}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+The result doesn't look pretty and actually, when you see documents produced by
+\TEX\ using alignments you should not be surprised to notice rather ugly spacing.
+The user (or the macropackage) should deal with that explicitly, and this is not
+always the case.
+
+\startlinecorrection
+\getbuffer
+\stoplinecorrection
+
+The solution is often easy:
+
+\startbuffer
+\halign {
+ \ignorespaces\strut\alignmark\unskip\hss \tabskip 1em \aligntab
+ \hss\ignorespaces\strut\alignmark\unskip\hss \tabskip 1em \aligntab
+ \hss\ignorespaces\strut\alignmark\unskip \tabskip 0pt \cr
+ \noalign{\hrule}
+ 1.1 \aligntab 2,2 \aligntab 3=3 \cr
+ \noalign{\hrule}
+ 11.11 \aligntab 22,22 \aligntab 33=33 \cr
+ \noalign{\hrule}
+ 111.111 \aligntab 222,222 \aligntab 333=333 \cr
+ \noalign{\hrule}
+}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\startlinecorrection
+\getbuffer
+\stoplinecorrection
+
+The user will not notice it but alignments put some pressure on the general \TEX\
+scanner. Actually, the scanner is either scanning an alignment or it expects
+regular text (including math). When you look at the previous example you see
+\type {\noalign}. When the preamble is read, \TEX\ will pick up rows till it
+finds the final brace. Each row is added to a temporary list and the \type
+{\noalign} will enter a mode where other stuff gets added to that list. It all
+involves subtle look ahead but with minimal overhead. When the whole alignment is
+collected a final pass over that list will package the cells and rows (lines) in
+the appropriate way using information collected (like the maximum width of a cell
+and width of the current cell. It will also deal with spanning cells then.
+
+So let's summarize what happens:
+
+\startitemize[n,packed]
+\startitem
+ scan the preamble that defines the cells (where the last one is repeated
+ when needed)
+\stopitem
+\startitem
+ check for \type {\cr}, \type {\noalign} or a right brace; when a row is
+ entered scan for cells in parallel the preamble so that cell specifications
+ can be applied (then start again)
+\stopitem
+\startitem
+ package the preamble based on information with regards to the cells in
+ a column
+\stopitem
+\startitem
+ apply the preamble packaging information to the columns and also deal with
+ pending cell spans
+\stopitem
+\startitem
+ flush the result to the current list
+\stopitem
+\stopitemize
+
+The second (repeated) step is complicated by the fact that the scanner has to
+look ahead for a \type {\noalign}, \type {\cr}, \type {\omit} or \type {\span}
+and when doing that it has to expand what comes. This can give side effects and
+often results in obscure error messages. When for instance an \type {\if} is seen
+and expanded, the wrong branch can be entered. And when you use protected macros
+embedded alignment commands are not seen at all. Also, nesting \type {\noalign}
+is not permitted.
+
+All these side effects are to be handled in a macro package when it wraps
+alignments in a high level interface and \CONTEXT\ does that for you. But because
+the code doesn't always look pretty then, in \LUAMETATEX\ the alignment mechanism
+has been extended a bit over time.
+
+The first extension was to permit nested usage of \type {\noalign}. This has
+resulted of a little reorganization of the code. A next extension showed up when
+overload protection was introduced and extra prefixes were added. We can signal
+the scanner that a macro is actually a \type {\noalign} variant: \footnote {A
+better prefix would have been \type {\peekaligned} because in the meantime other
+alignment primitives also can use this property.}
+
+\starttyping[option=TEX]
+\noaligned\protected\def\InBetween{\noalign{...}}
+\stoptyping
+
+This extension resulted in a second bit of reorganization (think of internal
+command codes and such) but still the original processing of alignments was
+there.
+
+A third overhaul of the code actually did lead to some adaptations in the way
+alignments are constructed so let's move on to that.
+
+\stopsection
+
+\startsection[title={Pre-, inter- and post-tab skips}]
+
+The basic structure of a preamble and row is actually not that complex: it is
+a mix of tab skip glue and cells (that are just boxes):
+
+\startbuffer
+\tabskip 10pt
+\halign {
+ \strut\alignmark\tabskip 12pt\aligntab
+ \strut\alignmark\tabskip 14pt\aligntab
+ \strut\alignmark\tabskip 16pt\cr
+ \noalign{\hrule}
+ cell 1.1\aligntab cell 1.2\aligntab cell 1.3\cr
+ \noalign{\hrule}
+ cell 2.1\aligntab cell 2.2\aligntab cell 2.3\cr
+ \noalign{\hrule}
+}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+The tab skips are set in advance and apply to the next cell (or after the last
+one).
+
+\startbuffer[blownup-1]
+\startlinecorrection
+{\showmakeup[glue]\scale[width=\textwidth]{\vbox{\getbuffer}}}
+\stoplinecorrection
+\stopbuffer
+
+\getbuffer[blownup-1]
+
+% \normalizelinemode \zerocount % \discardzerotabskipsnormalizecode
+
+In the \CONTEXT\ table mechanisms the value of \type {\tabskip} is zero
+in most cases. As in:
+
+\startbuffer
+\tabskip 0pt
+\halign {
+ \strut\alignmark\aligntab
+ \strut\alignmark\aligntab
+ \strut\alignmark\cr
+ \noalign{\hrule}
+ cell 1.1\aligntab cell 1.2\aligntab cell 1.3\cr
+ \noalign{\hrule}
+ cell 2.1\aligntab cell 2.2\aligntab cell 2.3\cr
+ \noalign{\hrule}
+}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+When these ships are zero, they still show up in the end:
+
+\getbuffer[blownup-1]
+
+Normally, in order to achieve certain effects there will be more align entries in
+the preamble than cells in the table, for instance because you want vertical
+lines between cells. When these are not used, you can get quite a bit of empty
+boxes and zero skips. Now, of course this is seldom a problem, but when you have
+a test document where you want to show font properties in a table and that font
+supports a script with some ten thousand glyphs, you can imagine that it
+accumulates and in \LUATEX\ (and \LUAMETATEX) nodes are larger so it is one of
+these cases where in \CONTEXT\ we get messages on the console that node memory is
+bumped.
+
+After playing a bit with stripping zero tab skips I found that the code would not
+really benefit from such a feature: lots of extra tests made if quite ugly. As a
+result a first alternative was to just strip zero skips before an alignment got
+flushed. At least we're then a bit leaner in the processes that come after it.
+This feature is now available as one of the normalizer bits.
+
+But, as we moved on, a more natural approach was to keep the skips in the
+preamble, because that is where a guaranteed alternating skip|/|box is assumed.
+It also makes that the original documentation is still valid. However, in the
+rows construction we can be lean. This is driven by a keyword to \type {\halign}:
+
+\startbuffer
+\tabskip 0pt
+\halign noskips {
+ \strut\alignmark\aligntab
+ \strut\alignmark\aligntab
+ \strut\alignmark\cr
+ \noalign{\hrule}
+ cell 1.1\aligntab cell 1.2\aligntab cell 1.3\cr
+ \noalign{\hrule}
+ cell 2.1\aligntab cell 2.2\aligntab cell 2.3\cr
+ \noalign{\hrule}
+}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+No zero tab skips show up here:
+
+\getbuffer[blownup-1]
+
+When playing with all this the \LUAMETATEX\ engine also got a tracing option for
+alignments. We already had one that showed some of the \type{\noalign} side
+effects, but showing the preamble was not yet there. This is what \typ
+{\tracingalignments = 2} results in:
+
+% {\tracingalignments2 \setbox0\vbox{\getbuffer}}
+
+\starttyping[option=TEX]
+<preamble>
+\glue[ignored][...] 0.0pt
+\alignrecord
+..{\strut }
+..<content>
+..{\endtemplate }
+\glue[ignored][...] 0.0pt
+\alignrecord
+..{\strut }
+..<content>
+..{\endtemplate }
+\glue[ignored][...] 0.0pt
+\alignrecord
+..{\strut }
+..<content>
+..{\endtemplate }
+\glue[ignored][...] 0.0pt
+\stoptyping
+
+The \type {ignored} subtype is (currently) only used for these alignment tab
+skips and it triggers a check later on when the rows are constructed. The \type
+{<content>} is what get injected in the cell (represented by \type {\alignmark}).
+The pseudo primitives are internal and not public.
+
+\stopsection
+
+\startsection[title={Cell widths}]
+
+Imagine this:
+
+\startbuffer
+\halign {
+ x\hbox to 3cm{\strut \alignmark\hss}\aligntab
+ x\hbox to 3cm{\strut\hss\alignmark\hss}\aligntab
+ x\hbox to 3cm{\strut\hss\alignmark }\cr
+ cell 1.1\aligntab cell 1.2\aligntab cell 1.3\cr
+ cell 2.1\aligntab cell 2.2\aligntab cell 2.3\cr
+}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+which renders as:
+
+\startbuffer[blownup-2]
+\startlinecorrection
+{\showboxes\scale[width=\textwidth]{\vbox{\getbuffer}}}
+\stoplinecorrection
+\stopbuffer
+
+{\showboxes\getbuffer[blownup-2]}
+
+A reason to have boxes here is that it enforces a cell width but that is done at
+the cost of an extra wrapper. In \LUAMETATEX\ the \type {hlist} nodes are rather
+large because we have more options than in original \TEX, for instance offsets
+and orientation. So, in a table with 10K rows of 4 cells yet get 40K extra \type
+{hlist} nodes allocated. Now, one can argue that we have plenty of memory but
+being lazy is not really a sign of proper programming.
+
+\startbuffer
+\halign {
+ x\tabsize 3cm\strut \alignmark\hss\aligntab
+ x\tabsize 3cm\strut\hss\alignmark\aligntab
+ x\tabsize 3cm\strut\hss\alignmark\hss\cr
+ cell 1.1\aligntab cell 1.2\aligntab cell 1.3\cr
+ cell 2.1\aligntab cell 2.2\aligntab cell 2.3\cr
+}
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+If you look carefully you will see that this time we don't have the embedded
+boxes:
+
+{\showboxes\getbuffer[blownup-2]}
+
+So, both the sparse skip and new \type {\tabsize} feature help to make these
+extreme tables (spanning hundreds of pages) not consume irrelevant memory and
+also make that later on we don't have to consult useless nodes.
+
+\stopsection
+
+\startsection[title=Plugins]
+
+Yet another \LUAMETATEX\ extension is a callback that kicks in between the
+preamble preroll and finalizing the alignment. Initially as test and
+demonstration a basic character alignment feature was written but that works so
+well that in some places it can replace (or compliment) the already existing
+features in the \CONTEXT\ table mechanisms.
+
+\startbuffer
+\starttabulate[|lG{.}|cG{,}|rG{=}|cG{x}|]
+\NC 1.1 \NC 2,2 \NC 3=3 \NC a 0xFF \NC \NR
+\NC 11.11 \NC 22,22 \NC 33=33 \NC b 0xFFF \NC \NR
+\NC 111.111 \NC 222,222 \NC 333=333 \NC c 0xFFFF \NC \NR
+\stoptabulate
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+The tabulate mechanism in \CONTEXT\ is rather old and stable and it is the
+preferred way to deal with tabular content in the text flow. However, adding the
+\type {G} specifier (as variant of the \type {g} one) could be done without
+interference or drop in performance. This new \type {G} specifier tells the
+tabulate mechanism that in that column the given character is used to vertically
+align the content that has this character.
+
+\blank\getbuffer\blank
+
+Let's make clear that this is {\em not} an engine feature but a \CONTEXT\ one. It
+is however made easy by this callback mechanism. We can of course use this feature
+with the low level alignment primitives, assuming that you tell the machinery that
+the plugin is to be kicked in.
+
+\startbuffer
+\halign noskips \alignmentcharactertrigger \bgroup
+ \tabskip2em
+ \setalignmentcharacter.\ignorespaces\alignmark\unskip\hss \aligntab
+ \hss\setalignmentcharacter,\ignorespaces\alignmark\unskip\hss \aligntab
+ \hss\setalignmentcharacter=\ignorespaces\alignmark\unskip \aligntab
+ \hss \ignorespaces\alignmark\unskip\hss \cr
+ 1.1 \aligntab 2,2 \aligntab 3=3 \aligntab \setalignmentcharacter{.}\relax 4.4\cr
+ 11.11 \aligntab 22,22 \aligntab 33=33 \aligntab \setalignmentcharacter{,}\relax 44,44\cr
+ 111.111 \aligntab 222,222 \aligntab 333=333 \aligntab \setalignmentcharacter{!}\relax 444!444\cr
+ x \aligntab x \aligntab x \aligntab \setalignmentcharacter{/}\relax /\cr
+ .1 \aligntab ,2 \aligntab =3 \aligntab \setalignmentcharacter{?}\relax ?4\cr
+ .111 \aligntab ,222 \aligntab =333 \aligntab \setalignmentcharacter{=}\relax 44=444\cr
+\egroup
+\stopbuffer
+
+{\switchtobodyfont[8pt] \typebuffer[option=TEX]}
+
+This rather verbose setup renders as:
+
+\blank\getbuffer\blank
+
+Using a high level interface makes sense but local control over such alignment too, so
+here follow some more examples. Here we use different alignment characters:
+
+\startbuffer
+\starttabulate[|lG{.}|cG{,}|rG{=}|cG{x}|]
+\NC 1.1 \NC 2,2 \NC 3=3 \NC a 0xFF \NC \NR
+\NC 11.11 \NC 22,22 \NC 33=33 \NC b 0xFFF \NC \NR
+\NC 111.111 \NC 222,222 \NC 333=333 \NC c 0xFFFF \NC \NR
+\stoptabulate
+\stopbuffer
+
+\typebuffer[option=TEX] \getbuffer
+
+In this example we specify the characters in the cells. We still need to add a
+specifier in the preamble definition because that will trigger the plugin.
+
+\startbuffer
+\starttabulate[|lG{}|lG{}|]
+\NC \showglyphs \setalignmentcharacter{.}1.1 \NC \setalignmentcharacter{.}1.1 \NC\NR
+\NC \showglyphs \setalignmentcharacter{,}11,11 \NC \setalignmentcharacter{,}11,11 \NC\NR
+ \NC \showglyphs \setalignmentcharacter{=}111=111 \NC \setalignmentcharacter{=}111=111 \NC\NR
+\stoptabulate
+\stopbuffer
+
+{\switchtobodyfont[8pt] \typebuffer[option=TEX]} \getbuffer
+
+You can mix these approaches:
+
+\startbuffer
+\starttabulate[|lG{.}|lG{}|]
+\NC 1.1 \NC \setalignmentcharacter{.}1.1 \NC\NR
+\NC 11.11 \NC \setalignmentcharacter{.}11.11 \NC\NR
+\NC 111.111 \NC \setalignmentcharacter{.}111.111 \NC\NR
+\stoptabulate
+\stopbuffer
+
+\typebuffer[option=TEX] \getbuffer
+
+Here the already present alignment feature, that at some point in tabulate might
+use this new feature, is meant for numbers, but here we can go wild with words,
+although of course you need to keep in mind that we deal with typeset text, so
+there may be no match.
+
+\startbuffer
+\starttabulate[|lG{.}|rG{.}|]
+\NC foo.bar \NC foo.bar \NC \NR
+\NC oo.ba \NC oo.ba \NC \NR
+\NC o.b \NC o.b \NC \NR
+\stoptabulate
+\stopbuffer
+
+\typebuffer[option=TEX] \getbuffer
+
+This feature will only be used in know situations and those seldom involve advanced
+typesetting. However, the following does work: \footnote {Should this be an option
+instead?}
+
+\startbuffer
+\starttabulate[|cG{d}|]
+\NC \smallcaps abcdefgh \NC \NR
+\NC xdy \NC \NR
+\NC \sl xdy \NC \NR
+\NC \tttf xdy \NC \NR
+\NC \tfd d \NC \NR
+\stoptabulate
+\stopbuffer
+
+\typebuffer[option=TEX] \getbuffer
+
+As always with such mechanisms, the question is \quotation {Where to stop?} But it
+makes for nice demos and as long as little code is needed it doesn't hurt.
+
+\stopsection
+
+\startsection[title=Pitfalls and tricks]
+
+The next example mixes bidirectional typesetting. It might look weird at first
+sight but the result conforms to what we discussed in previous paragraphs.
+
+\startbuffer
+\starttabulate[|lG{.}|lG{}|]
+\NC \righttoleft 1.1 \NC \righttoleft \setalignmentcharacter{.}1.1 \NC\NR
+\NC 1.1 \NC \setalignmentcharacter{.}1.1 \NC\NR
+\NC \righttoleft 1.11 \NC \righttoleft \setalignmentcharacter{.}1.11 \NC\NR
+\NC 1.11 \NC \setalignmentcharacter{.}1.11 \NC\NR
+\NC \righttoleft 1.111 \NC \righttoleft \setalignmentcharacter{.}1.111 \NC\NR
+\NC 1.111 \NC \setalignmentcharacter{.}1.111 \NC\NR
+\stoptabulate
+\stopbuffer
+
+{\switchtobodyfont[8pt] \typebuffer[option=TEX]} \getbuffer
+
+In case of doubt, look at this:
+
+\startbuffer
+\starttabulate[|lG{.}|lG{}|lG{.}|lG{}|]
+\NC \righttoleft 1.1 \NC \righttoleft \setalignmentcharacter{.}1.1 \NC
+ 1.1 \NC \setalignmentcharacter{.}1.1 \NC\NR
+\NC \righttoleft 1.11 \NC \righttoleft \setalignmentcharacter{.}1.11 \NC
+ 1.11 \NC \setalignmentcharacter{.}1.11 \NC\NR
+\NC \righttoleft 1.111 \NC \righttoleft \setalignmentcharacter{.}1.111 \NC
+ 1.111 \NC \setalignmentcharacter{.}1.111 \NC\NR
+\stoptabulate
+\stopbuffer
+
+{\switchtobodyfont[8pt] \typebuffer[option=TEX]} \getbuffer
+
+The next example shows the effect of \type {\omit} and \type {\span}. The first one
+makes that in this cell the preamble template is ignored.
+
+\startbuffer
+\halign \bgroup
+ \tabsize 2cm\relax [\alignmark]\hss \aligntab
+ \tabsize 2cm\relax \hss[\alignmark]\hss \aligntab
+ \tabsize 2cm\relax \hss[\alignmark]\cr
+ 1\aligntab 2\aligntab 3\cr
+ \omit 1\aligntab \omit 2\aligntab \omit 3\cr
+ 1\aligntab 2\span 3\cr
+ 1\span 2\aligntab 3\cr
+ 1\span 2\span 3\cr
+ 1\span \omit 2\span \omit 3\cr
+ \omit 1\span \omit 2\span \omit 3\cr
+\egroup
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+Spans are applied at the end so you see a mix of templates applied.
+
+{\showboxes\getbuffer[blownup-2]}
+
+When you define an alignment inside a macro, you need to duplicate the \type {\alignmark}
+signals. This is similar to embedded macro definitions. But in \LUAMETATEX\ we can get
+around that by using \type {\aligncontent}. Keep in mind that when the preamble is scanned there
+is no doesn't expand with the exception of the token after \type {\span}.
+
+\startbuffer
+\halign \bgroup
+ \tabsize 2cm\relax \aligncontent\hss \aligntab
+ \tabsize 2cm\relax \hss\aligncontent\hss \aligntab
+ \tabsize 2cm\relax \hss\aligncontent\cr
+ 1\aligntab 2\aligntab 3\cr
+ A\aligntab B\aligntab C\cr
+\egroup
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+\blank\getbuffer\blank
+
+In this example we still have to be verbose in the way we align but we can do this:
+
+\startbuffer
+\halign \bgroup
+ \tabsize 2cm\relax \aligncontentleft \aligntab
+ \tabsize 2cm\relax \aligncontentmiddle\aligntab
+ \tabsize 2cm\relax \aligncontentright \cr
+ 1\aligntab 2\aligntab 3\cr
+ A\aligntab B\aligntab C\cr
+\egroup
+\stopbuffer
+
+\typebuffer[option=TEX]
+
+Where the helpers are defined as:
+
+\starttyping[option=TEX]
+\noaligned\protected\def\aligncontentleft
+ {\ignorespaces\aligncontent\unskip\hss}
+
+\noaligned\protected\def\aligncontentmiddle
+ {\hss\ignorespaces\aligncontent\unskip\hss}
+
+\noaligned\protected\def\aligncontentright
+ {\hss\ignorespaces\aligncontent\unskip}
+\stoptyping
+
+The preamble scanner see such macros as candidates for a single level expansion
+so it will inject the meaning and see the \type {\aligncontent} eventually.
+
+\blank\getbuffer\blank
+
+The same effect could be achieved by using the \type {\span} prefix:
+
+\starttyping[option=TEX]
+\def\aligncontentleft{\ignorespaces\aligncontent\unskip\hss}
+
+\halign { ... \span\aligncontentleft ...}
+\stoptyping
+
+One of the reasons for not directly using the low level \type {\halign} command is
+that it's a lot of work but by providing a set of helpers like here might change
+that a bit. Keep in mind that much of the above is not new in the sense that we
+could not achieve the same already, it's just a bit programmer friendly.
+
+\stopsection
+
+\startsection[title=Remark]
+
+It can be that the way alignments are interfaced with respect to attributes is a bit
+different between \LUATEX\ and \LUAMETATEX\ but because the former is frozen (in
+order not to interfere with current usage patterns) this is something that we will
+deal with deep down in \CONTEXT\ \LMTX.
+
+In principle we can have hooks into the rows for pre and post material but it
+doesn't really pay of as grouping will still interfere. So for now I decided not
+to add these.
+
+\stopsection
+
+\stopdocument