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.
|
* позиции чтения. Разделяемые символы и API — в mdview2.h.
|
||||||
*
|
*
|
||||||
* Приватно (не торчит в .h): idx_rec_t, cur_rec/cur_seg_off, буфер ячеек
|
* Приватно (не торчит в .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 <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <conio.h> /* COLOR()/COLOR_* для ATTR_* в styles_map */
|
#include <conio.h> /* COLOR()/COLOR_* для ATTR_* в сканерах */
|
||||||
#include <sprinter.h>
|
#include <sprinter.h>
|
||||||
#include <sprinter_mem.h> /* bank_read/bank_write (idx_get/idx_put) */
|
#include <sprinter_mem.h> /* bank_read/bank_write (idx_get/idx_put) */
|
||||||
#include "mdview2.h"
|
#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_* — в 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(). */
|
/* Тип строки, который возвращает classify_line(). */
|
||||||
#define LK_PLAIN 0
|
#define LK_PLAIN 0
|
||||||
#define LK_H1 1
|
#define LK_H1 1
|
||||||
@@ -373,7 +367,7 @@ static uint8_t is_table_raw(uint32_t p)
|
|||||||
* маркер обработан (вызывающий делает continue), 0 — символ обычный (пробел/
|
* маркер обработан (вызывающий делает continue), 0 — символ обычный (пробел/
|
||||||
* текст), его кладёт сам вызывающий (и проверяет перенос). Меняет
|
* текст), его кладёт сам вызывающий (и проверяет перенос). Меняет
|
||||||
* *pq/*pls/*pseg/*pprev. attr берётся как emph_to_attr(line_style, base) —
|
* *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,
|
static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls,
|
||||||
uint8_t *pseg, char *pprev, uint8_t base)
|
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; /* пробел или обычный символ — обрабатывает вызывающий */
|
return 0; /* пробел или обычный символ — обрабатывает вызывающий */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Общий сканер inline-форматирования и переносов.
|
#define JOIN_MODE_NONE 0u
|
||||||
* Идёт от 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_LIST 1u
|
#define JOIN_MODE_LIST 1u
|
||||||
#define JOIN_MODE_QUOTE 2u
|
#define JOIN_MODE_QUOTE 2u
|
||||||
#define JOIN_MODE_PLAIN 3u
|
#define JOIN_MODE_PLAIN 3u
|
||||||
|
|
||||||
/* Общий потоковый сканер для index_lines().
|
/* ================== Единый сканер сегмента ================================
|
||||||
* Убирает дублирование между ветками list/quote/plain:
|
* Общий цикл inline-форматирования, переносов и (в join-режимах) склейки
|
||||||
* - mode задаёт правила обработки перевода строки,
|
* физических строк — бывшие inline_scan + scan_join_stream. mode:
|
||||||
* - остальная inline-логика (emphasis/таб/перенос) едина.
|
* JOIN_MODE_NONE — сканировать РОВНО [q, bound) без склейки; '\n' в
|
||||||
* Возвращает позицию q, на которой сканер остановился, и обновляет стиль. */
|
* диапазоне не ожидается (вызывающие дают строку/ячейку до EOL);
|
||||||
static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col,
|
* активны base_attr (title-attr заголовка) и nowrap (замер таблиц);
|
||||||
uint8_t ckind, uint8_t *io_line_style, uint8_t mode)
|
* 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;
|
uint32_t last_space = 0xFFFFFFFFu;
|
||||||
uint8_t last_space_style = *io_line_style; /* снимок line_style на момент last_space */
|
uint8_t last_space_style = *io_line_style; /* снимок line_style на момент last_space */
|
||||||
char prev_ch = ' ';
|
char prev_ch = ' ';
|
||||||
uint8_t seg_col = col;
|
uint8_t seg_col = col;
|
||||||
uint8_t line_style = *io_line_style;
|
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);
|
char ch = fb(q);
|
||||||
uint8_t soft_break = 0;
|
uint8_t soft_break = 0;
|
||||||
|
|
||||||
if (ch == '\n') {
|
if (ch == '\n') {
|
||||||
uint32_t next = q + 1;
|
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 (mode == JOIN_MODE_LIST) {
|
||||||
if (is_line_blank(next)) break;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (next < file_size) {
|
while (next < bound) {
|
||||||
char ws = fb(next);
|
char ws = fb(next);
|
||||||
if (ws == ' ' || ws == '\t') next++;
|
if (ws == ' ' || ws == '\t') next++;
|
||||||
else break;
|
else break;
|
||||||
@@ -612,7 +564,7 @@ static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col,
|
|||||||
q++;
|
q++;
|
||||||
/* как render: пропускаем ведущие пробелы/табы строки-продолжения,
|
/* как render: пропускаем ведущие пробелы/табы строки-продолжения,
|
||||||
* склейка даёт РОВНО один пробел (иначе merge эмитит отступ как лишние ячейки) */
|
* склейка даёт РОВНО один пробел (иначе merge эмитит отступ как лишние ячейки) */
|
||||||
while (q < file_size && (fb(q) == ' ' || fb(q) == '\t')) q++;
|
while (q < bound && (fb(q) == ' ' || fb(q) == '\t')) q++;
|
||||||
soft_break = 1;
|
soft_break = 1;
|
||||||
ch = ' ';
|
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) — общий разбор; при
|
/* Inline-маркеры (\X [x] ` ** ~~ * _ \t) — общий разбор; при
|
||||||
* soft_break ch==' ' (синтетический пробел склейки) — маркеры
|
* 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;
|
continue;
|
||||||
|
|
||||||
if (ch == ' ') {
|
if (ch == ' ') {
|
||||||
gc_put(' ', styles_map[line_style]);
|
gc_put(' ', emph_to_attr(line_style, base));
|
||||||
if (soft_break) {
|
if (soft_break) {
|
||||||
last_space = q;
|
last_space = q; /* синтетический пробел склейки */
|
||||||
last_space_style = line_style;
|
|
||||||
seg_col++;
|
|
||||||
} else {
|
} else {
|
||||||
last_space = q + 1;
|
last_space = q + 1;
|
||||||
last_space_style = line_style;
|
|
||||||
seg_col++;
|
|
||||||
q++;
|
q++;
|
||||||
}
|
}
|
||||||
|
last_space_style = line_style;
|
||||||
g_ncells_at_space = g_ncells; /* снимок для усечения */
|
g_ncells_at_space = g_ncells; /* снимок для усечения */
|
||||||
|
seg_col++;
|
||||||
prev_ch = ' ';
|
prev_ch = ' ';
|
||||||
} else {
|
} else {
|
||||||
gc_put(ch, styles_map[line_style]);
|
gc_put(ch, emph_to_attr(line_style, base));
|
||||||
seg_col++;
|
seg_col++;
|
||||||
q++; /* soft_break здесь всегда 0 (ch != ' ') */
|
q++; /* soft_break здесь всегда 0 (ch != ' ') */
|
||||||
prev_ch = 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);
|
uint8_t use_ls = (uint8_t)(last_space != 0xFFFFFFFFu && last_space > cur_seg_off);
|
||||||
uint32_t wrap_at = use_ls ? last_space : q;
|
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;
|
uint8_t wrap_style = use_ls ? last_space_style : line_style;
|
||||||
if (use_ls) g_ncells = g_ncells_at_space; /* усечь до пробела */
|
if (use_ls) g_ncells = g_ncells_at_space; /* усечь до пробела */
|
||||||
emit_seg(wrap_at, wrap_style, ckind, 1);
|
emit_seg(wrap_at, wrap_style, ckind, 1);
|
||||||
|
base = ATTR_TEXT; /* continuation заголовка → PLAIN (в join base и так ATTR_TEXT) */
|
||||||
/* Префикс continuation-сегмента: список и отступленный абзац (#4) —
|
/* Префикс continuation-сегмента: список и отступленный абзац (#4) —
|
||||||
* col пробелов выравнивания; цитата — отступ + маркер 0xB3 + пробел.
|
* col пробелов выравнивания; цитата — отступ + маркер 0xB3 + пробел.
|
||||||
* Для обычного абзаца без отступа col==0 → префикса нет. */
|
* Для обычного абзаца без отступа col==0 → префикса нет; CK_OTHER
|
||||||
|
* (заголовки/таблицы, NONE-режим) — префикса нет. */
|
||||||
if (ckind == CK_LIST || ckind == CK_PLAIN) {
|
if (ckind == CK_LIST || ckind == CK_PLAIN) {
|
||||||
gc_fill(' ', ATTR_TEXT, col);
|
gc_fill(' ', ATTR_TEXT, col);
|
||||||
} else if (ckind == CK_QUOTE) {
|
} 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;
|
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. */
|
/* Отрисовка таблиц (row_end/table_*) — в модуле mdview2_table.c. */
|
||||||
|
|
||||||
/* Строит индекс сегментов рендера по загруженному файлу. */
|
/* Строит индекс сегментов рендера по загруженному файлу. */
|
||||||
|
|||||||
Reference in New Issue
Block a user