mdview2: единая позиция при переключениях F2 (MD↔RAW) и F8 (8bit↔UTF-8)

Валюта позиции — байт-offset активного буфера:
- line_at_off(off): обратный перевод offset → MD-строка (бинарный поиск
  по seg_off, оффсеты сегментов монотонны);
- map_off(off, from, to): пропорциональный перенос позиции между
  буферами разного размера — один цикл restoring-деления, без
  __mullong/__divulong, точность from/65536;
- raw_pos()/raw_reanchor(off) в RAW-модуле; raw_seed_from через
  reanchor, raw_home стал приватным (только клавиша Home).

F2 RAW→MD: top_line = line_at_off(raw_pos()) — точное позиционирование.
F8 между готовыми наборами: view_pos → map_off → view_reanchor вместо
восстановления сохранённой позиции набора. Ленивая сборка — по-прежнему
с начала (в RAW с raw_reanchor(0) и откатом при неудаче).

Попутно: F8 в RAW-режиме больше не рисует MD-вид поверх RAW —
перерисовка по g_view.

exe 25100 → 25725 (+625). Проверено в MAME.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-04 22:00:24 +03:00
parent adf667c087
commit e5866d6ba4
4 changed files with 100 additions and 14 deletions
+61 -1
View File
@@ -826,6 +826,43 @@ static int build_doc(uint8_t slot, uint8_t enc, uint8_t visible)
return 0; return 0;
} }
/* Пропорциональный перенос байт-позиции между буферами разного размера
* (8bit ↔ UTF-8 при F8): off * to / from БЕЗ __mullong/__divulong. Один
* цикл restoring-деления: бит k дроби off/from (сравнение с from/2^k)
* сразу добавляет to/2^k в результат — сумма to/2^k по установленным
* битам и есть off*to/from. Точность from/65536 (≤4 байта на максимальном
* файле); ляжет на границу строки через line_at_off / raw_reanchor. */
static uint32_t map_off(uint32_t off, uint32_t from, uint32_t to)
{
if (from == to || from == 0) return off;
uint32_t res = 0;
for (uint8_t i = 0; i < 16; i++) {
from >>= 1;
to >>= 1;
if (off >= from) { off -= from; res += to; }
}
return res;
}
/* Байт-позиция текущего вида (верх экрана) — единая «валюта» для переноса
* позиции между видами/наборами. */
static uint32_t view_pos(void)
{
#if WITH_RAW
if (g_view != VIEW_MD) return raw_pos();
#endif
return seg_off(top_line);
}
/* Поставить текущий вид на байт-позицию off (после doc_switch). */
static void view_reanchor(uint32_t off)
{
#if WITH_RAW
if (g_view != VIEW_MD) { raw_reanchor(off); return; }
#endif
top_line = line_at_off(off);
}
/* Переключает кодировку на `next` (вызывается из главного цикла по F8). /* Переключает кодировку на `next` (вызывается из главного цикла по F8).
* 8-бит↔8-бит внутри одного набора — мгновенный ремап; переход в другой набор * 8-бит↔8-бит внутри одного набора — мгновенный ремап; переход в другой набор
* — мгновенный doc_switch, если он построен, иначе ЛЕНИВАЯ сборка по месту * — мгновенный doc_switch, если он построен, иначе ЛЕНИВАЯ сборка по месту
@@ -842,13 +879,22 @@ static void switch_encoding(uint8_t next)
set_encoding(next); set_encoding(next);
g_doc[target].enc = next; g_doc[target].enc = next;
} else if (g_doc[target].built) { /* набор готов — мгновенный свап */ } else if (g_doc[target].built) { /* набор готов — мгновенный свап */
/* Позицию переносим пропорцией байтов (буферы наборов разного
* размера), а не из сохранённой в g_doc копии — иначе каждый набор
* скачет по своей старой позиции. */
uint32_t old_off = view_pos();
uint32_t old_size = file_size;
doc_switch(target); doc_switch(target);
g_disp_doc = target; g_disp_doc = target;
set_encoding(next); set_encoding(next);
g_doc[target].enc = next; g_doc[target].enc = next;
view_reanchor(map_off(old_off, old_size, file_size));
} else { /* строим набор лениво */ } else { /* строим набор лениво */
doc_save_live(g_active_doc); /* сохранить текущий (позиция и т.п.) */ doc_save_live(g_active_doc); /* сохранить текущий (позиция и т.п.) */
#if WITH_RAW #if WITH_RAW
uint32_t raw_save = raw_pos();
if (g_view != VIEW_MD)
raw_reanchor(0); /* прогрессивный показ нового буфера — с начала */
g_ready = 0; g_ready = 0;
render_menu(); /* идёт индексация — прячем F2 */ render_menu(); /* идёт индексация — прячем F2 */
#endif #endif
@@ -864,6 +910,10 @@ static void switch_encoding(uint8_t next)
if (target == DOC_UTF8) if (target == DOC_UTF8)
g_utf_failed = 1; g_utf_failed = 1;
doc_load_live(g_active_doc); doc_load_live(g_active_doc);
#if WITH_RAW
if (g_view != VIEW_MD)
raw_reanchor(raw_save); /* буфер прежний — вернуть позицию RAW */
#endif
return; return;
} }
g_disp_doc = target; /* build_doc уже выставил active/enc */ g_disp_doc = target; /* build_doc уже выставил active/enc */
@@ -1036,6 +1086,7 @@ int main(int argc, char **argv)
} else { } else {
g_raw_last = g_view; /* запомнить под-режим RAW */ g_raw_last = g_view; /* запомнить под-режим RAW */
g_view = VIEW_MD; g_view = VIEW_MD;
top_line = line_at_off(raw_pos()); /* позиция из RAW */
clamp_top(); clamp_top();
render_full_status(); render_menu(); render_full_status(); render_menu();
draw_viewport_from_cache(); draw_viewport_from_cache();
@@ -1052,7 +1103,16 @@ int main(int argc, char **argv)
switch_encoding(next); switch_encoding(next);
if (g_abort) if (g_abort)
exit(0); /* Esc/F10 во время ленивой досборки */ exit(0); /* Esc/F10 во время ленивой досборки */
render_full_status(); /* перерисовка — в MD-режиме */ /* Перерисовка — по текущему виду (switch_encoding сам не рисует). */
#if WITH_RAW
if (g_view != VIEW_MD) {
status_encoding();
render_raw_status();
raw_draw();
continue;
}
#endif
render_full_status();
draw_viewport_from_cache(); draw_viewport_from_cache();
continue; continue;
} }
+4 -2
View File
@@ -139,12 +139,14 @@ void win_rest(uint8_t row, uint8_t col, uint8_t h, uint8_t w, uint8_t page,
void fill_row(uint8_t y, uint8_t attr); void fill_row(uint8_t y, uint8_t attr);
uint8_t pct16(uint16_t num, uint16_t den); /* num*100/den (0..100), 16-бит, без __divulong */ uint8_t pct16(uint16_t num, uint16_t den); /* num*100/den (0..100), 16-бит, без __divulong */
uint32_t seg_off(uint16_t idx); /* offset исходника для логической строки idx */ uint32_t seg_off(uint16_t idx); /* offset исходника для логической строки idx */
uint16_t line_at_off(uint32_t off); /* обратно: последняя строка с seg_off <= off */
/* ================================================================== * /* ================================================================== *
* API модуля RAW (mdview2_raw.c). * API модуля RAW (mdview2_raw.c).
* ================================================================== */ * ================================================================== */
void raw_seed_from(uint16_t md_top_line); /* вход в RAW: позиция по top_line */ void raw_seed_from(uint16_t md_top_line); /* вход в RAW: позиция по top_line */
void raw_home(void); /* сброс в начало (смена буфера при F8 в/из UTF-8) */ uint32_t raw_pos(void); /* текущий байт-offset верха экрана RAW */
void raw_reanchor(uint32_t off); /* поставить RAW на строку с байтом off */
void raw_draw(void); /* перерисовать область документа в RAW */ void raw_draw(void); /* перерисовать область документа в RAW */
uint8_t raw_pct(void); /* % прокрутки по байтам (для render_raw_status_numbers) */ uint8_t raw_pct(void); /* % прокрутки по байтам (для render_raw_status_numbers) */
uint8_t raw_key(uint8_t scan); /* навигация RAW; 1 = обработано */ uint8_t raw_key(uint8_t scan); /* навигация RAW; 1 = обработано */
+14
View File
@@ -98,6 +98,20 @@ uint32_t seg_off(uint16_t idx)
return r.off; return r.off;
} }
/* Последняя строка индекса с исходным offset <= off — обратный перевод
* байт-позиции в MD-строку (позиционирование при F2/F8). Оффсеты сегментов
* монотонны, поэтому бинарный поиск (~15 bank-чтений). */
uint16_t line_at_off(uint32_t off)
{
uint16_t lo = 0, hi = n_lines;
while (lo < hi) {
uint16_t mid = (uint16_t)((lo + hi) >> 1);
if (seg_off(mid) <= off) lo = (uint16_t)(mid + 1);
else hi = mid;
}
return (lo > 0) ? (uint16_t)(lo - 1) : 0;
}
/* Установка флагов (маска IF_*) всегда идёт в последний emit_seg (n_lines-1). /* Установка флагов (маска IF_*) всегда идёт в последний emit_seg (n_lines-1).
* Используется near-копия cur_rec, чтобы не делать обратное чтение из банка. */ * Используется near-копия 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_cur_flags(uint8_t f) { cur_rec.flags |= f; idx_put((uint16_t)(n_lines - 1), &cur_rec); }
+16 -6
View File
@@ -173,17 +173,27 @@ uint8_t calc_raw_pct(void)
/* ---- посев позиции / пересев при смене активного буфера ----------- */ /* ---- посев позиции / пересев при смене активного буфера ----------- */
void raw_seed_from(uint16_t md_top_line) uint32_t raw_pos(void)
{ {
uint32_t off = seg_off(md_top_line); return raw_top;
if (off > file_size) off = 0; }
/* Поставить RAW на начало строки, содержащей байт off (позиционирование
* при F2 MD→RAW и F8-переносе позиции между наборами). */
void raw_reanchor(uint32_t off)
{
if (off > file_size) off = file_size;
raw_top = raw_line_start(off); raw_top = raw_line_start(off);
raw_hpan = 0; raw_hpan = 0;
} }
/* Сброс в начало — при смене активного буфера (F8 в/из UTF-8: размеры/offset'ы void raw_seed_from(uint16_t md_top_line)
* разные, старый raw_top невалиден). Без percent-математики. */ {
void raw_home(void) raw_reanchor(seg_off(md_top_line));
}
/* Сброс в начало (клавиша Home). */
static void raw_home(void)
{ {
raw_top = 0; raw_top = 0;
raw_hpan = 0; raw_hpan = 0;