mdview: multiline quote paragraphs with quote-aware joining
- Quote blocks now join consecutive quote lines into one paragraph with
soft breaks and wrap continuation under quote prefix.
- Empty quote lines ('>' / '> ') act as quote-paragraph separators.
- Nested quote starts ('> > ...') are not merged into the parent text and
stay separate rows.
- Keeps existing inline emphasis handling inside quote content.
Co-Authored-By: Oz <oz-agent@warp.dev>
This commit is contained in:
+147
-7
@@ -1039,15 +1039,155 @@ static void index_lines(void)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Quote line: single line, not merged ---- */
|
/* ---- Quote paragraph: may span multiple source quote lines ---- */
|
||||||
if (kind == LK_QUOTE) {
|
if (kind == LK_QUOTE) {
|
||||||
uint8_t col = marker_visible_col(kind, p, content_off);
|
uint8_t col = marker_visible_col(kind, p, content_off);
|
||||||
emit_seg(p, INIT_STYLE_PLAIN, CK_QUOTE, 0);
|
uint8_t line_style = INIT_STYLE_PLAIN;
|
||||||
uint32_t line_end = content_off;
|
uint32_t q = content_off;
|
||||||
while (line_end < file_size && fb(line_end) != '\n') line_end++;
|
uint32_t last_space = 0xFFFFFFFFu;
|
||||||
(void)inline_scan(content_off, line_end, col, CK_QUOTE, INIT_STYLE_PLAIN);
|
char prev_ch = ' ';
|
||||||
while (p < file_size && fb(p) != '\n') p++;
|
uint8_t seg_col = col;
|
||||||
if (p < file_size) p++;
|
|
||||||
|
emit_seg(p, line_style, CK_QUOTE, 0);
|
||||||
|
|
||||||
|
while (q < file_size && n_lines < max_lines) {
|
||||||
|
char ch = fb(q);
|
||||||
|
uint8_t soft_break = 0;
|
||||||
|
|
||||||
|
if (ch == '\n') {
|
||||||
|
uint32_t next = q + 1;
|
||||||
|
if (next >= file_size) break;
|
||||||
|
|
||||||
|
/* Real blank line ends the quote block. */
|
||||||
|
if (is_line_blank(next)) break;
|
||||||
|
if (is_fence_raw(next) || is_hr_raw(next) || is_table_raw(next)) break;
|
||||||
|
|
||||||
|
/* Only the next quote line may continue this paragraph. */
|
||||||
|
uint32_t next_content = next;
|
||||||
|
uint8_t next_kind = classify_line(next, &next_content);
|
||||||
|
if (next_kind != LK_QUOTE) break;
|
||||||
|
|
||||||
|
/* Determine the physical end of that quote line. */
|
||||||
|
uint32_t next_line_end = next_content;
|
||||||
|
while (next_line_end < file_size && fb(next_line_end) != '\n') next_line_end++;
|
||||||
|
|
||||||
|
/* An empty quote line (`>`/`> `) is a paragraph separator:
|
||||||
|
* stop here; the outer loop will emit it as its own quoted row. */
|
||||||
|
uint8_t empty_quote = 1;
|
||||||
|
for (uint32_t z = next_content; z < next_line_end; z++) {
|
||||||
|
char c = fb(z);
|
||||||
|
if (c != ' ' && c != '\t') { empty_quote = 0; break; }
|
||||||
|
}
|
||||||
|
if (empty_quote) break;
|
||||||
|
|
||||||
|
/* Nested quote (`> > ...`) starts a new quote paragraph;
|
||||||
|
* keep it as a separate row instead of joining into text. */
|
||||||
|
{
|
||||||
|
uint32_t t = next_content;
|
||||||
|
while (t < next_line_end && (fb(t) == ' ' || fb(t) == '\t')) t++;
|
||||||
|
if (t < next_line_end && fb(t) == '>') break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Soft-join with one separating space; trim leading
|
||||||
|
* indentation in the continued quote content. */
|
||||||
|
while (next_content < next_line_end) {
|
||||||
|
char ws = fb(next_content);
|
||||||
|
if (ws == ' ' || ws == '\t') next_content++;
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
q = next_content;
|
||||||
|
soft_break = 1;
|
||||||
|
ch = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == '`') {
|
||||||
|
if (line_style == INIT_STYLE_CODE) line_style = INIT_STYLE_PLAIN;
|
||||||
|
else line_style = INIT_STYLE_CODE;
|
||||||
|
if (!soft_break) q++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ch == '*' && (q + 1) < file_size && fb(q + 1) == '*') {
|
||||||
|
char next_ch = ((q + 2) < file_size) ? fb(q + 2) : '\n';
|
||||||
|
if (is_emph_flanked(prev_ch, next_ch)) {
|
||||||
|
if (line_style == INIT_STYLE_PLAIN && ws_or_eol(prev_ch)) {
|
||||||
|
line_style = INIT_STYLE_BOLD;
|
||||||
|
if (!soft_break) q += 2; else q++;
|
||||||
|
continue;
|
||||||
|
} else if (line_style == INIT_STYLE_BOLD && ws_or_eol_or_delim(next_ch)) {
|
||||||
|
line_style = INIT_STYLE_PLAIN;
|
||||||
|
if (!soft_break) q += 2; else q++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
seg_col += 2;
|
||||||
|
if (!soft_break) q += 2; else q++;
|
||||||
|
prev_ch = '*'; continue;
|
||||||
|
}
|
||||||
|
if (ch == '~' && (q + 1) < file_size && fb(q + 1) == '~') {
|
||||||
|
char next_ch = ((q + 2) < file_size) ? fb(q + 2) : '\n';
|
||||||
|
if (is_emph_flanked(prev_ch, next_ch)) {
|
||||||
|
if (line_style == INIT_STYLE_PLAIN && ws_or_eol(prev_ch)) {
|
||||||
|
line_style = INIT_STYLE_STRIKE;
|
||||||
|
if (!soft_break) q += 2; else q++;
|
||||||
|
continue;
|
||||||
|
} else if (line_style == INIT_STYLE_STRIKE && ws_or_eol_or_delim(next_ch)) {
|
||||||
|
line_style = INIT_STYLE_PLAIN;
|
||||||
|
if (!soft_break) q += 2; else q++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
seg_col += 2;
|
||||||
|
if (!soft_break) q += 2; else q++;
|
||||||
|
prev_ch = '~'; continue;
|
||||||
|
}
|
||||||
|
if (ch == '*' || ch == '_') {
|
||||||
|
char next_ch = ((q + 1) < file_size) ? fb(q + 1) : '\n';
|
||||||
|
if (is_emph_flanked(prev_ch, next_ch)) {
|
||||||
|
uint8_t new_style = (ch == '*') ? INIT_STYLE_ITALIC : INIT_STYLE_UNDER;
|
||||||
|
if (line_style == INIT_STYLE_PLAIN && ws_or_eol(prev_ch)) {
|
||||||
|
line_style = new_style;
|
||||||
|
if (!soft_break) q++; else q++;
|
||||||
|
continue;
|
||||||
|
} else if (line_style == new_style && ws_or_eol_or_delim(next_ch)) {
|
||||||
|
line_style = INIT_STYLE_PLAIN;
|
||||||
|
if (!soft_break) q++; else q++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
seg_col++;
|
||||||
|
if (!soft_break) q++; else q++;
|
||||||
|
prev_ch = ch; continue;
|
||||||
|
}
|
||||||
|
if (ch == '\t') {
|
||||||
|
uint8_t tgt = (uint8_t)((seg_col & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP);
|
||||||
|
if (tgt > SCREEN_W) tgt = SCREEN_W;
|
||||||
|
seg_col = tgt;
|
||||||
|
if (!soft_break) q++; else q++;
|
||||||
|
prev_ch = ' '; continue;
|
||||||
|
}
|
||||||
|
if (ch == ' ') {
|
||||||
|
if (soft_break) last_space = q;
|
||||||
|
else last_space = q + 1;
|
||||||
|
seg_col++;
|
||||||
|
if (!soft_break) q++; else q++;
|
||||||
|
prev_ch = ' ';
|
||||||
|
} else {
|
||||||
|
seg_col++;
|
||||||
|
if (!soft_break) q++; else q++;
|
||||||
|
prev_ch = ch;
|
||||||
|
}
|
||||||
|
if (seg_col >= SCREEN_W) {
|
||||||
|
uint32_t wrap_at = (last_space != 0xFFFFFFFFu && last_space > cur_seg_off)
|
||||||
|
? last_space : q;
|
||||||
|
if (wrap_at >= file_size || wrap_at == cur_seg_off) break;
|
||||||
|
emit_seg(wrap_at, line_style, CK_QUOTE, 1);
|
||||||
|
seg_col = col; last_space = 0xFFFFFFFFu; prev_ch = ' '; q = wrap_at;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (q < file_size && fb(q) != '\n') q++;
|
||||||
|
if (q < file_size) q++;
|
||||||
|
p = q;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user