From d726f72916bcdcdc3b5fb8d49d97beb6aaf784c2 Mon Sep 17 00:00:00 2001
From: Robin Stocker <robin@nibor.org>
Date: Tue, 6 Feb 2024 12:52:43 +1100
Subject: [PATCH] Change isTight/setTight to use push/pop pattern like other
 settings

---
 .../markdown/CoreMarkdownNodeRenderer.java    | 10 ++--
 .../renderer/markdown/MarkdownWriter.java     | 49 +++++++++++--------
 2 files changed, 32 insertions(+), 27 deletions(-)

diff --git a/commonmark/src/main/java/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java b/commonmark/src/main/java/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java
index 714144e8..c7fa9be7 100644
--- a/commonmark/src/main/java/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java
+++ b/commonmark/src/main/java/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java
@@ -205,23 +205,21 @@ public class CoreMarkdownNodeRenderer extends AbstractVisitor implements NodeRen
 
     @Override
     public void visit(BulletList bulletList) {
-        boolean oldTight = writer.getTight();
-        writer.setTight(bulletList.isTight());
+        writer.pushTight(bulletList.isTight());
         listHolder = new BulletListHolder(listHolder, bulletList);
         visitChildren(bulletList);
         listHolder = listHolder.parent;
-        writer.setTight(oldTight);
+        writer.popTight();
         writer.block();
     }
 
     @Override
     public void visit(OrderedList orderedList) {
-        boolean oldTight = writer.getTight();
-        writer.setTight(orderedList.isTight());
+        writer.pushTight(orderedList.isTight());
         listHolder = new OrderedListHolder(listHolder, orderedList);
         visitChildren(orderedList);
         listHolder = listHolder.parent;
-        writer.setTight(oldTight);
+        writer.popTight();
         writer.block();
     }
 
diff --git a/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownWriter.java b/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownWriter.java
index f648d9c4..1231a4a7 100644
--- a/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownWriter.java
+++ b/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownWriter.java
@@ -13,10 +13,13 @@ public class MarkdownWriter {
     private final Appendable buffer;
 
     private int blockSeparator = 0;
-    private boolean tight;
     private char lastChar;
     private boolean atLineStart = true;
+
+    // Stacks of settings that affect various rendering behaviors. The common pattern here is that callers use "push" to
+    // change a setting, render some nodes, and then "pop" the setting off the stack again to restore previous state.
     private final LinkedList<String> prefixes = new LinkedList<>();
+    private final LinkedList<Boolean> tight = new LinkedList<>();
     private final LinkedList<CharMatcher> rawEscapes = new LinkedList<>();
 
     public MarkdownWriter(Appendable out) {
@@ -72,7 +75,7 @@ public class MarkdownWriter {
     public void block() {
         // Remember whether this should be a tight or loose separator now because tight could get changed in between
         // this and the next flush.
-        blockSeparator = tight ? 1 : 2;
+        blockSeparator = isTight() ? 1 : 2;
         atLineStart = true;
     }
 
@@ -104,6 +107,25 @@ public class MarkdownWriter {
         prefixes.removeLast();
     }
 
+    /**
+     * Change whether blocks are tight or loose. Loose is the default where blocks are separated by a blank line. Tight
+     * is where blocks are not separated by a blank line. Tight blocks are used in lists, if there are no blank lines
+     * within the list.
+     * <p>
+     * Note that changing this does not affect block separators that have already been enqueued with {@link #block()},
+     * only future ones.
+     */
+    public void pushTight(boolean tight) {
+        this.tight.addLast(tight);
+    }
+
+    /**
+     * Remove the last "tight" setting from the top of the stack.
+     */
+    public void popTight() {
+        this.tight.removeLast();
+    }
+
     /**
      * Escape the characters matching the supplied matcher, in all text (text and raw). This might be useful to
      * extensions that add another layer of syntax, e.g. the tables extension that uses `|` to separate cells and needs
@@ -136,25 +158,6 @@ public class MarkdownWriter {
         return atLineStart;
     }
 
-    /**
-     * @return whether blocks are currently set to tight or loose, see {@link #setTight(boolean)}
-     */
-    public boolean getTight() {
-        return tight;
-    }
-
-    /**
-     * Change whether blocks are tight or loose. Loose is the default where blocks are separated by a blank line. Tight
-     * is where blocks are not separated by a blank line. Tight blocks are used in lists, if there are no blank lines
-     * within the list.
-     * <p>
-     * Note that changing this does not affect block separators that have already been enqueued (with {@link #block()},
-     * only future ones.
-     */
-    public void setTight(boolean tight) {
-        this.tight = tight;
-    }
-
     private void write(String s, CharMatcher escape) {
         try {
             if (rawEscapes.isEmpty() && escape == null) {
@@ -224,6 +227,10 @@ public class MarkdownWriter {
         }
     }
 
+    private boolean isTight() {
+        return !tight.isEmpty() && tight.getLast();
+    }
+
     private boolean needsEscaping(char c, CharMatcher escape) {
         return (escape != null && escape.matches(c)) || rawNeedsEscaping(c);
     }
-- 
GitLab