fd5f4d0eda
FIX 1 (src/markdown_table.py): Cells now use imgui_md.render(c) instead of imgui.text_wrapped(c). imgui_md uses MD4C which strips backtick-delimited inline code spans BEFORE rendering, so backticks no longer appear as literal characters in cell content. Side benefit: inline emphasis (*foo*, **bar**) now renders in cells too. FIX 2 (src/markdown_helper.py): Added MarkdownRenderer._normalize_nested_list_endings. Upstream imgui_md (mekhontsev/imgui_md) BLOCK_UL exit only calls ImGui::NewLine() for top-level list endings. For nested list endings, no NewLine is emitted, so the next text starts at the same Y as the last list item, causing visual overlap. The preprocessor inserts a blank line before any line that follows a list item with MORE indent than itself, forcing a paragraph break. Cannot fix the C++ from Python. Tests: - test_markdown_table_wrapped.py: updated to assert imgui_md.render is called for cell content (not imgui.text_wrapped). - test_markdown_helper_bullets.py: added 4 tests for the new preprocessors (nested-list blank insertion + bullet delimiter conversion + edge cases). 20/20 markdown unit tests pass. 1-space indentation throughout. KNOWN LIMITATIONS (cannot fix without forking imgui_md C++): - Inline code spans render as plain text (no monospace font in cells) - The ' * ' bullet delimiter has a Y-overlap bug upstream (workaround: pre-convert to '- ' via _normalize_bullet_delimiters) - Nested list ending overlap (workaround: insert blank line via _normalize_nested_list_endings)
72 lines
3.2 KiB
Python
72 lines
3.2 KiB
Python
from unittest.mock import patch, MagicMock
|
|
from src.markdown_table import render_table, TableBlock
|
|
|
|
def _setup_mocks(mock_imgui):
|
|
mock_imgui.TableFlags_ = type("T", (), {"borders": 1, "row_bg": 2, "resizable": 4})()
|
|
mock_imgui.TableColumnFlags_ = type("C", (), {"width_stretch": 8, "width_fixed": 16})()
|
|
mock_imgui.begin_table.return_value = True
|
|
mock_imgui.table_next_column = MagicMock()
|
|
mock_imgui.table_next_row = MagicMock()
|
|
mock_imgui.table_headers_row = MagicMock()
|
|
mock_imgui.text_wrapped = MagicMock()
|
|
mock_imgui.text = MagicMock()
|
|
mock_imgui.end_table = MagicMock()
|
|
|
|
def _setup_md_mocks():
|
|
from src.markdown_table import imgui_md as md_mod
|
|
md_mod.render = MagicMock()
|
|
|
|
def test_render_table_routes_cell_content_through_imgui_md():
|
|
block = TableBlock(headers=["A"], rows=[["hello"]], span=(0, 2))
|
|
with patch("src.markdown_table.imgui") as mock_imgui, \
|
|
patch("src.markdown_table.imgui_md") as mock_md:
|
|
_setup_mocks(mock_imgui)
|
|
_setup_md_mocks()
|
|
render_table(block)
|
|
mock_md.render.assert_any_call("hello"), "cell content must be routed through imgui_md.render (so MD4C strips backticks and handles inline emphasis)"
|
|
|
|
def test_render_table_uses_table_headers_row_for_headers():
|
|
block = TableBlock(headers=["A"], rows=[["hello"]], span=(0, 2))
|
|
with patch("src.markdown_table.imgui") as mock_imgui, \
|
|
patch("src.markdown_table.imgui_md") as mock_md:
|
|
_setup_mocks(mock_imgui)
|
|
_setup_md_mocks()
|
|
render_table(block)
|
|
assert mock_imgui.table_headers_row.called, "table_headers_row must be called exactly once to render the header row"
|
|
assert mock_imgui.table_headers_row.call_count == 1, f"table_headers_row must be called exactly once (not {mock_imgui.table_headers_row.call_count} — would cause double-header bug)"
|
|
|
|
def test_render_table_does_not_use_text_for_cells():
|
|
block = TableBlock(headers=["A"], rows=[["hello"]], span=(0, 2))
|
|
with patch("src.markdown_table.imgui") as mock_imgui, \
|
|
patch("src.markdown_table.imgui_md") as mock_md:
|
|
_setup_mocks(mock_imgui)
|
|
_setup_md_mocks()
|
|
render_table(block)
|
|
assert not mock_imgui.text.called, "imgui.text should not be called for table cells"
|
|
|
|
def test_render_table_uses_width_stretch_for_columns():
|
|
block = TableBlock(headers=["A", "B"], rows=[["1", "2"]], span=(0, 3))
|
|
with patch("src.markdown_table.imgui") as mock_imgui, \
|
|
patch("src.markdown_table.imgui_md") as mock_md:
|
|
_setup_mocks(mock_imgui)
|
|
_setup_md_mocks()
|
|
render_table(block)
|
|
assert mock_imgui.table_setup_column.call_count == 2
|
|
for call in mock_imgui.table_setup_column.call_args_list:
|
|
args, _ = call
|
|
assert imgui_flags_passed(args), f"table_setup_column must include width_stretch flag, got args={args}"
|
|
|
|
def imgui_flags_passed(args) -> bool:
|
|
if len(args) < 2: return False
|
|
return bool(args[1] & 8)
|
|
|
|
def test_render_table_routes_every_cell_through_imgui_md():
|
|
block = TableBlock(headers=["A", "B"], rows=[["1", "2"], ["3", "4"], ["5", "6"]], span=(0, 5))
|
|
with patch("src.markdown_table.imgui") as mock_imgui, \
|
|
patch("src.markdown_table.imgui_md") as mock_md:
|
|
_setup_mocks(mock_imgui)
|
|
_setup_md_mocks()
|
|
render_table(block)
|
|
cell_count = 6
|
|
assert mock_md.render.call_count == cell_count, f"imgui_md.render must be called exactly {cell_count} times (one per cell, not headers), got {mock_md.render.call_count}"
|