From 8bb985021b2f40fd3c9ef24653decafda35a3592 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Tue, 30 Jun 2020 21:21:36 +0000 Subject: cloze (30jun20) git-svn-id: svn://tug.org/texlive/trunk@55704 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/scripts/cloze/cloze.lua | 679 ++++++++++++++++-------------- 1 file changed, 361 insertions(+), 318 deletions(-) (limited to 'Master/texmf-dist/scripts/cloze/cloze.lua') diff --git a/Master/texmf-dist/scripts/cloze/cloze.lua b/Master/texmf-dist/scripts/cloze/cloze.lua index b9490354a5b..1563155a1b7 100755 --- a/Master/texmf-dist/scripts/cloze/cloze.lua +++ b/Master/texmf-dist/scripts/cloze/cloze.lua @@ -7,18 +7,20 @@ -- * _Variable_ names for _nodes_ are suffixed with `_node`, for example -- `head_node`. -- * _Variable_ names for _lengths_ (dimensions) are suffixed with --- `_length`, for example `width_length`. +-- `_length`, for example `width`. -- -- @module cloze --- __cloze.lua}__ +-- luacheck: globals node tex modules luatexbase callback + +-- __cloze.lua__ -- __Initialisation of the function tables__ -- It is good form to provide some background informations about this Lua -- module. if not modules then modules = { } end modules ['cloze'] = { - version = '1.5', + version = '1.6', comment = 'cloze', author = 'Josef Friedrich, R.-M. Huber', copyright = 'Josef Friedrich, R.-M. Huber', @@ -58,13 +60,6 @@ registry.defaults = { registry.global_options = {} registry.local_options = {} --- All those functions are stored in the table `cloze` that are --- registered as callbacks to the pre and post linebreak filters. -local cloze = {} --- In the status table are stored state information, which are necessary --- for the recursive cloze generation. -cloze.status = {} - -- The `base` table contains some basic functions. `base` is the only -- table of this Lua module that will be exported. local base = {} @@ -135,7 +130,7 @@ function nodex.insert_list(position, current, list, head_node) if not head_node then head_node = current end - for i, insert in ipairs(list) do + for _, insert in ipairs(list) do if position == 'after' then head_node, current = node.insert_after(head_node, current, insert) elseif position == 'before' then @@ -154,7 +149,7 @@ end -- -- -- --- +-- -- -- -- @@ -162,13 +157,13 @@ end -- -- -- --- +-- -- -- -- -- -- --- +-- -- -- -- @@ -267,13 +262,13 @@ end -- -- @treturn node hlist_node -- @treturn node strut_node --- @treturn node head_node +-- @treturn node prev_head_node local function insert_strut_into_hlist(hlist_node) - local head_node = hlist_node.head + local prev_head_node = hlist_node.head local kern_node = create_kern_node(0) - local strut_node = node.insert_before(hlist_node.head, head_node, kern_node) - hlist_node.head = head_node.prev - return hlist_node, strut_node, head_node + local strut_node = node.insert_before(hlist_node.head, prev_head_node, kern_node) + hlist_node.head = prev_head_node.prev + return hlist_node, strut_node, prev_head_node end --- Write kern nodes to the current node list. This kern nodes can be used @@ -283,14 +278,14 @@ function nodex.write_margin() node.write(kern) end ---- Search for a `hlist` (subtype `line`). --- --- Insert a strut node into the list if a hlist is found. +--- Search for a `hlist` (subtype `line`) and nsert a strut node into +-- the list if a hlist is found. -- -- @tparam node head_node The head of a node list. -- --- @treturn node|false Return false, if no `hlist` can --- be found. +-- @treturn node hlist_node +-- @treturn node strut_node +-- @treturn node prev_head_node local function search_hlist(head_node) while head_node do if head_node.id == node.id('hlist') and head_node.subtype == 1 then @@ -298,7 +293,6 @@ local function search_hlist(head_node) end head_node = head_node.next end - return false end --- Option handling. @@ -330,7 +324,7 @@ function registry.create_marker(index) return marker end ---- Write a marker node to \TeX's current node list. +--- Write a marker node to TeX's current node list. -- -- The argument `mode` accepts the string values `basic`, `fix` and -- `par`. The argument `position`. The argument `position` is either set @@ -410,8 +404,13 @@ end --- This function removes a given whatsit marker. -- -- It only deletes a node, if a marker is given. +-- +-- @treturn node head +-- @treturn node current function registry.remove_marker(marker) - if registry.is_marker(marker) then node.remove(marker, marker) end + if registry.is_marker(marker) then + return node.remove(marker, marker) + end end -- __Storage functions (storage)__ @@ -536,280 +535,296 @@ end --- Assembly to cloze texts. -- @section cloze_functions ---- The function `cloze.basic_make()` makes one gap. The argument `start` --- is the node, where the gap begins. The argument `stop` is the node, --- where the gap ends. -function cloze.basic_make(start_node, end_node) - local node_head = start_node - if not start_node or not end_node then - return - end - local line_width = node.dimensions( - cloze.status.hlist.glue_set, - cloze.status.hlist.glue_sign, - cloze.status.hlist.glue_order, - start_node, - end_node - ) - local line_node = nodex.insert_line(start_node, line_width) - local color_text_node = nodex.insert_list('after', line_node, {nodex.create_color('text')}) - if registry.get_value_show() then - nodex.insert_list('after', color_text_node, {create_kern_node(-line_width)}) - nodex.insert_list('before', end_node, {nodex.create_color('reset')}, node_head) - else - line_node.next = end_node.next - end_node.prev = line_node -- not line_node.prev -> line color leaks out - end - -- In some edge cases the lua callbacks get fired up twice. After the - -- cloze has been created, the start and stop whatsit markers can be - -- deleted. - registry.remove_marker(start_node) - registry.remove_marker(end_node) -end - ---- Search for a stop marker. --- --- @tparam node head_node The head of a node list. +--- Assemble a possibly muliline cloze. -- --- @treturn node The end node. -function cloze.basic_search_stop(head_node) - local end_node - while head_node do - cloze.status.continue = true - end_node = head_node - if registry.check_marker(end_node, 'basic', 'stop') then - cloze.status.continue = false - break - end - head_node = head_node.next - end - return end_node -end - ---- Search for a start marker or begin a new cloze if the value --- `cloze.status.continue` is true. +-- The corresponding LaTeX command to this Lua function is `\cloze`. +-- This function is used by other cloze TeX macros too: `\clozenol`, +-- `\clozefil` -- --- We have to find a hlist node and use its on the field `head` --- attached node list to search for a start marker. +-- @tparam node head_node_input The head of a node list. -- --- @tparam node head_node The head of a node list. -function cloze.basic_search_start(head_node) - local start_node, end_node, hlist_node - if cloze.status.continue then - hlist_node = search_hlist(head_node) - if hlist_node then - cloze.status.hlist = hlist_node - start_node = hlist_node.head +-- @treturn node The head of the node list. +local function make_basic(head_node_input) + -- This local variables are overloaded by function who + -- call each other. + local continue_cloze, search_stop + + --- The function `make_single()` makes one gap. The argument + -- `start_node` is the node where the gap begins. The argument + -- `stop_node` is the node where the gap ends. + -- + -- @tparam node start_node The node to start / begin a new cloze. + -- @tparam node stop_node The node to stop / end a new cloze. + -- @tparam node parent_node The parent node (hlist) of the start and + -- the stop node. + -- + -- @treturn node stop_node The stop node. + -- @treturn parent_node The parent node (hlist) of the stop node. + local function make_single(start_node, stop_node, parent_node) + local node_head = start_node + local line_width = node.dimensions( + parent_node.glue_set, + parent_node.glue_sign, + parent_node.glue_order, + start_node, + stop_node + ) + local line_node = nodex.insert_line(start_node, line_width) + local color_text_node = nodex.insert_list('after', line_node, {nodex.create_color('text')}) + if registry.get_value_show() then + nodex.insert_list('after', color_text_node, {create_kern_node(-line_width)}) + nodex.insert_list('before', stop_node, {nodex.create_color('reset')}, node_head) + else + line_node.next = stop_node.next + stop_node.prev = line_node -- not line_node.prev -> line color leaks out end - elseif registry.check_marker(head_node, 'basic', 'start') then - start_node = head_node - end - if start_node then - end_node = cloze.basic_search_stop(start_node) - cloze.basic_make(start_node, end_node) + -- In some edge cases the lua callbacks get fired up twice. After the + -- cloze has been created, the start and stop whatsit markers can be + -- deleted. + registry.remove_marker(start_node) + return registry.remove_marker(stop_node), parent_node end -end ---- Parse the node tree recursivley. --- --- Start and end markers could be nested deeply in the node tree. --- --- @tparam node head_node The head of a node list. -function cloze.basic_recursion(head_node) - while head_node do - if head_node.head then - cloze.status.hlist = head_node - cloze.basic_recursion(head_node.head) + --- Search for a stop marker or make a cloze up to the end of the node + -- list. + -- + -- @tparam node start_node The node to start a new cloze. + -- @tparam node parent_node The parent node (hlist) of the start node. + -- + -- @treturn head_node The fast forwarded new head of the node list. + -- @treturn parent_node The parent node (hlist) of the head node. + function search_stop(start_node, parent_node) + local head_node = start_node + local last_node + while head_node do + if registry.check_marker(head_node, 'basic', 'stop') then + return make_single(start_node, head_node, parent_node) + end + last_node = head_node + head_node = head_node.next + end + -- Make a cloze until the end of the node list. + head_node = make_single(start_node, last_node, parent_node) + if parent_node.next then + return continue_cloze(parent_node.next) else - cloze.basic_search_start(head_node) + return head_node, parent_node end - head_node = head_node.next end -end ---- The corresponding LaTeX command to this lua function is `\cloze`. --- --- @tparam node head_node The head of a node list. --- --- @treturn node The head of the node list. -function cloze.basic(head_node) - cloze.status.continue = false - cloze.basic_recursion(head_node) - return head_node -end + --- Continue a multiline cloze. + -- + -- @tparam node parent_node A parent node to search for a hlist node. + -- + -- @treturn head_node The fast forwarded new head of the node list. + -- @treturn parent_node The parent node (hlist) of the head node. + function continue_cloze(parent_node) + local hlist_node = search_hlist(parent_node) + if hlist_node then + local start_node = hlist_node.head + return search_stop(start_node, hlist_node) + end + end ---- Calculate the length of the whitespace before (`l.kern_start`) and --- after (`l.kern_stop`) the text. -function cloze.fix_length(start, stop) - local l = {} - l.width = tex.sp(registry.get_value('width')) - l.text_width = node.dimensions(start, stop) - l.align = registry.get_value('align') - if l.align == 'right' then - l.kern_start = - l.text_width - l.kern_stop = 0 - elseif l.align == 'center' then - l.half = (l.width - l.text_width) / 2 - l.kern_start = - l.half - l.text_width - l.kern_stop = l.half - else - l.kern_start = - l.width - l.kern_stop = l.width - l.text_width + --- Search for a start marker. + -- + -- @tparam node head_node The head of a node list. + -- @tparam node parent_node The parent node (hlist) of the head node. + local function search_start(head_node, parent_node) + while head_node do + if head_node.head then + search_start(head_node.head, head_node) + elseif registry.check_marker(head_node, 'basic', 'start') and + parent_node and + parent_node.id == node.id('hlist') then + -- Adds also a strut at the first position. It prepars the + -- hlist and makes it ready to build a cloze. + search_hlist(parent_node) + head_node, parent_node = search_stop(head_node, parent_node) + end + if head_node then + head_node = head_node.next + else + break + end + end end - return l.width, l.kern_start, l.kern_stop + + search_start(head_node_input) + return head_node_input end ---- The function `cloze.fix_make` generates a gap of fixed width. --- --- __Node lists__ --- --- __Show text:__ --- ---
`n.color_line``color_line_node``whatsit``pdf_colorstack`Line color
`n.line``line_node``rule``width`
`n.color_reset``color_reset_node``whatsit``pdf_colorstack`Reset color
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
`n.start``whatsit``user_definded``index`
`n.line``rule``l.width`
`n.kern_start``kern`& Depends on `align`
`n.color_text``whatsit``pdf_colorstack`Text color
`glyphs`& Text to show
`n.color_reset``whatsit``pdf_colorstack`Reset color
`n.kern_stop``kern`& Depends on `align`
`n.stop``whatsit``user_definded``index`
--- --- __Hide text:__ --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
`n.start``whatsit``user_definded``index`
`n.line``rule``l.width`
`n.stop``whatsit``user_definded``index`
+--- The corresponding LaTeX command to this Lua function is `\clozefix`. -- --- Make fixed size cloze. --- --- @param start The node, where the gap begins --- @param stop The node, where the gap ends -function cloze.fix_make(start, stop) - local l = {} -- length - local n = {} -- node - l.width, l.kern_start, l.kern_stop = cloze.fix_length(start, stop) - n.line = nodex.insert_line(start, l.width) - if registry.get_value_show() then - nodex.insert_list( - 'after', - n.line, - { - create_kern_node(l.kern_start), - nodex.create_color('text') - } - ) - nodex.insert_list( - 'before', - stop, - { - nodex.create_color('reset'), - create_kern_node(l.kern_stop) - }, - start - ) - else - n.line.next = stop.next +-- @tparam node head_node_input The head of a node list. +local function make_fix(head_node_input) + + --- Calculate the length of the whitespace before (`kern_start_length`) and + -- after (`kern_stop_length`) the text. + local function calculate_length(start, stop) + local width, kern_start_length, kern_stop_length, text_width, half_length, align + width = tex.sp(registry.get_value('width')) + text_width = node.dimensions(start, stop) + align = registry.get_value('align') + if align == 'right' then + kern_start_length = - text_width + kern_stop_length = 0 + elseif align == 'center' then + half_length = (width - text_width) / 2 + kern_start_length = - half_length - text_width + kern_stop_length = half_length + else + kern_start_length = - width + kern_stop_length = width - text_width + end + return width, kern_start_length, kern_stop_length end - registry.remove_marker(start) - registry.remove_marker(stop) -end ---- Function to recurse the node list and search after marker. --- --- @tparam node head_node The head of a node list. -function cloze.fix_recursion(head_node) - local n = {} -- node - n.start, n.stop = false - while head_node do - if head_node.head then - cloze.fix_recursion(head_node.head) + --- The function `make_single` generates a gap of fixed width. + -- + -- __Node lists__ + -- + -- __Show text:__ + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + --
`start_node``whatsit``user_definded``index`
`line_node``rule``width`
`kern_start_node``kern`& Depends on `align`
`color_text_node``whatsit``pdf_colorstack`Text color
`glyphs`& Text to show
`color_reset_node``whatsit``pdf_colorstack`Reset color
`kern_stop_node``kern`& Depends on `align`
`stop_node``whatsit``user_definded``index`
+ -- + -- __Hide text:__ + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + -- + --
`start_node``whatsit``user_definded``index`
`line_node``rule``width`
`stop_node``whatsit``user_definded``index`
+ -- + -- Make fixed size cloze. + -- + -- @param start The node, where the gap begins + -- @param stop The node, where the gap ends + local function make_single(start, stop) + local width, kern_start_length, kern_stop_length, line_node + width, kern_start_length, kern_stop_length = calculate_length(start, stop) + line_node = nodex.insert_line(start, width) + if registry.get_value_show() then + nodex.insert_list( + 'after', + line_node, + { + create_kern_node(kern_start_length), + nodex.create_color('text') + } + ) + nodex.insert_list( + 'before', + stop, + { + nodex.create_color('reset'), + create_kern_node(kern_stop_length) + }, + start + ) else - if not n.start then - n.start = registry.get_marker(head_node, 'fix', 'start') - end - if not n.stop then - n.stop = registry.get_marker(head_node, 'fix', 'stop') - end - if n.start and n.stop then - cloze.fix_make(n.start, n.stop) - n.start, n.stop = false + line_node.next = stop.next + end + registry.remove_marker(start) + registry.remove_marker(stop) + end + + --- Function to recurse the node list and search after marker. + -- + -- @tparam node head_node The head of a node list. + local function make_fix_recursion(head_node) + local start_node, stop_node = false, false + while head_node do + if head_node.head then + make_fix_recursion(head_node.head) + else + if not start_node then + start_node = registry.get_marker(head_node, 'fix', 'start') + end + if not stop_node then + stop_node = registry.get_marker(head_node, 'fix', 'stop') + end + if start_node and stop_node then + make_single(start_node, stop_node) + start_node, stop_node = false, false + end end + head_node = head_node.next end - head_node = head_node.next end -end ---- The corresponding LaTeX command to this Lua function is `\clozefix`. --- --- @tparam node head_node The head of a node list. -function cloze.fix(head_node) - cloze.fix_recursion(head_node) - return head_node + make_fix_recursion(head_node_input) + return head_node_input end --- The corresponding LaTeX environment to this lua function is @@ -822,7 +837,7 @@ end -- -- -- --- +-- -- -- -- @@ -830,19 +845,19 @@ end -- -- -- --- +-- -- -- --- +-- -- -- --- +-- -- -- --- +-- -- -- --- +-- -- -- -- @@ -854,13 +869,13 @@ end -- -- -- --- +-- -- -- -- -- -- --- +-- -- -- -- @@ -873,7 +888,7 @@ end --
`n.strut``strut_node``kern`width = 0
`n.line``line_node``rule``l.width` (Width from hlist)`width` (Width from hlist)
`n.kern``kern_node``kern``-l.width``-width`
`n.color_text``color_text_node``whatsit``pdf_colorstack`Text colorText to show
`n.tail``tail_node``glyph`Last glyph in hlist
`n.color_reset``color_reset_node``whatsit``pdf_colorstack`Reset color
-- -- --- +-- -- -- -- @@ -881,31 +896,30 @@ end -- -- -- --- +-- -- -- --- +-- -- -- --
`n.strut``strut_node``kern`width = 0
`n.line``line_node``rule``l.width` (Width from hlist)`width` (Width from hlist)
-- -- @tparam node head_node The head of a node list. -function cloze.par(head_node) - local l = {} -- length - local n = {} -- node - for hlist in node.traverse_id(node.id('hlist'), head_node) do - for whatsit in node.traverse_id(node.id('whatsit'), hlist.head) do +local function make_par(head_node) + local strut_node, line_node, width + for hlist_node in node.traverse_id(node.id('hlist'), head_node) do + for whatsit in node.traverse_id(node.id('whatsit'), hlist_node.head) do registry.get_marker(whatsit, 'par', 'start') end - l.width = hlist.width - hlist, n.strut, n.head = insert_strut_into_hlist(hlist) - n.line = nodex.insert_line(n.strut, l.width) + width = hlist_node.width + hlist_node, strut_node, _ = insert_strut_into_hlist(hlist_node) + line_node = nodex.insert_line(strut_node, width) if registry.get_value_show() then nodex.insert_list( 'after', - n.line, + line_node, { - create_kern_node(-l.width), + create_kern_node(-width), nodex.create_color('text') } ) @@ -915,53 +929,82 @@ function cloze.par(head_node) {nodex.create_color('reset')} ) else - n.line.next = nil + line_node.next = nil end end return head_node end +--- +-- @tparam string callback_name The name of a callback +-- @tparam function func A function to register for the callback +-- @tparam string description Only used in LuaLatex +local function register_callback(callback_name, func, description) + if luatexbase then + luatexbase.add_to_callback( + callback_name, + func, + description + ) + else + callback.register(callback_name, func) + end +end + +--- +-- @tparam string callback_name The name of a callback +-- @tparam string description Only used in LuaLatex +local function unregister_callback(callback_name, description) + if luatexbase then + luatexbase.remove_from_callback( + callback_name, + description + ) + else + callback.register(callback_name, nil) + end +end + --- Basic module functions. -- The `base` table contains functions which are published to the -- `cloze.sty` file. -- @section base ---- This function registers the functions `cloze.par`, `cloze.basic` and --- `cloze.fix` the Lua callbacks. +--- This function registers the functions `make_par`, `make_basic` and +-- `make_fix` the Lua callbacks. -- --- `cloze.par` and `cloze.basic` are registered to the callback --- `post_linebreak_filter` and `cloze.fix` to the callback +-- `make_par` and `make_basic` are registered to the callback +-- `post_linebreak_filter` and `make_fix` to the callback -- `pre_linebreak_filter`. The argument `mode` accepts the string values -- `basic`, `fix` and `par`. A special treatment is needed for clozes in -- display math mode. The `post_linebreak_filter` is not called on -- display math formulas. I’m not sure if the `pre_output_filter` is the -- right choice to capture the display math formulas. function base.register(mode) - local basic if mode == 'par' then - luatexbase.add_to_callback( + register_callback( 'post_linebreak_filter', - cloze.par, + make_par, mode ) return true end if not base.is_registered[mode] then if mode == 'basic' then - luatexbase.add_to_callback( + register_callback( 'post_linebreak_filter', - cloze.basic, + make_basic, mode ) - luatexbase.add_to_callback( + register_callback( 'pre_output_filter', - cloze.basic, + make_basic, mode ) elseif mode == 'fix' then - luatexbase.add_to_callback( + register_callback( 'pre_linebreak_filter', - cloze.fix, + make_fix, mode ) else @@ -978,12 +1021,12 @@ end -- `basic`, `fix` and `par`. function base.unregister(mode) if mode == 'basic' then - luatexbase.remove_from_callback('post_linebreak_filter', mode) - luatexbase.remove_from_callback('pre_output_filter', mode) + unregister_callback('post_linebreak_filter', mode) + unregister_callback('pre_output_filter', mode) elseif mode == 'fix' then - luatexbase.remove_from_callback('pre_linebreak_filter', mode) + unregister_callback('pre_linebreak_filter', mode) else - luatexbase.remove_from_callback('post_linebreak_filter', mode) + unregister_callback('post_linebreak_filter', mode) end end -- cgit v1.2.3