summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/luatex/luatexbase/luatexbase-mcb.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/luatex/luatexbase/luatexbase-mcb.dtx')
-rw-r--r--Master/texmf-dist/source/luatex/luatexbase/luatexbase-mcb.dtx984
1 files changed, 628 insertions, 356 deletions
diff --git a/Master/texmf-dist/source/luatex/luatexbase/luatexbase-mcb.dtx b/Master/texmf-dist/source/luatex/luatexbase/luatexbase-mcb.dtx
index 09c8c1c53dd..b944cd261e4 100644
--- a/Master/texmf-dist/source/luatex/luatexbase/luatexbase-mcb.dtx
+++ b/Master/texmf-dist/source/luatex/luatexbase/luatexbase-mcb.dtx
@@ -69,6 +69,11 @@ See source file '\inFileName' for details.
\file{mcb.lua}{\from{luatexbase-mcb.dtx}{lua}}%
}
+\generate{%
+ \usedir{doc/luatex/luatexbase}%
+ \file{test-mcb.lua}{\from{luatexbase-mcb.dtx}{testlua}}%
+}
+
\obeyspaces
\Msg{************************************************************************}
\Msg{*}
@@ -113,73 +118,163 @@ See source file '\inFileName' for details.
% Grave accent \` Left brace \{ Vertical bar \|
% Right brace \} Tilde \~}
%
-% \title{The \textsf{luatexbase-mcb} package}
-% \date{2010/05/27 v0.2a}
-% \author{%
-% Manuel P\'egouri\'e-Gonnard \\ \email{mpg@elzevir.fr} \and
-% \'Elie Roux \\ \email{elie.roux@telecom-bretagne.eu}}
+% \pkdate{luatexbase-mcb}{2010/10/10 v0.3}
%
% \maketitle
%
% \begin{abstract}
-% This package manages the callback adding and removing, by providing new
-% functions and overwriting \texttt{callback.register}. It also allows to
-% create and call new callbacks.\par
-% The user part of this documentation is currently very incomplete, and will
-% be expanded later. Sorry for this and thanks for your patience.\par
-% \textbf{Warning.} Currently assumes that \textsf{luatexbase-modutils}
-% has been previously loaded. (This is a temporary limitation.)
+% The primary feature of this package is to allow many functions to be
+% registered in the same callback. Depending of the type of the callback, the
+% functions will be combined in some way when the callback is called. Functions
+% are provided for addition and removal of individual functions from a
+% callback's list, with a priority system.\par
+% Additionally, you can create new callbacks that will be handled the same way
+% as predefined callbacks, except that they must be called explicitly.
% \end{abstract}
%
% \tableofcontents
%
% \section{Documentation}
%
-% Lua\TeX\ provides an extremely interesting feature, named callbacks. It
-% allows to call some lua functions at some points of the \TeX\ algorithm (a
+% Before we start, let me mention that test files are provided (they should be
+% in the same directory as this PDF file). You can have a look at them,
+% compile them and have a look at the log, if you want examples of how this
+% module works.
+%
+% \subsection{Managing functions in callbacks}
+%
+% \luatex provides an extremely interesting feature, named callbacks. It
+% allows to call some Lua functions at some points of the \TeX\ algorithm (a
% \emph{callback}), like when \TeX\ breaks likes, puts vertical spaces, etc.
-% The Lua\TeX\ core offers a function called \texttt{callback.register} that
+% The \luatex core offers a function called \texttt{callback.register} that
% enables to register a function in a callback.
%
-% The problem with \texttt{callback.register} is that is registers only one
-% function in a callback. For a lot of callbacks it can be common to have
-% several packages registering their function in a callback, and thus it is
-% impossible with them to be compatible with each other.
-%
-% This package solves this problem by adding mainly one new function
-% \verb+luatexbase.add_to_callback+ that adds a function in a callback. With
-% this function it is possible for packages to register their function in a
-% callback without overwriting the functions of the other packages. There are
-% also functions to remove one or all functions from a callback.
-% See comments in the implementation section for details.
-%
-% The functions are called in a certain order, and when a package registers a
-% callback it can assign a priority to its function. Conflicts can still
-% remain even with the priority mechanism, for example in the case where two
-% packages want to have the highest priority. In these cases the packages have
-% to solve the conflicts themselves.
-% See comments in the implementation section for details.
-%
-% This package also privides a way to create and call new callbacks, in
-% addition to the default Lua\TeX\ callbacks.
-% See comments in the implementation section for details.
-%
-% \subsubsection*{Limitations}
-%
-% This package only works for callbacks where it's safe to add multiple
-% functions without changing the functions' signatures. There are callbacks,
-% though, where registering several functions is not possible without changing
-% the function's signatures, like for example the readers callbacks. These
-% callbacks take a filename and give the datas in it. One solution would be to
-% change the functions' signature to open it when the function is the first,
-% and to take the datas and modify them eventually if they are called after
-% the first. But it seems rather fragile and useless, so it's not implemented.
-% With these callbacks, in this package we simply execute the first function
-% in the list.
-%
-% Other callbacks in this case are \texttt{define\_font} and
-% \texttt{open\_read\_file}. There is though a solution for several packages
-% to use these callbacks, see the implementation of \texttt{luatextra}.
+% The problem with |callback.register| is that is registers only one function
+% in a callback. This package solves the problem by disabling
+% |callback.register| and providing a new interface allowing many functions to
+% be registered in a single callback.
+%
+% The way the functions are combined together depends on
+% the type of the callback. There are currently 4 types of callback, depending
+% on the calling convention of the functions the callback can hold:
+% \begin{description}
+% \item[simple] is for functions that don't return anything: they are called
+% in order, all with the same argument;
+% \item[data] is for functions receiving a piece of data of nay type
+% except node list head (and possibly other arguments) and returning it
+% (possibly modified): the functions are called in order, and each is
+% passed the return value of the previous (and the other arguments
+% untouched, if any). The return value is that of the last function;
+% \item[list] is a specialized variant of \emph{data} for functions
+% filtering node lists. Such functions may return either the head of a
+% modified node list, or the boolean values |true| or |false|. The
+% functions are chained the same way as for \emph{data} except that for
+% the following. If
+% one function returns |false|, then |false| is immediately return and the
+% following functions are \emph{not} called. If one function returns
+% |true|, then the same head is passed to the next function. If all
+% functions return |true|, then |true| is returned, otherwise the return
+% value of the last function not returning |true| is used.
+% \item[first] is for functions with more complex signatures; functions in
+% this type of callback are \emph{not} combined: only the first one
+% (according to priorities) is executed.
+% \end{description}
+%
+% To add a function to a callback, use:
+% \begin{verbatim}
+% luatexbase.add_to_callback(name, func, description, priority)
+% \end{verbatim}
+% The first argument is the name of the callback, the second is a function,
+% the third one is a string used to identify the function later, and the
+% optional priority is a positive integer, representing the rank of the
+% function in the list of functions to be executing for this callback. So,
+% |1| is the highest priority. If no priority is specified, the function is
+% appended to the list, that is, its priority is the one of the last function
+% plus one.
+%
+% The priority system is intended to help resolving conflicts between packages
+% competing on the same callback, but it cannot solve every possible issue. If
+% two packages request priority |1| on the same callback, then the last one
+% loaded will win.
+%
+% To remove a function from a callback, use:
+% \begin{verbatim}
+% luatexbase.remove_from_callback(name, description)
+% \end{verbatim}
+% The first argument must be the name of the callback, and the second one the
+% description used when adding the function to this callback. You can also
+% remove all functions from a callback at once using
+% \begin{verbatim}
+% luatexbase.reset_callback(name, make_false)
+% \end{verbatim}
+% The |make_false| argument is optional. If it is |true| (repeat: |true|, not
+% |false|) then the value |false| is registered in the callback, which has a
+% special meaning for some callback.
+%
+% Note that |reset_callback| is very invasive since it removes all functions
+% possibly installed by other packages in this callback. So, use it with care
+% if there is any chance that another package wants to share this callback
+% with you.
+%
+% When new functions are added at the beginning of the list, other functions
+% are shifted down the list. To get the current rank of a function in a
+% callback's list, use:
+% \begin{verbatim}
+% priority = luatexbase.priority_in_callback(name, description)
+% \end{verbatim}
+% Again, the description is the string used when adding the function. If the
+% function identified by this string is not in this callback's list, the
+% priority returned is the boolean value |false|.
+%
+% \subsection{Creating new callbacks}
+%
+% This package also provides a way to create and call new callbacks, in
+% addition to the default \luatex callbacks.
+% \begin{verbatim}
+% luatexbase.create_callback(name, type, default)
+% \end{verbatim}
+% The first argument is the callback's name, it must be unique. Then, the type
+% goes as explained above, it is given as a string. Finally all user-defined
+% callbacks have a default function which must\footnote{You can obviously
+% provide a dummy function. If you're doing so often, please tell me, I may
+% want to make this argument optional.} be provided as the third
+% argument. It will be used when no other function is registered for this
+% callback.
+%
+% Functions are added to and removed from user-defined callbacks just the same
+% way as predefined callback, so the previous section still applies. There is
+% one difference, however: user-defined callbacks must be called explicitly at
+% some point in your code, while predefined callbacks are called automatically
+% by \luatex. To do so, use:
+% \begin{verbatim}
+% luatexbase.call_callback(name, arguments...)
+% \end{verbatim}
+% The functions registered for this callback (or the default function) will be
+% called with |arguments...| as arguments.
+%
+% \subsubsection{Limitations}
+%
+% For callbacks of type |first|, our new management system isn't actually
+% better than good old |callback.register|. For some of them, is may be
+% possible to split them into many callbacks, so that these callbacks can
+% accept multiple functions. However, its seems risky and limited in use and
+% is therefore nor implemented.
+%
+% At some point, \pk{luatextra} used to split |open_read_file| that way, but
+% support for this was removed. It may be added back (as well as support for
+% other split callbacks) if it appears there is an actual need for it.
+%
+% \subsection{Compatibility}
+%
+% Some callbacks have a calling convention that varies depending on the
+% version of \luatex used. This package \emph{does not} try to track the type
+% of the callbacks in every possible version of \luatex. The types are based
+% on the last stable beta version (0.60.2 at the time this doc is written).
+%
+% However, for callbacks that have the same calling convention for every
+% version of \luatex, this package should work with the same range of \luatex
+% version as other packages in the \pk{luatexbase} bundle (currently, 0.25.4
+% to 0.60.2).
%
% \section{Implementation}
%
@@ -245,7 +340,7 @@ See source file '\inFileName' for details.
\let\x\ProvidesPackage
\fi
\expandafter\endgroup
-\x{luatexbase-mcb}[2010/05/27 v0.2a Callback management for LuaTeX]
+\x{luatexbase-mcb}[2010/10/10 v0.3 Callback management for LuaTeX]
% \end{macrocode}
%
% Make sure \luatex is used.
@@ -259,14 +354,17 @@ See source file '\inFileName' for details.
\fi
\ifluatex\else
\begingroup
- \expandafter\ifx\csname PackageWarningNoLine\endcsname\relax
- \def\x#1#2{\begingroup\newlinechar10
- \immediate\write16{Package #1 warning: #2}\endgroup}
+ \expandafter\ifx\csname PackageError\endcsname\relax
+ \def\x#1#2#3{\begingroup \newlinechar10
+ \errhelp{#3}\errmessage{Package #1 error: #2}\endgroup}
\else
- \let\x\PackageWarningNoLine
+ \let\x\PackageError
\fi
\expandafter\endgroup
- \x{luatexbase-mcb}{LuaTeX is required for this package. Aborting.}
+ \x{luatexbase-attr}{LuaTeX is required for this package. Aborting.}{%
+ This package can only be used with the LuaTeX engine^^J%
+ (command `lualatex' or `luatex').^^J%
+ Package loading has been stopped to prevent additional errors.}
\lltxb@mcb@AtEnd
\expandafter\endinput
\fi
@@ -304,309 +402,280 @@ See source file '\inFileName' for details.
%
% \begin{macrocode}
module('luatexbase', package.seeall)
-luatexbase.provides_module({
- name = "luamcallbacks",
+local err, warning, info = luatexbase.provides_module({
+ name = "luatexbase-mcb",
version = 0.2,
date = "2010/05/12",
description = "register several functions in a callback",
- author = "Hans Hagen, Elie Roux and Manuel Pégourie-Gonnard",
- copyright = "Hans Hagen, Elie Roux and Manuel Pégourie-Gonnard",
+ author = "Hans Hagen, Elie Roux and Manuel Pegourie-Gonnard",
+ copyright = "Hans Hagen, Elie Roux and Manuel Pegourie-Gonnard",
license = "CC0",
})
% \end{macrocode}
%
-% Shortcuts for error functions.
+% \subsubsection{Housekeeping}
+%
+% The main table: keys are callback names, and values are the associated
+% lists of functions. More precisely, the entries in the list are tables
+% holding the actual function as |func| and the identifying description as
+% |description|. Only callbacks with a non-empty list of functions have an
+% entry in this list.
%
% \begin{macrocode}
-local log = log or function(...)
- luatexbase.module_log('luamcallbacks', string.format(...))
-end
-local info = info or function(...)
- luatexbase.module_info('luamcallbacks', string.format(...))
-end
-local warning = warning or function(...)
- luatexbase.module_warning('luamcallbacks', string.format(...))
-end
-local err = err or function(...)
- luatexbase.module_error('luamcallbacks', string.format(...))
-end
+local callbacklist = callbacklist or { }
% \end{macrocode}
%
-% \subsubsection{Initialisations}
+% Numerical codes for callback types, and name to value association (the
+% table keys are strings, the values are numbers).
%
-% \texttt{callbacklist} is the main list, that contains the callbacks as
-% keys and a table of the registered functions a values.
+% \begin{macrocode}
+local list, data, first, simple = 1, 2, 3, 4
+local types = {
+ list = list,
+ data = data,
+ first = first,
+ simple = simple,
+}
+% \end{macrocode}
+%
+% Now, list all predefined callbacks with their current type, based on the
+% LuaTeX manual version 0.60.2.
%
% \begin{macrocode}
-local callbacklist = callbacklist or { }
+local callbacktypes = callbacktypes or {
% \end{macrocode}
%
-% A table with the default functions of the created callbacks. See
-% \texttt{create} for further informations.
+% Section 4.1.1: file discovery callbacks.
%
% \begin{macrocode}
-local lua_callbacks_defaults = { }
+ find_read_file = first,
+ find_write_file = first,
+ find_font_file = data,
+ find_output_file = data,
+ find_format_file = data,
+ find_vf_file = data,
+ find_ocp_file = data,
+ find_map_file = data,
+ find_enc_file = data,
+ find_sfd_file = data,
+ find_pk_file = data,
+ find_data_file = data,
+ find_opentype_file = data,
+ find_truetype_file = data,
+ find_type1_file = data,
+ find_image_file = data,
% \end{macrocode}
%
-% There are 4 types of callback:
-% \begin{itemize}
-% \item the ones taking a list of nodes and returning a boolean and
-% eventually a new head (\texttt{list})
-% \item the ones taking datas and returning the modified ones
-% (\texttt{data})
-% \item the ones that can't have multiple functions registered in them
-% (\texttt{first})
-% \item the ones for functions that don't return anything (\texttt{simple})
-% \end{itemize}
+% Section 4.1.2: file reading callbacks.
%
% \begin{macrocode}
-local list = 1
-local data = 2
-local first = 3
-local simple = 4
+ open_read_file = first,
+ read_font_file = first,
+ read_vf_file = first,
+ read_ocp_file = first,
+ read_map_file = first,
+ read_enc_file = first,
+ read_sfd_file = first,
+ read_pk_file = first,
+ read_data_file = first,
+ read_truetype_file = first,
+ read_type1_file = first,
+ read_opentype_file = first,
% \end{macrocode}
%
-% \texttt{callbacktypes} is the list that contains the callbacks as keys
-% and the type (list or data) as values.
+% Section 4.1.3: data processing callbacks.
%
% \begin{macrocode}
-local callbacktypes = callbacktypes or {
- buildpage_filter = simple,
- token_filter = first,
- pre_output_filter = list,
- hpack_filter = list,
- process_input_buffer = data,
- mlist_to_hlist = list,
- vpack_filter = list,
- define_font = first,
- open_read_file = first,
- linebreak_filter = list,
- post_linebreak_filter = list,
- pre_linebreak_filter = list,
- start_page_number = simple,
- stop_page_number = simple,
- start_run = simple,
- show_error_hook = simple,
- stop_run = simple,
- hyphenate = simple,
- ligaturing = simple,
- kerning = data,
- find_write_file = first,
- find_read_file = first,
- find_vf_file = data,
- find_map_file = data,
- find_format_file = data,
- find_opentype_file = data,
- find_output_file = data,
- find_truetype_file = data,
- find_type1_file = data,
- find_data_file = data,
- find_pk_file = data,
- find_font_file = data,
- find_image_file = data,
- find_ocp_file = data,
- find_sfd_file = data,
- find_enc_file = data,
- read_sfd_file = first,
- read_map_file = first,
- read_pk_file = first,
- read_enc_file = first,
- read_vf_file = first,
- read_ocp_file = first,
- read_opentype_file = first,
- read_truetype_file = first,
- read_font_file = first,
- read_type1_file = first,
- read_data_file = first,
-}
+ process_input_buffer = data,
+ process_output_buffer = data,
+ token_filter = first,
% \end{macrocode}
%
-% In Lua\TeX\ version 0.43, a new callback called |process_output_buffer|
-% appeared, so we enable it. Test the version using the compat package for,
-% well, compatibility.
+% Section 4.1.4: node list processiong callbacks.
%
% \begin{macrocode}
-if luatexbase.luatexversion > 42 then
- callbacktypes["process_output_buffer"] = data
-end
+ buildpage_filter = simple,
+ pre_linebreak_filter = list,
+ linebreak_filter = list,
+ post_linebreak_filter = list,
+ hpack_filter = list,
+ vpack_filter = list,
+ pre_output_filter = list,
+ hyphenate = simple,
+ ligaturing = simple,
+ kerning = simple,
+ mlist_to_hlist = list,
% \end{macrocode}
%
-% As we overwrite \texttt{callback.register}, we save it as
-% \texttt{internalregister}.
+% Section 4.1.5: information reporting callbacks.
%
% \begin{macrocode}
-local internalregister = internalregister or callback.register
+ start_run = simple,
+ stop_run = simple,
+ start_page_number = simple,
+ stop_page_number = simple,
+ show_error_hook = simple,
% \end{macrocode}
%
-% \subsubsection{Unsorted stuff}
+% Section 4.1.6: font-related callbacks.
%
-% A simple function we'll use later to understand the arguments of the
-% \texttt{create} function. It takes a string and returns the type
-% corresponding to the string or nil.
+% \begin{macrocode}
+ define_font = first,
+}
+% \end{macrocode}
+%
+% All user-defined callbacks have a default function. The following table's
+% keys are the names of the user-defined callback, the associated value is
+% the default functon for this callback. This table is also used to
+% identify the user-defined callbacks.
%
% \begin{macrocode}
-local function str_to_type(str)
- if str == 'list' then
- return list
- elseif str == 'data' then
- return data
- elseif str == 'first' then
- return first
- elseif str == 'simple' then
- return simple
- else
- return nil
- end
+local lua_callbacks_defaults = { }
+% \end{macrocode}
+%
+% Overwrite |callback.register|, but save it first. Also define a wrapper
+% that automatically raise an error when something goes wrong.
+%
+% \begin{macrocode}
+local original_register = original_register or callback.register
+callback.register = function ()
+ err("function callback.register has been trapped,\n"
+ .."please use luatexbase.add_to_callback instead.")
+end
+local function register_callback(...)
+ return assert(original_register(...))
end
% \end{macrocode}
%
-% This function and the following ones are only internal. This one is the
-% handler for the first type of callbacks: the ones that take a list head
-% and return true, false, or a new list head.
+% \subsubsection{Handlers}
+%
+% Normal (as opposed to user-defined) callbacks have handlers depending on
+% their type. The handler function is registered into the callback when the
+% first function is added to this callback's list. Then, when the callback
+% is called, then handler takes care of running all functions in the list.
+% When the last function is removed from the callback's list, the handler
+% is unregistered.
+%
+% More precisely, the functions below are used to generate a specialized
+% function (closure) for a given callback, which is the actual handler.
+%
+% Handler for |list| callbacks.
%
% \begin{macrocode}
--- local
-function listhandler (name)
+local function listhandler (name)
return function(head,...)
- local l = callbacklist[name]
- if l then
- local done = true
- for _, f in ipairs(l) do
- -- the returned value is either true or a new head plus true
- rtv1, rtv2 = f.func(head,...)
- if type(rtv1) == 'boolean' then
- done = rtv1
- elseif type (rtv1) == 'userdata' then
- head = rtv1
- end
- if type(rtv2) == 'boolean' then
- done = rtv2
- elseif type(rtv2) == 'userdata' then
- head = rtv2
- end
- if done == false then
- err("function \"%s\" returned false in callback '%s'",
- f.description, name)
- end
+ local ret
+ local alltrue = true
+ for _, f in ipairs(callbacklist[name]) do
+ ret = f.func(head, ...)
+ if ret == false then
+ warn("function '%s' returned false\nin callback '%s'",
+ f.description, name)
+ break
+ end
+ if ret ~= true then
+ alltrue = false
+ head = ret
end
- return head, done
- else
- return head, false
end
+ return alltrue and true or head
end
end
% \end{macrocode}
%
-% The handler for callbacks taking datas and returning modified ones.
+% Handler for |data| callbacks.
%
% \begin{macrocode}
local function datahandler (name)
- return function(data,...)
- local l = callbacklist[name]
- if l then
- for _, f in ipairs(l) do
- data = f.func(data,...)
- end
+ return function(data, ...)
+ for _, f in ipairs(callbacklist[name]) do
+ data = f.func(data, ...)
end
return data
end
end
% \end{macrocode}
%
-% This function is for the handlers that don't support more than one
-% functions in them. In this case we only call the first function of the
-% list.
+% Handler for |first| callbacks. We can assume |callbacklist[name]| is not
+% empty: otherwise, the function wouldn't be registered in the callback any
+% more.
%
% \begin{macrocode}
local function firsthandler (name)
return function(...)
- local l = callbacklist[name]
- if l then
- local f = l[1].func
- return f(...)
- else
- return nil, false
- end
+ return callbacklist[name][1].func(...)
end
end
% \end{macrocode}
%
-% Handler for simple functions that don't return anything.
+% Handler for |simple| callbacks.
%
% \begin{macrocode}
local function simplehandler (name)
return function(...)
- local l = callbacklist[name]
- if l then
- for _, f in ipairs(l) do
- f.func(...)
- end
+ for _, f in ipairs(callbacklist[name]) do
+ f.func(...)
end
end
end
% \end{macrocode}
%
-% \subsubsection{Public functions}
+% Finally, keep a handlers table for indexed access.
%
-% The main function. The signature is \texttt{add (name,
-% func, description, priority)} with \texttt{name} being the name of the
-% callback in which the function is added; \texttt{func} is the added
-% function; \texttt{description} is a small character string describing the
-% function, and \texttt{priority} an optional argument describing the
-% priority the function will have.
+% \begin{macrocode}
+local handlers = {
+ [list] = listhandler,
+ [data] = datahandler,
+ [first] = firsthandler,
+ [simple] = simplehandler,
+}
+% \end{macrocode}
%
-% The functions for a callbacks are added in a list (in
-% \texttt{callbacklist\\.callbackname}). If they have no
-% priority or a high priority number, they will be added at the end of the
-% list, and will be called after the others. If they have a low priority
-% number, the will be added at the beginning of the list and will be called
-% before the others.
+% \subsubsection{Public functions for functions management}
%
-% Something that must be made clear, is that there is absolutely no
-% solution for packages conflicts: if two packages want the top priority on
-% a certain callback, they will have to decide the priority they will give
-% to their function themselves. Most of the time, the priority is not needed.
+% Add a function to a callback. First check arguments.
%
% \begin{macrocode}
function add_to_callback (name,func,description,priority)
if type(func) ~= "function" then
- err("unable to add function, no proper function passed")
- return
+ return err("unable to add function:\nno proper function passed")
end
if not name or name == "" then
- err("unable to add function, no proper callback name passed")
+ err("unable to add function:\nno proper callback name passed")
return
elseif not callbacktypes[name] then
- err("unable to add function, '%s' is not a valid callback", name)
+ err("unable to add function:\n'%s' is not a valid callback", name)
return
end
if not description or description == "" then
- err("unable to add function to '%s', no proper description passed",
+ err("unable to add function to '%s':\nno proper description passed",
name)
return
end
- if priority_in_callback(name, description) ~= 0 then
- warning("function '%s' already registered in callback '%s'",
+ if priority_in_callback(name, description) then
+ err("function '%s' already registered\nin callback '%s'",
description, name)
+ return
end
+% \end{macrocode}
+%
+% Then test if this callback is already in use. If not, initialise its list
+% and register the proper handler.
+%
+% \begin{macrocode}
local l = callbacklist[name]
if not l then
l = {}
callbacklist[name] = l
if not lua_callbacks_defaults[name] then
- if callbacktypes[name] == list then
- internalregister(name, listhandler(name))
- elseif callbacktypes[name] == data then
- internalregister(name, datahandler(name))
- elseif callbacktypes[name] == simple then
- internalregister(name, simplehandler(name))
- elseif callbacktypes[name] == first then
- internalregister(name, firsthandler(name))
- else
- err("unknown callback type")
- end
+ register_callback(name, handlers[callbacktypes[name]](name))
end
end
+% \end{macrocode}
+%
+% Actually register the function.
+%
+% \begin{macrocode}
local f = {
func = func,
description = description,
@@ -617,33 +686,35 @@ function add_to_callback (name,func,description,priority)
elseif priority < 1 then
priority = 1
end
- if callbacktypes[name] == first and (priority ~= 1 or #l ~= 0) then
- warning("several callbacks registered in callback '%s', "
- .."only the first function will be active.", name)
- end
table.insert(l,priority,f)
- log("inserting function '%s' at position %s in callback list for '%s'",
+% \end{macrocode}
+%
+% Keep user informed.
+%
+% \begin{macrocode}
+ if callbacktypes[name] == first and #l ~= 1 then
+ warning("several functions in '%s',\n"
+ .."only one will be active.", name)
+ end
+ info("inserting '%s'\nat position %s in '%s'",
description, priority, name)
end
% \end{macrocode}
%
-% The function that removes a function from a callback. The signature is
-% \texttt{remove (name, description)} with \texttt{name} being
-% the name of callbacks, and description the description passed to
-% \texttt{add}.
+% Remove a function from a callback. First check arguments.
%
% \begin{macrocode}
function remove_from_callback (name, description)
if not name or name == "" then
- err("unable to remove function, no proper callback name passed")
+ err("unable to remove function:\nno proper callback name passed")
return
elseif not callbacktypes[name] then
- err("unable to remove function, '%s' is not a valid callback", name)
+ err("unable to remove function:\n'%s' is not a valid callback", name)
return
end
if not description or description == "" then
err(
- "unable to remove function from '%s', no proper description passed",
+ "unable to remove function from '%s':\nno proper description passed",
name)
return
end
@@ -652,45 +723,84 @@ function remove_from_callback (name, description)
err("no callback list for '%s'",name)
return
end
+% \end{macrocode}
+%
+% Then loop over the callback's function list until we find a matching
+% entry. Remove it and check if the list gets empty: if so, unregister the
+% callback handler unless the callback is user-defined.
+%
+% \begin{macrocode}
+ local index = false
for k,v in ipairs(l) do
if v.description == description then
- table.remove(l,k)
- log("removing function '%s' from '%s'",description,name)
- if not next(l) then
- callbacklist[name] = nil
- if not lua_callbacks_defaults[name] then
- internalregister(name, nil)
- end
- end
- return
+ index = k
+ break
+ end
+ end
+ if not index then
+ err("unable to remove '%s'\nfrom '%s'", description, name)
+ return
+ end
+ table.remove(l, index)
+ info("removing '%s'\nfrom '%s'", description, name)
+ if table.maxn(l) == 0 then
+ callbacklist[name] = nil
+ if not lua_callbacks_defaults[name] then
+ register_callback(name, nil)
end
end
- warning("unable to remove function '%s' from '%s'",description,name)
+ return
end
% \end{macrocode}
%
-% This function removes all the functions registered in a callback.
+% Remove all the functions registered in a callback. Unregisters the
+% callback handler unless the callback is user-defined.
%
% \begin{macrocode}
-function reset_callback (name)
+function reset_callback (name, make_false)
if not name or name == "" then
- err("unable to reset, no proper callback name passed")
+ err("unable to reset:\nno proper callback name passed")
return
elseif not callbacktypes[name] then
- err("reset error, '%s' is not a valid callback", name)
+ err("unable to reset '%s':\nis not a valid callback", name)
return
end
+ info("resetting callback '%s'", name)
+ callbacklist[name] = nil
if not lua_callbacks_defaults[name] then
- internalregister(name, nil)
+ if make_false == true then
+ info("setting '%s' to false", name)
+ register_callback(name, false)
+ else
+ register_callback(name, nil)
+ end
+ end
+end
+% \end{macrocode}
+%
+% Get a function's priority in a callback list, or false if the function is
+% not in the list.
+%
+% \begin{macrocode}
+function priority_in_callback (name, description)
+ if not name or name == ""
+ or not callbacktypes[name]
+ or not description then
+ return false
end
local l = callbacklist[name]
- if l then
- log("resetting callback list '%s'",name)
- callbacklist[name] = nil
+ if not l then return false end
+ for p, f in pairs(l) do
+ if f.description == description then
+ return p
+ end
end
+ return false
end
% \end{macrocode}
%
+% \subsubsection{Public functions for user-defined callbacks}
+%
% This first function creates a new callback. The signature is
% \texttt{create(name, ctype, default)} where \texttt{name} is the name of
% the new callback to create, \texttt{ctype} is the type of callback, and
@@ -705,24 +815,24 @@ end
% \begin{macrocode}
function create_callback(name, ctype, default)
if not name then
- err("unable to call callback, no proper name passed", name)
+ err("unable to call callback:\nno proper name passed", name)
return nil
end
if not ctype or not default then
- err("unable to create callback '%s': "
+ err("unable to create callback '%s':\n"
.."callbacktype or default function not specified", name)
return nil
end
if callbacktypes[name] then
- err("unable to create callback '%s', callback already exists", name)
+ err("unable to create callback '%s':\ncallback already exists", name)
return nil
end
- local temp = str_to_type(ctype)
- if not temp then
- err("unable to create callback '%s', type '%s' undefined", name, ctype)
+ ctype = types[ctype]
+ if not ctype then
+ err("unable to create callback '%s':\ntype '%s' undefined", name, ctype)
return nil
end
- ctype = temp
+ info("creating '%s' type %s", name, ctype)
lua_callbacks_defaults[name] = default
callbacktypes[name] = ctype
end
@@ -734,11 +844,11 @@ end
% \begin{macrocode}
function call_callback(name, ...)
if not name then
- err("unable to call callback, no proper name passed", name)
+ err("unable to call callback:\nno proper name passed", name)
return nil
end
if not lua_callbacks_defaults[name] then
- err("unable to call lua callback '%s', unknown callback", name)
+ err("unable to call lua callback '%s':\nunknown callback", name)
return nil
end
local l = callbacklist[name]
@@ -746,92 +856,254 @@ function call_callback(name, ...)
if not l then
f = lua_callbacks_defaults[name]
else
- if callbacktypes[name] == list then
- f = listhandler(name)
- elseif callbacktypes[name] == data then
- f = datahandler(name)
- elseif callbacktypes[name] == simple then
- f = simplehandler(name)
- elseif callbacktypes[name] == first then
- f = firsthandler(name)
- else
+ f = handlers[callbacktypes[name]](name)
+ if not f then
err("unknown callback type")
+ return
end
end
return f(...)
end
% \end{macrocode}
%
-% This function tells if a function has already been registered in a
-% callback, and gives its current priority. The arguments are the name of
-% the callback and the description of the function. If it has already been
-% registered, it gives its priority, and if not it returns false.
+% That's all folks!
%
% \begin{macrocode}
-function priority_in_callback (name, description)
- if not name or name == ""
- or not callbacktypes[name]
- or not description then
- return 0
- end
- local l = callbacklist[name]
- if not l then return 0 end
- for p, f in pairs(l) do
- if f.description == description then
- return p
- end
- end
- return 0
+%</lua>
+% \end{macrocode}
+%
+% \section{Test files}
+%
+% A few basic tests for Plain and LaTeX. Use a separate Lua file for
+% convenience, since this package works on the Lua side of the force.
+%
+% \begin{macrocode}
+%<*testlua>
+local msg = texio.write_nl
+% \end{macrocode}
+%
+% Test the management functions with a predefined callback.
+%
+% \begin{macrocode}
+local function sample(head,...)
+ return head, true
end
+local prio = luatexbase.priority_in_callback
+msg("\n*********\n* Testing management functions\n*********")
+luatexbase.add_to_callback("hpack_filter", sample, "sample one", 1)
+luatexbase.add_to_callback("hpack_filter", sample, "sample two", 2)
+luatexbase.add_to_callback("hpack_filter", sample, "sample three", 1)
+assert(prio("hpack_filter", "sample three"))
+luatexbase.remove_from_callback("hpack_filter", "sample three")
+assert(not prio("hpack_filter", "sample three"))
+luatexbase.reset_callback("hpack_filter")
+assert(not prio("hpack_filter", "sample one"))
% \end{macrocode}
%
-% Finally we
-% overwrite \texttt{callback.register} so that it outputs an error.
+% Create a callback, and check that the managment functions work with this
+% callback too.
%
% \begin{macrocode}
-callback.register = function ()
-err("function callback.register has been deleted by luamcallbacks, "
-.."please use luatexbase.add_to_callback instead.")
+local function data_one(s)
+ texio.write_nl("I'm data 1 whith argument: "..s)
+ return s
end
+local function data_two(s)
+ texio.write_nl("I'm data 2 whith argument: "..s)
+ return s
+end
+local function data_three(s)
+ texio.write_nl("I'm data 3 whith argument: "..s)
+ return s
+end
+msg("\n*********\n* Testing user-defined callbacks\n*********")
+msg("* create one")
+luatexbase.create_callback("fooback", "data", data_one)
+msg("* call it")
+luatexbase.call_callback("fooback", "default")
+msg("* add two functions")
+luatexbase.add_to_callback("fooback", data_two, "function two", 2)
+luatexbase.add_to_callback("fooback", data_three, "function three", 1)
+msg("* call")
+luatexbase.call_callback("fooback", "all")
+msg("* rm one function")
+luatexbase.remove_from_callback("fooback", "function three")
+msg("* call")
+luatexbase.call_callback("fooback", "all but three")
+msg("* reset")
+luatexbase.reset_callback("fooback")
+msg("* call")
+luatexbase.call_callback("fooback", "default")
% \end{macrocode}
%
-% That's all folks!
+% Now, we want to make each handler run at least once. So, define dummy
+% functions and register them in various callbacks. We will make sure the
+% callbacks are executed on the \tex end. Also, we want to check that
+% everything works when we unload the functions either one by one, or using
+% reset.
+%
+% A |list| callback.
%
% \begin{macrocode}
-%</lua>
+function add_hpack_filter()
+ luatexbase.add_to_callback('hpack_filter', function(head, ...)
+ texio.write_nl("I'm a dummy hpack_filter")
+ return head
+ end,
+ 'dummy hpack filter')
+ luatexbase.add_to_callback('hpack_filter', function(head, ...)
+ texio.write_nl("I'm an optimized dummy hpack_filter")
+ return true
+ end,
+ 'optimized dummy hpack filter')
+end
+function rm_one_hpack_filter()
+ luatexbase.remove_from_callback('hpack_filter', 'dummy hpack filter')
+end
+function rm_two_hpack_filter()
+ luatexbase.remove_from_callback('hpack_filter',
+ 'optimized dummy hpack filter')
+end
% \end{macrocode}
%
-% \section{Test files}
+% A |simple| callback.
+%
+% \begin{macrocode}
+function add_hyphenate()
+ luatexbase.add_to_callback('hyphenate', function(head, tail)
+ texio.write_nl("I'm a dummy hyphenate")
+ end,
+ 'dummy hyphenate')
+ luatexbase.add_to_callback('hyphenate', function(head, tail)
+ texio.write_nl("I'm an other dummy hyphenate")
+ end,
+ 'other dummy hyphenate')
+end
+function rm_one_hyphenate()
+ luatexbase.remove_from_callback('hyphenate', 'dummy hyphenate')
+end
+function rm_two_hyphenate()
+ luatexbase.remove_from_callback('hyphenate', 'other dummy hyphenate')
+end
+% \end{macrocode}
+%
+% A |first| callback.
+%
+% \begin{macrocode}
+function add_find_write_file()
+ luatexbase.add_to_callback('find_write_file', function(id, name)
+ texio.write_nl("I'm a dummy find_write_file")
+ return "dummy-"..name
+ end,
+ 'dummy find_write_file')
+ luatexbase.add_to_callback('find_write_file', function(id, name)
+ texio.write_nl("I'm an other dummy find_write_file")
+ return "dummy-other-"..name
+ end,
+ 'other dummy find_write_file')
+end
+function rm_one_find_write_file()
+ luatexbase.remove_from_callback('find_write_file',
+ 'dummy find_write_file')
+end
+function rm_two_find_write_file()
+ luatexbase.remove_from_callback('find_write_file',
+ 'other dummy find_write_file')
+end
+% \end{macrocode}
%
-% A few basic tests for Plain and LaTeX.
+% A |data| callback.
+%
+% \begin{macrocode}
+function add_process_input_buffer()
+ luatexbase.add_to_callback('process_input_buffer', function(buffer)
+ return buffer.."\\msg{dummy}"
+ end,
+ 'dummy process_input_buffer')
+ luatexbase.add_to_callback('process_input_buffer', function(buffer)
+ return buffer.."\\msg{otherdummy}"
+ end,
+ 'other dummy process_input_buffer')
+end
+function rm_one_process_input_buffer()
+ luatexbase.remove_from_callback('process_input_buffer',
+ 'dummy process_input_buffer')
+end
+function rm_two_process_input_buffer()
+ luatexbase.remove_from_callback('process_input_buffer',
+ 'other dummy process_input_buffer')
+end
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</testlua>
+% \end{macrocode}
%
% \begin{macrocode}
%<testplain>\input luatexbase-mcb.sty
%<testlatex>\RequirePackage{luatexbase-mcb}
%<*testplain,testlatex>
\catcode 64 11
-\luatexbase@directlua{
- local function one(head,...)
- texio.write_nl("I'm number 1")
- return head, true
- end
-
- local function two(head,...)
- texio.write_nl("I'm number 2")
- return head, true
- end
-
- local function three(head,...)
- texio.write_nl("I'm number 3")
- return head, true
- end
-
- luatexbase.add_to_callback("hpack_filter",one,"my sample function one",1)
- luatexbase.add_to_callback("hpack_filter",two,"my sample function two",2)
- luatexbase.add_to_callback("hpack_filter",three,"my sample function three",1)
-
- luatexbase.remove_from_callback("hpack_filter","my sample function three")
+\def\msg{\immediate\write16}
+\msg{===== BEGIN =====}
+% \end{macrocode}
+%
+% Loading the lua files tests that the management functions can be called
+% without raising errors.
+%
+% \begin{macrocode}
+\luatexbase@directlua{dofile('test-mcb.lua')}
+% \end{macrocode}
+%
+% We now want to load and unload stuff from the various callbacks have them
+% called to test the handlers. Here is a helper macro for that.
+%
+% \begin{macrocode}
+\def\test#1#2{%
+ \msg{^^J*********^^J* Testing #1 (type #2)^^J*********}
+ \msg{* Add two functions}
+ \luatexbase@directlua{add_#1()}
+ \csname test_#1\endcsname
+ \msg{* Remove one}
+ \luatexbase@directlua{rm_one_#1()}
+ \csname test_#1\endcsname
+ \msg{* Remove the second}
+ \luatexbase@directlua{rm_two_#1()}
+ \csname test_#1\endcsname
+ \msg{* Add two functions again}
+ \luatexbase@directlua{add_#1()}
+ \csname test_#1\endcsname
+ \msg{* Remove all functions}
+ \luatexbase@directlua{luatexbase.reset_callback("#1")}
+ \csname test_#1\endcsname
}
+% \end{macrocode}
+%
+% For each callback, we need a specific macro that triggers it. For the
+% hyphenate test, we need to untrap |\everypar| first, in the \latex case.
+%
+% \begin{macrocode}
+\catcode`\_ 11
+%<testlatex>\everypar{}
+\def\test_hpack_filter{\setbox0=\hbox{bla}}
+\def\test_hyphenate{\showhyphens{hyphenation}}
+\def\test_find_write_file{\immediate\openout15 test-mcb-out.log}
+\def\test_process_input_buffer{\input test-mcb-aux.tex}
+% \end{macrocode}
+%
+% Now actually test them
+%
+% \begin{macrocode}
+\test{hpack_filter}{list}
+\test{hyphenate}{simple}
+\test{find_write_file}{first}
+\test{process_input_buffer}{data}
+% \end{macrocode}
+%
+% Done.
+%
+% \begin{macrocode}
+\msg{===== END =====}
%</testplain,testlatex>
%<testplain>\bye
%<testlatex>\stop