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 <oz-agent@warp.dev>
This commit is contained in:
2026-06-07 12:50:30 +03:00
parent 47c9cd326a
commit 394ee3a2cd
+35 -3
View File
@@ -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);
}