mdview2: оптимизация размера — exe 28215 → 25616 (−2599 Б)

Раунд 1 (−1411): --max-allocs 100000 в Makefile (−848 кода) + снятие
всех нулевых инициализаторов file-scope переменных (_INITIALIZER
583→24; _DATA теперь зануляется в crt0).

Раунд 2 (−1188, индексатор 11019→9839): дедупликации в mdview2_index.c:
- classify_line: копия HR-проверки → вызов is_hr_raw;
- next_line() поверх row_end() вместо 9 копий «домотать до \n»;
- inline_marker: emph_to_attr(ls, base) вычисляется один раз (at);
- set_{nowrap,blank,code,hscroll}_cur → set_cur_flags(mask): строка
  code-блока делает один idx_put вместо трёх.

Проверено в MAME (README/UTF8TEST: маркеры, списки, цитаты, таблицы,
code-блоки, F8).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-04 21:15:43 +03:00
parent 961cfb786d
commit ffd179e064
5 changed files with 95 additions and 118 deletions
+3
View File
@@ -10,6 +10,9 @@ PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := mdview2 EXAMPLE := mdview2
EXTRA_SRCS := mdview2_raw.c mdview2_help.c mdview2_table.c mdview2_enc.c mdview2_md.c mdview2_status.c mdview2_index.c EXTRA_SRCS := mdview2_raw.c mdview2_help.c mdview2_table.c mdview2_enc.c mdview2_md.c mdview2_status.c mdview2_index.c
MEMORY := small MEMORY := small
# --max-allocs 100000: агрессивнее аллокация регистров SDCC — минус ~850 байт
# кода на всём приложении (замер 2026-07-04) ценой более долгой компиляции.
EXTRA_FLAGS := --max-allocs 100000
include $(PROJ_ROOT)/app.mk include $(PROJ_ROOT)/app.mk
+38 -38
View File
@@ -101,15 +101,15 @@ static const uint8_t md_pallete[16 * 4] = {
/* MAX_CACHE_LINE_LEN — в mdview2.h (буфер ячеек g_cells живёт в mdview2_index.c). */ /* MAX_CACHE_LINE_LEN — в mdview2.h (буфер ячеек g_cells живёт в mdview2_index.c). */
#define MAX_CACHE_CONTENT_PAGES 40u /* кап контента на набор: 40 * 16 КБ = 640 КБ (80 на оба набора) */ #define MAX_CACHE_CONTENT_PAGES 40u /* кап контента на набор: 40 * 16 КБ = 640 КБ (80 на оба набора) */
static uint8_t cache_dir_blk = 0; static uint8_t cache_dir_blk;
static uint8_t cache_dir_pages = 0; static uint8_t cache_dir_pages;
static uint8_t cache_dir_phys[MAX_CACHE_DIR_PAGES] = {0}; static uint8_t cache_dir_phys[MAX_CACHE_DIR_PAGES];
static uint8_t cache_content_blk[MAX_CACHE_CONTENT_PAGES] = {0}; /* id блока на каждую выделенную страницу */ static uint8_t cache_content_blk[MAX_CACHE_CONTENT_PAGES]; /* id блока на каждую выделенную страницу */
static uint8_t cache_content_phys[MAX_CACHE_CONTENT_PAGES] = {0}; static uint8_t cache_content_phys[MAX_CACHE_CONTENT_PAGES];
static uint8_t cache_content_n_pages = 0; /* сколько страниц уже выделено */ static uint8_t cache_content_n_pages; /* сколько страниц уже выделено */
static uint8_t cache_cur_page_idx = 0; /* индекс текущей страницы записи в cache_content_phys */ static uint8_t cache_cur_page_idx; /* индекс текущей страницы записи в cache_content_phys */
static uint16_t cache_cur_off = 0; /* текущее смещение записи в этой странице */ static uint16_t cache_cur_off; /* текущее смещение записи в этой странице */
/* Доступ к директории рендер-кэша — тот же приём, что у idx_get/idx_put. */ /* Доступ к директории рендер-кэша — тот же приём, что у idx_get/idx_put. */
void cache_dir_get(uint16_t idx, cache_rec_t *r) void cache_dir_get(uint16_t idx, cache_rec_t *r)
@@ -175,41 +175,41 @@ void cache_commit(uint8_t page, uint16_t off, const void *buf, uint16_t len)
/* emph_to_attr определена ниже (рядом с inline-логикой), нужна в inline_scan. */ /* emph_to_attr определена ниже (рядом с inline-логикой), нужна в inline_scan. */
/* emph_to_attr — в mdview2_index.c. */ /* emph_to_attr — в mdview2_index.c. */
uint16_t n_lines = 0; /* extern: всего строк в индексе текущего набора */ uint16_t n_lines; /* extern: всего строк в индексе текущего набора */
uint16_t max_lines = 0; /* extern: ёмкость индекса: index_pages * 2048 */ uint16_t max_lines; /* extern: ёмкость индекса: index_pages * 2048 */
uint16_t top_line = 0; /* extern: верхняя видимая строка */ uint16_t top_line; /* extern: верхняя видимая строка */
uint32_t file_size = 0; /* extern: размер активного буфера документа */ uint32_t file_size; /* extern: размер активного буфера документа */
static uint8_t file_blk = 0; static uint8_t file_blk;
static uint8_t file_pages = 0; static uint8_t file_pages;
static uint8_t file_phys[MAX_PAGES] = {0}; static uint8_t file_phys[MAX_PAGES];
uint8_t index_blk = 0; /* extern: дескриптор EMM-блока индекса */ uint8_t index_blk; /* extern: дескриптор EMM-блока индекса */
uint8_t index_pages = 0; /* */ uint8_t index_pages; /* */
uint8_t index_phys[MAX_INDEX_PAGES] = {0}; /* extern: физ. страницы индекса */ uint8_t index_phys[MAX_INDEX_PAGES]; /* extern: физ. страницы индекса */
uint8_t index_truncated = 0; /* выставляется при исчерпании ёмкости индекса */ uint8_t index_truncated; /* выставляется при исчерпании ёмкости индекса */
uint8_t g_file_clamped = 0; /* extern: файл >256 КБ обрезан до MAX_FILE при загрузке */ uint8_t g_file_clamped; /* extern: файл >256 КБ обрезан до MAX_FILE при загрузке */
/* cur_seg_off/cur_rec (near-состояние индексатора) — в mdview2_index.c. */ /* cur_seg_off/cur_rec (near-состояние индексатора) — в mdview2_index.c. */
uint8_t cur_page = 0xFF; /* extern: 0xFF = в W3 ещё не показана ни одна страница (extern: кодек инвалидирует) */ uint8_t cur_page = 0xFF; /* extern: 0xFF = в W3 ещё не показана ни одна страница (extern: кодек инвалидирует) */
uint8_t viewport_x = 0; /* extern: горизонтальный сдвиг для nowrap-строк */ uint8_t viewport_x; /* extern: горизонтальный сдвиг для nowrap-строк */
char filename[64] = {0}; /* extern: статус-бар */ char filename[64]; /* extern: статус-бар */
/* Прогрессивная загрузка: рисуем первый экран и пускаем скролл, пока /* Прогрессивная загрузка: рисуем первый экран и пускаем скролл, пока
* index_lines() ещё досканирует файл (кооперативно, без прерываний). */ * index_lines() ещё досканирует файл (кооперативно, без прерываний). */
uint8_t g_view = VIEW_MD; /* extern: активный режим просмотра (существует даже когда доступен только MD режим) */ uint8_t g_view = VIEW_MD; /* extern: активный режим просмотра (существует даже когда доступен только MD режим) */
#if WITH_RAW #if WITH_RAW
uint8_t g_ready = 0; /* extern: 1 когда первичный документ построен — F2 (RAW) рабочий */ uint8_t g_ready; /* extern: 1 когда первичный документ построен — F2 (RAW) рабочий */
static uint8_t g_raw_last = VIEW_RAW_WRAP; /* последний под-режим RAW (возврат по F2) */ static uint8_t g_raw_last = VIEW_RAW_WRAP; /* последний под-режим RAW (возврат по F2) */
#endif #endif
uint8_t g_loading = 0; /* extern: 1 во время index_lines() */ uint8_t g_loading; /* extern: 1 во время index_lines() */
static uint8_t g_first_drawn = 0; /* первый экран уже нарисован */ static uint8_t g_first_drawn; /* первый экран уже нарисован */
uint8_t g_f8_enabled = 0; /* extern: можно ли сейчас переключать кодировку */ uint8_t g_f8_enabled; /* extern: можно ли сейчас переключать кодировку */
static uint8_t g_utf_failed = 0; /* UTF-8 набор не удалось построить (нет EMM) — пропускаем UTF-8 в цикле F8 */ static uint8_t g_utf_failed; /* UTF-8 набор не удалось построить (нет EMM) — пропускаем UTF-8 в цикле F8 */
uint8_t g_abort = 0; /* extern: F10/Esc во время загрузки → прервать индексацию */ uint8_t g_abort; /* extern: F10/Esc во время загрузки → прервать индексацию */
static uint16_t g_last_prog = 0; /* n_lines на последнем обновлении статуса */ static uint16_t g_last_prog; /* n_lines на последнем обновлении статуса */
/* Кодировки (ENC_*) и g_encoding — в mdview2.h / mdview2_enc.c. */ /* Кодировки (ENC_*) и g_encoding — в mdview2.h / mdview2_enc.c. */
const uint8_t *g_remap = 0; /* extern: активная таблица ремапа (CP1251/KOI8), 0 = без ремапа (CP866)*/ const uint8_t *g_remap; /* extern: активная таблица ремапа (CP1251/KOI8), 0 = без ремапа (CP866)*/
uint8_t g_scratch_phys = 0; /* extern: EMM-страница под ремап-буфер строки для win_rest */ uint8_t g_scratch_phys; /* extern: EMM-страница под ремап-буфер строки для win_rest */
/* UTF-8 набор (utf_blk/utf_pages/utf_phys/utf_size/utf_avail, конвертер и его /* UTF-8 набор (utf_blk/utf_pages/utf_phys/utf_size/utf_avail, конвертер и его
* состояние) — в mdview2_enc.c; разделяемые символы и API — в mdview2.h. * состояние) — в mdview2_enc.c; разделяемые символы и API — в mdview2.h.
@@ -218,10 +218,10 @@ uint8_t g_scratch_phys = 0; /* extern: EMM-страни
/* Снимок исходного (8-битного) файлового буфера — набор DOC_8BIT читает его /* Снимок исходного (8-битного) файлового буфера — набор DOC_8BIT читает его
* как есть, набор DOC_UTF8 читает utf_phys. */ * как есть, набор DOC_UTF8 читает utf_phys. */
static uint8_t orig_file_blk = 0; static uint8_t orig_file_blk;
uint8_t orig_file_pages = 0; /* extern: читает кодек (utf_alloc) */ uint8_t orig_file_pages; /* extern: читает кодек (utf_alloc) */
uint32_t orig_file_size = 0; /* extern: читает кодек (utf_convert_more) */ uint32_t orig_file_size; /* extern: читает кодек (utf_convert_more) */
uint8_t orig_file_phys[MAX_PAGES] = {0}; /* extern: читает кодек (cv_read) */ uint8_t orig_file_phys[MAX_PAGES]; /* extern: читает кодек (cv_read) */
/* ----------------- Два сосуществующих набора индекс/кэш ----------------- /* ----------------- Два сосуществующих набора индекс/кэш -----------------
* DOC_8BIT — исходные байты (CP866/CP1251/KOI8R, различаются ремапом на * DOC_8BIT — исходные байты (CP866/CP1251/KOI8R, различаются ремапом на
@@ -250,9 +250,9 @@ typedef struct {
#define DOC_8BIT 0 #define DOC_8BIT 0
#define DOC_UTF8 1 #define DOC_UTF8 1
static docset_t g_doc[2] = {{0}, {0}}; /* ОБЯЗАТЕЛЬНО инициализировать: built=0 для обоих static docset_t g_doc[2]; /* built=0 для обоих: _DATA зануляется в crt0 gsinit
* (SDCC z80 не обнуляет неинициализированные статики * (с 2026-07-04), явный {0}-инициализатор больше
* надёжно — см. memory/sdcc_static_storage_gotcha) */ * не нужен — он стоил ~260 байт _INITIALIZER */
static uint8_t g_active_doc = DOC_8BIT; /* какой набор сейчас в живых глобалах */ static uint8_t g_active_doc = DOC_8BIT; /* какой набор сейчас в живых глобалах */
static uint8_t g_disp_doc = DOC_8BIT; /* какой набор показан пользователю */ static uint8_t g_disp_doc = DOC_8BIT; /* какой набор показан пользователю */
+10 -10
View File
@@ -23,16 +23,16 @@
/* ---- Состояние кодировки/UTF (владелец — этот модуль) ------------- */ /* ---- Состояние кодировки/UTF (владелец — этот модуль) ------------- */
uint8_t g_encoding = ENC_CP866; /* активная кодировка */ uint8_t g_encoding = ENC_CP866; /* активная кодировка */
uint8_t utf_blk = 0; uint8_t utf_blk;
uint8_t utf_pages = 0; uint8_t utf_pages;
uint8_t utf_phys[MAX_PAGES] = {0}; uint8_t utf_phys[MAX_PAGES];
uint32_t utf_size = 0; uint32_t utf_size;
uint8_t utf_avail = 0; /* 1 = страницы UTF-8 выделены */ uint8_t utf_avail; /* 1 = страницы UTF-8 выделены */
uint8_t g_utf_building = 0; /* 1 пока конвертация не дошла до конца исходника */ uint8_t g_utf_building; /* 1 пока конвертация не дошла до конца исходника */
/* Приватное состояние конвертера (наружу не торчит — сброс через /* Приватное состояние конвертера (наружу не торчит — сброс через
* utf_conv_reset(), чтение оригинала через cv_read). */ * utf_conv_reset(), чтение оригинала через cv_read). */
static uint32_t utf_src = 0; /* позиция чтения ОРИГИНАЛА (utf-8) */ static uint32_t utf_src; /* позиция чтения ОРИГИНАЛА (utf-8) */
static uint8_t cv_page = 0xFF; /* orig-страница в W3 для cv_read (своя от cur_page) */ static uint8_t cv_page = 0xFF; /* orig-страница в W3 для cv_read (своя от cur_page) */
/* Таблицы ремапа СТАРШИХ байтов [128-255] в CP866. Индексируются (ch-0x80): /* Таблицы ремапа СТАРШИХ байтов [128-255] в CP866. Индексируются (ch-0x80):
@@ -180,9 +180,9 @@ static const uint8_t utf_cyr_to_866[96] = {
/* Поток вывода конвертера: пишем в utf_phys чанками по странице, буфер в /* Поток вывода конвертера: пишем в utf_phys чанками по странице, буфер в
* near-памяти, флаш через один bank_write (не побайтовый swap W3). */ * near-памяти, флаш через один bank_write (не побайтовый swap W3). */
static uint8_t conv_buf[256]; static uint8_t conv_buf[256];
static uint16_t conv_n = 0; /* байт в буфере, ещё не сброшено */ static uint16_t conv_n; /* байт в буфере, ещё не сброшено */
static uint8_t conv_page = 0; /* индекс текущей страницы в utf_phys */ static uint8_t conv_page; /* индекс текущей страницы в utf_phys */
static uint16_t conv_off = 0; /* уже сброшенное смещение в этой странице */ static uint16_t conv_off; /* уже сброшенное смещение в этой странице */
static void conv_flush(void) static void conv_flush(void)
{ {
+42 -68
View File
@@ -20,6 +20,14 @@
#include "mdview2.h" #include "mdview2.h"
static uint8_t emph_to_attr(uint8_t emph, uint8_t base_attr); /* forward (нужна в inline-сканере) */ static uint8_t emph_to_attr(uint8_t emph, uint8_t base_attr); /* forward (нужна в inline-сканере) */
static uint8_t is_hr_raw(uint32_t p); /* forward (нужна в classify_line) */
/* Начало следующей физической строки (байт после '\n', либо file_size). */
static uint32_t next_line(uint32_t p)
{
p = row_end(p);
return (p < file_size) ? p + 1 : p;
}
typedef struct idx_rec_s { typedef struct idx_rec_s {
uint32_t off; /* смещение первого байта исходного текста для сегмента */ uint32_t off; /* смещение первого байта исходного текста для сегмента */
@@ -31,7 +39,7 @@ typedef struct idx_rec_s {
#define INDEX_REC_SIZE 8u #define INDEX_REC_SIZE 8u
static uint32_t cur_seg_off = 0; /* смещение последнего emit_seg (кэш в near-памяти) */ static uint32_t cur_seg_off; /* смещение последнего emit_seg (кэш в near-памяти) */
static idx_rec_t cur_rec; /* near-копия последней записанной idx-записи */ static idx_rec_t cur_rec; /* near-копия последней записанной idx-записи */
/* Для проверки границ выделения считаем пробел/таб/конец строки/0 пробельными. */ /* Для проверки границ выделения считаем пробел/таб/конец строки/0 пробельными. */
@@ -90,12 +98,9 @@ uint32_t seg_off(uint16_t idx)
return r.off; return r.off;
} }
/* Установка флагов всегда идёт в последний emit_seg (n_lines-1). /* Установка флагов (маска IF_*) всегда идёт в последний emit_seg (n_lines-1).
* Используется near-копия cur_rec, чтобы не делать обратное чтение из банка. */ * Используется near-копия cur_rec, чтобы не делать обратное чтение из банка. */
static void set_nowrap_cur(void) { cur_rec.flags |= IF_NOWRAP; idx_put((uint16_t)(n_lines - 1), &cur_rec); } static void set_cur_flags(uint8_t f) { cur_rec.flags |= f; idx_put((uint16_t)(n_lines - 1), &cur_rec); }
static void set_blank_cur(void) { cur_rec.flags |= IF_BLANK; idx_put((uint16_t)(n_lines - 1), &cur_rec); }
static void set_code_cur(void) { cur_rec.flags |= IF_CODE; idx_put((uint16_t)(n_lines - 1), &cur_rec); }
static void set_hscroll_cur(void){ cur_rec.flags |= IF_HSCROLL; idx_put((uint16_t)(n_lines - 1), &cur_rec); }
/* ================================================================== /* ==================================================================
* Индексация строк * Индексация строк
@@ -157,22 +162,7 @@ static uint8_t classify_line(uint32_t p_start, uint32_t *out_content)
/* Горизонтальный разделитель. /* Горизонтальный разделитель.
* Проверяется до списков, чтобы `- - -` не стало маркером списка. */ * Проверяется до списков, чтобы `- - -` не стало маркером списка. */
if (c0 == '-' || c0 == '*' || c0 == '_') { if (is_hr_raw(p_start)) return LK_HR;
char marker = 0;
uint8_t count = 0;
uint8_t ok = 1;
uint32_t p = p_start;
while (p < file_size) {
char ch = fb(p++);
if (ch == '\n' || ch == '\r') break;
if (ch == ' ' || ch == '\t') continue;
if (ch != '-' && ch != '*' && ch != '_') { ok = 0; break; }
if (marker == 0) marker = ch;
else if (ch != marker) { ok = 0; break; }
count++;
}
if (ok && count >= 3) return LK_HR;
}
/* Для списков/цитат дополнительно допускаем ведущие пробелы /* Для списков/цитат дополнительно допускаем ведущие пробелы
* (базовая поддержка вложенности). */ * (базовая поддержка вложенности). */
@@ -237,15 +227,15 @@ static uint8_t marker_visible_col(uint8_t kind, uint32_t p_start, uint32_t conte
* g_ncells_at_space — снимок длины на последнем пробеле для усечения буфера * g_ncells_at_space — снимок длины на последнем пробеле для усечения буфера
* при переносе строки (отбрасываем уехавшее за границу слово). */ * при переносе строки (отбрасываем уехавшее за границу слово). */
static uint8_t g_cells[MAX_CACHE_LINE_LEN * 2]; static uint8_t g_cells[MAX_CACHE_LINE_LEN * 2];
uint8_t g_ncells = 0; /* ячеек в текущем сегменте (общий с таблицами) */ uint8_t g_ncells; /* ячеек в текущем сегменте (общий с таблицами) */
static uint8_t g_ncells_at_space = 0; /* снимок g_ncells на последнем пробеле (для усечения при переносе) */ static uint8_t g_ncells_at_space; /* снимок g_ncells на последнем пробеле (для усечения при переносе) */
static uint8_t g_skip_flush = 0; /* следующий emit_seg пропустит lag-флаш (pending уже зафлашен вручную, напр. перед таблицей) */ static uint8_t g_skip_flush; /* следующий emit_seg пропустит lag-флаш (pending уже зафлашен вручную, напр. перед таблицей) */
/* Обрыв индексации: причина (0 / TRUNC_CONTENT / TRUNC_LINES) и строка, на /* Обрыв индексации: причина (0 / TRUNC_CONTENT / TRUNC_LINES) и строка, на
* которой упёрлись. Выставляются ОДИН раз (первая причина побеждает); по ним * которой упёрлись. Выставляются ОДИН раз (первая причина побеждает); по ним
* index_lines() в конце пишет строку-сообщение (IF_TRUNC_MSG, рисуется вживую). */ * index_lines() в конце пишет строку-сообщение (IF_TRUNC_MSG, рисуется вживую). */
static uint8_t g_trunc_cause = 0; static uint8_t g_trunc_cause;
static uint16_t g_trunc_line = 0; static uint16_t g_trunc_line;
/* Пишет одну (char,attr)-ячейку в g_cells, молча игнорируя переполнение /* Пишет одну (char,attr)-ячейку в g_cells, молча игнорируя переполнение
* MAX_CACHE_LINE_LEN. Оставлена ФУНКЦИЕЙ намеренно: инлайн в сильно * MAX_CACHE_LINE_LEN. Оставлена ФУНКЦИЕЙ намеренно: инлайн в сильно
@@ -390,10 +380,11 @@ static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls,
uint32_t q = *pq; uint32_t q = *pq;
uint8_t ls = *pls; uint8_t ls = *pls;
char ch = fb(q); char ch = fb(q);
uint8_t at = emph_to_attr(ls, base); /* атрибут литералов текущего стиля */
if (ls != INIT_STYLE_CODE && ch == '\\' && (q + 1) < bound && is_escapable(fb(q + 1))) { if (ls != INIT_STYLE_CODE && ch == '\\' && (q + 1) < bound && is_escapable(fb(q + 1))) {
char nb = fb(q + 1); char nb = fb(q + 1);
gc_put(nb, emph_to_attr(ls, base)); gc_put(nb, at);
*pseg += 1; *pq = q + 2; *pprev = nb; return 1; *pseg += 1; *pq = q + 2; *pprev = nb; return 1;
} }
if (ls != INIT_STYLE_CODE && ch == '[' && (q + 2) < bound && fb(q + 2) == ']') { if (ls != INIT_STYLE_CODE && ch == '[' && (q + 2) < bound && fb(q + 2) == ']') {
@@ -403,7 +394,7 @@ static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls,
if (ch == '`') { if (ch == '`') {
if (ls == INIT_STYLE_PLAIN) { *pls = INIT_STYLE_CODE; *pq = q + 1; return 1; } 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; } 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; gc_put('`', at); *pseg += 1; *pq = q + 1; *pprev = '`'; return 1;
} }
if (ch == '*' && (q + 1) < bound && fb(q + 1) == '*') { if (ch == '*' && (q + 1) < bound && fb(q + 1) == '*') {
char nx = ((q + 2) < bound) ? fb(q + 2) : '\n'; char nx = ((q + 2) < bound) ? fb(q + 2) : '\n';
@@ -411,7 +402,7 @@ static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls,
if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = INIT_STYLE_BOLD; *pq = q + 2; return 1; } 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; } 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)); gc_put('*', at); gc_put('*', at);
*pseg += 2; *pq = q + 2; *pprev = '*'; return 1; *pseg += 2; *pq = q + 2; *pprev = '*'; return 1;
} }
if (ch == '~' && (q + 1) < bound && fb(q + 1) == '~') { if (ch == '~' && (q + 1) < bound && fb(q + 1) == '~') {
@@ -420,7 +411,7 @@ static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls,
if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = INIT_STYLE_STRIKE; *pq = q + 2; return 1; } 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; } 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)); gc_put('~', at); gc_put('~', at);
*pseg += 2; *pq = q + 2; *pprev = '~'; return 1; *pseg += 2; *pq = q + 2; *pprev = '~'; return 1;
} }
if (ch == '*' || ch == '_') { if (ch == '*' || ch == '_') {
@@ -430,13 +421,13 @@ static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls,
if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = ns; *pq = q + 1; return 1; } 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; } 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; gc_put(ch, at); *pseg += 1; *pq = q + 1; *pprev = ch; return 1;
} }
if (ch == '\t') { if (ch == '\t') {
uint8_t old_sc = *pseg; uint8_t old_sc = *pseg;
uint8_t tgt_full = (uint8_t)((old_sc & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP); 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; 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)); gc_fill(' ', at, (uint8_t)(tgt_full - old_sc));
*pseg = tgt; *pq = q + 1; *pprev = ' '; return 1; *pseg = tgt; *pq = q + 1; *pprev = ' '; return 1;
} }
return 0; /* пробел или обычный символ — обрабатывает вызывающий */ return 0; /* пробел или обычный символ — обрабатывает вызывающий */
@@ -556,8 +547,7 @@ static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col,
if (next_kind != LK_QUOTE) break; if (next_kind != LK_QUOTE) break;
{ {
uint32_t next_line_end = next_content; uint32_t next_line_end = row_end(next_content);
while (next_line_end < file_size && fb(next_line_end) != '\n') next_line_end++;
{ {
uint8_t empty_quote = 1; uint8_t empty_quote = 1;
@@ -722,12 +712,11 @@ void index_lines(void)
/* Пустая строка — разделитель параграфов. /* Пустая строка — разделитель параграфов.
* В индекс добавляем не более одной пустой экранной строки подряд. */ * В индекс добавляем не более одной пустой экранной строки подряд. */
if (is_line_blank(p)) { if (is_line_blank(p)) {
while (p < file_size && fb(p) != '\n') p++; p = next_line(p);
if (p < file_size) p++;
/* Серия пустых строк схлопывается в одну визуальную строку. */ /* Серия пустых строк схлопывается в одну визуальную строку. */
if (n_lines == 0 || !(cur_rec.flags & IF_BLANK)) { if (n_lines == 0 || !(cur_rec.flags & IF_BLANK)) {
emit_seg(p, INIT_STYLE_PLAIN, CK_PLAIN, 0); emit_seg(p, INIT_STYLE_PLAIN, CK_PLAIN, 0);
set_blank_cur(); set_cur_flags(IF_BLANK);
} }
continue; continue;
} }
@@ -736,16 +725,13 @@ void index_lines(void)
if (is_fence_raw(p)) { if (is_fence_raw(p)) {
in_block = !in_block; in_block = !in_block;
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
set_nowrap_cur(); set_cur_flags(IF_NOWRAP);
while (p < file_size && fb(p) != '\n') p++; p = next_line(p);
if (p < file_size) p++;
continue; continue;
} }
if (in_block) { if (in_block) {
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
set_nowrap_cur(); set_cur_flags(IF_NOWRAP | IF_CODE | IF_HSCROLL); /* код скроллится целым блоком */
set_code_cur();
set_hscroll_cur(); /* код скроллится целым блоком */
{ {
uint8_t ccx = 0; uint8_t ccx = 0;
uint32_t pp = p; uint32_t pp = p;
@@ -763,18 +749,16 @@ void index_lines(void)
pp++; pp++;
} }
} }
while (p < file_size && fb(p) != '\n') p++; p = next_line(p);
if (p < file_size) p++;
continue; continue;
} }
/* ---- горизонтальный разделитель ---- */ /* ---- горизонтальный разделитель ---- */
if (is_hr_raw(p)) { if (is_hr_raw(p)) {
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
set_nowrap_cur(); set_cur_flags(IF_NOWRAP);
gc_fill(0xC4, ATTR_HR, SCREEN_W); gc_fill(0xC4, ATTR_HR, SCREEN_W);
while (p < file_size && fb(p) != '\n') p++; p = next_line(p);
if (p < file_size) p++;
continue; continue;
} }
@@ -814,21 +798,21 @@ void index_lines(void)
/* Проход 2: верхняя рамка → строки/разделитель → нижняя рамка. */ /* Проход 2: верхняя рамка → строки/разделитель → нижняя рамка. */
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
set_nowrap_cur(); set_hscroll_cur(); set_cur_flags(IF_NOWRAP | IF_HSCROLL);
table_border(widths, ncols, TBL_TL, TBL_TM, TBL_TR); table_border(widths, ncols, TBL_TL, TBL_TM, TBL_TR);
r = p; r = p;
while (r < tbl_end) { while (r < tbl_end) {
uint32_t le = row_end(r); uint32_t le = row_end(r);
emit_seg(r, INIT_STYLE_PLAIN, CK_OTHER, 0); emit_seg(r, INIT_STYLE_PLAIN, CK_OTHER, 0);
set_nowrap_cur(); set_hscroll_cur(); set_cur_flags(IF_NOWRAP | IF_HSCROLL);
if (table_is_sep_row(r, le)) table_border(widths, ncols, TBL_ML, TBL_MM, TBL_MR); if (table_is_sep_row(r, le)) table_border(widths, ncols, TBL_ML, TBL_MM, TBL_MR);
else table_data_row(r, le, widths, ncols); else table_data_row(r, le, widths, ncols);
r = (le < file_size) ? le + 1 : le; r = (le < file_size) ? le + 1 : le;
} }
emit_seg(tbl_end, INIT_STYLE_PLAIN, CK_OTHER, 0); emit_seg(tbl_end, INIT_STYLE_PLAIN, CK_OTHER, 0);
set_nowrap_cur(); set_hscroll_cur(); set_cur_flags(IF_NOWRAP | IF_HSCROLL);
table_border(widths, ncols, TBL_BL, TBL_BM, TBL_BR); table_border(widths, ncols, TBL_BL, TBL_BM, TBL_BR);
p = tbl_end; p = tbl_end;
@@ -849,11 +833,9 @@ void index_lines(void)
default: hbase = ATTR_TEXT_TITLE4; break; default: hbase = ATTR_TEXT_TITLE4; break;
} }
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
uint32_t line_end = content_off; uint32_t line_end = row_end(content_off);
while (line_end < file_size && fb(line_end) != '\n') line_end++;
(void)inline_scan(content_off, line_end, 0, CK_OTHER, INIT_STYLE_PLAIN, hbase, 0); (void)inline_scan(content_off, line_end, 0, CK_OTHER, INIT_STYLE_PLAIN, hbase, 0);
while (p < file_size && fb(p) != '\n') p++; p = next_line(p);
if (p < file_size) p++;
continue; continue;
} }
@@ -882,10 +864,7 @@ void index_lines(void)
q = scan_join_stream(q, p, col, CK_LIST, &line_style, JOIN_MODE_LIST); q = scan_join_stream(q, p, col, CK_LIST, &line_style, JOIN_MODE_LIST);
/* Переходим к началу следующей физической строки после пункта. */ /* Переходим к началу следующей физической строки после пункта. */
if (q < file_size) { q = next_line(q);
while (q < file_size && fb(q) != '\n') q++;
if (q < file_size) q++;
}
/* Одна пустая строка между соседними маркерами списка подавляется. /* Одна пустая строка между соседними маркерами списка подавляется.
* Две и более пустых строки оставляют видимый разделитель. */ * Две и более пустых строки оставляют видимый разделитель. */
@@ -893,8 +872,7 @@ void index_lines(void)
uint32_t t = q; uint32_t t = q;
uint8_t blanks = 0; uint8_t blanks = 0;
while (t < file_size && is_line_blank(t)) { while (t < file_size && is_line_blank(t)) {
while (t < file_size && fb(t) != '\n') t++; t = next_line(t);
if (t < file_size) t++;
blanks++; blanks++;
// if (blanks >= 2) break; // if (blanks >= 2) break;
} }
@@ -934,9 +912,7 @@ void index_lines(void)
} }
q = scan_join_stream(q, p, col, CK_QUOTE, &line_style, JOIN_MODE_QUOTE); q = scan_join_stream(q, p, col, CK_QUOTE, &line_style, JOIN_MODE_QUOTE);
while (q < file_size && fb(q) != '\n') q++; p = next_line(q);
if (q < file_size) q++;
p = q;
continue; continue;
} }
@@ -962,9 +938,7 @@ void index_lines(void)
/* q стоит на завершающем newline параграфа. /* q стоит на завершающем newline параграфа.
* Переходим на следующую строку, чтобы внешняя петля её * Переходим на следующую строку, чтобы внешняя петля её
* корректно переклассифицировала. */ * корректно переклассифицировала. */
while (q < file_size && fb(q) != '\n') q++; p = next_line(q);
if (q < file_size) q++;
p = q;
} }
} }
} }
+2 -2
View File
@@ -19,8 +19,8 @@
#if WITH_RAW /* весь модуль — опционален (mdview_conf.h); при 0 пустой объектник */ #if WITH_RAW /* весь модуль — опционален (mdview_conf.h); при 0 пустой объектник */
static uint32_t raw_top = 0; /* байт-offset верхней строки экрана */ static uint32_t raw_top; /* байт-offset верхней строки экрана */
static uint16_t raw_hpan = 0; /* гориз. сдвиг (только HSCROLL) */ static uint16_t raw_hpan; /* гориз. сдвиг (только HSCROLL) */
/* ---- примитивы навигации по физическим строкам / переносам -------- */ /* ---- примитивы навигации по физическим строкам / переносам -------- */