texlive[62525] Master/texmf-dist: lua-widow-control (8mar22)

commits+karl at tug.org commits+karl at tug.org
Tue Mar 8 22:25:59 CET 2022


Revision: 62525
          http://tug.org/svn/texlive?view=revision&revision=62525
Author:   karl
Date:     2022-03-08 22:25:59 +0100 (Tue, 08 Mar 2022)
Log Message:
-----------
lua-widow-control (8mar22)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/luatex/lua-widow-control/README.md
    trunk/Master/texmf-dist/doc/luatex/lua-widow-control/lua-widow-control.pdf
    trunk/Master/texmf-dist/source/luatex/lua-widow-control/lwc-documentation.mkxl
    trunk/Master/texmf-dist/source/luatex/lua-widow-control/lwc-documentation.tex
    trunk/Master/texmf-dist/tex/context/third/lua-widow-control/t-lua-widow-control.mkiv
    trunk/Master/texmf-dist/tex/context/third/lua-widow-control/t-lua-widow-control.mkxl
    trunk/Master/texmf-dist/tex/lualatex/lua-widow-control/lua-widow-control.sty
    trunk/Master/texmf-dist/tex/luatex/lua-widow-control/lua-widow-control.lua
    trunk/Master/texmf-dist/tex/luatex/lua-widow-control/lua-widow-control.tex
    trunk/Master/texmf-dist/tex/optex/lua-widow-control/lua-widow-control.opm

Added Paths:
-----------
    trunk/Master/texmf-dist/tex/lualatex/lua-widow-control/lua-widow-control-2022-02-22.sty

Modified: trunk/Master/texmf-dist/doc/luatex/lua-widow-control/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/lua-widow-control/README.md	2022-03-08 21:25:37 UTC (rev 62524)
+++ trunk/Master/texmf-dist/doc/luatex/lua-widow-control/README.md	2022-03-08 21:25:59 UTC (rev 62525)
@@ -35,6 +35,11 @@
 |OpTeX        |`\load[lua-widow-control]`      |
 
 
+Contributing
+------------
+
+Please see [`contributing.md`](https://github.com/gucci-on-fleek/lua-widow-control/blob/master/.github/contributing.md).
+
 Licence
 -------
 
@@ -43,4 +48,4 @@
 Please note that a compiled document is absolutely **not** considered to be an "Executable Form" as defined by the MPL. The use of lua-widow-control in a document does not place **any** obligations on the document's author or distributors. The MPL and CC-BY-SA licenses **only** apply to you if you distribute the lua-widow-control source code or documentation. 
 
 ---
-_v1.1.6 (2022-02-22)_ <!--%%version %%date-->
+_v2.0.0 (2022-03-07)_ <!--%%version %%dashdate-->

Modified: trunk/Master/texmf-dist/doc/luatex/lua-widow-control/lua-widow-control.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/source/luatex/lua-widow-control/lwc-documentation.mkxl
===================================================================
--- trunk/Master/texmf-dist/source/luatex/lua-widow-control/lwc-documentation.mkxl	2022-03-08 21:25:37 UTC (rev 62524)
+++ trunk/Master/texmf-dist/source/luatex/lua-widow-control/lwc-documentation.mkxl	2022-03-08 21:25:59 UTC (rev 62525)
@@ -138,6 +138,8 @@
     after={\blank[medium]}
 ]
 
+\define[1]\meta{\m{\langle}\italic{#1}\m{\rangle}}
+
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %%%%% Section Commands %%%%%
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -183,6 +185,7 @@
     \stopcurrentlistentrywrapper
 
     \par
+    \nobreak
 
     \setbox0=\vbox{\framedtext[ % Align the subsection titles under the section title
         frame=off,
@@ -199,6 +202,8 @@
     \else
         \box0
     \fi
+    
+    \goodbreak
 \stoptexdefinition
 
 \newif\iffirstsubsection

Modified: trunk/Master/texmf-dist/source/luatex/lua-widow-control/lwc-documentation.tex
===================================================================
--- trunk/Master/texmf-dist/source/luatex/lua-widow-control/lwc-documentation.tex	2022-03-08 21:25:37 UTC (rev 62524)
+++ trunk/Master/texmf-dist/source/luatex/lua-widow-control/lwc-documentation.tex	2022-03-08 21:25:59 UTC (rev 62525)
@@ -24,6 +24,7 @@
 \def\waos/{widows and orphans}
 \def\wao/{widow and orphan}
 \def\woo/{widow or orphan}
+\def\plainop/{Plain~\TeX{}/Op\TeX{}}
 \let\q=\quotation
 
 \def\titlecite[#1]{\cite[title][#1]\cite[#1]}
@@ -37,7 +38,7 @@
 \startdocument[
     title=lua-widow-control,
     author=Max Chernoff,
-    version=1.1.6, %%version
+    version=2.0.0, %%version
     github=https://github.com/gucci-on-fleek/lua-widow-control,
     ctan=https://www.ctan.org/pkg/lua-widow-control,
 ]
@@ -45,7 +46,7 @@
 \Lwc/ is a Plain~\TeX/\LaTeX/\ConTeXt{}/Op\TeX{} package that removes \waos/ without any user intervention. Using the power of \LuaTeX{}, it does so \emph{without} stretching any glue or shortening any pages or columns. Instead, \lwc/ automatically lengthens a paragraph on a page or column where a \woo/ would otherwise occur. 
 
 \section{Quick Start}
-Ensure that your \TeX~Live/Mik\TeX{} distribution is up-to-date. Then, \LaTeX{} users just need to place \inlineTEX{\usepackage{lua-widow-control}} in the preamble of your document. For more details, see the \goto{Installation and Usage sections}[sec:install].
+Ensure that your \TeX~Live/Mik\TeX{} distribution is up-to-date. Then, \LaTeX{} users just need to place \inlineTEX{\usepackage{lua-widow-control}} in the preamble of your document. For more details, see the \goto{Usage sections}[sec:usage].
 
 \subject{Contents}
 \placecontent[criterium=all]
@@ -157,7 +158,7 @@
 
 \Lwc/ automatically finds the \q{best} paragraph to stretch, so the increase in interword spaces should almost always be minimal.
 
-\section[sec:install]{Installation}
+\section{Installation}
 Most up-to-date \TeX~Live and Mik\TeX{} systems should already have \lwc/ installed. However, a manual installation may occasionally be required. 
 
 \subsection{\TeX~Live}
@@ -190,8 +191,12 @@
 \Lwc/ requires \LuaTeX{} ($\ge$ 0.85) and the most recent version of \sans{luatexbase} (2015/10/04). Any version of \TeX~Live $\ge$ 2016 will meet these requirements.
 
 \subsection{\LaTeX{}}
-\Lwc/ requires \LuaTeX{} ($\ge$ 0.85), \LaTeX{} ($\ge$ 2015/01/01), \sans{microtype} (any version), and \sans{etoolbox} (any version). Any version of \TeX~Live $\ge$ 2016 will meet these requirements.
+\Lwc/ requires \LuaTeX{} ($\ge$ 0.85), \LaTeX{} ($\ge$ 2020/10/01), and \sans{microtype} (any version). Any version of \TeX~Live $\ge$ 2021 will meet these requirements.
 
+\Lwc/ also supports a \q{legacy} mode for older \LaTeX{} kernels. This uses an older version of the \LaTeX{} code while still using the most recent Lua code. This mode requires \LuaTeX{} ($\ge$ 0.85), \LaTeX{} ($\ge$ 2015/01/01), \sans{microtype} (any version), and \sans{etoolbox} (any version). Any version of \TeX~Live $\ge$ 2016 will meet these requirements.
+
+Please note that when running in legacy mode, you cannot use the key--value interface. Instead, you should follow the \q{Plain~\TeX{}} interface.
+
 \subsection{\ConTeXt{}}
 \Lwc/ supports both \ConTeXt{} \acronym{MKXL} (\LuaMetaTeX{}) and \ConTeXt{} \acronym{MKIV} (\LuaTeX{}).
 
@@ -199,88 +204,221 @@
 \Lwc/ works with any version of Op\TeX{} and has no dependencies.
 
 
-\section{Loading the Package}
+\section[sec:usage]{Loading the Package}
 
-\subsection{Plain \TeX}
+\setupTABLE[frame=off]
+\setupTABLE[column][first][roffset=1.5em]
+\setupTABLE[row][first][toffset=0.25ex]
 
-\inlineTEX{\input  lua-widow-control}
+\startTABLE
+    \NC Plain~\TeX{}
+    \NC\inlineTEX{\input  lua-widow-control}
+    \NC\NR
+    \NC \LaTeX{}
+    \NC\inlineTEX{\usepackage{lua-widow-control}}
+    \NC\NR
+    \NC \ConTeXt{}
+    \NC\inlineTEX{\usemodule[lua-widow-control]}
+    \NC\NR
+    \NC Op\TeX{}
+    \NC\inlineTEX{\load[lua-widow-control]}
+    \NC\NR
+\stopTABLE
 
-\subsection{\LaTeX}
+\section{Options}
 
-\inlineTEX{\usepackage{lua-widow-control}}
+\Lwc/ is automatically enabled with the default settings as soon as you load it. Most users should not need to configure \lwc/; however, the packages provides a few commands.
 
-\subsection{\ConTeXt}
+\subsection{Overview}
 
-\inlineTEX{\usemodule[lua-widow-control]}
+\LaTeX{} users can set the options either when loading the package (\tex{usepackage[\meta{options}]\{lua-widow-control\}}) or at any point using \tex{lwcsetup\{\meta{options}\}}.
 
-\subsection{Op\TeX{}}
+\ConTeXt{} users should always use the \tex{setuplwc[\meta{options}]} command.
 
-\inlineTEX{\load[lua-widow-control]}
+Plain~\TeX{} and Op\TeX{} are a little different. Some options have commands provided (i.e., \tex{lwcemergencystretch=\meta{dimension}}), while others must be set manually (i.e., \tex{directlua\{lwc.debug = true\}}).
 
-\section{Columns}
+Also, please note that not all commands are provided for all formats.
 
-Since \TeX{} implements column breaking and page breaking through the same internal mechanisms, \lwc/ should remove \waos/ between columns just as well as it does with \waos/ between pages. This has been tested with the standard \LaTeX{} class option \type{twocolumn} and the two-column output routine from Chapter~23 of \cite[title][texbook].\cite[texbook] \Lwc/ should presumably work with any other multi-column implementation; however, due to the diversity and complexity of output~routines, this cannot be guaranteed.
+\subsection{Enabling}
 
-\section{Advanced Usage}
+\Lwc/ is enabled by default as soon as you load it. Nevertheless, you may need to explicitly reenable it if you have previously disabled it.
 
-\Lwc/ is automatically enabled with the default settings as soon as you load it. Most users should not need to configure \lwc/; however, the packages does provide a few commands.
-
-\setupTABLE[frame=off]
-
-\subsection{Plain \TeX{}}
 \startTABLE
-\NC Enable (default) \NC \inlineTEX{\lwcenable}  \NC\NR
-\NC Disable \NC \inlineTEX{\lwcdisable}  \NC\NR
-\NC \estretch/ \NC \inlineTEX{\lwcemergencystretch=3em} \NC\NR
-\NC Selectively Disable \NC \inlineTEX{\lwcdisablecmd{\cmd}}  \NC\NR
+    \NC \plainop/
+    \NC\inlineTEX{\lwcenable}
+    \NC\NR
+    \NC \LaTeX{}
+    \NC\inlineTEX{\lwcsetup{enable}}
+    \NC\NR
+    \NC \ConTeXt{}
+    \NC\inlineTEX{\setuplwc[state = start]}
+    \NC\NR
 \stopTABLE
 
-\subsection{\LaTeX{}}
-\startTABLE
-\NC Enable (default) \NC \inlineTEX{\lwcenable}  \NC\NR
-\NC Disable \NC \inlineTEX{\lwcdisable}  \NC\NR
-\NC \estretch/ \NC \inlineTEX{\setlength{\lwcemergencystretch}{3em}}  \NC\NR
-\NC Selectively Disable \NC \inlineTEX{\lwcdisablecmd{\cmd}}  \NC\NR
-\stopTABLE
+\subsection{Disabling}
 
-\subsection{\ConTeXt{}}
 \startTABLE
-\NC Enable (default) \NC \inlineTEX{\setuplwc[state = start]}  \NC\NR
-\NC Disable \NC \inlineTEX{\setuplwc[state = stop]}  \NC\NR
-\NC \estretch/ \NC \inlineTEX{\setuplwc[emergencystretch = 3em]} \NC\NR
-\NC Selectively Disable \NC \inlineTEX{\prependtoks\lwc at patch@pre \to\everybeforefoo} \NC\NR
-\NC          \NC \inlineTEX{\prependtoks\lwc at patch@post\to\everyafterfoo} \NC\NR
+    \NC \plainop/
+    \NC\inlineTEX{\lwcdisable}
+    \NC\NR
+    \NC \LaTeX{}
+    \NC\inlineTEX{\lwcsetup{disable}}
+    \NC\NR
+    \NC \ConTeXt{}
+    \NC\inlineTEX{\setuplwc[state = stop]}
+    \NC\NR
 \stopTABLE
 
-\subsection{Op\TeX{}}
+\subsection{Strict Mode}
+
+By default, \lwc/ takes all possible measures to remove \waos/. This normally works out pretty well; however, sometimes these measures may be a little more aggressive than certain users want. 
+
+\Lwc/ offers a \q{strict} mode that will only make modification to the page that are near-imperceptible to remove \waos/.
+
 \startTABLE
-\NC Enable (default) \NC \inlineTEX{\lwcenable}  \NC\NR
-\NC Disable \NC \inlineTEX{\lwcdisable}  \NC\NR
-\NC \estretch/ \NC \inlineTEX{\lwcemergencystretch=3em} \NC\NR
-\NC Selectively Disable \NC \emph{Not Implemented} \NC\NR
+    \NC \LaTeX{}
+    \NC\inlineTEX{\lwcsetup{strict}}
+    \NC\NR
 \stopTABLE
 
+Internally, this sets {\tt emergencystretch = 0pt}, {\tt max-cost = 2500}, and {\tt nobreak = warn}.
+
 \subsection{\estretch/}
 
 You can configure the \estretch/ used when stretching a paragraph. The default value is 3~em. 
 
-\Lwc/ will only use use the \estretch/ when it cannot lengthen a paragraph in any other way, so it is fairly safe to set this to a large value. \TeX{} still accumulates badness when \estretch/ is used, so its pretty rare that a paragraph that requires any \estretch/ will actually be used on the page.
+\Lwc/ will only use the \estretch/ when it cannot lengthen a paragraph in any other way, so it is fairly safe to set this to a large value. \TeX{} still accumulates badness when \estretch/ is used, so it's pretty rare that a paragraph that requires any \estretch/ will actually be used on the page.
 
+\startTABLE
+    \NC \plainop/
+    \NC\tex{lwcemergencystretch=\meta{dimension}}
+    \NC\NR
+    \NC \LaTeX{}
+    \NC\tex{lwcsetup\{emergencystretch = \meta{dimension}\}}
+    \NC\NR
+    \NC \ConTeXt{}
+    \NC\tex{setuplwc[emergencystretch = \meta{dimension}]}
+    \NC\NR
+\stopTABLE
+
 \subsection{Selectively Disabling}
 Sometimes, you may want to disable \lwc/ for certain commands where stretching is undesirable. For example, you typically wouldn't want section headings to be stretched. 
 
-You could just disable then reenable \lwc/ every time that you use the command; however, \lwc/ provides a convenience macro that will do this automatically for you. Place \inlineTEX{\lwcdisablecmd{\cmd}} in the preamble, and \lwc/ will not expand any arguments of \tex{cmd} in the document.
+You could just disable then reenable \lwc/ every time that you use the command; however, \lwc/ provides a convenience macro that will do this automatically for you.
 
-\Lwc/ automatically patches the default \LaTeX{}, \ConTeXt{}, and Plain~\TeX{} section commands, so you shouldn't need to patch these yourself; however, \lwc/ does \bold{not} patch the non-standard section commands provided by \sans{memoir}, \sans{\acronym{KOMA}-script}, \sans{titlesec}, and others. You'll need to patch these yourself.
+\Lwc/ automatically patches the default \LaTeX{}, \ConTeXt{}, and Plain~\TeX{} section commands, so you shouldn't need to patch these yourself; however, \lwc/ does \bold{not} patch the non-standard section commands provided by \sans{memoir}, \sans{\acronym{KOMA}-script}, \sans{titlesec}, Op\TeX{}, and others. You'll need to add these yourself.
 
-Under \ConTeXt{}, you need to use a different method to selectively disable \lwc/. Instead of using \tex{lwcdisablecmd}, you should use one of the preexisting \tex{everyfoo} hooks as shown in the table above since patching commands is rarely advisable in \ConTeXt{} 
+\startTABLE
+    \NC Plain \TeX{}
+    \NC \tex{lwcdisablecmd\{\meta{\backslash macro}\}}
+    \NC\NR
+    \NC \LaTeX{}
+    \NC \tex{lwcsetup\{disablecmds = \{\meta{\backslash macroone}, \meta{\backslash macrotwo}\}\}}
+    \NC\NR
+    \NC \ConTeXt{}
+    \NC \tex{prependtoks\backslash lwc at patch@pre\backslash to\backslash everybefore\meta{hook}}
+    \NC\NR\NC\NC \tex{prependtoks\backslash lwc at patch@post\backslash to\backslash everyafter\meta{hook}}
+    \NC\NR
+\stopTABLE
 
-No commands are provided for selectively disabling \lwc/ with Op\TeX{}. Users must implement this functionality themselves.
+\subsection{Widow and Orphan Penalties}
 
+You can also manually adjust the penalties that \TeX{} assigns to \waos/. Usually, the defaults are fine, but advanced users may want to change them.
+
+\startTABLE
+    \NC \plainop/
+    \NC\tex{widowpenalty=\meta{integer}}
+    \NC\NR\NC\NC\tex{clubpenalty=\meta{integer}}
+    \NC\NR
+    \NC \LaTeX{}
+    \NC\tex{lwcsetup\{widowpenalty = \meta{integer}\}}
+    \NC\NR\NC\NC\tex{lwcsetup\{orphanpenalty = \meta{integer}\}}
+    \NC\NR
+    \NC \ConTeXt{}
+    \NC\tex{setuplwc[widowpenalty = \meta{integer}]}
+    \NC\NR\NC\NC\tex{setuplwc[orphanpenalty = \meta{integer}]}
+    \NC\NR
+\stopTABLE
+
+Some suitable integers:
+
+\startTABLE
+    \NC Ignore widows/orphans \NC 0 \NC\NR
+    \NC Default \NC 1 \NC\NR
+    \NC Disable \lwc/ \NC 10\,000 \NC\NR
+\stopTABLE
+
+\subsection{\tex{nobreak} Behaviour}
+
+When \lwc/ encounters an orphan, it removes it by removing the orphaned line to the next page. However, sometimes, an orphan is immediately preceded by a section heading or a \tex{nobreak} command. By moving the orphan to the next page, you would naïvely separate a section (or other such material) from the line that follows. This really ought to be avoided, so \lwc/ provides some options to avoid this.
+
+\startTABLE
+    \NC \plainop/
+    \NC\tex{directlua\{lwc.nobreak_behaviour = "\meta{value}"\}}
+    \NC\NR
+    \NC \LaTeX{}
+    \NC\tex{lwcsetup\{nobreak = \meta{value}\}}
+    \NC\NR
+    \NC \ConTeXt{}
+    \NC\tex{setuplwc[nobreak = \meta{value}]}
+    \NC\NR
+\stopTABLE
+
+The default value, \type{keep}, \emph{keep}s the section heading with the orphan by moving both to the next page.
+
+The value \type{split} \emph{split}s up the section heading and the orphan by moving the orphan to the next page while leaving the heading behind. This is usually a bad idea.
+
+The value \type{warn} causes \lwc/ to give up on the page and do nothing, leaving an orphaned line. \Lwc/ \emph{warn}s the user so that they can manually remove the orphan.
+
+\subsection{Maximum Cost}
+
+When \TeX{} breaks a paragraph, it scores it by the number of \q{demerits}. The demerits for a paragraph is the sum of the squared badnesses for each line, plus any \q{additional demerits} added for any other reason. The badness for a line is proportional the cube of the glue stretch ratio, so demerits grow with the sixth power of glue stretch.
+
+To choose the \q{best} paragraph on the page, \lwc/ uses a \q{cost function} $C$ that is initially defined as \startformula
+    C = \frac{d}{2\sqrt{l}}
+\stopformula where $d$ is the total demerits of the paragraph, and $l$ is the number of lines in the paragraph. 
+
+By default, \lwc/ just selects the paragraph on the page with the lowest cost; however, you can configure it to only select paragraphs below a selected cost. If there aren't any paragraphs below the set threshold, then \lwc/ won't remove the \woo/ and will instead issue a warning.
+
+\startTABLE
+    \NC \plainop/
+    \NC\tex{lwcmaxcost=\meta{integer}}
+    \NC\NR
+    \NC \LaTeX{}
+    \NC\tex{lwcsetup\{max-cost = \meta{integer}\}}
+    \NC\NR
+    \NC \ConTeXt{}
+    \NC\tex{setuplwc[maxcost = \meta{integer}]}
+    \NC\NR
+\stopTABLE
+
+Very advanced users may also set a custom cost function by redefining the \inlineLUA{lwc.paragraph_cost(demerits, lines)} function.
+
+\subsection{Debug Mode}
+
+\Lwc/ offers a \q{debug} mode that prints extra information in the log files. This may be helpful to understand how \lwc/ is processing paragraphs and pages.
+
+\startTABLE
+    \NC \plainop/
+    \NC\inlineTEX{\directlua{lwc.debug = true}}
+    \NC\NR\NC\NC\inlineTEX{\directlua{lwc.debug = false}}
+    \NC\NR
+    \NC \LaTeX{}
+    \NC\inlineTEX{\lwcsetup{debug = true}}
+    \NC\NR\NC\NC\inlineTEX{\lwcsetup{debug = false}}
+    \NC\NR
+    \NC \ConTeXt{}
+    \NC\inlineTEX{\setuplwc[state = start]}
+    \NC\NR\NC\NC\inlineTEX{\setuplwc[state = stop]}
+    \NC\NR
+\stopTABLE
+
+\section{Columns}
+
+Since \TeX{} implements column breaking and page breaking through the same internal mechanisms, \lwc/ should remove \waos/ between columns just as well as it does with \waos/ between pages. This has been tested with the standard \LaTeX{} class option \type{twocolumn} and the two-column output routine from Chapter~23 of \cite[title][texbook].\cite[texbook] \Lwc/ should presumably work with any other multi-column implementation; however, due to the diversity and complexity of output~routines, this cannot be guaranteed.
+
 \section[sec:issues]{Known Issues}
 \startitemize
-    \item \Lwc/ will not expand the first paragraph of a document.
-
     \item \Lwc/ will rarely fail to correctly move the last line on an expanded page to the next page in documents with \emph{very} small paper sizes.
 
     \item When a 3-line paragraph is at the end of a page forming a widow, \lwc/ will remove the widow; however, it will leave an orphan. This issue is inherent to any process that removes widows through paragraph expansion and is thus unavoidable. Orphans are better than widows, so this is still an improvement.
@@ -289,7 +427,9 @@
 
     \item When running under \LuaMetaTeX, the log may be filled with lines like \q{\tt luatex warning  > tex: left parfill skip is gone}. This is harmless and can be ignored. % I have no idea how to fix this. The LMTX manual has "\parfillleftskip" in the indices, but that's all that I can find about it.
 
-    \item \Lwc/ will rarely raise a \q{\tt Circular node list detected!} warning. This occurs when the replacement paragraph node list loops back on itself. Since there is no \q{end} to the paragraph, \lwc/ cannot splice the paragraph into the page. The only reasonable option in this scenario is to stop processing the page, without removing the \woo/.
+    \item \Lwc/ will rarely raise a \q{\tt Circular node list detected!} warning. This occurs when the replacement paragraph node list loops back on itself. This is usually harmless, but can rarely cause the entire compile to completely fail. 
+
+    \item \TeX{} may warn you about over or underfull vboxes on pages where \lwc/ removed a \woo/. This is a false alarm and can be ignored.
 \stopitemize
 
 \section{The Algorithm}
@@ -307,7 +447,7 @@
 
 First, \lwc/ analyzes the \openalty/ of the page or column. If the page was broken at a \woo/, the \openalty/ will equal either \tex{widow\allowbreak penalty} or \tex{orphan\allowbreak penalty}. If the \openalty/ is not indicative of a \woo/, \lwc/ will stop and return \tex{box255} unmodified.
 
-At this point, we know that we have a \woo/ on the page, so we must lengthen the page by 1~line. We iterate through the list of saved paragraphs to find the lengthened paragraph with the least demerits. Once we've selected a paragraph to replace, we can now traverse through the page to find the original version of this paragraph that \TeX{} originally typeset. Once we find the original paragraph, we \q{splice} the lengthened paragraph in the place of the original.
+At this point, we know that we have a \woo/ on the page, so we must lengthen the page by 1~line. We iterate through the list of saved paragraphs to find the lengthened paragraph with the least cost. Once we've selected a paragraph to replace, we can now traverse through the page to find the original version of this paragraph that \TeX{} originally typeset. Once we find the original paragraph, we \q{splice} the lengthened paragraph in the place of the original.
 
 Since the page is now 1~line longer than it was before, we pull the last line off of the page to bring it back to its original length. We place the line onto the top of the \emph{recent contributions} list so that it is added to the start of the next page. Now, we can return the new, widow-free page to the output routine.
 
@@ -353,14 +493,10 @@
 \typeTEXfile{../source/lua-widow-control.sty}
 
 \def\module{\tex{module}}
-\subsection{t-lua-widow-control.mkxl}
+\subsection{t-lua-widow-control.mkxl/mkiv}
 
 \typeTEXfile{../source/t-lua-widow-control.mkxl}
 
-\subsection{t-lua-widow-control.mkiv}
-
-\typeTEXfile{../source/t-lua-widow-control.mkiv}
-
 \subsection{lua-widow-control.opm}
 
 \typeTEXfile{../source/lua-widow-control.opm}

Modified: trunk/Master/texmf-dist/tex/context/third/lua-widow-control/t-lua-widow-control.mkiv
===================================================================
--- trunk/Master/texmf-dist/tex/context/third/lua-widow-control/t-lua-widow-control.mkiv	2022-03-08 21:25:37 UTC (rev 62524)
+++ trunk/Master/texmf-dist/tex/context/third/lua-widow-control/t-lua-widow-control.mkiv	2022-03-08 21:25:59 UTC (rev 62525)
@@ -1,10 +1,10 @@
 %D \module
 %D   [     file=t-lua-widow-control,
-%D      version=1.1.6, %%version
+%D      version=2.0.0, %%version
 %D        title=lua-widow-control,
 %D     subtitle=\ConTeXt module for lua-widow-control,
 %D       author=Max Chernoff,
-%D         date=2022-02-22, %%date
+%D         date=2022-03-07, %%dashdate
 %D    copyright=Max Chernoff,
 %D      license=MPL-2.0+,
 %D          url=https://github.com/gucci-on-fleek/lua-widow-control]
@@ -15,9 +15,9 @@
 
 \installcommandhandler \????lwc {lwc} \????lwc
 
-\newdimen\lwcemergencystretch
+\newdimen\lwc_emergency_stretch
 \appendtoks
-    \lwcemergencystretch=\lwcparameter{emergencystretch}
+    \lwc_emergency_stretch=\lwcparameter{emergencystretch}
 \to\everysetuplwc
 
 \appendtoks
@@ -28,24 +28,50 @@
     }
 \to\everysetuplwc
 
+\appendtoks
+    \doifelse{\lwcparameter{debug}}\v!start{
+        \ctxlua{lwc.debug = true}
+    }{
+        \ctxlua{lwc.debug = false}
+    }
+\to\everysetuplwc
+
+\appendtoks
+    \ctxlua{lwc.nobreak_behaviour = "\lwcparameter{nobreak}"}
+\to\everysetuplwc
+
+\newcount\lwc_max_cost
+\appendtoks
+    \lwc_max_cost=\lwcparameter{maxcost}
+\to\everysetuplwc
+
+\appendtoks
+    % We can't just set the penalties because they will be reset automatically
+    % at \\starttext.
+    \startsetups[*default]
+        \clubpenalty=\lwcparameter{orphanpenalty}
+        \widowpenalty=\lwcparameter{widowpenalty}
+        \displaywidowpenalty=\lwcparameter{widowpenalty}
+    \stopsetups
+
+    \setups[*default]
+\to\everysetuplwc
+
 \define\iflwc{\ctxlua{lwc.if_lwc_enabled()}}
 
 \ctxloadluafile{lua-widow-control}
 
-\setuplwc[emergencystretch=3em, \c!state=\v!start]
+\setuplwc[
+    emergencystretch=3em,
+    \c!state=\v!start,
+    debug=\v!stop,
+    orphanpenalty=1,
+    widowpenalty=1,
+    nobreak=keep,
+    maxcost=2147483647,
+]
 
-% We can't just set the penalties because they will be reset automatically
-% at \\starttext.
-\startsetups[*default]
-    \clubpenalty=1
-    \widowpenalty=1
-    \displaywidowpenalty=0
-    \interlinepenalty=0
-    \brokenpenalty=0
-\stopsetups
 
-\setups[*default]
-
 % Here, we enable font expansion/contraction. It isn't strictly necessary for
 % \lwc/'s functionality; however, it is required for the
 % lengthened paragraphs to not have terrible spacing. 
@@ -55,23 +81,23 @@
 % Expansion of some parts of the document, such as section headings, is quite
 % undesirable, so we'll disable \lwc/ for certain commands.
 % We should only reenable \lwc/ at the end if it was already enabled.
-\newif\iflwc at should@reenable
+\newif\iflwc_should_reenable
 
-\define\lwc at patch@pre{%
+\define\lwc_patch_pre{%
     \iflwc%
-        \lwc at should@reenabletrue%
+        \lwc_should_reenabletrue%
         \setuplwc[state=stop]%
     \else%
-        \lwc at should@reenablefalse
+        \lwc_should_reenablefalse
     \fi%
 }
 
-\define\lwc at patch@post{\iflwc at should@reenable%
+\define\lwc_patch_post{\iflwc_should_reenable%
     \setuplwc[state=start]%
 \fi}
 
-\prependtoks\lwc at patch@pre\to\everybeforesectionheadhandle % Sectioning
-\prependtoks\lwc at patch@post\to\everyaftersectionheadhandle
+\prependtoks\lwc_patch_pre\to\everybeforesectionheadhandle % Sectioning
+\prependtoks\lwc_patch_post\to\everyaftersectionheadhandle
 
 \protect
 \stopmodule

Modified: trunk/Master/texmf-dist/tex/context/third/lua-widow-control/t-lua-widow-control.mkxl
===================================================================
--- trunk/Master/texmf-dist/tex/context/third/lua-widow-control/t-lua-widow-control.mkxl	2022-03-08 21:25:37 UTC (rev 62524)
+++ trunk/Master/texmf-dist/tex/context/third/lua-widow-control/t-lua-widow-control.mkxl	2022-03-08 21:25:59 UTC (rev 62525)
@@ -1,10 +1,10 @@
 %D \module
 %D   [     file=t-lua-widow-control,
-%D      version=1.1.6, %%version
+%D      version=2.0.0, %%version
 %D        title=lua-widow-control,
 %D     subtitle=\ConTeXt module for lua-widow-control,
 %D       author=Max Chernoff,
-%D         date=2022-02-22, %%date
+%D         date=2022-03-07, %%dashdate
 %D    copyright=Max Chernoff,
 %D      license=MPL-2.0+,
 %D          url=https://github.com/gucci-on-fleek/lua-widow-control]
@@ -15,9 +15,9 @@
 
 \installcommandhandler \????lwc {lwc} \????lwc
 
-\newdimen\lwcemergencystretch
+\newdimen\lwc_emergency_stretch
 \appendtoks
-    \lwcemergencystretch=\lwcparameter{emergencystretch}
+    \lwc_emergency_stretch=\lwcparameter{emergencystretch}
 \to\everysetuplwc
 
 \appendtoks
@@ -28,24 +28,50 @@
     }
 \to\everysetuplwc
 
+\appendtoks
+    \doifelse{\lwcparameter{debug}}\v!start{
+        \ctxlua{lwc.debug = true}
+    }{
+        \ctxlua{lwc.debug = false}
+    }
+\to\everysetuplwc
+
+\appendtoks
+    \ctxlua{lwc.nobreak_behaviour = "\lwcparameter{nobreak}"}
+\to\everysetuplwc
+
+\newcount\lwc_max_cost
+\appendtoks
+    \lwc_max_cost=\lwcparameter{maxcost}
+\to\everysetuplwc
+
+\appendtoks
+    % We can't just set the penalties because they will be reset automatically
+    % at \\starttext.
+    \startsetups[*default]
+        \clubpenalty=\lwcparameter{orphanpenalty}
+        \widowpenalty=\lwcparameter{widowpenalty}
+        \displaywidowpenalty=\lwcparameter{widowpenalty}
+    \stopsetups
+
+    \setups[*default]
+\to\everysetuplwc
+
 \define\iflwc{\ctxlua{lwc.if_lwc_enabled()}}
 
 \ctxloadluafile{lua-widow-control}
 
-\setuplwc[emergencystretch=3em, \c!state=\v!start]
+\setuplwc[
+    emergencystretch=3em,
+    \c!state=\v!start,
+    debug=\v!stop,
+    orphanpenalty=1,
+    widowpenalty=1,
+    nobreak=keep,
+    maxcost=2147483647,
+]
 
-% We can't just set the penalties because they will be reset automatically
-% at \\starttext.
-\startsetups[*default]
-    \clubpenalty=1
-    \widowpenalty=1
-    \displaywidowpenalty=0
-    \interlinepenalty=0
-    \brokenpenalty=0
-\stopsetups
 
-\setups[*default]
-
 % Here, we enable font expansion/contraction. It isn't strictly necessary for
 % \lwc/'s functionality; however, it is required for the
 % lengthened paragraphs to not have terrible spacing. 
@@ -55,23 +81,23 @@
 % Expansion of some parts of the document, such as section headings, is quite
 % undesirable, so we'll disable \lwc/ for certain commands.
 % We should only reenable \lwc/ at the end if it was already enabled.
-\newif\iflwc at should@reenable
+\newif\iflwc_should_reenable
 
-\define\lwc at patch@pre{%
+\define\lwc_patch_pre{%
     \iflwc%
-        \lwc at should@reenabletrue%
+        \lwc_should_reenabletrue%
         \setuplwc[state=stop]%
     \else%
-        \lwc at should@reenablefalse
+        \lwc_should_reenablefalse
     \fi%
 }
 
-\define\lwc at patch@post{\iflwc at should@reenable%
+\define\lwc_patch_post{\iflwc_should_reenable%
     \setuplwc[state=start]%
 \fi}
 
-\prependtoks\lwc at patch@pre\to\everybeforesectionheadhandle % Sectioning
-\prependtoks\lwc at patch@post\to\everyaftersectionheadhandle
+\prependtoks\lwc_patch_pre\to\everybeforesectionheadhandle % Sectioning
+\prependtoks\lwc_patch_post\to\everyaftersectionheadhandle
 
 \protect
 \stopmodule

Added: trunk/Master/texmf-dist/tex/lualatex/lua-widow-control/lua-widow-control-2022-02-22.sty
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/lua-widow-control/lua-widow-control-2022-02-22.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/lualatex/lua-widow-control/lua-widow-control-2022-02-22.sty	2022-03-08 21:25:59 UTC (rev 62525)
@@ -0,0 +1,94 @@
+% lua-widow-control
+% https://github.com/gucci-on-fleek/lua-widow-control
+% SPDX-License-Identifier: MPL-2.0+
+% SPDX-FileCopyrightText: 2022 Max Chernoff
+
+\NeedsTeXFormat{LaTeX2e}[2015/01/01] % Formats built after 2015 include \LuaTeX{}Base
+\ProvidesPackage{lua-widow-control}%
+    [2022/02/22 v1.1.6]
+
+\PackageWarning{lua-widow-control}{%
+    Old LaTeX format detected!\MessageBreak\MessageBreak
+    Lua-widow-control prefers a LaTeX format\MessageBreak
+    newer than November 2020. I'll still run\MessageBreak
+    the latest Lua code, but I'm using an older\MessageBreak
+    version of the LaTeX code. This means that\MessageBreak
+    the key-value interface is *UNSUPPORTED*.\MessageBreak
+}
+
+\ifdefined\directlua\else
+    \PackageError{lua-widow-control}{%
+        LuaTeX is required for this package.\MessageBreak
+        Make sure to compile with `lualatex'%
+    }{}
+\fi
+
+\clubpenalty=1
+\widowpenalty=1
+\displaywidowpenalty=1
+
+% We can't use \\newlength since that makes a \TeX{} "skip", not a "dimen"
+\newdimen\lwcemergencystretch
+\lwcemergencystretch=3em
+
+\newcount\lwcmaxcost
+\lwcmaxcost=2147483647
+
+\directlua{require "lua-widow-control"}
+
+% Here, we enable font expansion/contraction. It isn't strictly necessary for
+% \lwc/'s functionality; however, it is required for the
+% lengthened paragraphs to not have terrible spacing. 
+\RequirePackage{etoolbox}
+\AtEndPreamble{
+    \@ifpackageloaded{microtype}{}{ % Only load if not already loaded
+        \RequirePackage[
+            final,
+            activate={true,nocompatibility}
+        ]{microtype}
+    }
+}
+
+% Define \TeX{} wrappers for Lua functions
+\newcommand{\lwcenable}{\directlua{lwc.enable_callbacks()}}
+\newcommand{\lwcdisable}{\directlua{lwc.disable_callbacks()}}
+\newcommand{\iflwc}{\directlua{lwc.if_lwc_enabled()}}
+
+% Enable \lwc/ by default when the package is loaded.
+\lwcenable
+
+% Expansion of some parts of the document, such as section headings, is quite
+% undesirable, so we'll disable \lwc/ for certain commands.
+\newcommand{\lwc at patch@warning}[1]{\PackageWarning{lua-widow-control}{%
+    Patching the \protect#1 command failed%
+}}
+
+% We should only reenable \lwc/ at the end if it was already enabled.
+\newif\iflwc at should@reenable
+
+\newcommand{\lwc at patch@pre}{%
+    \iflwc%
+        \lwc at should@reenabletrue%
+        \lwcdisable%
+    \else%
+        \lwc at should@reenablefalse%
+    \fi%
+}
+
+\newcommand{\lwc at patch@post}{%
+    \iflwc at should@reenable%
+        \lwcenable%
+    \fi%
+}
+
+\newcommand{\lwcdisablecmd}[1]{%
+    \ifdefined#1
+        \pretocmd{#1}{\lwc at patch@pre}{}{\lwc at patch@warning{#1}}%
+        \apptocmd{#1}{\lwc at patch@post}{}{\lwc at patch@warning{#1}}%
+    \fi
+}
+
+\lwcdisablecmd{\@sect} % Sectioning
+
+
+\endinput


Property changes on: trunk/Master/texmf-dist/tex/lualatex/lua-widow-control/lua-widow-control-2022-02-22.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/texmf-dist/tex/lualatex/lua-widow-control/lua-widow-control.sty
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/lua-widow-control/lua-widow-control.sty	2022-03-08 21:25:37 UTC (rev 62524)
+++ trunk/Master/texmf-dist/tex/lualatex/lua-widow-control/lua-widow-control.sty	2022-03-08 21:25:59 UTC (rev 62525)
@@ -3,82 +3,232 @@
 % SPDX-License-Identifier: MPL-2.0+
 % SPDX-FileCopyrightText: 2022 Max Chernoff
 
-\NeedsTeXFormat{LaTeX2e}[2015/01/01] % Formats built after 2015 include \LuaTeX{}Base
-\ProvidesPackage{lua-widow-control}%
-    [2022/02/22 v1.1.6] %%version %%date
+% Formats built after 2015 include \LuaTeX{}Base, so this is the absolute
+% minimum version that we will run under.
+\NeedsTeXFormat{LaTeX2e}[2015/01/01] 
 
-\ifdefined\directlua\else
-    \PackageError{lua-widow-control}{%
-        LuaTeX is required for this package.\MessageBreak
-        Make sure to compile with `lualatex'%
-    }{}
-\fi
+% For _really_ old formats
+\providecommand\DeclareRelease[3]{}
+\providecommand\DeclareCurrentRelease[2]{}
 
-\clubpenalty=1
-\widowpenalty=1
-\displaywidowpenalty=0
-\interlinepenalty=0
-\brokenpenalty=0
+\DeclareRelease{}{0000-00-00}{lua-widow-control-2022-02-22.sty}
+\DeclareRelease{v1.1.6}{2022-02-22}{lua-widow-control-2022-02-22.sty}
+\DeclareCurrentRelease{v2.0.0}{2022-03-07} %%version %%dashdate
 
-% We can't use \\newlength since that makes a \TeX{} "skip", not a "dimen"
-\newdimen\lwcemergencystretch
-\lwcemergencystretch=3em
+% If this version of LaTeX doesn't support command hooks, then we load
+% the last v1.1.X version of the package.
+\providecommand\IfFormatAtLeastTF{\@ifl at t@r\fmtversion}
+\IfFormatAtLeastTF{2020/10/01}{}{\input{lua-widow-control-2022-02-22.sty}}
+\IfFormatAtLeastTF{2020/10/01}{}{\endinput}
 
-\directlua{require "lua-widow-control"}
+\ProvidesExplPackage
+    {lua-widow-control}
+    {2022/03/07} %%slashdate
+    {v2.0.0} %%version
+    {Use Lua to remove widows and orphans}
 
+% Unconditional Package Loads
+\RequirePackage { l3keys2e }
+
+% Message and String Constants
+\str_const:Nn \c__lwc_name_str { lua-widow-control }
+
+\msg_new:nnn
+    { \c__lwc_name_str }
+    { no-luatex }
+    {
+        LuaTeX~ is~ REQUIRED! \\
+        Make~ sure~ to~ compile~ your~ document~ with~ `lualatex'.
+    }
+
+\msg_new:nnn
+    { \c__lwc_name_str }
+    { patch-failed }
+    { 
+        Patching~ \c_backslash_str #1~ failed. \\
+        Please~ ensure~ that~ \c_backslash_str #1~ exists. 
+    }
+
+\msg_new:nnn
+    { \c__lwc_name_str }
+    { old-format-patch }
+    { 
+        Patching~ not~ supported~ with~ old~ LaTeX. \\
+        Please~ use~ a~ LaTeX~ format~ >=~ 2021/06/01.
+    }
+
+\msg_new:nnn
+    { \c__lwc_name_str }
+    { old-command }
+    { 
+        \c_backslash_str #1~ has~ been~ REMOVED! \\
+        Please~ use~ \c_backslash_str setuplwc \c_left_brace_str #2 
+        \c_right_brace_str\ instead.
+    }
+
+% Don't let the user proceed unless they are using \LuaTeX{}.
+\sys_if_engine_luatex:F {
+    \msg_critical:nn { \c__lwc_name_str } { no-luatex }
+}
+
+% Define (most of) the keys
+\keys_define:nn { \c__lwc_name_str } {
+    emergencystretch .dim_gset:N       = \g__lwc_emergencystretch_dim,
+    emergencystretch .value_required:n = true,
+    emergencystretch .initial:x        = \dim_max:nn { 3em } { 30pt },
+
+    max-cost .int_gset:N       = \g__lwc_maxcost_int,
+    max-cost .value_required:n = true,
+    max-cost .initial:x        = \c_max_int,
+
+    widowpenalty .code:n = \int_gset:Nn \tex_widowpenalty:D        { #1 }
+                           \int_gset:Nn \tex_displaywidowpenalty:D { #1 },
+    widowpenalty .value_required:n = true,
+    widowpenalty .initial:n        = 1,
+
+    orphanpenalty .code:n = \int_gset:Nn \tex_clubpenalty:D  { #1 }
+                            \int_gset:Nn \@clubpenalty       { #1 },
+    orphanpenalty .value_required:n = true,
+    orphanpenalty .initial:n        = 1,
+
+    microtype .bool_gset:N      = \g__lwc_use_microtype_bool,
+    microtype .value_required:n = true,
+    microtype .initial:n        = true,
+    microtype .usage:n          = preamble,
+
+    disablecmds .clist_gset:N     = \g__lwc_disablecmds_cl,
+    disablecmds .value_required:n = false,
+    disablecmds .initial:n        = { \@sect },
+    disablecmds .usage:n          = preamble,
+
+    nobreak .choice:,
+    nobreak / keep  .code:n = \lua_now:n { lwc.nobreak_behaviour = "keep"  },
+    nobreak / split .code:n = \lua_now:n { lwc.nobreak_behaviour = "split" },
+    nobreak / warn  .code:n = \lua_now:n { lwc.nobreak_behaviour = "warn"  },
+}
+
+% Load the Lua code
+\lua_now:n { require "lua-widow-control" }
+
 % Here, we enable font expansion/contraction. It isn't strictly necessary for
 % \lwc/'s functionality; however, it is required for the
-% lengthened paragraphs to not have terrible spacing. 
-\RequirePackage{etoolbox}
-\AtEndPreamble{
-    \@ifpackageloaded{microtype}{}{ % Only load if not already loaded
-        \RequirePackage[
-            final,
-            activate={true,nocompatibility}
-        ]{microtype}
+% lengthened paragraphs to not have terrible spacing.
+\bool_if:NT \g__lwc_use_microtype_bool {
+    \hook_gput_code:nnn { begindocument / before } { \c__lwc_name_str } {
+        \@ifpackageloaded { microtype } {} {
+            \RequirePackage[
+                final,
+                activate = { true, nocompatibility }
+            ]
+            { microtype }
+        }
     }
 }
 
-% Define \TeX{} wrappers for Lua functions
-\newcommand{\lwcenable}{\directlua{lwc.enable_callbacks()}}
-\newcommand{\lwcdisable}{\directlua{lwc.disable_callbacks()}}
-\newcommand{\iflwc}{\directlua{lwc.if_lwc_enabled()}}
+% Core Function Definitions
+\cs_new:Npn \__lwc_enable: {
+    \lua_now:n { lwc.enable_callbacks() }
+}
 
-% Enable \lwc/ by default when the package is loaded.
-\lwcenable
+\cs_new:Npn \__lwc_disable: {
+    \lua_now:n { lwc.disable_callbacks() }
+}
 
+\prg_set_conditional:Nnn \__lwc_if_enabled: { T, F, TF } {
+    \lua_now:n { lwc.if_lwc_enabled() }
+        \prg_return_true:
+    \else
+        \prg_return_false:
+    \fi
+}
+
 % Expansion of some parts of the document, such as section headings, is quite
 % undesirable, so we'll disable \lwc/ for certain commands.
-\newcommand{\lwc at patch@warning}[1]{\PackageWarning{lua-widow-control}{%
-    Patching the \protect#1 command failed%
-}}
+\bool_new:N \g__lwc_should_reenable_bool
 
-% We should only reenable \lwc/ at the end if it was already enabled.
-\newif\iflwc at should@reenable
+\cs_new:Npn \__lwc_patch_cmd:c #1 {
+    \IfFormatAtLeastTF {2021/06/01 } {
+        \hook_gput_code:nnn { cmd / #1 / before } { \c__lwc_name_str } {
+            % We should only reenable \lwc/ at the end if it was already enabled.
+            \__lwc_if_enabled:TF {
+                \bool_gset_true:N \g__lwc_should_reenable_bool
+                \__lwc_disable:
+            } {
+                \bool_gset_false:N \g__lwc_should_reenable_bool
+            }
+        }
+        \hook_gput_code:nnn { cmd / #1 / after } { \c__lwc_name_str } {
+            \bool_if:NT { \g__lwc_should_reenable_bool } {
+                \__lwc_enable:
+            }
+        }
+    } {
+        \msg_warning:nn
+                { \c__lwc_name_str }
+                { old-format-patch }
+    }
+}
 
-\newcommand{\lwc at patch@pre}{%
-    \iflwc%
-        \lwc at should@reenabletrue%
-        \lwcdisable%
-    \else%
-        \lwc at should@reenablefalse%
-    \fi%
+\cs_new:Npn \__lwc_patch_cmd:N #1 {
+    \__lwc_patch_cmd:c { \cs_to_str:N #1 }
 }
 
-\newcommand{\lwc at patch@post}{%
-    \iflwc at should@reenable%
-        \lwcenable%
-    \fi%
+\hook_gput_code:nnn { begindocument / before } { \c__lwc_name_str } {
+    \clist_map_function:NN \g__lwc_disablecmds_cl \__lwc_patch_cmd:N
 }
 
-\newcommand{\lwcdisablecmd}[1]{%
-    \ifdefined#1
-        \pretocmd{#1}{\lwc at patch@pre}{}{\lwc at patch@warning{#1}}%
-        \apptocmd{#1}{\lwc at patch@post}{}{\lwc at patch@warning{#1}}%
-    \fi
+% Define some final keys
+\keys_define:nn { \c__lwc_name_str } {
+    enable .choice:,
+    enable / true  .code:n     = \__lwc_enable:,
+    enable / false .code:n     = \__lwc_disable:,
+    enable .initial:n          = true,
+    enable .default:n          = true,
+    enable .value_required:n   = false,
+
+    disable .code:n            = \__lwc_disable:,
+    disable .value_forbidden:n = true,
+
+    debug .choice:,
+    debug / true  .code:n = \lua_now:n { lwc.debug = true },
+    debug / false .code:n = \lua_now:n { lwc.debug = false },
+
+    strict .meta:n = { emergencystretch = 0pt,
+                       max-cost         = 2500,
+                       nobreak          = warn, 
+                     },
+    strict .value_forbidden:n = true,
+
+    default .meta:n = { emergencystretch = 3em,
+                        max-cost         = \c_max_int,
+                        nobreak          = keep, 
+                      },
+    default .value_forbidden:n = true,  
 }
 
-\lwcdisablecmd{\@sect} % Sectioning
+% Add the user interface for the keys
+\ProcessKeysPackageOptions{ \c__lwc_name_str }
 
+\NewDocumentCommand \lwcsetup {m} { \keys_set:nn{\c__lwc_name_str}{#1} }
 
+% Legacy Commands
+\NewDocumentCommand \lwcemergencystretch { } {
+    \msg_error:nnnnn
+        { \c__lwc_name_str }
+        { old-command }
+        { lwcemergencystretch }
+        { emergencystretch=XXXpt }
+}
+
+\NewDocumentCommand \lwcdisablecmd { m } {
+    \msg_error:nnxx
+        { \c__lwc_name_str }
+        { old-command }
+        { lwcdisablecmd }
+        { disablecmds={\c_backslash_str aaa,~ \c_backslash_str bbb} }
+}
+
+\cs_new_eq:NN \lwcenable  \__lwc_enable:
+\cs_new_eq:NN \lwcdisable \__lwc_disable:
+
 \endinput

Modified: trunk/Master/texmf-dist/tex/luatex/lua-widow-control/lua-widow-control.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lua-widow-control/lua-widow-control.lua	2022-03-08 21:25:37 UTC (rev 62524)
+++ trunk/Master/texmf-dist/tex/luatex/lua-widow-control/lua-widow-control.lua	2022-03-08 21:25:59 UTC (rev 62525)
@@ -5,9 +5,25 @@
     SPDX-FileCopyrightText: 2022 Max Chernoff
   ]]
 
-lwc = {}
+lwc = lwc or {}
 lwc.name = "lua-widow-control"
+lwc.nobreak_behaviour = "keep"
 
+local write_nl = texio.write_nl
+local string_rep = string.rep
+local function debug_print(title, text)
+    if not lwc.debug then return end
+
+    local filler = 15 - #title
+
+    if text then
+        write_nl("log", "LWC (" .. title .. string_rep(" ", filler) .. "): " .. text)
+    else
+        write_nl("log", "LWC: " .. string_rep(" ", 18) .. title)
+    end
+end
+
+
 --[[
     \lwc/ is intended to be format-agonistic. It only runs on Lua\TeX{},
     but there are still some slight differences between formats. Here, we
@@ -37,71 +53,91 @@
 local copy = node.copy_list
 local par_id = node.id("par") or node.id("local_par")
 local glue_id = node.id("glue")
-local set_attribute = node.set_attribute
-local has_attribute = node.has_attribute
+local glyph_id = node.id("glyph")
+local penalty_id = node.id("penalty")
+local hlist_id = node.id("hlist")
+local traverse = node.traverse
+local set_attribute = node.set_attribute or node.setattribute
+local find_attribute = node.find_attribute or node.findattribute
 local flush_list = node.flush_list or node.flushlist
 local free = node.free
-local min_col_width = tex.sp("25em")
-local maxdimen = 1073741823 -- \\maxdimen in sp
+local min_col_width = tex.sp("250pt")
 
 --[[
     Package/module initialization
   ]]
-local warning, info, attribute, contrib_head, stretch_order, pagenum
+local warning,
+      info,
+      attribute,
+      contrib_head,
+      stretch_order,
+      pagenum,
+      emergencystretch,
+      max_cost
 
-if context and lmtx then
-    warning = logs.reporter("module", lwc.name)
-    info = logs.reporter("module", lwc.name)
-    attribute = attributes.public(lwc.name)
-    contrib_head = 'contributehead' -- For \LuaMetaTeX{}
+if lmtx then
+    debug_print("LMTX")
+    contrib_head = 'contributehead'
     stretch_order = "stretchorder"
-    pagenum = function() return tex.count["realpageno"] end
-elseif context and not lmtx then
+else
+    contrib_head = 'contrib_head'
+    stretch_order = "stretch_order"
+end
+
+if context then
+    debug_print("ConTeXt")
     warning = logs.reporter("module", lwc.name)
     info = logs.reporter("module", lwc.name)
     attribute = attributes.public(lwc.name)
-    contrib_head = 'contrib_head'
-    stretch_order = "stretch_order"
     pagenum = function() return tex.count["realpageno"] end
-elseif plain or latex then
-    luatexbase.provides_module {
-        name = lwc.name,
-        date = "2022/02/22", --%%date
-        version = "1.1.6", --%%version
-        description = [[
+    emergencystretch = "lwc_emergency_stretch"
+    max_cost = "lwc_max_cost"
+elseif plain or latex or optex then
+    pagenum = function() return tex.count[0] end
 
-    This module provides a LuaTeX-based solution to prevent
-    widows and orphans from appearing in a document. It does
-    so by increasing or decreasing the lengths of previous
-    paragraphs.
-        ]],
-    }
-    warning = function(str) luatexbase.module_warning(lwc.name, str) end
-    info = function(str) luatexbase.module_info(lwc.name, str) end
-    attribute = luatexbase.new_attribute(lwc.name)
-    contrib_head = 'contrib_head' -- For \LuaTeX{}
-    stretch_order = "stretch_order"
-    pagenum = function() return tex.count[0] end
-elseif optex then
-    local write_nl = texio.write_nl
-    warning = function(str) write_nl(lwc.name .. " Warning: " .. str) end
-    info = function(str) write_nl("log", lwc.name .. " Info: " .. str) end
-    attribute = alloc.new_attribute(lwc.name)
-    contrib_head = 'contrib_head'
-    stretch_order = "stretch_order"
-    pagenum = function() return tex.count[0] end
+    if tex.isdimen("g__lwc_emergencystretch_dim") then
+        emergencystretch = "g__lwc_emergencystretch_dim"
+        max_cost = "g__lwc_maxcost_int"
+    else
+        emergencystretch = "lwcemergencystretch"
+        max_cost = "lwcmaxcost"
+    end
+
+    if plain or latex then
+        debug_print("Plain/LaTeX")
+        luatexbase.provides_module {
+            name = lwc.name,
+            date = "2022/03/07", --%%slashdate
+            version = "2.0.0", --%%version
+            description = [[
+
+This module provides a LuaTeX-based solution to prevent
+widows and orphans from appearing in a document. It does
+so by increasing or decreasing the lengths of previous
+paragraphs.]],
+        }
+        warning = function(str) luatexbase.module_warning(lwc.name, str) end
+        info = function(str) luatexbase.module_info(lwc.name, str) end
+        attribute = luatexbase.new_attribute(lwc.name)
+    elseif optex then
+        debug_print("OpTeX")
+        warning = function(str) write_nl(lwc.name .. " Warning: " .. str) end
+        info = function(str) write_nl("log", lwc.name .. " Info: " .. str) end
+        attribute = alloc.new_attribute(lwc.name)
+    end
 else -- uh oh
-    error [[
-        Unsupported format.
+    error [[Unsupported format.
 
-        Please use (Lua)LaTeX, Plain (Lua)TeX, ConTeXt (MKXL/LMTX),
-        or OpTeX.
-    ]]
+Please use LaTeX, Plain TeX, ConTeXt or OpTeX.]]
 end
 
 local paragraphs = {} -- List to hold the alternate paragraph versions
 
+local function get_location()
+    return "At " .. pagenum() .. "/" .. #paragraphs
+end
 
+
 --[[
     Function definitions
   ]]
@@ -165,11 +201,39 @@
     end
 end
 
+local function get_chars(head)
+    if not lwc.debug then return end
+
+    local chars = ""
+    for n in traverse(head) do
+        if n.id == glyph_id then
+            if n.char < 127 then
+                chars = chars .. string.char(n.char)
+            else
+                chars = chars .. "#"
+            end
+        elseif n.id == glue_id then
+            chars = chars .. " "
+        end
+        if #chars > 25 then
+            break
+        end
+    end
+
+    debug_print(get_location(), chars)
+end
+
+
+function lwc.paragraph_cost(demerits, lines)
+    return demerits / (2 * math.sqrt(lines))
+end
+
+
 --- Saves each paragraph, but lengthened by 1 line
 function lwc.save_paragraphs(head)
     -- Prevent the "underfull hbox" warnings when we store a potential paragraph
     local renable_box_warnings
-    if (latex or plain) and
+    if (context or optex) or
        #luatexbase.callback_descriptions("hpack_quality") == 0
     then -- See #18 and michal-h21/linebreaker#3
         renable_box_warnings = true
@@ -194,7 +258,7 @@
     -- Break the paragraph 1 line longer than natural
     local long_node, long_info = tex.linebreak(new_head, {
         looseness = 1,
-        emergencystretch = tex.dimen.lwcemergencystretch,
+        emergencystretch = tex.getdimen(emergencystretch),
     })
 
     -- Break the natural paragraph so we know how long it was
@@ -205,21 +269,32 @@
         lwc.callbacks.disable_box_warnings.disable()
     end
 
-    -- If we can't lengthen the paragraph, assign a \emph{very} large demerit value
-    local long_demerits
-    if long_info.prevgraf == natural_info.prevgraf then
-        long_demerits = maxdimen
-    else
-        long_demerits = long_info.demerits
-    end
-
     -- Offset the accumulated \\prevdepth
     local prevdepth = node.new("glue")
     prevdepth.width = natural_info.prevdepth - long_info.prevdepth
     last(long_node).next = prevdepth
 
-    table.insert(paragraphs, { demerits = long_demerits, node = long_node })
+    if long_info.prevgraf == natural_info.prevgraf + 1 then
+        table.insert(paragraphs, {
+            cost = lwc.paragraph_cost(long_info.demerits, long_info.prevgraf),
+            node = long_node
+        })
+    end
 
+    get_chars(head)
+    debug_print(get_location(), "nat  lines    " .. natural_info.prevgraf)
+    debug_print(
+        get_location(),
+        "nat  cost " ..
+        lwc.paragraph_cost(natural_info.demerits, natural_info.prevgraf)
+    )
+    debug_print(get_location(), "long lines    " .. long_info.prevgraf)
+    debug_print(
+        get_location(),
+        "long cost " ..
+        lwc.paragraph_cost(long_info.demerits, long_info.prevgraf)
+    )
+
     -- \LuaMetaTeX{} crashes if we return `true`
     return head
 end
@@ -251,11 +326,12 @@
 
         if ids[id] then
             warning [[Circular node list detected!
-This should never happen. I'll try and recover, but your output may be 
-corrupted. As a workaround, disable lua-widow-control for the
-affected paragraph or change the page breaks in your document.]]
+This should never happen. I'll try and 
+recover, but your output may be corrupted.
+(Internal Error)]]
+            prev.next = nil
+            debug_print("safe_last", node.type(head.id) .. " " .. node.type(prev.id))
 
-            prev.next = nil
             return prev
         end
 
@@ -276,6 +352,8 @@
 --- replace one paragraph with the same paragraph, but lengthened by one line.
 --- Then, we can push the bottom line of the page to the next page.
 function lwc.remove_widows(head)
+    local head_save = head -- Save the start of the `head` linked-list
+
     local penalty = tex.outputpenalty - tex.interlinepenalty
     local widowpenalty = tex.widowpenalty
     local clubpenalty = tex.clubpenalty
@@ -282,6 +360,8 @@
     local displaywidowpenalty = tex.displaywidowpenalty
     local brokenpenalty = tex.brokenpenalty
 
+    debug_print("outputpenalty", tex.outputpenalty .. " " .. #paragraphs)
+
     --[[
         We only need to process pages that have orphans or widows. If `paragraphs`
         is empty, then there is nothing that we can do.
@@ -289,7 +369,9 @@
         The list of penalties is from:
         https://tug.org/TUGboat/tb39-3/tb123mitt-widows-code.pdf#subsection.0.2.1
       ]]
-    if (penalty == widowpenalty or
+    if  penalty ~= 0 and
+        penalty <  10000 and
+       (penalty == widowpenalty or
         penalty == displaywidowpenalty or
         penalty == clubpenalty or
         penalty == clubpenalty + widowpenalty or
@@ -302,28 +384,26 @@
         penalty == brokenpenalty + clubpenalty + displaywidowpenalty) and
         #paragraphs >= 1 then
     else
+        paragraphs = {}
         return head
     end
 
     info("Widow/orphan detected. Attempting to remove.")
 
-    local head_save = head -- Save the start of the `head` linked-list
-
     --[[
-        Find the paragraph on the page with the minimum penalty.
-
-        This would be a 1-liner in Python or JavaScript, but Lua is pretty low-level,
-        so there's quite a bit of code here.
+        Find the paragraph on the page with the least cost.
       ]]
     local paragraph_index = 1
-    local minimum_demerits = paragraphs[paragraph_index].demerits
+    local best_cost = paragraphs[paragraph_index].cost
 
     -- We find the current "best" replacement, then free the unused ones
     for i, paragraph in pairs(paragraphs) do
-        if paragraph.demerits < minimum_demerits and i <= #paragraphs - 1 then
+        if paragraph.cost < best_cost and i <= #paragraphs - 1 then
+            -- Clear the old best paragraph
             flush_list(paragraphs[paragraph_index].node)
             paragraphs[paragraph_index].node = nil
-            paragraph_index, minimum_demerits = i, paragraph.demerits
+            -- Set the new best paragraph
+            paragraph_index, best_cost = i, paragraph.cost
         elseif i > 1 then
             -- Not sure why `i > 1` is required?
             flush_list(paragraph.node)
@@ -331,44 +411,99 @@
         end
     end
 
+    debug_print(
+        "selected para",
+        pagenum() ..
+        "/" ..
+        paragraph_index ..
+        " (" ..
+        best_cost ..
+        ")"
+    )
+
+    if best_cost > tex.getcount(max_cost) then
+        -- If the best replacement is too bad, we can't do anything
+        warning("Widow/Orphan NOT removed on page " .. pagenum())
+        paragraphs = {}
+        return head_save
+    end
+
     local target_node = paragraphs[paragraph_index].node
-    local clear_flag = false
 
-    -- Loop through all of the nodes on the page
+    -- Start of final paragraph
+    debug_print("remove_widows", "moving last line")
+
+    head = last(head_save).prev
+    local big_penalty_found, last_line, hlist_head
     while head do
+        if head.id == glue_id then
+            -- Ignore any glue nodes
+        elseif head.id == penalty_id and head.penalty >= 10000 then
+            -- Infinite break penalty
+            big_penalty_found = true
+        elseif big_penalty_found and head.id == hlist_id then
+            -- Line before the penalty
+            if lwc.nobreak_behaviour == "keep" then
+                hlist_head = head
+                big_penalty_found = false
+            elseif lwc.nobreak_behaviour == "split" then
+                head = last(head_save)
+                break
+            elseif lwc.nobreak_behaviour == "warn" then
+                warning("Widow/Orphan NOT removed on page " .. pagenum())
+                paragraphs = {}
+                return head_save
+            end
+        else
+            -- Not found
+            if hlist_head then
+                head = hlist_head
+            else
+                head = last(head_save)
+            end
+            break
+        end
+        head = head.prev
+    end
+
+    last_line = copy(head)
+    last(last_line).next = copy(tex.lists[contrib_head])
+
+    head.prev.prev.next = nil
+    -- Move the last line to the next page
+    tex.lists[contrib_head] = last_line
+
+    local free_next_nodes = false
+
+    -- Loop through all of the nodes on the page with the lwc attribute
+    head = head_save
+    while head do
+        local value
+        value, head = find_attribute(head, attribute)
+
+        if not head then
+            break
+        end
+
+        debug_print("remove_widows", "found " .. value)
+
         -- Insert the start of the replacement paragraph
-        if has_attribute(head, attribute, paragraph_index) then
+        if value == paragraph_index then
+            debug_print("remove_widows", "replacement start")
             safe_last(target_node) -- Remove any loops
 
             head.prev.next = target_node
-            clear_flag = true
+            free_next_nodes = true
         end
 
         -- Insert the end of the replacement paragraph
-        if has_attribute(head, attribute, -1 * paragraph_index) then
+        if value == -1 * paragraph_index then
+            debug_print("remove_widows", "replacement end")
             safe_last(target_node).next = head.next
-            clear_flag = false
+            break
         end
 
-        -- Start of final paragraph
-        if has_attribute(head, attribute, #paragraphs) then
-            local last_line = copy(last(head))
-
-            last(last_line).next = copy(tex.lists[contrib_head])
-
-            last(head).prev.prev.next = nil
-            -- Move the last line to the next page
-            tex.lists[contrib_head] = last_line
-            info(
-            "Widow/orphan successfully removed at paragraph "
-                .. paragraph_index
-                .. " on page "
-                .. pagenum()
-                .. "."
-            )
-        end
-
-        if clear_flag then
+        if free_next_nodes then
             head = free(head)
         else
             head = head.next
@@ -375,6 +510,14 @@
         end
     end
 
+    info(
+    "Widow/orphan successfully removed at paragraph "
+        .. paragraph_index
+        .. " on page "
+        .. pagenum()
+        .. "."
+    )
+
     paragraphs = {} -- Clear paragraphs array at the end of the page
 
     return head_save
@@ -413,18 +556,19 @@
 
 local enabled = false
 function lwc.enable_callbacks()
+    debug_print("callbacks", "enabling")
     if not enabled then
-        lwc.callbacks.remove_widows.enable()
         lwc.callbacks.save_paragraphs.enable()
         lwc.callbacks.mark_paragraphs.enable()
 
         enabled = true
     else
-        warning("Already enabled")
+        info("Already enabled")
     end
 end
 
 function lwc.disable_callbacks()
+    debug_print("callbacks", "disabling")
     if enabled then
         lwc.callbacks.save_paragraphs.disable()
         lwc.callbacks.mark_paragraphs.disable()
@@ -436,11 +580,12 @@
 
         enabled = false
     else
-        warning("Already disabled")
+        info("Already disabled")
     end
 end
 
 function lwc.if_lwc_enabled()
+    debug_print("iflwc")
     if enabled then
         tex.sprint("\\iftrue")
     else
@@ -448,4 +593,49 @@
     end
 end
 
+--- Silence the luatexbase "Enabling/Removing <callback>" info messages
+---
+--- Every time that a paragraph is typeset, \lwc/ hooks in
+--- and typesets the paragraph 1 line longer. Some of these longer paragraphs
+--- will have pretty bad badness values, so TeX will issue an over/underfull
+--- hbox warning. To block these warnings, we hook into the `hpack_quality`
+--- callback and disable it so that no warning is generated.
+---
+--- However, each time that we enable/disable the null `hpack_quality` callback,
+--- luatexbase puts an info message in the log. This completely fills the log file
+--- with useless error messages, so we disable it here.
+---
+--- This uses the Lua `debug` library to internally modify the log upvalue in the
+--- `add_to_callback` function. This is almost certainly a terrible idea, but I don't
+--- know of a better way.
+local function silence_luatexbase()
+    local nups = debug.getinfo(luatexbase.add_to_callback).nups
+
+    for x = 1, nups do
+        local name, func = debug.getupvalue(luatexbase.add_to_callback, x)
+        if name == "luatexbase_log" then
+            debug.setupvalue(
+                luatexbase.add_to_callback,
+                x,
+                function(text)
+                    if text:match("^Inserting") or text:match("^Removing") then
+                        return
+                    else
+                        func(text)
+                    end
+                end
+            )
+            return
+        end
+    end
+end
+
+
+-- Activate \lwc/
+if plain or latex then
+    silence_luatexbase()
+end
+
+lwc.callbacks.remove_widows.enable()
+
 return lwc

Modified: trunk/Master/texmf-dist/tex/luatex/lua-widow-control/lua-widow-control.tex
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lua-widow-control/lua-widow-control.tex	2022-03-08 21:25:37 UTC (rev 62524)
+++ trunk/Master/texmf-dist/tex/luatex/lua-widow-control/lua-widow-control.tex	2022-03-08 21:25:59 UTC (rev 62525)
@@ -3,7 +3,7 @@
 % SPDX-License-Identifier: MPL-2.0+
 % SPDX-FileCopyrightText: 2022 Max Chernoff
 
-\wlog{lua-widow-control v1.1.6} %%version
+\wlog{lua-widow-control v2.0.0} %%version
 
 \ifx\directlua\undefined
     \errmessage{%
@@ -16,13 +16,14 @@
 
 \clubpenalty=1
 \widowpenalty=1
-\displaywidowpenalty=0
-\interlinepenalty=0
-\brokenpenalty=0
+\displaywidowpenalty=1
 
 \newdimen\lwcemergencystretch
 \lwcemergencystretch=3em
 
+\newcount\lwcmaxcost
+\lwcmaxcost=2147483647
+
 \directlua{require "lua-widow-control"}
 
 % Here, we enable font expansion/contraction. It isn't strictly necessary for

Modified: trunk/Master/texmf-dist/tex/optex/lua-widow-control/lua-widow-control.opm
===================================================================
--- trunk/Master/texmf-dist/tex/optex/lua-widow-control/lua-widow-control.opm	2022-03-08 21:25:37 UTC (rev 62524)
+++ trunk/Master/texmf-dist/tex/optex/lua-widow-control/lua-widow-control.opm	2022-03-08 21:25:59 UTC (rev 62525)
@@ -3,17 +3,18 @@
 % SPDX-License-Identifier: MPL-2.0+
 % SPDX-FileCopyrightText: 2022 Max Chernoff
 
-\_codedecl\lwcenable{lua-widow-control <v1.1.6>} %%version
+\_codedecl\lwcenable{lua-widow-control <v2.0.0>} %%version
 
 \_clubpenalty=1
 \_widowpenalty=1
-\_displaywidowpenalty=0
-\_interlinepenalty=0
-\_brokenpenalty=0
+\_displaywidowpenalty=1
 
 \_newdimen\lwcemergencystretch
 \lwcemergencystretch=3em
 
+\_newcount\lwcmaxcost
+\lwcmaxcost=2147483647
+
 \_directlua{require "lua-widow-control"}
 
 % Define \TeX{} wrappers for Lua functions



More information about the tex-live-commits mailing list.