summaryrefslogtreecommitdiff
path: root/doc/context/sources/general/manuals/still/still-simple.tex
diff options
context:
space:
mode:
Diffstat (limited to 'doc/context/sources/general/manuals/still/still-simple.tex')
-rw-r--r--doc/context/sources/general/manuals/still/still-simple.tex330
1 files changed, 330 insertions, 0 deletions
diff --git a/doc/context/sources/general/manuals/still/still-simple.tex b/doc/context/sources/general/manuals/still/still-simple.tex
new file mode 100644
index 000000000..9416dd028
--- /dev/null
+++ b/doc/context/sources/general/manuals/still/still-simple.tex
@@ -0,0 +1,330 @@
+% language=uk
+
+\environment still-environment
+
+\startcomponent still-simple
+
+\startchapter[title=Removing something (typeset)]
+
+\startsection[title=Introduction]
+
+The primitive \type {\unskip} often comes in handy when you want to remove a
+space (or more precisely: a glue item) but sometimes you want to remove more.
+Consider for instance the case where a sentence is built up stepwise from data.
+At some point you need to insert some punctuation but as you cannot look ahead it
+needs to be delayed. Keeping track of accumulated content is no fun, and a quick
+and dirty solution is to just inject it and remove it when needed. One way to
+achieve this is to wrap this optional content in a box with special dimensions.
+Just before the next snippet is injected we can look back for that box (that can
+then be recognized by those special dimensions) and either remove it or unbox it
+back into the stream.
+
+To be honest, one seldom needs this feature. In fact I never needed it until
+Alan Braslau and I were messing around with (indeed messy) bibliographic
+rendering and we thought it would be handy to have a helper that could remove
+punctuation. Think of situations like this:
+
+\starttyping
+John Foo, Mary Bar and others.
+John Foo, Mary Bar, and others.
+\stoptyping
+
+One can imagine this list to be constructed programmatically, in which case the
+comma before the \type {and} can be superfluous. So, the \type {and others} can
+be done like this:
+
+\startbuffer
+\def\InjectOthers
+ {\removeunwantedspaces
+ \removepunctuation
+ \space and others}
+
+John Foo, Mary Bar, \InjectOthers.
+\stopbuffer
+
+\typebuffer
+
+Notice that we first remove spaces. This will give:
+
+\blank {\bf \getbuffer} \blank
+
+where the commas after the names are coming from some not|-|too|-|clever automatism
+or are the side effect of lazy programming. In the sections below I will describe
+a bit more generic mechanism and also present a solution for non|-|\CONTEXT\ users.
+
+\stopsection
+
+\startsection[title=Marked content]
+
+The example above can be rewritten in a more general way. We define a
+couple macros (using \CONTEXT\ functionality):
+
+\startbuffer
+\def\InjectComma
+ {\markcontent
+ [punctuation]
+ {\removeunwantedspaces,\space}}
+
+\def\InjectOthers
+ {\removemarkedcontent[punctuation]%
+ \space and others}
+\stopbuffer
+
+\typebuffer \getbuffer
+
+These can be used as:
+
+\startbuffer
+John Foo\InjectComma Mary Bar\InjectComma \InjectOthers.
+\stopbuffer
+
+\typebuffer
+
+Which gives us:
+
+\blank {\bf \getbuffer} \blank
+
+Normally one doesn't need this kind of magic for lists because the length of the
+list is known and injection can be done using the index in the list. Here is a more
+practical example:
+
+\startbuffer
+\def\SomeTitle {Just a title}
+\def\SomeAuthor{Just an author}
+\def\SomeYear {2015}
+\stopbuffer
+
+\typebuffer \getbuffer
+
+We paste the three snippets together:
+
+\startbuffer
+\SomeTitle,\space \SomeAuthor\space (\SomeYear).
+\stopbuffer
+
+\typebuffer \blank {\bf \getbuffer} \blank
+
+But to get even more abstract, we can do this:
+
+\startbuffer
+\def\PlaceTitle
+ {\SomeTitle
+ \markcontent[punctuation]{.}}
+
+\def\PlaceAuthor
+ {\removemarkedcontent[punctuation]%
+ \markcontent[punctuation]{,\space}%
+ \SomeAuthor
+ \markcontent[punctuation]{,\space}}
+
+\def\PlaceYear
+ {\removemarkedcontent[punctuation]%
+ \space(\SomeYear)%
+ \markcontent[punctuation]{.}}
+\stopbuffer
+
+\typebuffer \getbuffer
+
+Used as:
+
+\startbuffer
+\PlaceTitle\PlaceAuthor\PlaceYear
+\stopbuffer
+
+\typebuffer
+
+we get the output:
+
+\blank {\bf \getbuffer} \blank
+
+but when we have no author,
+
+\startbuffer
+\def\SomeAuthor{}
+
+\PlaceTitle\PlaceAuthor\PlaceYear
+\stopbuffer
+
+\typebuffer
+
+Now we get:
+
+\blank {\bf \getbuffer} \blank
+
+Even more clever is this:
+
+\def\PlaceYear
+ {\removemarkedcontent[punctuation]%
+ \markcontent[punctuation]{\space(\SomeYear).}}
+
+\startbuffer
+\def\SomeAuthor{}
+\def\SomeYear{}
+\def\SomePeriod{\removemarkedcontent[punctuation].}
+
+\PlaceTitle\PlaceAuthor\PlaceYear\SomePeriod
+\stopbuffer
+
+\typebuffer
+
+The output is:
+
+\blank {\bf \getbuffer} \blank
+
+Of course we can just test for a variable like \type {\SomeAuthor} being empty
+before we place punctuation, but there are cases where a period becomes a comma
+or a comma becomes a semicolon. Especially with bibliographies your worst
+typographical nightmares come true, so it is handy to have such a mechanism
+available when it's needed.
+
+\stopsection
+
+\startsection[title=A plain solution]
+
+For users of \LUATEX\ who don't want to use \CONTEXT\ I will now present an
+alternative implementation. Of course more clever variants are possible but the
+principle remains. The trick is simple enough to show here as an example of \LUA\
+coding as it doesn't need much help from the infrastructure that the macro
+package provides. The only pitfall is the used signal (attribute number) but you
+can set another one if needed. We use the \type {gadgets} namespace to isolate
+the code.
+
+\startbuffer
+\directlua {
+ gadgets = gadgets or { }
+ local marking = { }
+ gadgets.marking = marking
+
+ local marksignal = 5001
+ local lastmarked = 0
+ local marked = { }
+ local local_par = 6
+ local whatsit_node = 8
+
+ function marking.setsignal(n)
+ marksignal = tonumber(n) or marksignal
+ end
+
+ function marking.mark(str)
+ local currentmarked = marked[str]
+ if not currentmarked then
+ lastmarked = lastmarked + 1
+ currentmarked = lastmarked
+ marked[str] = currentmarked
+ end
+ tex.setattribute(marksignal,currentmarked)
+ end
+
+ function marking.remove(str)
+ local attr = marked[str]
+ if not attr then
+ return
+ end
+ local list = tex.nest[tex.nest.ptr]
+ if list then
+ local head = list.head
+ local tail = list.tail
+ local last = tail
+ if last[marksignal] == attr then
+ local first = last
+ while true do
+ local prev = first.prev
+ if not prev or prev[marksignal] ~= attr or
+ (prev.id == whatsit_node and
+ prev.subtype == local_par) then
+ break
+ else
+ first = prev
+ end
+ end
+ if first == head then
+ list.head = nil
+ list.tail = nil
+ else
+ local prev = first.prev
+ list.tail = prev
+ prev.next = nil
+ end
+ node.flush_list(first)
+ end
+ end
+ end
+}
+\stopbuffer
+\stopluacode
+
+\typebuffer \getbuffer
+
+These functions are called from macros. We use symbolic names for the marked
+snippets. We could have used numbers but meaningful tags can be supported with
+negligible overhead. The remover starts at the end of the current list and
+goes backwards till no matching attribute value is seen. When a valid range is
+found it gets removed.
+
+\startbuffer
+\def\setmarksignal#1%
+ {\directlua{gadgets.marking.setsignal(\number#1)}}
+
+\def\marksomething#1#2%
+ {{\directlua{gadgets.marking.mark("#1")}{#2}}}
+
+\def\unsomething#1%
+ {\directlua{gadgets.marking.remove("#1")}}
+\stopbuffer
+
+\typebuffer \getbuffer
+
+The working of these macros can best be shown from a few examples:
+
+\startbuffer
+before\marksomething{gone}{\em HERE}\unsomething{gone}after
+before\marksomething{kept}{\em HERE}\unsomething{gone}after
+\marksomething{gone}{\em HERE}\unsomething{gone}last
+\marksomething{kept}{\em HERE}\unsomething{gone}last
+\stopbuffer
+
+\typebuffer
+
+This renders as: \blank \startlines\bf\getbuffer\stoplines
+
+The remover needs to look at the beginning of a paragraph marked by a local par
+whatsit. If we removed that, \LUATEX\ would crash because the list head
+(currently) cannot be set to nil. This is no big deal because this macro is not
+meant to clean up across paragraphs.
+
+A close look at the definition of \type {\marksomething} will reveal
+an extra grouping in the definition. This is needed to make content that uses
+\type {\aftergroup} trickery work correctly. Here is another example:
+
+\startbuffer
+\def\SnippetOne {first\marksomething{punctuation}{, }}
+\def\SnippetTwo {second\marksomething{punctuation}{, }}
+\def\SnippetThree{\unsomething{punctuation} and third.}
+\stopbuffer
+
+\typebuffer \getbuffer
+
+We can paste these snippets together and make the last one use \type {and}
+instead of a comma.
+
+\startbuffer
+\SnippetOne \SnippetTwo \SnippetThree\par
+\SnippetOne \SnippetThree\par
+\stopbuffer
+
+\typebuffer
+
+We get: \blank {\bf \getbuffer} \blank
+
+Of course in practice one probably knows how many snippets there are and using a
+counter to keep track of the state is more efficient than first typesetting
+something and removing it afterwards. But still it looks like a cool feature and
+it can come in handy at some point, as with the title|-|author|-|year example given
+before.
+
+The plain code shown here is in the distribution in the file \type
+{luatex-gadgets} and gets preloaded in the \type {luatex-plain} format.
+
+\stopsection
+
+\stopchapter