From ccdeba54657bf2fd2c825f81c4f4db16d6a7f5d9 Mon Sep 17 00:00:00 2001
From: Udi Fogiel <udifoglle@gmail.com>
Date: Mon, 3 Feb 2025 01:35:20 +0200
Subject: [PATCH] add mathemptydisplaymode primitive

By default paragraphs created because of interuption of display eqautaion containing
dir nodes are never ignored. Changing that could break existing documents,
but when you set mathemptydisplaymode to 1 paragraphs containing only dir nodes
and a local par node before a display equation will be ignored.
---
 manual/luatex-modifications.tex               |  5 +++
 source/texk/web2c/luatexdir/tex/commands.c    |  1 +
 source/texk/web2c/luatexdir/tex/equivalents.h |  6 ++-
 source/texk/web2c/luatexdir/tex/texmath.c     | 40 +++++++++++++------
 4 files changed, 38 insertions(+), 14 deletions(-)

diff --git a/manual/luatex-modifications.tex b/manual/luatex-modifications.tex
index 8d67d7dfa..261bed895 100644
--- a/manual/luatex-modifications.tex
+++ b/manual/luatex-modifications.tex
@@ -1164,6 +1164,11 @@ Because \type {\noindent} doesn't inject anything but a \type {\indent} injects
 an box, paragraphs with only an indent and directions are handled as paragraphs
 with content.
 
+By default paragraphs created because of interuption of display eqautaion containing
+dir nodes are never ignored. Changing that could break existing documents, 
+but when you set \lpr {mathemptydisplaymode} to~\type {1} empty paragraphs before 
+a display equation will be ignored.
+
 \stopsubsection
 
 \startsubsection[title={Controlling glue with \lpr {breakafterdirmode}}]
diff --git a/source/texk/web2c/luatexdir/tex/commands.c b/source/texk/web2c/luatexdir/tex/commands.c
index 371626daf..e794bf0f3 100644
--- a/source/texk/web2c/luatexdir/tex/commands.c
+++ b/source/texk/web2c/luatexdir/tex/commands.c
@@ -180,6 +180,7 @@ void initialize_commands(void)
     primitive_luatex("mathdefaultsmode", assign_int_cmd, int_base + math_defaults_mode_code, int_base);
     primitive_luatex("discretionaryligaturemode", assign_int_cmd, int_base + discretionary_ligature_mode_code, int_base);
     primitive_etex("partokencontext", assign_int_cmd, int_base + partoken_context_code, int_base);
+    primitive_luatex("mathemptydisplaymode", assign_int_cmd, int_base + math_empty_display_mode_code, int_base);
 
     /*tex
 
diff --git a/source/texk/web2c/luatexdir/tex/equivalents.h b/source/texk/web2c/luatexdir/tex/equivalents.h
index bf329b3a1..988d7ae39 100644
--- a/source/texk/web2c/luatexdir/tex/equivalents.h
+++ b/source/texk/web2c/luatexdir/tex/equivalents.h
@@ -318,7 +318,9 @@ the |number_regs| \.{\\dimen} registers.
 
 #  define ignore_primitive_error_code 125 				/*ignore some primitive/engine errors*/
 
-#  define math_option_code 126
+#  define math_empty_display_mode_code 126
+
+#  define math_option_code 127
 
 
 #  define mathoption_int_base_code (math_option_code+1)                 /* one reserve */
@@ -830,6 +832,8 @@ extern halfword last_cs_name;
 
 #define show_stream_par                    int_par(show_stream_code)
 
+#define math_empty_display_mode_par        int_par(math_empty_display_mode_code)
+
 /* */
 
 #define math_use_current_family_code 7
diff --git a/source/texk/web2c/luatexdir/tex/texmath.c b/source/texk/web2c/luatexdir/tex/texmath.c
index 4b34c1723..54c5c4817 100644
--- a/source/texk/web2c/luatexdir/tex/texmath.c
+++ b/source/texk/web2c/luatexdir/tex/texmath.c
@@ -1084,6 +1084,18 @@ static boolean math_and_text_reversed_p(void)
 
 */
 
+static int only_dirs(halfword n)
+{
+    while (n) {
+        if (type(n) == local_par_node || type(n) == dir_node) {
+            n = vlink(n);
+        } else {
+            return 0;
+        }
+    }
+    return 1;
+}
+
 void enter_display_math(void)
 {
     /*tex new or partial |pre_display_size| */
@@ -1101,19 +1113,21 @@ void enter_display_math(void)
         \.{\$\${ }\$\$}
 
     */
-    if (head == tail ||
-        (vlink(head) == tail &&
-         type(tail) == local_par_node && vlink(tail) == null)) {
-        if (vlink(head) == tail) {
-            /*tex
-
-                |resume_after_display| inserts a |local_par_node|, but if there
-                is another display immediately following, we have to get rid of
-                that node.
-
-            */
-            flush_node(tail);
-        }
+    if (head == tail) {
+        pop_nest();
+        w = -max_dimen;
+    } else if ((vlink(head) == tail &&
+               type(tail) == local_par_node && vlink(tail) == null) ||
+               ((math_empty_display_mode_par == 1) && (only_dirs(vlink(head))))) {
+        /*tex
+
+            We ignore |null| paragraphs, that is those that only have a local par node,
+            for example because |resume_after_display| inserts a |local_par_node|.
+            If |\mathdisplayemptymode=1| paragraphs caontaining only local par node
+            and possibly a few dir nodes are ignored as well.
+
+        */
+        flush_node(vlink(head));
         pop_nest();
         w = -max_dimen;
     } else {
-- 
2.44.0

