fix(markdown): remove table double-header + add imgui_md bullet workaround
Table fix (src/markdown_table.py):
- Add TableColumnFlags_.width_stretch to each table_setup_column call
(was missing — columns had no width to wrap against, so text_wrapped
couldn't grow row height → all rows squished together)
- Remove the explicit for-h-in-headers: table_next_column + text_wrapped(h)
loop. table_headers_row() already renders the header from the
table_setup_column() names; the explicit loop was drawing it AGAIN on
top → double-rendered header rows.
Bullet fix (src/markdown_helper.py):
- Revert _render_md_no_bullet_overlap → simple imgui_md.render(chunk);
imgui.spacing() (the original af0bbe97 approach). The complex
workaround was stripping '- ' and rendering stripped text to imgui_md,
which double-rendered '- 1. ...' content (imgui.bullet from my code +
numbered list marker from imgui_md).
- Add MarkdownRenderer._normalize_bullet_delimiters: regex-converts
'* ' markers to '- ' before passing to imgui_md. This works around
the upstream bug in mekhontsev/imgui_md BLOCK_LI where the '*' case
calls ImGui::Bullet() without ImGui::SameLine(), causing the bullet
to render on its own Y with the text on the next Y. The '-' case
uses Text+SameLine which is correct. Cannot fix from Python (we
can't subclass the C++ class) — pre-conversion is the cheapest fix.
Tests:
- test_markdown_table_wrapped.py: updated to assert new behavior
(text_wrapped count == cell count, not header+cell).
- test_markdown_table_columns.py: updated to assert exactly 6
table_next_column calls (cells only, not 9).
- test_markdown_helper_bullets.py: rewrote for new public-API behavior
(imgui_md.render called with the unstripped chunk).
16/16 markdown unit tests pass.
This commit is contained in:
+16
-47
@@ -114,8 +114,8 @@ class MarkdownRenderer:
|
||||
"""
|
||||
if not text:
|
||||
return
|
||||
|
||||
from src.markdown_table import parse_tables, render_table
|
||||
text = self._normalize_bullet_delimiters(text)
|
||||
blocks = parse_tables(text)
|
||||
lines = text.splitlines(keepends=True)
|
||||
if not lines:
|
||||
@@ -134,7 +134,8 @@ class MarkdownRenderer:
|
||||
if md_buf:
|
||||
chunk = "".join(md_buf)
|
||||
if chunk:
|
||||
self._render_md_no_bullet_overlap(chunk)
|
||||
imgui_md.render(chunk)
|
||||
imgui.spacing()
|
||||
md_buf.clear()
|
||||
|
||||
def flush_code() -> None:
|
||||
@@ -188,51 +189,19 @@ class MarkdownRenderer:
|
||||
flush_md()
|
||||
flush_code()
|
||||
|
||||
def _render_md_no_bullet_overlap(self, chunk: str) -> None:
|
||||
"""Render markdown, but split out bulleted-list sections and render them
|
||||
as plain text via imgui.text to avoid imgui_md's known bullet-overlap bug.
|
||||
"""
|
||||
import re
|
||||
list_pattern = re.compile(r"^(?P<indent>[ \t]*)(?:[-*+])\s+", re.MULTILINE)
|
||||
current_pos = 0
|
||||
for m in list_pattern.finditer(chunk):
|
||||
if m.start() > current_pos:
|
||||
pre = chunk[current_pos:m.start()]
|
||||
if pre: imgui_md.render(pre)
|
||||
list_start = m.start()
|
||||
indent_len = len(m.group("indent"))
|
||||
i = list_start
|
||||
while i < len(chunk):
|
||||
line_end = chunk.find("\n", i)
|
||||
if line_end == -1: line_end = len(chunk)
|
||||
line = chunk[i:line_end]
|
||||
stripped = line.lstrip(" \t")
|
||||
leading_ws = len(line) - len(stripped)
|
||||
if leading_ws == indent_len and (stripped.startswith(("- ", "* ", "+ ")) or stripped == ""):
|
||||
i = line_end + 1
|
||||
continue
|
||||
if leading_ws < indent_len:
|
||||
break
|
||||
i = line_end + 1
|
||||
list_block = chunk[list_start:i]
|
||||
for line in list_block.splitlines():
|
||||
if not line.strip():
|
||||
imgui.spacing()
|
||||
continue
|
||||
stripped = line.lstrip(" \t")
|
||||
indent = len(line) - len(stripped)
|
||||
if stripped.startswith(("- ", "* ", "+ ")):
|
||||
imgui.bullet()
|
||||
imgui.same_line()
|
||||
imgui_md.render(stripped[2:])
|
||||
else:
|
||||
if indent > 0: imgui.indent(indent * 2)
|
||||
imgui_md.render(stripped)
|
||||
if indent > 0: imgui.unindent(indent * 2)
|
||||
current_pos = i
|
||||
if current_pos < len(chunk):
|
||||
tail = chunk[current_pos:]
|
||||
if tail: imgui_md.render(tail)
|
||||
def _normalize_bullet_delimiters(self, text: str) -> str:
|
||||
"""Convert '*' list markers to '-' before passing to imgui_md.
|
||||
|
||||
Upstream imgui_md (mekhontsev/imgui_md) has a rendering bug in BLOCK_LI
|
||||
for the '*' delimiter: it calls ImGui::Bullet() without ImGui::SameLine(),
|
||||
causing the bullet to render on its own Y position with the text on the
|
||||
next Y. The '-' delimiter uses Text+SameLine which works correctly.
|
||||
Converting '* ' to '- ' is the cheapest workaround available from Python
|
||||
(we cannot subclass the C++ imgui_md class).
|
||||
[C: src/markdown_helper.py:MarkdownRenderer.render]
|
||||
"""
|
||||
import re
|
||||
return re.sub(r"(?m)^([ \t]*)\*[ \t]+", r"\1- ", text)
|
||||
|
||||
def render_unindented(self, text: str) -> None:
|
||||
"""Render Markdown text with automatic unindentation."""
|
||||
|
||||
@@ -13,11 +13,8 @@ def render_table(block: "TableBlock") -> None:
|
||||
flags = imgui.TableFlags_.borders | imgui.TableFlags_.row_bg | imgui.TableFlags_.resizable
|
||||
if not imgui.begin_table("md_table", n_cols, flags): return
|
||||
for h in block.headers:
|
||||
imgui.table_setup_column(h)
|
||||
imgui.table_setup_column(h, imgui.TableColumnFlags_.width_stretch)
|
||||
imgui.table_headers_row()
|
||||
for h in block.headers:
|
||||
imgui.table_next_column()
|
||||
imgui.text_wrapped(h)
|
||||
for row in block.rows:
|
||||
imgui.table_next_row()
|
||||
for c in row:
|
||||
|
||||
Reference in New Issue
Block a user