From 394ee3a2cd136160bbbead8b6308830efd2f056f Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Sun, 7 Jun 2026 12:50:30 +0300 Subject: [PATCH] mdview: blank-row block separation; tables nowrap; fix continuation marker - Paragraph scanning no longer swallows the blank line separating it from the next block; runs of blank lines collapse to one row. Restores blank separation between paragraphs, headers and horizontal rules. - Detect table rows (first non-space char '|') as nowrap segments; they are no longer merged into surrounding text or each other. - Continuation (wrapped) rows render content as plain text and are no longer re-classified, so a wrapped word starting with '-'/'#'/'>' is not mis-drawn as a list/heading/quote marker. Co-Authored-By: Oz --- examples/mdview/mdview.c | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/examples/mdview/mdview.c b/examples/mdview/mdview.c index 2af0b3d..50209ad 100644 --- a/examples/mdview/mdview.c +++ b/examples/mdview/mdview.c @@ -668,6 +668,18 @@ static uint8_t is_hr_raw(uint32_t p) return ok && count >= 3; } +/* A table row: the first non-space character on the line is '|'. Leading + * spaces are allowed. Table rows are nowrap and never merged. */ +static uint8_t is_table_raw(uint32_t p) +{ + while (p < file_size) { + char c = fb(p); + if (c == ' ' || c == '\t') { p++; continue; } + return (uint8_t)(c == '|'); + } + return 0; +} + /* Shared inline-emphasis / wrap scanner for a single line or paragraph stream. * Starts at `q`, processes until `q_end` (or paragraph break for plain text), * emits continuation segs as needed. `col` is the starting visible column @@ -770,7 +782,8 @@ static void index_lines(void) if (is_line_blank(p)) { while (p < file_size && fb(p) != '\n') p++; if (p < file_size) p++; - if (n_lines == 0 || cur_seg_off != p) { + /* Collapse a run of blank lines to a single blank row. */ + if (n_lines == 0 || !(cur_rec.flags & IF_BLANK)) { emit_seg(p, INIT_STYLE_PLAIN, CK_PLAIN, 0); set_blank_cur(); } @@ -804,6 +817,15 @@ static void index_lines(void) continue; } + /* ---- table row (| ... |): one nowrap seg per source line ---- */ + if (is_table_raw(p)) { + emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); + set_nowrap_cur(); + while (p < file_size && fb(p) != '\n') p++; + if (p < file_size) p++; + continue; + } + /* ---- classify current line ---- */ uint32_t content_off = p; uint8_t kind = classify_line(p, &content_off); @@ -864,6 +886,7 @@ static void index_lines(void) uint32_t next = q + 1; if (is_fence_raw(next)) break; if (is_hr_raw(next)) break; + if (is_table_raw(next)) break; uint32_t dummy; if (classify_line(next, &dummy) != LK_PLAIN) break; if (is_line_blank(next)) break; @@ -984,9 +1007,12 @@ static void index_lines(void) if (q >= file_size) { p = file_size; } else { + /* q sits on the paragraph's trailing newline; resume at the + * next line so the outer loop classifies it. A following + * blank line then becomes the single separating blank row + * (previously it was swallowed, gluing paragraphs together). */ while (q < file_size && fb(q) != '\n') q++; - if (q + 1 < file_size && fb(q + 1) == '\n') q += 2; - else if (q < file_size) q++; + if (q < file_size) q++; p = q; } } @@ -1062,6 +1088,12 @@ static void render_line(uint16_t line_idx, uint8_t row) cur_attr = ATTR_TEXT_CODE; parse_inline = 0; kind = LK_PLAIN; + } else if (cont) { + /* Continuation row: the parent prefix/indent is already drawn + * above. Render the wrapped content as plain text — do NOT + * re-classify it, or a wrapped word beginning with '-', '#', + * '>' etc. would be mis-drawn as a new marker. */ + kind = LK_PLAIN; } else { kind = classify_line(p, &content_off); }