mdview2: B2 stage 1 — вынос inline_marker (-870 байт)

Разбор inline-маркеров (\X, [x], `, **, ~~, *, _, \t) был продублирован
в inline_scan() и scan_join_stream(). Вынесен в общий inline_marker():
возвращает 1 если токен обработан, 0 если обычный символ/пробел (его кладёт
вызывающий). attr = emph_to_attr(ls, base); для join base=ATTR_TEXT, что
тождественно прежнему styles_map[ls]. В scan_join маркеры пропускаются при
soft_break (синтетический пробел склейки кладёт ветка пробела).

scan_join_stream 3794→2329, inline_scan 1842→700, inline_marker +1436.
_CODE 22246→21376. Рендер идентичный (проверено в MAME).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-25 14:33:17 +03:00
parent 6d515e4b9a
commit 17639ed62d
+71 -171
View File
@@ -1011,6 +1011,70 @@ static uint8_t is_table_raw(uint32_t p)
return 0;
}
/* Разбор одного inline-МАРКЕРА в позиции *pq (< bound): \X, [x], `, **, ~~,
* *, _, \t. Общий код для inline_scan() и scan_join_stream(). Возврат: 1 —
* маркер обработан (вызывающий делает continue), 0 — символ обычный (пробел/
* текст), его кладёт сам вызывающий (и проверяет перенос). Меняет
* *pq/*pls/*pseg/*pprev. attr берётся как emph_to_attr(line_style, base) —
* для join base=ATTR_TEXT, что тождественно styles_map[line_style]. */
static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls,
uint8_t *pseg, char *pprev, uint8_t base)
{
uint32_t q = *pq;
uint8_t ls = *pls;
char ch = fb(q);
if (ls != INIT_STYLE_CODE && ch == '\\' && (q + 1) < bound && is_escapable(fb(q + 1))) {
char nb = fb(q + 1);
gc_put(nb, emph_to_attr(ls, base));
*pseg += 1; *pq = q + 2; *pprev = nb; return 1;
}
if (ls != INIT_STYLE_CODE && ch == '[' && (q + 2) < bound && fb(q + 2) == ']') {
gc_put('[', ATTR_TEXT_BOLD); gc_put(fb(q + 1), ATTR_TEXT_BOLD); gc_put(']', ATTR_TEXT_BOLD);
*pseg += 3; *pq = q + 3; *pprev = ']'; return 1;
}
if (ch == '`') {
if (ls == INIT_STYLE_PLAIN) { *pls = INIT_STYLE_CODE; *pq = q + 1; return 1; }
if (ls == INIT_STYLE_CODE) { *pls = INIT_STYLE_PLAIN; *pq = q + 1; return 1; }
gc_put('`', emph_to_attr(ls, base)); *pseg += 1; *pq = q + 1; *pprev = '`'; return 1;
}
if (ch == '*' && (q + 1) < bound && fb(q + 1) == '*') {
char nx = ((q + 2) < bound) ? fb(q + 2) : '\n';
if (is_emph_flanked(*pprev, nx)) {
if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = INIT_STYLE_BOLD; *pq = q + 2; return 1; }
if (ls == INIT_STYLE_BOLD && ws_or_eol_or_delim(nx)) { *pls = INIT_STYLE_PLAIN; *pq = q + 2; return 1; }
}
gc_put('*', emph_to_attr(ls, base)); gc_put('*', emph_to_attr(ls, base));
*pseg += 2; *pq = q + 2; *pprev = '*'; return 1;
}
if (ch == '~' && (q + 1) < bound && fb(q + 1) == '~') {
char nx = ((q + 2) < bound) ? fb(q + 2) : '\n';
if (is_emph_flanked(*pprev, nx)) {
if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = INIT_STYLE_STRIKE; *pq = q + 2; return 1; }
if (ls == INIT_STYLE_STRIKE && ws_or_eol_or_delim(nx)) { *pls = INIT_STYLE_PLAIN; *pq = q + 2; return 1; }
}
gc_put('~', emph_to_attr(ls, base)); gc_put('~', emph_to_attr(ls, base));
*pseg += 2; *pq = q + 2; *pprev = '~'; return 1;
}
if (ch == '*' || ch == '_') {
char nx = ((q + 1) < bound) ? fb(q + 1) : '\n';
if (is_emph_flanked(*pprev, nx)) {
uint8_t ns = (ch == '*') ? INIT_STYLE_ITALIC : INIT_STYLE_UNDER;
if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = ns; *pq = q + 1; return 1; }
if (ls != INIT_STYLE_PLAIN && ws_or_eol_or_delim(nx)) { *pls = INIT_STYLE_PLAIN; *pq = q + 1; return 1; }
}
gc_put(ch, emph_to_attr(ls, base)); *pseg += 1; *pq = q + 1; *pprev = ch; return 1;
}
if (ch == '\t') {
uint8_t old_sc = *pseg;
uint8_t tgt_full = (uint8_t)((old_sc & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP);
uint8_t tgt = tgt_full; if (tgt > SCREEN_W) tgt = SCREEN_W;
gc_fill(' ', emph_to_attr(ls, base), (uint8_t)(tgt_full - old_sc));
*pseg = tgt; *pq = q + 1; *pprev = ' '; return 1;
}
return 0; /* пробел или обычный символ — обрабатывает вызывающий */
}
/* Общий сканер inline-форматирования и переносов.
* Идёт от q до q_end, учитывает стартовую колонку col и при необходимости
* эмитит continuation-сегменты. Возвращает итоговый line_style. */
@@ -1039,73 +1103,9 @@ static uint8_t inline_scan(uint32_t q, uint32_t q_end, uint8_t col,
continue;
}
/* Экранированная пунктуация: \* \_ \` … → литерал (не внутри кода). */
if (line_style != INIT_STYLE_CODE && ch == '\\' && (q + 1) < q_end && is_escapable(fb(q + 1))) {
char nb = fb(q + 1);
gc_put(nb, emph_to_attr(line_style, base));
seg_col++; q += 2; prev_ch = nb; continue;
}
/* [x] — ровно один символ в скобках → болд (task-list checkbox; не внутри кода). */
if (line_style != INIT_STYLE_CODE && ch == '[' && (q + 2) < q_end && fb(q + 2) == ']') {
char mid = fb(q + 1);
gc_put('[', ATTR_TEXT_BOLD);
gc_put(mid, ATTR_TEXT_BOLD);
gc_put(']', ATTR_TEXT_BOLD);
seg_col += 3; q += 3; prev_ch = ']'; continue;
}
/* Inline-маркеры (\X [x] ` ** ~~ * _ \t) — общий разбор. */
if (inline_marker(&q, q_end, &line_style, &seg_col, &prev_ch, base)) continue;
if (ch == '`') {
if (line_style == INIT_STYLE_PLAIN) { line_style = INIT_STYLE_CODE; q++; continue; }
if (line_style == INIT_STYLE_CODE) { line_style = INIT_STYLE_PLAIN; q++; continue; }
gc_put('`', emph_to_attr(line_style, base));
seg_col++; q++; prev_ch = '`'; continue;
}
if (ch == '*' && (q + 1) < q_end && fb(q + 1) == '*') {
char next_ch = ((q + 2) < q_end) ? 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; q += 2; continue;
} else if (line_style == INIT_STYLE_BOLD && ws_or_eol_or_delim(next_ch)) {
line_style = INIT_STYLE_PLAIN; q += 2; continue;
}
}
{ gc_put('*', emph_to_attr(line_style, base)); gc_put('*', emph_to_attr(line_style, base)); }
seg_col += 2; q += 2; prev_ch = '*'; continue;
}
if (ch == '~' && (q + 1) < q_end && fb(q + 1) == '~') {
char next_ch = ((q + 2) < q_end) ? 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; q += 2; continue;
} else if (line_style == INIT_STYLE_STRIKE && ws_or_eol_or_delim(next_ch)) {
line_style = INIT_STYLE_PLAIN; q += 2; continue;
}
}
{ gc_put('~', emph_to_attr(line_style, base)); gc_put('~', emph_to_attr(line_style, base)); }
seg_col += 2; q += 2; prev_ch = '~'; continue;
}
if (ch == '*' || ch == '_') {
char next_ch = ((q + 1) < q_end) ? 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; q++; continue;
} else if (line_style != INIT_STYLE_PLAIN && ws_or_eol_or_delim(next_ch)) {
/* как render: закрывает любой активный эмфазис */
line_style = INIT_STYLE_PLAIN; q++; continue;
}
}
gc_put(ch, emph_to_attr(line_style, base));
seg_col++; q++; prev_ch = ch; continue;
}
if (ch == '\t') {
uint8_t old_sc = seg_col;
uint8_t tgt_full = (uint8_t)((seg_col & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP);
uint8_t tgt = tgt_full;
if (tgt > SCREEN_W) tgt = SCREEN_W;
gc_fill(' ', emph_to_attr(line_style, base), (uint8_t)(tgt_full - old_sc));
seg_col = tgt; q++; prev_ch = ' '; continue;
}
if (ch == ' ') {
gc_put(' ', emph_to_attr(line_style, base));
last_space = q + 1;
@@ -1261,112 +1261,12 @@ static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col,
}
}
/* Экранированная пунктуация: \* \_ \` … → литерал (не внутри кода). */
if (line_style != INIT_STYLE_CODE && ch == '\\' && (q + 1) < file_size && is_escapable(fb(q + 1))) {
char nb = fb(q + 1);
gc_put(nb, styles_map[line_style]);
seg_col++; q += 2; prev_ch = nb; continue;
}
/* [x] — ровно один символ в скобках → болд (task-list checkbox; не внутри кода). */
if (line_style != INIT_STYLE_CODE && ch == '[' && (q + 2) < file_size && fb(q + 2) == ']') {
char mid = fb(q + 1);
gc_put('[', ATTR_TEXT_BOLD);
gc_put(mid, ATTR_TEXT_BOLD);
gc_put(']', ATTR_TEXT_BOLD);
seg_col += 3; q += 3; prev_ch = ']'; continue;
}
/* Inline-маркеры (\X [x] ` ** ~~ * _ \t) — общий разбор; при
* soft_break ch==' ' (синтетический пробел склейки) — маркеры
* пропускаем, его кладёт ветка пробела ниже. */
if (!soft_break && inline_marker(&q, file_size, &line_style, &seg_col, &prev_ch, ATTR_TEXT))
continue;
if (ch == '`') {
/* Как handle_inline_marker в render: backtick переключает code-режим
* ТОЛЬКО из PLAIN или CODE; если активен эмфазис (bold/italic/...),
* backtick — обычный литеральный символ (нет вложенного кода). */
if (line_style == INIT_STYLE_PLAIN) {
line_style = INIT_STYLE_CODE;
q++; /* soft_break здесь всегда 0 (ch!=' ') */
continue;
}
if (line_style == INIT_STYLE_CODE) {
line_style = INIT_STYLE_PLAIN;
q++; /* soft_break здесь всегда 0 (ch!=' ') */
continue;
}
gc_put('`', styles_map[line_style]);
seg_col++;
q++; /* soft_break здесь всегда 0 (ch!=' ') */
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_BOLD;
q += 2; /* soft_break здесь всегда 0 (ch!=' ') */
continue;
} else if (line_style == INIT_STYLE_BOLD && ws_or_eol_or_delim(next_ch)) {
line_style = INIT_STYLE_PLAIN;
q += 2; /* soft_break здесь всегда 0 (ch!=' ') */
continue;
}
}
{ gc_put('*', styles_map[line_style]); gc_put('*', styles_map[line_style]); }
seg_col += 2;
q += 2; /* soft_break здесь всегда 0 (ch!=' ') */
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;
q += 2; /* soft_break здесь всегда 0 (ch!=' ') */
continue;
} else if (line_style == INIT_STYLE_STRIKE && ws_or_eol_or_delim(next_ch)) {
line_style = INIT_STYLE_PLAIN;
q += 2; /* soft_break здесь всегда 0 (ch!=' ') */
continue;
}
}
{ gc_put('~', styles_map[line_style]); gc_put('~', styles_map[line_style]); }
seg_col += 2;
q += 2; /* soft_break здесь всегда 0 (ch!=' ') */
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;
q++; /* soft_break здесь всегда 0 (ch!=' ') */
continue;
} else if (line_style != INIT_STYLE_PLAIN && ws_or_eol_or_delim(next_ch)) {
/* как render: одиночный маркер закрывает ЛЮБОЙ активный эмфазис */
line_style = INIT_STYLE_PLAIN;
q++; /* soft_break здесь всегда 0 (ch!=' ') */
continue;
}
}
gc_put(ch, styles_map[line_style]);
seg_col++;
q++; /* soft_break здесь всегда 0 (ch!=' ') */
prev_ch = ch;
continue;
}
if (ch == '\t') {
uint8_t old_sc = seg_col;
uint8_t tgt_full = (uint8_t)((seg_col & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP);
uint8_t tgt = tgt_full;
if (tgt > SCREEN_W) tgt = SCREEN_W;
/* render заполняет до НЕкапнутого tgt_full (по cc); для plain cc==seg_col */
gc_fill(' ', styles_map[line_style], (uint8_t)(tgt_full - old_sc));
seg_col = tgt;
q++; /* soft_break здесь всегда 0 (ch!=' ') */
prev_ch = ' ';
continue;
}
if (ch == ' ') {
gc_put(' ', styles_map[line_style]);
if (soft_break) {