From f78bfeb6a9d2c5e7ce7e7cf40af76cf3ad517a54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Mon, 29 Mar 2010 16:57:52 +0200 Subject: Rename luamcallbacks. --- Changes | 5 +- Makefile | 13 +- luamcallbacks.dtx | 815 ----------------------------------------------------- luatexbase-mcb.dtx | 800 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 811 insertions(+), 822 deletions(-) delete mode 100644 luamcallbacks.dtx create mode 100644 luatexbase-mcb.dtx diff --git a/Changes b/Changes index b079d49..add6599 100644 --- a/Changes +++ b/Changes @@ -5,6 +5,7 @@ Summary of backwards-incompatible interface changes between 0.1 and 0.2: - the Lua tables tex.attributenumber and tex.catcodetablenumber are not created any more, use their couterparts in luatexbase. - \luatexsetcatcoderange has been renamed to \setcatcoderange + - renamed luamcallbacks; Lua objects are now in luatexbase.mcb 2010/03/29 all @@ -20,7 +21,9 @@ Summary of backwards-incompatible interface changes between 0.1 and 0.2: - don't create tex.catcodetablenumber - rename \luatexsetcatcoderange to \setcatcoderange - load lua-compat - luamcallbacks + luamcallbacks -> luatexbase-mcb + - rename package + - functions are now in luatexbase.mcb rather than luamcallbacks - test file now for plain and latex luatexbase-loader - require"foo.bar" now looks for foo/bar then foo.bar, see doc for diff --git a/Makefile b/Makefile index 8194e77..31a6841 100644 --- a/Makefile +++ b/Makefile @@ -9,11 +9,12 @@ DTXSTY = lltxb-dtxstyle.tex COMPAT_RUN = luatexbase-compat.sty LOADER_RUN = luatexbase-loader.sty luatexbase.loader.lua MOD_RUN = luatexbase-modutils.sty modutils.lua -LINKS = luatexbase.attr.lua luatexbase.cctb.lua luatexbase.modutils.lua +LINKS = luatexbase.attr.lua luatexbase.cctb.lua \ + luatexbase.mcb.lua luatexbase.modutils.lua # Files grouped by generation mode -UNPACKED_MCB = luamcallbacks.lua \ - test-callbacks-latex.tex test-callbacks-plain.tex +UNPACKED_MCB = mcb.lua \ + test-mcb-latex.tex test-mcb-plain.tex UNPACKED_REGS = luatexbase-regs.sty \ test-regs-plain.tex test-regs-latex.tex UNPACKED_ATTR = luatexbase-attr.sty attr.lua \ @@ -76,7 +77,7 @@ world: all ctan luatexbase.%.lua: %.lua ln -sf $< $@ -$(UNPACKED_MCB): luamcallbacks.dtx +$(UNPACKED_MCB): luatexbase-mcb.dtx $(DO_TEX) $(UNPACKED_REGS): luatexbase-regs.dtx @@ -118,8 +119,8 @@ check-modutils: $(UNPACKED_MODUTILS) $(LOADER_RUN) $(LINKS) $(COMPAT_RUN) lualatex --interaction=batchmode test-modutils-latex.tex >/dev/null check-mcb: $(UNPACKED_MCB) $(LOADER_RUN) $(MOD_RUN) $(LINKS) $(COMPAT_RUN) - luatex --interaction=batchmode test-callbacks-plain.tex >/dev/null - lualatex --interaction=batchmode test-callbacks-latex.tex >/dev/null + luatex --interaction=batchmode test-mcb-plain.tex >/dev/null + lualatex --interaction=batchmode test-mcb-latex.tex >/dev/null check-compat: $(UNPACKED_COMPAT) luatex --interaction=batchmode test-compat-plain.tex >/dev/null diff --git a/luamcallbacks.dtx b/luamcallbacks.dtx deleted file mode 100644 index 4b3149b..0000000 --- a/luamcallbacks.dtx +++ /dev/null @@ -1,815 +0,0 @@ -% \iffalse meta-comment -% -% Copyright (C) 2009 by Elie Roux -% -% This work is under the CC0 license. -% -% This work consists of the main source file luamcallbacks.dtx -% and the derived files -% luamcallbacks.sty, luamcallbacks.tex, luamcallbacks.lua -% test-luamcallbacks.tex, luamcallbacks.pdf. -% -% Unpacking: -% tex luamcallbacks.dtx -% Documentation: -% pdflatex luamcallbacks.dtx -% -%<*ignore> -\begingroup - \def\x{LaTeX2e}% -\expandafter\endgroup -\ifcase 0\ifx\install y1\fi\expandafter - \ifx\csname processbatchFile\endcsname\relax\else1\fi - \ifx\fmtname\x\else 1\fi\relax -\else\csname fi\endcsname -% -%<*install> -\input docstrip.tex - -\keepsilent -\askforoverwritefalse - -\let\MetaPrefix\relax - -\preamble - -Copyright (C) 2009 by Elie Roux - -This work is under the CC0 license. -See source file '\inFileName' for details. - -\endpreamble - -\let\MetaPrefix\DoubleperCent - -\generate{% - \usedir{doc/luatex/luatexbase}% - \file{test-callbacks-plain.tex}{\from{luamcallbacks.dtx}{testplain}}% - \file{test-callbacks-latex.tex}{\from{luamcallbacks.dtx}{testlatex}}% -} - -\def\MetaPrefix{-- } - -\def\luapostamble{% - \MetaPrefix^^J% - \MetaPrefix\space End of File `\outFileName'.% -} - -\def\currentpostamble{\luapostamble}% - -\generate{% - \usedir{tex/luatex/luatexbase}% - \file{luamcallbacks.lua}{\from{luamcallbacks.dtx}{lua}}% -} - -\obeyspaces -\Msg{************************************************************************} -\Msg{*} -\Msg{* To finish the installation you have to move the following} -\Msg{* files into a directory searched by TeX:} -\Msg{*} -\Msg{* luamcallbacks.lua} -\Msg{*} -\Msg{* Happy TeXing!} -\Msg{*} -\Msg{************************************************************************} - -\endbatchfile -% -%<*ignore> -\fi -% -%<*driver> -\documentclass{ltxdoc} -\input{lltxb-dtxstyle} -\begin{document} - \DocInput{luamcallbacks.dtx}% -\end{document} -% -% \fi -% -% \CheckSum{0} -% -% \CharacterTable -% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z -% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z -% Digits \0\1\2\3\4\5\6\7\8\9 -% Exclamation \! Double quote \" Hash (number) \# -% Dollar \$ Percent \% Ampersand \& -% Acute accent \' Left paren \( Right paren \) -% Asterisk \* Plus \+ Comma \, -% Minus \- Point \. Solidus \/ -% Colon \: Semicolon \; Less than \< -% Equals \= Greater than \> Question mark \? -% Commercial at \@ Left bracket \[ Backslash \\ -% Right bracket \] Circumflex \^ Underscore \_ -% Grave accent \` Left brace \{ Vertical bar \| -% Right brace \} Tilde \~} -% -% \GetFileInfo{luamcallbacks.drv} -% -% \title{The \textsf{luamcallbacks} package} -% \date{2009/09/18 v0.93} -% \author{Elie Roux \\ \texttt{elie.roux@telecom-bretagne.eu}} -% -% \maketitle -% -% \begin{abstract} -% This package manages the callback adding and removing, by adding -% \texttt{callback.add} and \texttt{callback.remove}, and overwriting -% \texttt{callback.register}. It also allows to create and call new callbacks. -% For an introduction on this package (among others), please refer to the -% document \texttt{luatextra-reference.pdf}. -% \par\textbf{Warning.} Currently assumes that \textsf{luatexbase-modutils} -% has been previously loaded. (This is a temporary limitation.) -% \end{abstract} -% -% \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 -% \emph{callback}), like when \TeX\ breaks likes, puts vertical spaces, etc. -% The Lua\TeX\ 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 -% \texttt{callback.\\add} 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. -% -% 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. -% -% This package also privides a way to create and call new callbacks, in -% addition to the default Lua\TeX\ callbacks. -% -% This package contains only a \texttt{.lua} file, that can be called by -% another lua script. -% -%\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}. -% -% \StopEventually{ -% } -% -% \section{Package code} -% -% \iffalse -%<*lua> -% \fi -% -% The package contains \texttt{luamcallbacks.lua} with the new functions, -% and an example of the use of luamcallbacks. -% -% First the \texttt{luamcallbacks} module is registered as a Lua\TeX\ -% module, with some informations. -% -% \begin{macrocode} - -luamcallbacks = { } - -luamcallbacks.module = { - name = "luamcallbacks", - version = 0.93, - date = "2009/09/18", - description = "Module to register several functions in a callback.", - author = "Hans Hagen & Elie Roux", - copyright = "Hans Hagen & Elie Roux", - license = "CC0", -} - -luatexbase.provides_module(luamcallbacks.module) - -% \end{macrocode} -% -% \texttt{callbacklist} is the main list, that contains the callbacks as -% keys and a table of the registered functions a values. -% -% \begin{macrocode} - -luamcallbacks.callbacklist = luamcallbacks.callbacklist or { } - -% \end{macrocode} -% -% A table with the default functions of the created callbacks. See -% \texttt{luamcallbacks.create} for further informations. -% -% \begin{macrocode} - -luamcallbacks.lua_callbacks_defaults = { } - -local format = string.format - -% \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} -% -% \begin{macrocode} - -local list = 1 -local data = 2 -local first = 3 -local simple = 4 - -% \end{macrocode} -% -% \texttt{callbacktypes} is the list that contains the callbacks as keys -% and the type (list or data) as values. -% -% \begin{macrocode} - -luamcallbacks.callbacktypes = luamcallbacks.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, -} - -% \end{macrocode} -% -% In Lua\TeX\ version 0.43, a new callback called |process_output_buffer| -% appeared, so we enable it. -% -% \begin{macrocode} - -if tex.luatexversion > 42 then - luamcallbacks.callbacktypes["process_output_buffer"] = data -end - -% \end{macrocode} -% -% As we overwrite \texttt{callback.register}, we save it as -% \texttt{luamcallbacks.internalregister}. After that we declare some -% functions to write the errors or the logs. -% -% \begin{macrocode} - -luamcallbacks.internalregister = luamcallbacks.internalregister or callback.register - -local callbacktypes = luamcallbacks.callbacktypes - -luamcallbacks.log = luamcallbacks.log or function(...) - luatexbase.module_log('luamcallbacks', format(...)) -end - -luamcallbacks.info = luamcallbacks.info or function(...) - luatexbase.module_info('luamcallbacks', format(...)) -end - -luamcallbacks.warning = luamcallbacks.warning or function(...) - luatexbase.module_warning('luamcallbacks', format(...)) -end - -luamcallbacks.error = luamcallbacks.error or function(...) - luatexbase.module_error('luamcallbacks', format(...)) -end - -% \end{macrocode} -% -% 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} - -function luamcallbacks.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 -end - -% \end{macrocode} -% -% \begin{macro}{luamcallbacks.create} -% -% 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 -% \texttt{default} is the default function to call if no function is -% registered in this callback. -% -% The created callback will behave the same way Lua\TeX\ callbacks do, you -% can add and remove functions in it. The difference is that the callback -% is not automatically called, the package developer creating a new -% callback must also call it, see next function. -% -% \begin{macrocode} - -function luamcallbacks.create(name, ctype, default) - if not name then - luamcallbacks.error(format("unable to call callback, no proper name passed", name)) - return nil - end - if not ctype or not default then - luamcallbacks.error(format("unable to create callback '%s', callbacktype or default function not specified", name)) - return nil - end - if callbacktypes[name] then - luamcallbacks.error(format("unable to create callback '%s', callback already exists", name)) - return nil - end - local temp = luamcallbacks.str_to_type(ctype) - if not temp then - luamcallbacks.error(format("unable to create callback '%s', type '%s' undefined", name, ctype)) - return nil - end - ctype = temp - luamcallbacks.lua_callbacks_defaults[name] = default - callbacktypes[name] = ctype -end - -% \end{macrocode} -% -% \end{macro} -% -% \begin{macro}{luamcallbacks.call} -% -% This function calls a callback. It can only call a callback created by -% the \texttt{create} function. -% -% \begin{macrocode} - -function luamcallbacks.call(name, ...) - if not name then - luamcallbacks.error(format("unable to call callback, no proper name passed", name)) - return nil - end - if not luamcallbacks.lua_callbacks_defaults[name] then - luamcallbacks.error(format("unable to call lua callback '%s', unknown callback", name)) - return nil - end - local l = luamcallbacks.callbacklist[name] - local f - if not l then - f = luamcallbacks.lua_callbacks_defaults[name] - else - if callbacktypes[name] == list then - f = luamcallbacks.listhandler(name) - elseif callbacktypes[name] == data then - f = luamcallbacks.datahandler(name) - elseif callbacktypes[name] == simple then - f = luamcallbacks.simplehandler(name) - elseif callbacktypes[name] == first then - f = luamcallbacks.firsthandler(name) - else - luamcallbacks.error("unknown callback type") - end - end - return f(...) -end - -% \end{macrocode} -% -% \end{macro} -% -% \begin{macro}{luamcallbacks.add} -% -% The main function. The signature is \texttt{luamcallbacks.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. -% -% The functions for a callbacks are added in a list (in -% \texttt{luamcallbacks.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. -% -% 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 themself. Most of the time, the priority is not needed. -% -% \begin{macrocode} - -function luamcallbacks.add (name,func,description,priority) - if type(func) ~= "function" then - luamcallbacks.error("unable to add function, no proper function passed") - return - end - if not name or name == "" then - luamcallbacks.error("unable to add function, no proper callback name passed") - return - elseif not callbacktypes[name] then - luamcallbacks.error( - format("unable to add function, '%s' is not a valid callback", - name)) - return - end - if not description or description == "" then - luamcallbacks.error( - format("unable to add function to '%s', no proper description passed", - name)) - return - end - if luamcallbacks.get_priority(name, description) ~= 0 then - luamcallbacks.warning( - format("function '%s' already registered in callback '%s'", - description, name)) - end - local l = luamcallbacks.callbacklist[name] - if not l then - l = {} - luamcallbacks.callbacklist[name] = l - if not luamcallbacks.lua_callbacks_defaults[name] then - if callbacktypes[name] == list then - luamcallbacks.internalregister(name, luamcallbacks.listhandler(name)) - elseif callbacktypes[name] == data then - luamcallbacks.internalregister(name, luamcallbacks.datahandler(name)) - elseif callbacktypes[name] == simple then - luamcallbacks.internalregister(name, luamcallbacks.simplehandler(name)) - elseif callbacktypes[name] == first then - luamcallbacks.internalregister(name, luamcallbacks.firsthandler(name)) - else - luamcallbacks.error("unknown callback type") - end - end - end - local f = { - func = func, - description = description, - } - priority = tonumber(priority) - if not priority or priority > #l then - priority = #l+1 - elseif priority < 1 then - priority = 1 - end - if callbacktypes[name] == first and (priority ~= 1 or #l ~= 0) then - luamcallbacks.warning(format("several callbacks registered in callback '%s', only the first function will be active.", name)) - end - table.insert(l,priority,f) - luamcallbacks.log( - format("inserting function '%s' at position %s in callback list for '%s'", - description,priority,name)) -end - -% \end{macrocode} -% -% \end{macro} -% -% \begin{macro}{luamcallbacks.get priority} -% -% 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. -% -% \begin{macrocode} - -function luamcallbacks.get_priority (name, description) - if not name or name == "" or not callbacktypes[name] or not description then - return 0 - end - local l = luamcallbacks.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 -end - -% \end{macrocode} -% -% \end{macro} -% -% \begin{macro}{luamcallbacks.remove} -% -% The function that removes a function from a callback. The signature is -% \texttt{mcallbacks.remove (name, description)} with \texttt{name} being -% the name of callbacks, and description the description passed to -% \texttt{mcallbacks.add}. -% -% \begin{macrocode} - -function luamcallbacks.remove (name, description) - if not name or name == "" then - luamcallbacks.error("unable to remove function, no proper callback name passed") - return - elseif not callbacktypes[name] then - luamcallbacks.error( - format("unable to remove function, '%s' is not a valid callback", - name)) - return - end - if not description or description == "" then - luamcallbacks.error( - format("unable to remove function from '%s', no proper description passed", - name)) - return - end - local l = luamcallbacks.callbacklist[name] - if not l then - luamcallbacks.error(format("no callback list for '%s'",name)) - return - end - for k,v in ipairs(l) do - if v.description == description then - table.remove(l,k) - luamcallbacks.log( - format("removing function '%s' from '%s'",description,name)) - if not next(l) then - luamcallbacks.callbacklist[name] = nil - if not luamcallbacks.lua_callbacks_defaults[name] then - luamcallbacks.internalregister(name, nil) - end - end - return - end - end - luamcallbacks.warning( - format("unable to remove function '%s' from '%s'",description,name)) -end - -% \end{macrocode} -% -% \end{macro} -% -% \begin{macro}{luamcallbacks.reset} -% -% This function removes all the functions registered in a callback. -% -% \begin{macrocode} - -function luamcallbacks.reset (name) - if not name or name == "" then - luamcallbacks.error("unable to reset, no proper callback name passed") - return - elseif not callbacktypes[name] then - luamcallbacks.error( - format("reset error, '%s' is not a valid callback", - name)) - return - end - if not luamcallbacks.lua_callbacks_defaults[name] then - luamcallbacks.internalregister(name, nil) - end - local l = luamcallbacks.callbacklist[name] - if l then - luamcallbacks.log(format("resetting callback list '%s'",name)) - luamcallbacks.callbacklist[name] = nil - end -end - -% \end{macrocode} -% -% \end{macro} -% -% 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. -% -% \begin{macro}{luamcallbacks.listhandler} -% -% \begin{macrocode} - -function luamcallbacks.listhandler (name) - return function(head,...) - local l = luamcallbacks.callbacklist[name] - if l then - local done = true - for _, f in ipairs(l) do - -- the returned value can be 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 - luamcallbacks.error(format( - "function \"%s\" returned false in callback '%s'", - f.description, name)) - end - end - return head, done - else - return head, false - end - end -end - -% \end{macrocode} -% -% \end{macro} -% -% The handler for callbacks taking datas and returning modified ones. -% -% \begin{macro}{luamcallbacks.datahandler} -% -% \begin{macrocode} - -function luamcallbacks.datahandler (name) - return function(data,...) - local l = luamcallbacks.callbacklist[name] - if l then - for _, f in ipairs(l) do - data = f.func(data,...) - end - end - return data - end -end - -% \end{macrocode} -% -% \end{macro} -% -% 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. -% -% \begin{macro}{luamcallbacks.firsthandler} -% -% \begin{macrocode} - -function luamcallbacks.firsthandler (name) - return function(...) - local l = luamcallbacks.callbacklist[name] - if l then - local f = l[1].func - return f(...) - else - return nil, false - end - end -end - -% \end{macrocode} -% -% \end{macro} -% -% Handler for simple functions that don't return anything. -% -% \begin{macro}{luamcallbacks.simplehandler} -% -% \begin{macrocode} - -function luamcallbacks.simplehandler (name) - return function(...) - local l = luamcallbacks.callbacklist[name] - if l then - for _, f in ipairs(l) do - f.func(...) - end - end - end -end - -% \end{macrocode} -% -% \end{macro} -% -% Finally we add some functions to the \texttt{callback} module, and we -% overwrite \texttt{callback.register} so that it outputs an error. -% -% \begin{macrocode} - -callback.add = luamcallbacks.add -callback.remove = luamcallbacks.remove -callback.reset = luamcallbacks.reset -callback.create = luamcallbacks.create -callback.call = luamcallbacks.call -callback.get_priority = luamcallbacks.get_priority - -callback.register = function (...) -luamcallbacks.error("function callback.register has been deleted by luamcallbacks, please use callback.add instead.") -end - -% \end{macrocode} -% -% \iffalse -% -% \fi -% -% \section{Test files} -% -% A few basic tests for Plain and LaTeX. -% -% \begin{macrocode} -%\input luatexbase-modutils.sty -%\RequirePackage{luatexbase-modutils} -%<*testplain,testlatex> -\catcode 64 11 -\luatexbase@directlua{ - require "luamcallbacks" - 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 - - callback.add("hpack_filter",one,"my example function one",1) - callback.add("hpack_filter",two,"my example function two",2) - callback.add("hpack_filter",three,"my example function three",1) - - callback.remove("hpack_filter","my example function three") -} -% -%\bye -%\stop -% \end{macrocode} -% -% \Finale -\endinput diff --git a/luatexbase-mcb.dtx b/luatexbase-mcb.dtx new file mode 100644 index 0000000..cec69e9 --- /dev/null +++ b/luatexbase-mcb.dtx @@ -0,0 +1,800 @@ +% \iffalse meta-comment +% +% Copyright (C) 2009 by Elie Roux +% +% This work is under the CC0 license. +% +% This work consists of the main source file luatexbase-mcb.dtx +% and the derived files +% luatexbase-mcb.sty, mcb.lua, luatexbase-mcb.pdf, +% test-mcb-plain.tex test-mcb-latex.tex +% +% Unpacking: +% tex luatexbase-mcb.dtx +% Documentation: +% pdflatex luatexbase-mcb.dtx +% +%<*ignore> +\begingroup + \def\x{LaTeX2e}% +\expandafter\endgroup +\ifcase 0\ifx\install y1\fi\expandafter + \ifx\csname processbatchFile\endcsname\relax\else1\fi + \ifx\fmtname\x\else 1\fi\relax +\else\csname fi\endcsname +% +%<*install> +\input docstrip.tex + +\keepsilent +\askforoverwritefalse + +\let\MetaPrefix\relax + +\preamble + +Copyright (C) 2009 by Elie Roux + +This work is under the CC0 license. +See source file '\inFileName' for details. + +\endpreamble + +\let\MetaPrefix\DoubleperCent + +\generate{% + \usedir{doc/luatex/luatexbase}% + \file{test-mcb-plain.tex}{\from{luatexbase-mcb.dtx}{testplain}}% + \file{test-mcb-latex.tex}{\from{luatexbase-mcb.dtx}{testlatex}}% +} + +\def\MetaPrefix{-- } + +\def\luapostamble{% + \MetaPrefix^^J% + \MetaPrefix\space End of File `\outFileName'.% +} + +\def\currentpostamble{\luapostamble}% + +\generate{% + \usedir{tex/luatex/luatexbase}% + \file{mcb.lua}{\from{luatexbase-mcb.dtx}{lua}}% +} + +\obeyspaces +\Msg{************************************************************************} +\Msg{*} +\Msg{* To finish the installation you have to move the following} +\Msg{* files into a directory searched by TeX:} +\Msg{*} +\Msg{* luatexbase-mcb.sty mcb.lua} +\Msg{*} +\Msg{* Happy TeXing!} +\Msg{*} +\Msg{************************************************************************} + +\endbatchfile +% +%<*ignore> +\fi +% +%<*driver> +\documentclass{ltxdoc} +\input{lltxb-dtxstyle} +\begin{document} + \DocInput{luatexbase-mcb.dtx}% +\end{document} +% +% \fi +% +% \CheckSum{0} +% +% \CharacterTable +% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z +% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z +% Digits \0\1\2\3\4\5\6\7\8\9 +% Exclamation \! Double quote \" Hash (number) \# +% Dollar \$ Percent \% Ampersand \& +% Acute accent \' Left paren \( Right paren \) +% Asterisk \* Plus \+ Comma \, +% Minus \- Point \. Solidus \/ +% Colon \: Semicolon \; Less than \< +% Equals \= Greater than \> Question mark \? +% Commercial at \@ Left bracket \[ Backslash \\ +% Right bracket \] Circumflex \^ Underscore \_ +% Grave accent \` Left brace \{ Vertical bar \| +% Right brace \} Tilde \~} +% +% \title{The \textsf{luatexbase-mcb} package} +% \date{2009/09/18 v0.93} +% \author{Elie Roux \\ \texttt{elie.roux@telecom-bretagne.eu}} +% +% \maketitle +% +% \begin{abstract} +% This package manages the callback adding and removing, by adding +% \texttt{callback.add} and \texttt{callback.remove}, and overwriting +% \texttt{callback.register}. It also allows to create and call new callbacks. +% For an introduction on this package (among others), please refer to the +% document \texttt{luatextra-reference.pdf}. +% \par\textbf{Warning.} Currently assumes that \textsf{luatexbase-modutils} +% has been previously loaded. (This is a temporary limitation.) +% \end{abstract} +% +% \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 +% \emph{callback}), like when \TeX\ breaks likes, puts vertical spaces, etc. +% The Lua\TeX\ 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 +% \texttt{callback.\\add} 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. +% +% 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. +% +% This package also privides a way to create and call new callbacks, in +% addition to the default Lua\TeX\ callbacks. +% +% This package contains only a \texttt{.lua} file, that can be called by +% another lua script. +% +%\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}. +% +% \section{Implementation} +% +% \subsection{Lua module} +% +% Module identification. +% +% \begin{macrocode} +%<*lua> +module('luatexbase.mcb', package.seeall) +luatexbase.provides_module({ + name = "luamcallbacks", + version = 0.93, + date = "2009/09/18", + 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", + license = "CC0", +}) +% \end{macrocode} +% +% \texttt{callbacklist} is the main list, that contains the callbacks as +% keys and a table of the registered functions a values. +% +% \begin{macrocode} + +callbacklist = callbacklist or { } + +% \end{macrocode} +% +% A table with the default functions of the created callbacks. See +% \texttt{create} for further informations. +% +% \begin{macrocode} + +lua_callbacks_defaults = { } + +local format = string.format + +% \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} +% +% \begin{macrocode} + +local list = 1 +local data = 2 +local first = 3 +local simple = 4 + +% \end{macrocode} +% +% \texttt{callbacktypes} is the list that contains the callbacks as keys +% and the type (list or data) as values. +% +% \begin{macrocode} + +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, +} + +% \end{macrocode} +% +% In Lua\TeX\ version 0.43, a new callback called |process_output_buffer| +% appeared, so we enable it. +% +% \begin{macrocode} + +if tex.luatexversion > 42 then + callbacktypes["process_output_buffer"] = data +end + +% \end{macrocode} +% +% As we overwrite \texttt{callback.register}, we save it as +% \texttt{internalregister}. After that we declare some +% functions to write the errors or the logs. +% +% \begin{macrocode} + +internalregister = internalregister or callback.register + +local callbacktypes = callbacktypes + +log = log or function(...) + luatexbase.module_log('luamcallbacks', format(...)) +end + +info = info or function(...) + luatexbase.module_info('luamcallbacks', format(...)) +end + +warning = warning or function(...) + luatexbase.module_warning('luamcallbacks', format(...)) +end + +error = error or function(...) + luatexbase.module_error('luamcallbacks', format(...)) +end + +% \end{macrocode} +% +% 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} + +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 +end + +% \end{macrocode} +% +% \begin{macro}{create} +% +% 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 +% \texttt{default} is the default function to call if no function is +% registered in this callback. +% +% The created callback will behave the same way Lua\TeX\ callbacks do, you +% can add and remove functions in it. The difference is that the callback +% is not automatically called, the package developer creating a new +% callback must also call it, see next function. +% +% \begin{macrocode} + +function create(name, ctype, default) + if not name then + error(format("unable to call callback, no proper name passed", name)) + return nil + end + if not ctype or not default then + error(format("unable to create callback '%s', callbacktype or default function not specified", name)) + return nil + end + if callbacktypes[name] then + error(format("unable to create callback '%s', callback already exists", name)) + return nil + end + local temp = str_to_type(ctype) + if not temp then + error(format("unable to create callback '%s', type '%s' undefined", name, ctype)) + return nil + end + ctype = temp + lua_callbacks_defaults[name] = default + callbacktypes[name] = ctype +end + +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{call} +% +% This function calls a callback. It can only call a callback created by +% the \texttt{create} function. +% +% \begin{macrocode} + +function call(name, ...) + if not name then + error(format("unable to call callback, no proper name passed", name)) + return nil + end + if not lua_callbacks_defaults[name] then + error(format("unable to call lua callback '%s', unknown callback", name)) + return nil + end + local l = callbacklist[name] + local f + 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 + error("unknown callback type") + end + end + return f(...) +end + +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{add} +% +% 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. +% +% 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. +% +% 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 themself. Most of the time, the priority is not needed. +% +% \begin{macrocode} + +function add (name,func,description,priority) + if type(func) ~= "function" then + error("unable to add function, no proper function passed") + return + end + if not name or name == "" then + error("unable to add function, no proper callback name passed") + return + elseif not callbacktypes[name] then + error( + format("unable to add function, '%s' is not a valid callback", + name)) + return + end + if not description or description == "" then + error( + format("unable to add function to '%s', no proper description passed", + name)) + return + end + if get_priority(name, description) ~= 0 then + warning( + format("function '%s' already registered in callback '%s'", + description, name)) + end + 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 + error("unknown callback type") + end + end + end + local f = { + func = func, + description = description, + } + priority = tonumber(priority) + if not priority or priority > #l then + priority = #l+1 + elseif priority < 1 then + priority = 1 + end + if callbacktypes[name] == first and (priority ~= 1 or #l ~= 0) then + warning(format("several callbacks registered in callback '%s', only the first function will be active.", name)) + end + table.insert(l,priority,f) + log( + format("inserting function '%s' at position %s in callback list for '%s'", + description,priority,name)) +end + +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{get priority} +% +% 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. +% +% \begin{macrocode} + +function get_priority (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 +end + +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{remove} +% +% The function that removes a function from a callback. The signature is +% \texttt{mcallbacks.remove (name, description)} with \texttt{name} being +% the name of callbacks, and description the description passed to +% \texttt{mcallbacks.add}. +% +% \begin{macrocode} + +function remove (name, description) + if not name or name == "" then + error("unable to remove function, no proper callback name passed") + return + elseif not callbacktypes[name] then + error( + format("unable to remove function, '%s' is not a valid callback", + name)) + return + end + if not description or description == "" then + error( + format("unable to remove function from '%s', no proper description passed", + name)) + return + end + local l = callbacklist[name] + if not l then + error(format("no callback list for '%s'",name)) + return + end + for k,v in ipairs(l) do + if v.description == description then + table.remove(l,k) + log( + format("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 + end + end + warning( + format("unable to remove function '%s' from '%s'",description,name)) +end + +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{reset} +% +% This function removes all the functions registered in a callback. +% +% \begin{macrocode} + +function reset (name) + if not name or name == "" then + error("unable to reset, no proper callback name passed") + return + elseif not callbacktypes[name] then + error( + format("reset error, '%s' is not a valid callback", + name)) + return + end + if not lua_callbacks_defaults[name] then + internalregister(name, nil) + end + local l = callbacklist[name] + if l then + log(format("resetting callback list '%s'",name)) + callbacklist[name] = nil + end +end + +% \end{macrocode} +% +% \end{macro} +% +% 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. +% +% \begin{macro}{listhandler} +% +% \begin{macrocode} + +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 can be 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 + error(format( + "function \"%s\" returned false in callback '%s'", + f.description, name)) + end + end + return head, done + else + return head, false + end + end +end + +% \end{macrocode} +% +% \end{macro} +% +% The handler for callbacks taking datas and returning modified ones. +% +% \begin{macro}{datahandler} +% +% \begin{macrocode} + +function datahandler (name) + return function(data,...) + local l = callbacklist[name] + if l then + for _, f in ipairs(l) do + data = f.func(data,...) + end + end + return data + end +end + +% \end{macrocode} +% +% \end{macro} +% +% 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. +% +% \begin{macro}{firsthandler} +% +% \begin{macrocode} + +function firsthandler (name) + return function(...) + local l = callbacklist[name] + if l then + local f = l[1].func + return f(...) + else + return nil, false + end + end +end + +% \end{macrocode} +% +% \end{macro} +% +% Handler for simple functions that don't return anything. +% +% \begin{macro}{simplehandler} +% +% \begin{macrocode} + +function simplehandler (name) + return function(...) + local l = callbacklist[name] + if l then + for _, f in ipairs(l) do + f.func(...) + end + end + end +end + +% \end{macrocode} +% +% \end{macro} +% +% Finally we add some functions to the \texttt{callback} module, and we +% overwrite \texttt{callback.register} so that it outputs an error. +% +% \begin{macrocode} + +callback.add = add +callback.remove = remove +callback.reset = reset +callback.create = create +callback.call = call +callback.get_priority = get_priority + +callback.register = function (...) +error("function callback.register has been deleted by luamcallbacks, please use callback.add instead.") +end + +% \end{macrocode} +% +% \iffalse +% +% \fi +% +% \section{Test files} +% +% A few basic tests for Plain and LaTeX. +% +% \begin{macrocode} +%\input luatexbase-modutils.sty +%\RequirePackage{luatexbase-modutils} +%<*testplain,testlatex> +\catcode 64 11 +\luatexbase@directlua{ + require "luatexbase.mcb" + 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 + + callback.add("hpack_filter",one,"my example function one",1) + callback.add("hpack_filter",two,"my example function two",2) + callback.add("hpack_filter",three,"my example function three",1) + + callback.remove("hpack_filter","my example function three") +} +% +%\bye +%\stop +% \end{macrocode} +% +% \Finale +\endinput -- cgit v1.2.3