diff --git a/examples/mdview2/Makefile b/examples/mdview2/Makefile index 2b3aa8f..a2d8378 100644 --- a/examples/mdview2/Makefile +++ b/examples/mdview2/Makefile @@ -10,6 +10,9 @@ PROJ_ROOT := $(abspath $(CURDIR)/../..) 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 MEMORY := small +# --max-allocs 100000: агрессивнее аллокация регистров SDCC — минус ~850 байт +# кода на всём приложении (замер 2026-07-04) ценой более долгой компиляции. +EXTRA_FLAGS := --max-allocs 100000 include $(PROJ_ROOT)/app.mk diff --git a/examples/mdview2/mdview2.c b/examples/mdview2/mdview2.c index c4ab922..f4cd485 100644 --- a/examples/mdview2/mdview2.c +++ b/examples/mdview2/mdview2.c @@ -101,15 +101,15 @@ static const uint8_t md_pallete[16 * 4] = { /* MAX_CACHE_LINE_LEN — в mdview2.h (буфер ячеек g_cells живёт в mdview2_index.c). */ #define MAX_CACHE_CONTENT_PAGES 40u /* кап контента на набор: 40 * 16 КБ = 640 КБ (80 на оба набора) */ -static uint8_t cache_dir_blk = 0; -static uint8_t cache_dir_pages = 0; -static uint8_t cache_dir_phys[MAX_CACHE_DIR_PAGES] = {0}; +static uint8_t cache_dir_blk; +static uint8_t cache_dir_pages; +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_phys[MAX_CACHE_CONTENT_PAGES] = {0}; -static uint8_t cache_content_n_pages = 0; /* сколько страниц уже выделено */ -static uint8_t cache_cur_page_idx = 0; /* индекс текущей страницы записи в cache_content_phys */ -static uint16_t cache_cur_off = 0; /* текущее смещение записи в этой странице */ +static uint8_t cache_content_blk[MAX_CACHE_CONTENT_PAGES]; /* id блока на каждую выделенную страницу */ +static uint8_t cache_content_phys[MAX_CACHE_CONTENT_PAGES]; +static uint8_t cache_content_n_pages; /* сколько страниц уже выделено */ +static uint8_t cache_cur_page_idx; /* индекс текущей страницы записи в cache_content_phys */ +static uint16_t cache_cur_off; /* текущее смещение записи в этой странице */ /* Доступ к директории рендер-кэша — тот же приём, что у idx_get/idx_put. */ 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 — в mdview2_index.c. */ -uint16_t n_lines = 0; /* extern: всего строк в индексе текущего набора */ -uint16_t max_lines = 0; /* extern: ёмкость индекса: index_pages * 2048 */ -uint16_t top_line = 0; /* extern: верхняя видимая строка */ -uint32_t file_size = 0; /* extern: размер активного буфера документа */ -static uint8_t file_blk = 0; -static uint8_t file_pages = 0; -static uint8_t file_phys[MAX_PAGES] = {0}; -uint8_t index_blk = 0; /* extern: дескриптор EMM-блока индекса */ -uint8_t index_pages = 0; /* */ -uint8_t index_phys[MAX_INDEX_PAGES] = {0}; /* extern: физ. страницы индекса */ -uint8_t index_truncated = 0; /* выставляется при исчерпании ёмкости индекса */ -uint8_t g_file_clamped = 0; /* extern: файл >256 КБ обрезан до MAX_FILE при загрузке */ +uint16_t n_lines; /* extern: всего строк в индексе текущего набора */ +uint16_t max_lines; /* extern: ёмкость индекса: index_pages * 2048 */ +uint16_t top_line; /* extern: верхняя видимая строка */ +uint32_t file_size; /* extern: размер активного буфера документа */ +static uint8_t file_blk; +static uint8_t file_pages; +static uint8_t file_phys[MAX_PAGES]; +uint8_t index_blk; /* extern: дескриптор EMM-блока индекса */ +uint8_t index_pages; /* */ +uint8_t index_phys[MAX_INDEX_PAGES]; /* extern: физ. страницы индекса */ +uint8_t index_truncated; /* выставляется при исчерпании ёмкости индекса */ +uint8_t g_file_clamped; /* extern: файл >256 КБ обрезан до MAX_FILE при загрузке */ /* cur_seg_off/cur_rec (near-состояние индексатора) — в mdview2_index.c. */ uint8_t cur_page = 0xFF; /* extern: 0xFF = в W3 ещё не показана ни одна страница (extern: кодек инвалидирует) */ -uint8_t viewport_x = 0; /* extern: горизонтальный сдвиг для nowrap-строк */ -char filename[64] = {0}; /* extern: статус-бар */ +uint8_t viewport_x; /* extern: горизонтальный сдвиг для nowrap-строк */ +char filename[64]; /* extern: статус-бар */ /* Прогрессивная загрузка: рисуем первый экран и пускаем скролл, пока * index_lines() ещё досканирует файл (кооперативно, без прерываний). */ uint8_t g_view = VIEW_MD; /* extern: активный режим просмотра (существует даже когда доступен только MD режим) */ #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) */ #endif -uint8_t g_loading = 0; /* extern: 1 во время index_lines() */ -static uint8_t g_first_drawn = 0; /* первый экран уже нарисован */ -uint8_t g_f8_enabled = 0; /* extern: можно ли сейчас переключать кодировку */ -static uint8_t g_utf_failed = 0; /* UTF-8 набор не удалось построить (нет EMM) — пропускаем UTF-8 в цикле F8 */ -uint8_t g_abort = 0; /* extern: F10/Esc во время загрузки → прервать индексацию */ -static uint16_t g_last_prog = 0; /* n_lines на последнем обновлении статуса */ +uint8_t g_loading; /* extern: 1 во время index_lines() */ +static uint8_t g_first_drawn; /* первый экран уже нарисован */ +uint8_t g_f8_enabled; /* extern: можно ли сейчас переключать кодировку */ +static uint8_t g_utf_failed; /* UTF-8 набор не удалось построить (нет EMM) — пропускаем UTF-8 в цикле F8 */ +uint8_t g_abort; /* extern: F10/Esc во время загрузки → прервать индексацию */ +static uint16_t g_last_prog; /* n_lines на последнем обновлении статуса */ /* Кодировки (ENC_*) и g_encoding — в mdview2.h / mdview2_enc.c. */ -const uint8_t *g_remap = 0; /* extern: активная таблица ремапа (CP1251/KOI8), 0 = без ремапа (CP866)*/ -uint8_t g_scratch_phys = 0; /* extern: EMM-страница под ремап-буфер строки для win_rest */ +const uint8_t *g_remap; /* extern: активная таблица ремапа (CP1251/KOI8), 0 = без ремапа (CP866)*/ +uint8_t g_scratch_phys; /* extern: EMM-страница под ремап-буфер строки для win_rest */ /* UTF-8 набор (utf_blk/utf_pages/utf_phys/utf_size/utf_avail, конвертер и его * состояние) — в mdview2_enc.c; разделяемые символы и API — в mdview2.h. @@ -218,10 +218,10 @@ uint8_t g_scratch_phys = 0; /* extern: EMM-страни /* Снимок исходного (8-битного) файлового буфера — набор DOC_8BIT читает его * как есть, набор DOC_UTF8 читает utf_phys. */ -static uint8_t orig_file_blk = 0; -uint8_t orig_file_pages = 0; /* extern: читает кодек (utf_alloc) */ -uint32_t orig_file_size = 0; /* extern: читает кодек (utf_convert_more) */ -uint8_t orig_file_phys[MAX_PAGES] = {0}; /* extern: читает кодек (cv_read) */ +static uint8_t orig_file_blk; +uint8_t orig_file_pages; /* extern: читает кодек (utf_alloc) */ +uint32_t orig_file_size; /* extern: читает кодек (utf_convert_more) */ +uint8_t orig_file_phys[MAX_PAGES]; /* extern: читает кодек (cv_read) */ /* ----------------- Два сосуществующих набора индекс/кэш ----------------- * DOC_8BIT — исходные байты (CP866/CP1251/KOI8R, различаются ремапом на @@ -250,9 +250,9 @@ typedef struct { #define DOC_8BIT 0 #define DOC_UTF8 1 -static docset_t g_doc[2] = {{0}, {0}}; /* ОБЯЗАТЕЛЬНО инициализировать: built=0 для обоих - * (SDCC z80 не обнуляет неинициализированные статики - * надёжно — см. memory/sdcc_static_storage_gotcha) */ +static docset_t g_doc[2]; /* built=0 для обоих: _DATA зануляется в crt0 gsinit + * (с 2026-07-04), явный {0}-инициализатор больше + * не нужен — он стоил ~260 байт _INITIALIZER */ static uint8_t g_active_doc = DOC_8BIT; /* какой набор сейчас в живых глобалах */ static uint8_t g_disp_doc = DOC_8BIT; /* какой набор показан пользователю */ diff --git a/examples/mdview2/mdview2_enc.c b/examples/mdview2/mdview2_enc.c index 2bec570..788883a 100644 --- a/examples/mdview2/mdview2_enc.c +++ b/examples/mdview2/mdview2_enc.c @@ -23,16 +23,16 @@ /* ---- Состояние кодировки/UTF (владелец — этот модуль) ------------- */ uint8_t g_encoding = ENC_CP866; /* активная кодировка */ -uint8_t utf_blk = 0; -uint8_t utf_pages = 0; -uint8_t utf_phys[MAX_PAGES] = {0}; -uint32_t utf_size = 0; -uint8_t utf_avail = 0; /* 1 = страницы UTF-8 выделены */ -uint8_t g_utf_building = 0; /* 1 пока конвертация не дошла до конца исходника */ +uint8_t utf_blk; +uint8_t utf_pages; +uint8_t utf_phys[MAX_PAGES]; +uint32_t utf_size; +uint8_t utf_avail; /* 1 = страницы UTF-8 выделены */ +uint8_t g_utf_building; /* 1 пока конвертация не дошла до конца исходника */ /* Приватное состояние конвертера (наружу не торчит — сброс через * 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) */ /* Таблицы ремапа СТАРШИХ байтов [128-255] в CP866. Индексируются (ch-0x80): @@ -180,9 +180,9 @@ static const uint8_t utf_cyr_to_866[96] = { /* Поток вывода конвертера: пишем в utf_phys чанками по странице, буфер в * near-памяти, флаш через один bank_write (не побайтовый swap W3). */ static uint8_t conv_buf[256]; -static uint16_t conv_n = 0; /* байт в буфере, ещё не сброшено */ -static uint8_t conv_page = 0; /* индекс текущей страницы в utf_phys */ -static uint16_t conv_off = 0; /* уже сброшенное смещение в этой странице */ +static uint16_t conv_n; /* байт в буфере, ещё не сброшено */ +static uint8_t conv_page; /* индекс текущей страницы в utf_phys */ +static uint16_t conv_off; /* уже сброшенное смещение в этой странице */ static void conv_flush(void) { diff --git a/examples/mdview2/mdview2_index.c b/examples/mdview2/mdview2_index.c index bad17ad..30aaf6d 100644 --- a/examples/mdview2/mdview2_index.c +++ b/examples/mdview2/mdview2_index.c @@ -20,6 +20,14 @@ #include "mdview2.h" 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 { uint32_t off; /* смещение первого байта исходного текста для сегмента */ @@ -31,7 +39,7 @@ typedef struct idx_rec_s { #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-записи */ /* Для проверки границ выделения считаем пробел/таб/конец строки/0 пробельными. */ @@ -90,12 +98,9 @@ uint32_t seg_off(uint16_t idx) return r.off; } -/* Установка флагов всегда идёт в последний emit_seg (n_lines-1). +/* Установка флагов (маска IF_*) всегда идёт в последний emit_seg (n_lines-1). * Используется 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_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); } +static void set_cur_flags(uint8_t f) { cur_rec.flags |= f; 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 == '_') { - 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; - } + if (is_hr_raw(p_start)) 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 — снимок длины на последнем пробеле для усечения буфера * при переносе строки (отбрасываем уехавшее за границу слово). */ static uint8_t g_cells[MAX_CACHE_LINE_LEN * 2]; -uint8_t g_ncells = 0; /* ячеек в текущем сегменте (общий с таблицами) */ -static uint8_t g_ncells_at_space = 0; /* снимок g_ncells на последнем пробеле (для усечения при переносе) */ -static uint8_t g_skip_flush = 0; /* следующий emit_seg пропустит lag-флаш (pending уже зафлашен вручную, напр. перед таблицей) */ +uint8_t g_ncells; /* ячеек в текущем сегменте (общий с таблицами) */ +static uint8_t g_ncells_at_space; /* снимок g_ncells на последнем пробеле (для усечения при переносе) */ +static uint8_t g_skip_flush; /* следующий emit_seg пропустит lag-флаш (pending уже зафлашен вручную, напр. перед таблицей) */ /* Обрыв индексации: причина (0 / TRUNC_CONTENT / TRUNC_LINES) и строка, на * которой упёрлись. Выставляются ОДИН раз (первая причина побеждает); по ним * index_lines() в конце пишет строку-сообщение (IF_TRUNC_MSG, рисуется вживую). */ -static uint8_t g_trunc_cause = 0; -static uint16_t g_trunc_line = 0; +static uint8_t g_trunc_cause; +static uint16_t g_trunc_line; /* Пишет одну (char,attr)-ячейку в g_cells, молча игнорируя переполнение * 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; uint8_t ls = *pls; 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))) { 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; } 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 (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; + gc_put('`', at); *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'; @@ -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_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; } 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_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; } 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_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') { 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)); + gc_fill(' ', at, (uint8_t)(tgt_full - old_sc)); *pseg = tgt; *pq = q + 1; *pprev = ' '; return 1; } 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; { - uint32_t next_line_end = next_content; - while (next_line_end < file_size && fb(next_line_end) != '\n') next_line_end++; + uint32_t next_line_end = row_end(next_content); { uint8_t empty_quote = 1; @@ -722,12 +712,11 @@ void index_lines(void) /* Пустая строка — разделитель параграфов. * В индекс добавляем не более одной пустой экранной строки подряд. */ if (is_line_blank(p)) { - while (p < file_size && fb(p) != '\n') p++; - if (p < file_size) p++; + p = next_line(p); /* Серия пустых строк схлопывается в одну визуальную строку. */ if (n_lines == 0 || !(cur_rec.flags & IF_BLANK)) { emit_seg(p, INIT_STYLE_PLAIN, CK_PLAIN, 0); - set_blank_cur(); + set_cur_flags(IF_BLANK); } continue; } @@ -736,16 +725,13 @@ void index_lines(void) if (is_fence_raw(p)) { in_block = !in_block; emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); - set_nowrap_cur(); - while (p < file_size && fb(p) != '\n') p++; - if (p < file_size) p++; + set_cur_flags(IF_NOWRAP); + p = next_line(p); continue; } if (in_block) { emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); - set_nowrap_cur(); - set_code_cur(); - set_hscroll_cur(); /* код скроллится целым блоком */ + set_cur_flags(IF_NOWRAP | IF_CODE | IF_HSCROLL); /* код скроллится целым блоком */ { uint8_t ccx = 0; uint32_t pp = p; @@ -763,18 +749,16 @@ void index_lines(void) pp++; } } - while (p < file_size && fb(p) != '\n') p++; - if (p < file_size) p++; + p = next_line(p); continue; } /* ---- горизонтальный разделитель ---- */ if (is_hr_raw(p)) { emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); - set_nowrap_cur(); + set_cur_flags(IF_NOWRAP); gc_fill(0xC4, ATTR_HR, SCREEN_W); - while (p < file_size && fb(p) != '\n') p++; - if (p < file_size) p++; + p = next_line(p); continue; } @@ -814,21 +798,21 @@ void index_lines(void) /* Проход 2: верхняя рамка → строки/разделитель → нижняя рамка. */ 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); r = p; while (r < tbl_end) { uint32_t le = row_end(r); 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); else table_data_row(r, le, widths, ncols); r = (le < file_size) ? le + 1 : le; } 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); p = tbl_end; @@ -849,11 +833,9 @@ void index_lines(void) default: hbase = ATTR_TEXT_TITLE4; break; } emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); - uint32_t line_end = content_off; - while (line_end < file_size && fb(line_end) != '\n') line_end++; + uint32_t line_end = row_end(content_off); (void)inline_scan(content_off, line_end, 0, CK_OTHER, INIT_STYLE_PLAIN, hbase, 0); - while (p < file_size && fb(p) != '\n') p++; - if (p < file_size) p++; + p = next_line(p); continue; } @@ -882,10 +864,7 @@ void index_lines(void) q = scan_join_stream(q, p, col, CK_LIST, &line_style, JOIN_MODE_LIST); /* Переходим к началу следующей физической строки после пункта. */ - if (q < file_size) { - while (q < file_size && fb(q) != '\n') q++; - if (q < file_size) q++; - } + q = next_line(q); /* Одна пустая строка между соседними маркерами списка подавляется. * Две и более пустых строки оставляют видимый разделитель. */ @@ -893,8 +872,7 @@ void index_lines(void) uint32_t t = q; uint8_t blanks = 0; while (t < file_size && is_line_blank(t)) { - while (t < file_size && fb(t) != '\n') t++; - if (t < file_size) t++; + t = next_line(t); blanks++; // 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); - while (q < file_size && fb(q) != '\n') q++; - if (q < file_size) q++; - p = q; + p = next_line(q); continue; } @@ -962,9 +938,7 @@ void index_lines(void) /* q стоит на завершающем newline параграфа. * Переходим на следующую строку, чтобы внешняя петля её * корректно переклассифицировала. */ - while (q < file_size && fb(q) != '\n') q++; - if (q < file_size) q++; - p = q; + p = next_line(q); } } } diff --git a/examples/mdview2/mdview2_raw.c b/examples/mdview2/mdview2_raw.c index ba1c6c0..2fdd1b6 100644 --- a/examples/mdview2/mdview2_raw.c +++ b/examples/mdview2/mdview2_raw.c @@ -19,8 +19,8 @@ #if WITH_RAW /* весь модуль — опционален (mdview_conf.h); при 0 пустой объектник */ -static uint32_t raw_top = 0; /* байт-offset верхней строки экрана */ -static uint16_t raw_hpan = 0; /* гориз. сдвиг (только HSCROLL) */ +static uint32_t raw_top; /* байт-offset верхней строки экрана */ +static uint16_t raw_hpan; /* гориз. сдвиг (только HSCROLL) */ /* ---- примитивы навигации по физическим строкам / переносам -------- */