Commit Graph

12 Commits

Author SHA1 Message Date
snark13 17639ed62d 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>
2026-06-25 14:33:17 +03:00
snark13 6d515e4b9a mdview2: уменьшение размера кода (пакет A + B1, -1038 байт)
A.1: таблицы ремапа cp1251/koi8r 256→128 (старшие байты; младшие в
     ремапе не используются — win_rest_remap трогает только ch>=0x80).
A.2: conv_emit_cp switch → таблица структур utf_sym_t {utf8, cp866}
     (читаемо, добавление символа = одна строка; … и BOM — спецветки).
A.3: common_* цепочки сравнений → таблицы детекции + in_set10.
B1: удалён мёртвый код в scan_join_stream — условия
    `if(!soft_break)...else q++` во всех непробельных ветках
    (там soft_break всегда 0, т.к. ch!=' ').

_CODE (mdview2.c): 23284 → 22246 байт. Поведение не менялось.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 14:22:08 +03:00
snark13 a14f19b657 mdview2: поддержка кодировок CP866/CP1251/KOI8-R/UTF-8 (F8)
- Детекция при открытии (BOM + эвристика по первым 4 КБ).
- 8-битные (CP866/CP1251/KOI8-R) — общий индекс/кэш, переключение
  мгновенным ремапом глифов [128-255] на отрисовке по attr (структурные
  глифы — рамка/HR/маркеры — не ремапятся).
- UTF-8 — отдельный набор: декодирование в CP866 (кириллица + ходовые
  символы: стрелки/галка/буллет/тире/кавычки/box), свой индекс/кэш.
- Два набора (docset_t g_doc[2]) со свапом «живых» глобалов; второй
  строится ЛЕНИВО при первом F8-переходе в него (build-on-demand).
- F8: цикл CP866→CP1251→KOI8R→UTF8; метка в меню видна только когда
  переключение возможно; во время сборки 8-бит первичного F8 крутит 8-бит.
- F1-справка: секция Encoding; меню разбито на блоки (F1/F8/F10).
- Фикс: g_doc обязан быть инициализирован (SDCC z80 не обнуляет статики
  надёжно) — иначе мусорный built вёл к показу неинициализированного набора.
- Убрана отладка времени обработки из статус-бара.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 11:00:49 +03:00
snark13 a6e0aacc80 mdview2: план поддержки кодировок CP1251/KOI8-R/UTF-8 (ревизия 2)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 22:39:49 +03:00
snark13 13ef8d2fa5 mdview2: выровненная отрисовка таблиц с рамкой
Таблица обрабатывается как блок в два прохода:
- проход 1: границы блока + ОТРЕНДЕРЕННЫЕ ширины колонок (мерим тем же
  inline_scan, что и при отрисовке — невидимые маркеры стиля **/код не
  раздувают столбцы);
- проход 2: верхняя рамка ┌┬┐ → строки данных │ ячейка<pad> │ → разделитель
  заголовка ├┼┤ (из строки |---|) → нижняя рамка └┴┘.

Колонки выровнены по содержимому, рамка CP866 box-drawing. Широкая таблица
остаётся nowrap+hscroll. Ячейки разбиваются cell-итератором (без массивов
на стеке); пустой g_cells под измерение освобождается ручным флашем
pending-сегмента (флаг g_skip_flush в emit_seg, чтобы не флашить повторно).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 22:23:57 +03:00
snark13 68201dca44 mdview2: 4 улучшения рендеринга markdown
1. Экранирование пунктуации: \* \_ \` \[ … → литерал, маркером НЕ считается
   (CommonMark ASCII-punctuation). Напр. "**...FILE\***" → болд "...FILE*".
   Не действует внутри инлайн-кода.
2. [x] / [ ] — ровно один символ в квадратных скобках → болд (нестандартно,
   для читабельного отображения task-list checkbox-ов). Не внутри кода.
3. Соседние пункты списка: если следующий имеет МЕНЬШИЙ отступ (dedent),
   пустую строку между ними больше не подавляем — пункты визуально разделены.
4. Абзац с ведущим отступом: все его перенесённые строки получают такой же
   отступ (continuation-префикс из col пробелов, как у списка/цитаты).

Реализация — в обоих inline-сканерах (inline_scan/scan_join_stream) + ветках
index_lines; новый helper is_escapable() и leading_spaces().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:38:54 +03:00
snark13 50596e6a2a mdview2: прогрессивная загрузка (первый экран сразу, скролл во время индексации)
Препроцессинг 52КБ занимает ~10с; теперь UI не ждёт его завершения.
index_lines() кооперативно вызывает progress_tick() (без прерываний —
IM2 отложен):
- первый экран рисуется, как только готово ≥ VIEW_H строк (~0.3с);
- статус-бар показывает растущее число обработанных строк с многоточием
  ("L 1-30 / 247...");
- ↑↓ PgUp PgDn Home End (=последняя готовая страница) и F1 работают по
  УЖЕ готовым строкам (drawable_lines = [0..n_lines-2], т.к. последний
  сегмент ещё в g_cells до flush);
- Esc/F10 во время загрузки — корректный выход (прерывание индексации
  флагом g_abort + штатный unload_file/pal_reset/clrscr).

После каждого тика форсируем ре-маппинг W3 (cur_page=0xFF), т.к.
отрисовка/WINREST/BIOS могли сбить страницу, на которую опирается fb().

Временно: debug-индикатор времени обработки "t=Ns" в зоне имени файла
(по просьбе — следить за временем при дальнейших оптимизациях).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 16:46:57 +03:00
snark13 de11882d16 mdview2: снять рудиментные merge-гварды (11→10с)
После полной миграции на merge флаги g_cur_merged/g_next_merged всегда
равны 1 (все типы блоков строит форвард-сканер). Убраны сами флаги и ~30
гвардов `if (g_cur_merged)` в горячем посимвольном цикле — это и небольшое
ускорение (ветка на символ), и чистка.

Проверено замером: инлайн gc_put (макрос/inline) в этот регистро-нагруженный
цикл, наоборот, ЗАМЕДЛЯЕТ (~11→13с) из-за роста спиллов — оставлен функцией.
Вывод: per-char микрооптимизации здесь исчерпаны (asm-fb не помог, инлайн
навредил, снятие гвардов дало ~1с).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 16:06:48 +03:00
snark13 92c0a9a4a9 mdview2: merge препроцессинга в один проход (22→11с)
Раньше препроцессинг сканировал файл ДВАЖДЫ: index_lines считал точки
переноса (forward-сканеры scan_join_stream/inline_scan), а отдельный
render_line_to_cache заново сканировал каждый сегмент для сборки
(char,attr)-ячеек. Профилирование показало, что всё время — в этих двух
посимвольных проходах (bank I/O и cont-walk ≈ 0).

Теперь forward-сканер собирает ячейки в g_cells ПО ХОДУ единственного
прохода; emit_seg флашит ячейки предыдущего сегмента в кэш (lag-1),
последний — после цикла. Перенос строки усекает буфер до снимка на
последнем пробеле (g_ncells_at_space), continuation-сегменты получают
префикс (отступ списка / маркер цитаты 0xB3 / title только в 1-й строке
заголовка). Прямые типы (код verbatim, HR, таблица через nowrap-inline,
blank/fence) строят ячейки на месте.

Миграция шла по типам блоков с dual-verify (старый render строил эталон,
merge сверял ячейки) — найдены и согласованы расхождения forward-сканера
со старым рендером: backtick внутри эмфазиса = литерал; одиночный маркер
закрывает ЛЮБОЙ активный эмфазис; soft-join пропускает ведущие пробелы
строки-продолжения; хвостовые пробелы нерелевантны. Все типы дали 0
расхождений, после чего render_line_to_cache / cc_put / cc_fill /
handle_inline_marker и verify-каркас удалены (−185 строк).

Итог на 52КБ README: 22→11с (2×). Совокупно с прошлым коммитом 24→11с.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 15:41:29 +03:00
snark13 2f4eaf5b25 mdview2: ускорение препроцессинга — asm fb() + инлайн рендер-цикла
Профилирование (52КБ README, раздельный замер): препроцессинг 24с, из них
bank I/O ~0с, cont-walk ~1с — всё время в посимвольных скан-циклах
(индексация 8с + рендер 13с, два прохода по файлу).

- fb() переписана на ассемблере (была закомментированная заготовка): убран
  вызов функции и 32-битная арифметика на каждый байт. Раскладка __sdcccall(1)
  для uint32 аргумента (p=HLDE) и возврат char в A сверены через sdcc -S;
  координация маппинга с bank_read держится на том, что _io_page_w3 — порт
  (__sfr 0xE2), пишем OUT — bank_read читает IN.
- render_line_to_cache: горячий путь обычного текста больше не вызывает
  handle_inline_marker (6 аргументов) на каждый символ — только на маркерах
  ` * _ ~; cc_put заинлайнен. Поведение идентично.

Итого 24→18с. Индекс-скан (8с) лёгкого инлайна не имеет (нет per-char
вызовов). Следующий шаг — merge двух проходов в один.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 11:23:21 +03:00
snark13 138e783e72 mdview2: горизонтальный скролл по типу блока + фиксы fence/HR/SDCC
Горизонтальный скролл (Фаза 5, финал):
- Скроллим по ТИПУ, а не по длине: новый флаг IF_HSCROLL ставится только
  на код и таблицы; HR и границы fence (IF_NOWRAP без IF_HSCROLL) не
  двигаются. Блок едет целиком, включая строки короче 80.
- Обход бага кодогенерации SDCC z80: `if (n!=g) g=n;` пишет (n-g) вместо n
  (SUB сравнения затирает A, store переиспользует испорченный A). Лечится
  записью viewport_x ДО сравнения. Минимальный репродьюсер и оба описания
  для трекера — в docs/bugs/sdcc-z80-cmp-store-a/ (воспроизводится на чистом
  sdcc 4.5, в т.ч. с --no-peep → это кодогенератор, не peephole).

Рендеринг:
- Отступленный fence (```c внутри списка) теперь распознаётся: is_fence_raw
  пропускает ведущие пробелы/табы; то же в рендере прячет строку-границу.
- Строки-разделители (HR, ровно 80) больше не участвуют в скролле.

Чистка: удалён мёртвый код (is_fence_delim, get_init_style[_raw], is_cont,
seg_flags). Makefile (mdview/mdview2): iconv UTF-8→CP866 завершается ненулевым
кодом при отбрасывании символов — игнорируем (|| true).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 10:11:27 +03:00
snark13 eab5a2d6ac mdview2: add render-cache markdown viewer (Phases 0-4)
New examples/mdview2 — render-cache version of mdview: each logical
line is rendered into an EMM (char,attr) buffer once during file load
(interleaved with index_lines()), then scrolling draws straight from
the cache via ESTEX WINREST, with no re-parsing or fb() access in the
steady state. Horizontal scroll still uses the old live-render path
(Phase 5, not yet migrated).

Format and budget were derisked empirically first (tests/winrest):
confirmed ESTEX WINCOPY/WINREST buffer layout and measured EMM free
space, both folded into the implementation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 22:41:32 +03:00