From 2bef09e696a4b8d5b77f1ce38badce95c9165dca Mon Sep 17 00:00:00 2001 From: Deyan Ginev Date: Thu, 26 Sep 2024 17:43:10 -0400 Subject: [PATCH] prune an empty \bibitem when auto-opened without need (#2409) --- lib/LaTeXML/Engine/LaTeX.pool.ltxml | 13 ++++++++++++- lib/LaTeXML/Package.pm | 16 +++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/LaTeXML/Engine/LaTeX.pool.ltxml b/lib/LaTeXML/Engine/LaTeX.pool.ltxml index f153a8696..1fac2dd67 100644 --- a/lib/LaTeXML/Engine/LaTeX.pool.ltxml +++ b/lib/LaTeXML/Engine/LaTeX.pool.ltxml @@ -4270,11 +4270,22 @@ DefConstructor('\lx@bibitem[] Semiverbatim', . "#tags" . "", afterDigest => sub { + # check if the previous bibitem had an empty body, + # in which case prune it and reuse its ID and counters. + # this happens due to auto-open being a bit fragile (see issue #2403) + my $pruned_prev = 0; + if (my $prev_item = $LaTeXML::LIST[-1]) { + if (ref $prev_item eq 'LaTeXML::Core::Whatsit' and + $prev_item->getDefinition->getCS->getCSName eq '\lx@bibitem' and + not($prev_item->getArg(1)) and IsEmpty($prev_item->getArg(2))) { + $pruned_prev = 1; + Info('empty', 'bibitem', $_[0], "Encountered an empty \\bibitem, likely auto-opened without need. Pruning and reusing its id."); + pop(@LaTeXML::LIST); } } my $tag = $_[1]->getArg(1); my $key = CleanBibKey($_[1]->getArg(2)); if ($tag) { $_[1]->setProperties(key => $key, - RefStepID('@bibitem'), + ($pruned_prev ? RefCurrentID('@bibitem') : RefStepID('@bibitem')), tags => Digest(T_BEGIN, T_CS('\def'), T_CS('\the@bibitem'), T_BEGIN, Revert($tag), T_END, Invocation(T_CS('\lx@make@tags'), T_OTHER('@bibitem')), diff --git a/lib/LaTeXML/Package.pm b/lib/LaTeXML/Package.pm index fdc5eaa2e..5dca9669a 100644 --- a/lib/LaTeXML/Package.pm +++ b/lib/LaTeXML/Package.pm @@ -68,7 +68,8 @@ our @EXPORT = (qw(&DefAutoload &DefExpandable &AddToMacro &AtBeginDocument &AtEndDocument), # Counter support - qw(&NewCounter &CounterValue &SetCounter &AddToCounter &StepCounter &RefStepCounter &RefStepID &ResetCounter + qw(&NewCounter &CounterValue &SetCounter &AddToCounter &StepCounter &RefStepCounter + &RefStepID &ResetCounter &RefCurrentID &GenerateID &AfterAssignment &MaybePeekLabel &MaybeNoteLabel), @@ -833,6 +834,15 @@ sub RefStepID { my $id = CleanID(ToString(DigestLiteral(T_CS("\\the$ctr\@ID")))); return (id => $id); } +# For UN-numbered units, recycle the last ID without incrementing +# (useful if the last ID-ed box got pruned) +sub RefCurrentID { + my ($type) = @_; + my $ctr = LookupMapping('counter_for_type', $type) || $type; + my $id = CleanID(ToString(DigestLiteral(T_CS("\\the$ctr\@ID")))); + return (id => $id); +} + sub ResetCounter { my ($ctr) = @_; AssignRegister('\c@' . $ctr => Number(0), 'global'); @@ -1779,7 +1789,7 @@ sub defmath_cons { ? $cs : $presentation->unlist); }; } $STATE->installDefinition(LaTeXML::Core::Definition::Constructor->new($defcs, $paramlist, ($nargs == 0 - # If trivial presentation, allow it in Text + # If trivial presentation, allow it in Text ? ($presentation !~ /(?:\(|\)|\\)/ ? "?#isMath(unlist } - map { (blessed $_ ? $_ : TokenizeInternal($_)) } ($defn->getExpansion, @tokens)), + map { (blessed $_ ? $_ : TokenizeInternal($_)) } ($defn->getExpansion, @tokens)), nopackParameters => 1, scope => 'global', locked => $$defn{locked}); } return; }