mdview2: слить inline_scan + scan_join_stream в scan_stream — exe 25616 → 25100
B2 stage2: общий цикл inline-форматирования/переносов/склейки в одном scan_stream (mode: NONE / LIST / QUOTE / PLAIN); дублировавшиеся блоки эмиссии пробела/символа, переноса с усечением и отката стиля — в одном экземпляре. Старые имена — тонкие обёртки, API inline_scan для table-модуля не изменился. styles_map умерла: emph_to_attr(ls, ATTR_TEXT) тождественна ей. Индексатор 9839 → 9315 Б. Проверено в MAME: переносы заголовков/списков/ цитат, таблицы, inline-маркеры на границе переноса, жёсткие переносы. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,12 +9,12 @@
|
||||
* позиции чтения. Разделяемые символы и API — в mdview2.h.
|
||||
*
|
||||
* Приватно (не торчит в .h): idx_rec_t, cur_rec/cur_seg_off, буфер ячеек
|
||||
* g_cells, styles_map, idx_get/idx_put и все классификаторы/сканеры.
|
||||
* g_cells, idx_get/idx_put и все классификаторы/сканеры.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <conio.h> /* COLOR()/COLOR_* для ATTR_* в styles_map */
|
||||
#include <conio.h> /* COLOR()/COLOR_* для ATTR_* в сканерах */
|
||||
#include <sprinter.h>
|
||||
#include <sprinter_mem.h> /* bank_read/bank_write (idx_get/idx_put) */
|
||||
#include "mdview2.h"
|
||||
@@ -108,12 +108,6 @@ static void set_cur_flags(uint8_t f) { cur_rec.flags |= f; idx_put((uint16_t)(n_
|
||||
|
||||
/* INIT_STYLE_* — в mdview2.h (общие с модулем таблиц). */
|
||||
|
||||
/* Таблица сопоставления init-style -> атрибут.
|
||||
* Порядок должен совпадать с INIT_STYLE_* / EM_* ниже. */
|
||||
static const uint8_t styles_map[] = {
|
||||
ATTR_TEXT, ATTR_TEXT_BOLD, ATTR_TEXT_ITALIC, ATTR_TEXT_UNDERSORE, ATTR_TEXT_CODE, ATTR_TEXT_STRIKE
|
||||
};
|
||||
|
||||
/* Тип строки, который возвращает classify_line(). */
|
||||
#define LK_PLAIN 0
|
||||
#define LK_H1 1
|
||||
@@ -373,7 +367,7 @@ static uint8_t is_table_raw(uint32_t p)
|
||||
* маркер обработан (вызывающий делает continue), 0 — символ обычный (пробел/
|
||||
* текст), его кладёт сам вызывающий (и проверяет перенос). Меняет
|
||||
* *pq/*pls/*pseg/*pprev. attr берётся как emph_to_attr(line_style, base) —
|
||||
* для join base=ATTR_TEXT, что тождественно styles_map[line_style]. */
|
||||
* для join base=ATTR_TEXT (атрибут «обычного» текста). */
|
||||
static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls,
|
||||
uint8_t *pseg, char *pprev, uint8_t base)
|
||||
{
|
||||
@@ -433,87 +427,45 @@ static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls,
|
||||
return 0; /* пробел или обычный символ — обрабатывает вызывающий */
|
||||
}
|
||||
|
||||
/* Общий сканер inline-форматирования и переносов.
|
||||
* Идёт от q до q_end, учитывает стартовую колонку col и при необходимости
|
||||
* эмитит continuation-сегменты. Возвращает итоговый line_style. */
|
||||
uint8_t inline_scan(uint32_t q, uint32_t q_end, uint8_t col,
|
||||
uint8_t ckind, uint8_t line_style, uint8_t base_attr,
|
||||
uint8_t nowrap)
|
||||
{
|
||||
uint32_t last_space = 0xFFFFFFFFu;
|
||||
uint8_t last_space_style = line_style; /* снимок line_style на момент last_space */
|
||||
char prev_ch = ' ';
|
||||
uint8_t seg_col = col;
|
||||
/* base — атрибут «обычного» текста сегмента. Для заголовка первый сегмент
|
||||
* имеет title-attr, а continuation-сегменты render рисует как PLAIN
|
||||
* (ATTR_TEXT, kind=LK_PLAIN при cont) — переключаем base после 1-го переноса. */
|
||||
uint8_t base = base_attr;
|
||||
|
||||
while (q < q_end && n_lines < max_lines) {
|
||||
char ch = fb(q);
|
||||
|
||||
if (ch == '\n') {
|
||||
/* Жёсткий перенос (уже проверен снаружи) или конец потока. */
|
||||
q++;
|
||||
emit_seg(q, line_style, ckind, (ckind == CK_LIST || ckind == CK_QUOTE) ? 1 : 0);
|
||||
base = ATTR_TEXT;
|
||||
last_space = 0xFFFFFFFFu; prev_ch = ' '; seg_col = col;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Inline-маркеры (\X [x] ` ** ~~ * _ \t) — общий разбор. */
|
||||
if (inline_marker(&q, q_end, &line_style, &seg_col, &prev_ch, base)) continue;
|
||||
|
||||
if (ch == ' ') {
|
||||
gc_put(' ', emph_to_attr(line_style, base));
|
||||
last_space = q + 1;
|
||||
last_space_style = line_style;
|
||||
g_ncells_at_space = g_ncells;
|
||||
seg_col++; q++; prev_ch = ' ';
|
||||
} else {
|
||||
gc_put(ch, emph_to_attr(line_style, base));
|
||||
seg_col++; q++; prev_ch = ch;
|
||||
}
|
||||
if (!nowrap && seg_col >= SCREEN_W) {
|
||||
uint8_t use_ls = (uint8_t)(last_space != 0xFFFFFFFFu && last_space > cur_seg_off);
|
||||
uint32_t wrap_at = use_ls ? last_space : q;
|
||||
if (wrap_at >= q_end || wrap_at == cur_seg_off) break;
|
||||
uint8_t wrap_style = use_ls ? last_space_style : line_style;
|
||||
if (use_ls) g_ncells = g_ncells_at_space;
|
||||
emit_seg(wrap_at, wrap_style, ckind, 1);
|
||||
base = ATTR_TEXT; /* continuation заголовка → PLAIN */
|
||||
seg_col = col; last_space = 0xFFFFFFFFu; prev_ch = ' '; q = wrap_at;
|
||||
line_style = wrap_style;
|
||||
}
|
||||
}
|
||||
return line_style;
|
||||
}
|
||||
|
||||
#define JOIN_MODE_NONE 0u
|
||||
#define JOIN_MODE_LIST 1u
|
||||
#define JOIN_MODE_QUOTE 2u
|
||||
#define JOIN_MODE_PLAIN 3u
|
||||
|
||||
/* Общий потоковый сканер для index_lines().
|
||||
* Убирает дублирование между ветками list/quote/plain:
|
||||
* - mode задаёт правила обработки перевода строки,
|
||||
* - остальная inline-логика (emphasis/таб/перенос) едина.
|
||||
* Возвращает позицию q, на которой сканер остановился, и обновляет стиль. */
|
||||
static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col,
|
||||
uint8_t ckind, uint8_t *io_line_style, uint8_t mode)
|
||||
/* ================== Единый сканер сегмента ================================
|
||||
* Общий цикл inline-форматирования, переносов и (в join-режимах) склейки
|
||||
* физических строк — бывшие inline_scan + scan_join_stream. mode:
|
||||
* JOIN_MODE_NONE — сканировать РОВНО [q, bound) без склейки; '\n' в
|
||||
* диапазоне не ожидается (вызывающие дают строку/ячейку до EOL);
|
||||
* активны base_attr (title-attr заголовка) и nowrap (замер таблиц);
|
||||
* ckind всегда CK_OTHER (continuation-префикс не эмитится).
|
||||
* JOIN_MODE_LIST/QUOTE/PLAIN — правила '\n'-склейки; bound == file_size,
|
||||
* base_attr == ATTR_TEXT (emph_to_attr(ls, ATTR_TEXT) тождественно
|
||||
* бывшему styles_map[ls]), nowrap == 0.
|
||||
* Возвращает позицию останова; итоговый стиль — через *io_line_style. */
|
||||
static uint32_t scan_stream(uint32_t q, uint32_t bound, uint32_t para_start,
|
||||
uint8_t col, uint8_t ckind, uint8_t *io_line_style,
|
||||
uint8_t base_attr, uint8_t nowrap, uint8_t mode)
|
||||
{
|
||||
uint32_t last_space = 0xFFFFFFFFu;
|
||||
uint8_t last_space_style = *io_line_style; /* снимок line_style на момент last_space */
|
||||
char prev_ch = ' ';
|
||||
uint8_t seg_col = col;
|
||||
uint8_t line_style = *io_line_style;
|
||||
/* base — атрибут «обычного» текста сегмента. Для заголовка первый сегмент
|
||||
* имеет title-attr, а continuation-сегменты render рисует как PLAIN —
|
||||
* переключаем base после 1-го переноса (в join-режимах base == ATTR_TEXT
|
||||
* и присваивание ничего не меняет). */
|
||||
uint8_t base = base_attr;
|
||||
|
||||
while (q < file_size && n_lines < max_lines) {
|
||||
while (q < bound && 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;
|
||||
if (mode == JOIN_MODE_NONE) break; /* NONE: '\n' в [q,bound) не ожидается */
|
||||
if (next >= bound) break;
|
||||
|
||||
if (mode == JOIN_MODE_LIST) {
|
||||
if (is_line_blank(next)) break;
|
||||
@@ -529,7 +481,7 @@ static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col,
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (next < file_size) {
|
||||
while (next < bound) {
|
||||
char ws = fb(next);
|
||||
if (ws == ' ' || ws == '\t') next++;
|
||||
else break;
|
||||
@@ -612,7 +564,7 @@ static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col,
|
||||
q++;
|
||||
/* как render: пропускаем ведущие пробелы/табы строки-продолжения,
|
||||
* склейка даёт РОВНО один пробел (иначе merge эмитит отступ как лишние ячейки) */
|
||||
while (q < file_size && (fb(q) == ' ' || fb(q) == '\t')) q++;
|
||||
while (q < bound && (fb(q) == ' ' || fb(q) == '\t')) q++;
|
||||
soft_break = 1;
|
||||
ch = ' ';
|
||||
}
|
||||
@@ -621,39 +573,39 @@ static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col,
|
||||
/* Inline-маркеры (\X [x] ` ** ~~ * _ \t) — общий разбор; при
|
||||
* soft_break ch==' ' (синтетический пробел склейки) — маркеры
|
||||
* пропускаем, его кладёт ветка пробела ниже. */
|
||||
if (!soft_break && inline_marker(&q, file_size, &line_style, &seg_col, &prev_ch, ATTR_TEXT))
|
||||
if (!soft_break && inline_marker(&q, bound, &line_style, &seg_col, &prev_ch, base))
|
||||
continue;
|
||||
|
||||
if (ch == ' ') {
|
||||
gc_put(' ', styles_map[line_style]);
|
||||
gc_put(' ', emph_to_attr(line_style, base));
|
||||
if (soft_break) {
|
||||
last_space = q;
|
||||
last_space_style = line_style;
|
||||
seg_col++;
|
||||
last_space = q; /* синтетический пробел склейки */
|
||||
} else {
|
||||
last_space = q + 1;
|
||||
last_space_style = line_style;
|
||||
seg_col++;
|
||||
q++;
|
||||
}
|
||||
g_ncells_at_space = g_ncells; /* снимок для усечения */
|
||||
last_space_style = line_style;
|
||||
g_ncells_at_space = g_ncells; /* снимок для усечения */
|
||||
seg_col++;
|
||||
prev_ch = ' ';
|
||||
} else {
|
||||
gc_put(ch, styles_map[line_style]);
|
||||
gc_put(ch, emph_to_attr(line_style, base));
|
||||
seg_col++;
|
||||
q++; /* soft_break здесь всегда 0 (ch!=' ') */
|
||||
q++; /* soft_break здесь всегда 0 (ch != ' ') */
|
||||
prev_ch = ch;
|
||||
}
|
||||
if (seg_col >= SCREEN_W) {
|
||||
if (!nowrap && seg_col >= SCREEN_W) {
|
||||
uint8_t use_ls = (uint8_t)(last_space != 0xFFFFFFFFu && last_space > cur_seg_off);
|
||||
uint32_t wrap_at = use_ls ? last_space : q;
|
||||
if (wrap_at >= file_size || wrap_at == cur_seg_off) break;
|
||||
if (wrap_at >= bound || wrap_at == cur_seg_off) break;
|
||||
uint8_t wrap_style = use_ls ? last_space_style : line_style;
|
||||
if (use_ls) g_ncells = g_ncells_at_space; /* усечь до пробела */
|
||||
emit_seg(wrap_at, wrap_style, ckind, 1);
|
||||
base = ATTR_TEXT; /* continuation заголовка → PLAIN (в join base и так ATTR_TEXT) */
|
||||
/* Префикс continuation-сегмента: список и отступленный абзац (#4) —
|
||||
* col пробелов выравнивания; цитата — отступ + маркер 0xB3 + пробел.
|
||||
* Для обычного абзаца без отступа col==0 → префикса нет. */
|
||||
* Для обычного абзаца без отступа col==0 → префикса нет; CK_OTHER
|
||||
* (заголовки/таблицы, NONE-режим) — префикса нет. */
|
||||
if (ckind == CK_LIST || ckind == CK_PLAIN) {
|
||||
gc_fill(' ', ATTR_TEXT, col);
|
||||
} else if (ckind == CK_QUOTE) {
|
||||
@@ -682,6 +634,25 @@ static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col,
|
||||
return q;
|
||||
}
|
||||
|
||||
/* Тонкая обёртка прежнего API: скан ровно [q, q_end) без склейки строк.
|
||||
* Вызывается для заголовков и ячеек таблиц (всегда CK_OTHER, col=0). */
|
||||
uint8_t inline_scan(uint32_t q, uint32_t q_end, uint8_t col,
|
||||
uint8_t ckind, uint8_t line_style, uint8_t base_attr,
|
||||
uint8_t nowrap)
|
||||
{
|
||||
(void)scan_stream(q, q_end, q, col, ckind, &line_style, base_attr, nowrap,
|
||||
JOIN_MODE_NONE);
|
||||
return line_style;
|
||||
}
|
||||
|
||||
/* Потоковый сканер для index_lines(): mode задаёт правила '\n'-склейки. */
|
||||
static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col,
|
||||
uint8_t ckind, uint8_t *io_line_style, uint8_t mode)
|
||||
{
|
||||
return scan_stream(q, file_size, para_start, col, ckind, io_line_style,
|
||||
ATTR_TEXT, 0, mode);
|
||||
}
|
||||
|
||||
/* Отрисовка таблиц (row_end/table_*) — в модуле mdview2_table.c. */
|
||||
|
||||
/* Строит индекс сегментов рендера по загруженному файлу. */
|
||||
|
||||
Reference in New Issue
Block a user