Repository: ajalt/mordant (kotlin)
Issue: Redundant vertical space between table caption and table body if table is too wide
If you have a table with `captionTop`, and the table is too wide for the terminal (truncation of content occurs), extra blank lines are inserted between the caption and the table body.
Pull request description: Fixes #216 

The issue was actually too much _horizontal_ space, which the terminal app hard wraps.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7ea8c214..eed16680 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,7 @@
 - Fixed ConcurrentModificationException from progress bars when updated under very high concurrency [(#204)](https://github.com/ajalt/mordant/issues/204)
 - Improved performance of progress bars under high concurrency. [(#207)](https://github.com/ajalt/mordant/issues/207)
 - Fixed `NoClassDefFoundError` when running with certain gradle plugins [(#217)](https://github.com/ajalt/mordant/issues/217)
+- Fixed whitespace of aligned table captions on narrow terminals [(#216)](https://github.com/ajalt/mordant/issues/216)
 
 ## 2.7.2
 ### Fixed
diff --git a/mordant/src/commonMain/kotlin/com/github/ajalt/mordant/table/VerticalLayout.kt b/mordant/src/commonMain/kotlin/com/github/ajalt/mordant/table/VerticalLayout.kt
index 909676bb..6a159ea8 100644
--- a/mordant/src/commonMain/kotlin/com/github/ajalt/mordant/table/VerticalLayout.kt
+++ b/mordant/src/commonMain/kotlin/com/github/ajalt/mordant/table/VerticalLayout.kt
@@ -58,7 +58,7 @@ internal class VerticalLayout private constructor(
             columnWidth is ColumnWidth.Expand -> width
             hasAlignedCells -> measure(t, width).max
             else -> width
-        }
+        }.coerceAtMost(width)
         val lines = mutableListOf<Line>()
         val spacingLine = when (textAlign) {
             NONE -> EMPTY_LINE
diff --git a/mordant/src/commonMain/kotlin/com/github/ajalt/mordant/widgets/Caption.kt b/mordant/src/commonMain/kotlin/com/github/ajalt/mordant/widgets/Caption.kt
index adf461e4..be3fffea 100644
--- a/mordant/src/commonMain/kotlin/com/github/ajalt/mordant/widgets/Caption.kt
+++ b/mordant/src/commonMain/kotlin/com/github/ajalt/mordant/widgets/Caption.kt
@@ -26,7 +26,7 @@ class Caption(
     }
 
     override fun render(t: Terminal, width: Int): Lines {
-        val captionWidth = content.measure(t, width).max
+        val captionWidth = content.measure(t, width).max.coerceAtMost(width)
         val lines = mutableListOf<Line>()
         top?.let { lines.addAll(it.render(t, captionWidth).lines) }
         lines.addAll(content.render(t, width).lines)


Test patch:
diff --git a/mordant/src/commonTest/kotlin/com/github/ajalt/mordant/table/TableTest.kt b/mordant/src/commonTest/kotlin/com/github/ajalt/mordant/table/TableTest.kt
index 6555e16b..a939ae53 100644
--- a/mordant/src/commonTest/kotlin/com/github/ajalt/mordant/table/TableTest.kt
+++ b/mordant/src/commonTest/kotlin/com/github/ajalt/mordant/table/TableTest.kt
@@ -11,6 +11,8 @@ import com.github.ajalt.mordant.terminal.Terminal
 import com.github.ajalt.mordant.test.RenderingTest
 import com.github.ajalt.mordant.widgets.Padding
 import com.github.ajalt.mordant.widgets.Text
+import io.kotest.data.blocking.forAll
+import io.kotest.data.row
 import kotlin.js.JsName
 import kotlin.test.Test
 
@@ -514,6 +516,25 @@ class TableTest : RenderingTest() {
         body { row(1, 2, 3) }
     }
 
+    @[Test JsName("caption_with_truncated_table")]
+    fun `caption with truncated table`() = forAll(
+        row(TextAlign.LEFT, "Caption   "),
+        row(TextAlign.RIGHT, "   Caption"),
+    ) { align, expected ->
+        doTest(
+            """
+    ░$expected░
+    ░┌────────┐░
+    ░│ long ce│░
+    ░└────────┘░
+    """,
+            width = 10
+        ) {
+            captionTop("Caption", align = align)
+            body { row("long cell") }
+        }
+    }
+
     @Test
     fun grid() = checkRender(
         grid {
@@ -542,8 +563,8 @@ class TableTest : RenderingTest() {
         body { row(111, 222, 333) }
     }
 
-    private fun doTest(expected: String, builder: TableBuilder.() -> Unit) {
-        checkRender(table(builder), expected)
+    private fun doTest(expected: String, width: Int = 79, builder: TableBuilder.() -> Unit) {
+        checkRender(table(builder), expected, width = width)
     }
 
     private fun doBodyTest(expected: String, builder: SectionBuilder.() -> Unit) {