@@ -269,6 +269,113 @@ def test_link_with_prefix_text(self):
269269 assert result == expected
270270
271271
272+ class TestConsecutiveBlankLines :
273+ """Tests for limiting consecutive blank lines.
274+
275+ Note: mdformat's AST-based rendering normalizes blank lines between
276+ content blocks to a single blank line. The postprocessor can only work
277+ on the rendered output, which has already been normalized.
278+
279+ These tests verify the postprocessor correctly handles:
280+ 1. Already-normalized content (pass-through)
281+ 2. Code blocks (preserve internal blank lines)
282+ 3. Direct function testing for edge cases
283+ """
284+
285+ def test_mdformat_normalizes_multiple_blank_lines (self ):
286+ """mdformat normalizes multiple blank lines to one during rendering."""
287+ # This verifies the expected behavior - mdformat already handles this
288+ input_text = "First paragraph.\n \n \n \n Second paragraph.\n "
289+ expected = "First paragraph.\n \n Second paragraph.\n "
290+ result = mdformat .text (input_text , extensions = {"space_control" })
291+ assert result == expected
292+
293+ def test_single_empty_line_unchanged (self ):
294+ """Single empty line should remain unchanged."""
295+ input_text = "First.\n \n Second.\n "
296+ expected = "First.\n \n Second.\n "
297+ result = mdformat .text (input_text , extensions = {"space_control" })
298+ assert result == expected
299+
300+ def test_code_block_preserved (self ):
301+ """Empty lines inside code blocks should be preserved."""
302+ input_text = "Text.\n \n ```\n \n \n \n \n code\n \n \n \n \n ```\n \n More text.\n "
303+ expected = "Text.\n \n ```\n \n \n \n \n code\n \n \n \n \n ```\n \n More text.\n "
304+ result = mdformat .text (input_text , extensions = {"space_control" })
305+ assert result == expected
306+
307+
308+ class TestConsecutiveBlankLinesFunction :
309+ """Direct tests for _normalize_consecutive_blank_lines function.
310+
311+ These tests verify the function works correctly in isolation,
312+ independent of mdformat's rendering normalization.
313+ """
314+
315+ def test_three_empty_lines_reduced_to_two (self ):
316+ """Three empty lines should be reduced to two."""
317+ from mdformat_space_control .plugin import _normalize_consecutive_blank_lines
318+
319+ input_text = "First paragraph.\n \n \n \n Second paragraph.\n "
320+ expected = "First paragraph.\n \n \n Second paragraph.\n "
321+ result = _normalize_consecutive_blank_lines (input_text )
322+ assert result == expected
323+
324+ def test_many_empty_lines_reduced_to_two (self ):
325+ """Many empty lines should be reduced to two."""
326+ from mdformat_space_control .plugin import _normalize_consecutive_blank_lines
327+
328+ input_text = "First.\n \n \n \n \n \n \n Second.\n "
329+ expected = "First.\n \n \n Second.\n "
330+ result = _normalize_consecutive_blank_lines (input_text )
331+ assert result == expected
332+
333+ def test_two_empty_lines_unchanged (self ):
334+ """Two empty lines should remain unchanged."""
335+ from mdformat_space_control .plugin import _normalize_consecutive_blank_lines
336+
337+ input_text = "First.\n \n \n Second.\n "
338+ expected = "First.\n \n \n Second.\n "
339+ result = _normalize_consecutive_blank_lines (input_text )
340+ assert result == expected
341+
342+ def test_single_empty_line_unchanged (self ):
343+ """Single empty line should remain unchanged."""
344+ from mdformat_space_control .plugin import _normalize_consecutive_blank_lines
345+
346+ input_text = "First.\n \n Second.\n "
347+ expected = "First.\n \n Second.\n "
348+ result = _normalize_consecutive_blank_lines (input_text )
349+ assert result == expected
350+
351+ def test_code_block_preserved (self ):
352+ """Empty lines inside code blocks should be preserved."""
353+ from mdformat_space_control .plugin import _normalize_consecutive_blank_lines
354+
355+ input_text = "Text.\n \n ```\n \n \n \n \n code\n \n \n \n \n ```\n \n More text.\n "
356+ expected = "Text.\n \n ```\n \n \n \n \n code\n \n \n \n \n ```\n \n More text.\n "
357+ result = _normalize_consecutive_blank_lines (input_text )
358+ assert result == expected
359+
360+ def test_multiple_sections (self ):
361+ """Multiple sections with excess blank lines."""
362+ from mdformat_space_control .plugin import _normalize_consecutive_blank_lines
363+
364+ input_text = "# One\n \n \n \n \n Para.\n \n \n \n \n # Two\n "
365+ expected = "# One\n \n \n Para.\n \n \n # Two\n "
366+ result = _normalize_consecutive_blank_lines (input_text )
367+ assert result == expected
368+
369+ def test_tilde_code_block_preserved (self ):
370+ """Empty lines inside tilde-fenced code blocks should be preserved."""
371+ from mdformat_space_control .plugin import _normalize_consecutive_blank_lines
372+
373+ input_text = "Text.\n \n ~~~\n \n \n \n \n code\n \n \n \n \n ~~~\n \n More text.\n "
374+ expected = "Text.\n \n ~~~\n \n \n \n \n code\n \n \n \n \n ~~~\n \n More text.\n "
375+ result = _normalize_consecutive_blank_lines (input_text )
376+ assert result == expected
377+
378+
272379class TestIntegration :
273380 """Integration tests for multiple features working together."""
274381
0 commit comments