mdview: faster file-byte access (fb byte-decode + page cache)
Decode page/offset from the 32-bit offset's bytes to avoid SDCC z80 32-bit shift/mask helpers on the hot path; map_page() uses a cached file_phys[] table to skip mem_get_page() on every W3 swap. Behaviour-preserving. Co-Authored-By: Oz <oz-agent@warp.dev>
This commit is contained in:
+455
-270
@@ -109,12 +109,10 @@ static const uint8_t md_pallete[16 * 4] = {
|
||||
* given time (FILE_BUF); fb() swaps pages on demand and tracks the
|
||||
* currently-mapped one in cur_page to avoid redundant OUTs.
|
||||
*
|
||||
* line_offset[] semantics depend on wrap_mode:
|
||||
* wrap_mode == 0 (truncate): one entry per logical line, value = byte
|
||||
* offset inside the logical file.
|
||||
* wrap_mode == 1 (wrap): one entry per VISIBLE viewport row. The
|
||||
* continuation flag for wrap segments lives
|
||||
* in the separate cont_flag[] bitmap.
|
||||
* line_offset[]: one entry per VISIBLE viewport row. Segments created by
|
||||
* word-wrap store the byte offset of the wrapped segment; continuation
|
||||
* flag is in cont_flag[]. nowrap_flag[] marks code block / HR / table
|
||||
* rows that do NOT wrap and support horizontal scrolling.
|
||||
*/
|
||||
|
||||
/* NOTE: explicit `= 0` initialisers are MANDATORY on SDCC z80.
|
||||
@@ -122,23 +120,28 @@ static const uint8_t md_pallete[16 * 4] = {
|
||||
* space — multiple such declarations end up at the SAME address and
|
||||
* silently overwrite each other. See memory/sdcc_static_storage_gotcha. */
|
||||
uint32_t line_offset[MAX_LINES]; // = {0};
|
||||
/* Bitmap: seg i is a continuation of the previous logical line — rendered
|
||||
* as plain text without markdown classification. */
|
||||
/* Bitmap: seg i is a continuation of the previous logical line. */
|
||||
uint8_t cont_flag[MAX_LINES / 8]; // = {0};
|
||||
/* Bitmap: seg i (non-CONT) is inside a fenced code block (between two
|
||||
* ``` lines). The fence delimiter lines themselves are NOT marked here —
|
||||
* they are detected dynamically via a 3-byte prefix check. */
|
||||
/* Bitmap: seg i (non-CONT) is inside a fenced code block. */
|
||||
uint8_t in_code[MAX_LINES / 8]; // = {0};
|
||||
/* Bitmap: seg i should NOT be wrapped (code block, HR, table). */
|
||||
uint8_t nowrap_flag[MAX_LINES / 8]; // = {0};
|
||||
/* Bitmap: seg i is a blank (empty visual line). */
|
||||
uint8_t blank_flag[MAX_LINES / 8]; // = {0};
|
||||
/* 2-bit continuation kind per seg (4 lines/byte): 0=PLAIN, 1=QUOTE, 2=LIST, 3=OTHER.
|
||||
* Used by render_line() to repeat prefix/indent on wrapped continuation rows. */
|
||||
uint8_t line_kind[MAX_LINES / 4]; // = {0};
|
||||
|
||||
uint8_t init_style[MAX_LINES / 4]; // = {0};
|
||||
uint8_t init_style[MAX_LINES]; // = {0};
|
||||
|
||||
static uint16_t n_lines = 0;
|
||||
static uint16_t top_line = 0;
|
||||
static uint32_t file_size = 0;
|
||||
static uint8_t file_blk = 0;
|
||||
static uint8_t file_pages = 0;
|
||||
static uint8_t file_phys[MAX_PAGES] = {0};
|
||||
static uint8_t cur_page = 0xFF; /* 0xFF = no page mapped yet */
|
||||
static uint8_t wrap_mode = 1; /* default: wrap on */
|
||||
static uint8_t viewport_x = 0; /* horizontal pan (truncate mode only) */
|
||||
static uint8_t viewport_x = 0; /* horizontal pan (for nowrap lines) */
|
||||
static char filename[64] = {0};
|
||||
|
||||
#define HPAN_STEP 8u
|
||||
@@ -197,18 +200,26 @@ static void spinner_show(uint8_t on)
|
||||
static void map_page(uint8_t page)
|
||||
{
|
||||
if (page != cur_page) {
|
||||
sprinter_page_w3(mem_get_page(file_blk, page));
|
||||
/* file_phys[] saves a BIOS mem_get_page() call on every page swap. */
|
||||
sprinter_page_w3(file_phys[page]);
|
||||
cur_page = page;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read one byte from the logical file at byte offset `p`. Swaps the W3
|
||||
* mapping if `p` falls into a different page than the one currently
|
||||
* mapped. Bounds checking is the caller's job. */
|
||||
* mapped. Bounds checking is the caller's job.
|
||||
*
|
||||
* Hot path: index_lines() calls this for almost every input byte. On z80
|
||||
* 32-bit shifts/masks are helper calls, so we decode page/offset from the
|
||||
* little-endian byte layout of `p`: page = bits 14..16, offset = bits 0..13. */
|
||||
static char fb(uint32_t p)
|
||||
{
|
||||
map_page((uint8_t)(p >> PAGE_BITS));
|
||||
return FILE_BUF[(uint16_t)(p & PAGE_MASK)];
|
||||
uint8_t *pb = (uint8_t *)&p;
|
||||
uint8_t page = (uint8_t)((pb[1] >> 6) | (pb[2] << 2));
|
||||
uint16_t off = (uint16_t)(((uint16_t)(pb[1] & 0x3F) << 8) | pb[0]);
|
||||
map_page(page);
|
||||
return FILE_BUF[off];
|
||||
}
|
||||
|
||||
/* Treat space/tab/EOL/NUL as whitespace for emphasis flanking checks. */
|
||||
@@ -244,6 +255,43 @@ static void set_cont(uint16_t idx)
|
||||
cont_flag[idx >> 3] |= (uint8_t)(1u << (idx & 7));
|
||||
}
|
||||
|
||||
static uint8_t is_nowrap(uint16_t idx)
|
||||
{
|
||||
return (uint8_t)((nowrap_flag[idx >> 3] >> (idx & 7)) & 1u);
|
||||
}
|
||||
|
||||
static void set_nowrap(uint16_t idx)
|
||||
{
|
||||
nowrap_flag[idx >> 3] |= (uint8_t)(1u << (idx & 7));
|
||||
}
|
||||
|
||||
static uint8_t is_blank(uint16_t idx)
|
||||
{
|
||||
return (uint8_t)((blank_flag[idx >> 3] >> (idx & 7)) & 1u);
|
||||
}
|
||||
|
||||
static void set_blank(uint16_t idx)
|
||||
{
|
||||
blank_flag[idx >> 3] |= (uint8_t)(1u << (idx & 7));
|
||||
}
|
||||
|
||||
/* Continuation kind: 0=PLAIN, 1=QUOTE, 2=LIST (ULIST/OLIST), 3=OTHER (HR/CODE/TABLE). */
|
||||
#define CK_PLAIN 0
|
||||
#define CK_QUOTE 1
|
||||
#define CK_LIST 2
|
||||
#define CK_OTHER 3
|
||||
|
||||
static uint8_t get_ckind(uint16_t idx)
|
||||
{
|
||||
return (uint8_t)((line_kind[idx >> 2] >> ((idx & 3) << 1)) & 3u);
|
||||
}
|
||||
|
||||
static void set_ckind(uint16_t idx, uint8_t kind)
|
||||
{
|
||||
uint8_t shift = (uint8_t)((idx & 3) << 1);
|
||||
line_kind[idx >> 2] = (uint8_t)((line_kind[idx >> 2] & ~(3u << shift)) | ((kind & 3u) << shift));
|
||||
}
|
||||
|
||||
static void set_pallete(void) {
|
||||
uint8_t buff[64];
|
||||
for(int i = 0; i < 16; i++) {
|
||||
@@ -284,9 +332,9 @@ static int load_file(const char *path)
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
|
||||
/* Allocate exactly the number of EMM pages we need (1..MAX_PAGES). */
|
||||
uint8_t pages_needed = (uint8_t)((file_size + PAGE_SIZE - 1u) / PAGE_SIZE);
|
||||
if (pages_needed == 0) pages_needed = 1;
|
||||
file_blk = mem_alloc_pages(pages_needed);
|
||||
file_pages = (uint8_t)((file_size + PAGE_SIZE - 1u) / PAGE_SIZE);
|
||||
if (file_pages == 0) file_pages = 1;
|
||||
file_blk = mem_alloc_pages(file_pages);
|
||||
if (file_blk == 0) {
|
||||
close(fd);
|
||||
return -3;
|
||||
@@ -296,9 +344,10 @@ static int load_file(const char *path)
|
||||
* `remaining` decreases by exactly PAGE_SIZE on each non-final iter;
|
||||
* the final iter reads whatever is left. */
|
||||
uint32_t remaining = file_size;
|
||||
for (uint8_t page = 0; page < pages_needed; page++) {
|
||||
for (uint8_t page = 0; page < file_pages; page++) {
|
||||
spinner_tick();
|
||||
uint8_t phys = mem_get_page(file_blk, page);
|
||||
file_phys[page] = phys;
|
||||
sprinter_page_w3(phys);
|
||||
cur_page = page;
|
||||
uint16_t chunk = (remaining > PAGE_SIZE)
|
||||
@@ -351,26 +400,27 @@ static uint8_t is_code_body(uint16_t idx)
|
||||
#define INIT_STYLE_BOLD 0x1
|
||||
#define INIT_STYLE_ITALIC 0x2
|
||||
#define INIT_STYLE_UNDER 0x3
|
||||
#define INIT_STYLE_CODE 0x4
|
||||
|
||||
static const uint8_t styles_map[] = {
|
||||
ATTR_TEXT, ATTR_TEXT_BOLD, ATTR_TEXT_ITALIC, ATTR_TEXT_UNDERSORE
|
||||
ATTR_TEXT, ATTR_TEXT_BOLD, ATTR_TEXT_ITALIC, ATTR_TEXT_UNDERSORE, ATTR_TEXT_CODE
|
||||
};
|
||||
|
||||
static uint8_t get_init_style(uint16_t idx)
|
||||
{
|
||||
if (idx >= n_lines) return styles_map[INIT_STYLE_PLAIN];
|
||||
return styles_map[(uint8_t)((init_style[idx >> 2] >> ((idx & 3) << 1)) & 3u)];
|
||||
return styles_map[init_style[idx] & 7u];
|
||||
}
|
||||
|
||||
static uint8_t get_init_style_raw(uint16_t idx)
|
||||
{
|
||||
if (idx >= n_lines) return INIT_STYLE_PLAIN;
|
||||
return (uint8_t)((init_style[idx >> 2] >> ((idx & 3) << 1)) & 3u);
|
||||
return init_style[idx] & 7u;
|
||||
}
|
||||
|
||||
static void set_init_style_raw(uint16_t idx, uint8_t style)
|
||||
{
|
||||
init_style[idx >> 2] = (init_style[idx >> 2] & ~(3u << ((idx & 3) << 1))) | ((style & 3u) << ((idx & 3) << 1));
|
||||
if (idx < MAX_LINES) init_style[idx] = style & 7u;
|
||||
}
|
||||
|
||||
/* Returns 1 iff the logical line at file offset `p_start` should NOT be
|
||||
@@ -529,206 +579,347 @@ static uint8_t marker_visible_col(uint8_t kind, uint32_t p_start, uint32_t conte
|
||||
}
|
||||
}
|
||||
|
||||
/* Emit a new segment entry. */
|
||||
static void emit_seg(uint32_t off, uint8_t style, uint8_t ckind, uint8_t cont)
|
||||
{
|
||||
if (n_lines >= MAX_LINES) return;
|
||||
line_offset[n_lines] = off;
|
||||
set_init_style_raw(n_lines, style);
|
||||
set_ckind(n_lines, ckind);
|
||||
if (cont) set_cont(n_lines);
|
||||
n_lines++;
|
||||
}
|
||||
|
||||
static uint8_t is_line_blank(uint32_t p)
|
||||
{
|
||||
while (p < file_size) {
|
||||
char c = fb(p);
|
||||
if (c == '\n') return 1;
|
||||
if (c != ' ' && c != '\t') return 0;
|
||||
p++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint8_t is_fence_raw(uint32_t p)
|
||||
{
|
||||
if (p + 2 >= file_size) return 0;
|
||||
return (fb(p) == '`' && fb(p + 1) == '`' && fb(p + 2) == '`');
|
||||
}
|
||||
|
||||
static uint8_t is_hr_raw(uint32_t p)
|
||||
{
|
||||
char c0 = fb(p);
|
||||
if (c0 != '-' && c0 != '*' && c0 != '_') return 0;
|
||||
char marker = 0;
|
||||
uint8_t count = 0, ok = 1;
|
||||
uint32_t hr_p = p;
|
||||
while (hr_p < file_size) {
|
||||
char ch = fb(hr_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++;
|
||||
}
|
||||
return ok && count >= 3;
|
||||
}
|
||||
|
||||
/* Shared inline-emphasis / wrap scanner for a single line or paragraph stream.
|
||||
* Starts at `q`, processes until `q_end` (or paragraph break for plain text),
|
||||
* emits continuation segs as needed. `col` is the starting visible column
|
||||
* (0 for plain/headers, marker width for list/quote). `ckind` is the
|
||||
* continuation-kind for wrap segs. Returns the final `line_style` (for
|
||||
* paragraph streams that carry emphasis across soft/hard breaks). */
|
||||
static uint8_t inline_scan(uint32_t q, uint32_t q_end, uint8_t col,
|
||||
uint8_t ckind, uint8_t line_style)
|
||||
{
|
||||
uint32_t last_space = 0xFFFFFFFFu;
|
||||
char prev_ch = ' ';
|
||||
uint8_t seg_col = col;
|
||||
|
||||
while (q < q_end && n_lines < MAX_LINES) {
|
||||
char ch = fb(q);
|
||||
|
||||
if (ch == '\n') {
|
||||
/* hard break (already checked by caller) or stream end */
|
||||
q++;
|
||||
emit_seg(q, line_style, ckind, (ckind == CK_LIST || ckind == CK_QUOTE) ? 1 : 0);
|
||||
last_space = 0xFFFFFFFFu; prev_ch = ' '; seg_col = col;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '`') {
|
||||
if (line_style == INIT_STYLE_CODE) line_style = INIT_STYLE_PLAIN;
|
||||
else line_style = INIT_STYLE_CODE;
|
||||
q++; continue;
|
||||
}
|
||||
if (ch == '*' && (q + 1) < q_end && fb(q + 1) == '*') {
|
||||
char next_ch = ((q + 2) < q_end) ? fb(q + 2) : '\n';
|
||||
if (is_emph_flanked(prev_ch, next_ch)) {
|
||||
if (line_style == INIT_STYLE_PLAIN && ws_or_eol(prev_ch)) {
|
||||
line_style = INIT_STYLE_BOLD; q += 2; continue;
|
||||
} else if (line_style == INIT_STYLE_BOLD && ws_or_eol_or_delim(next_ch)) {
|
||||
line_style = INIT_STYLE_PLAIN; q += 2; continue;
|
||||
}
|
||||
}
|
||||
seg_col += 2; q += 2; prev_ch = '*'; continue;
|
||||
}
|
||||
if (ch == '*' || ch == '_') {
|
||||
char next_ch = ((q + 1) < q_end) ? fb(q + 1) : '\n';
|
||||
if (is_emph_flanked(prev_ch, next_ch)) {
|
||||
uint8_t new_style = (ch == '*') ? INIT_STYLE_ITALIC : INIT_STYLE_UNDER;
|
||||
if (line_style == INIT_STYLE_PLAIN && ws_or_eol(prev_ch)) {
|
||||
line_style = new_style; q++; continue;
|
||||
} else if (line_style == new_style && ws_or_eol_or_delim(next_ch)) {
|
||||
line_style = INIT_STYLE_PLAIN; q++; continue;
|
||||
}
|
||||
}
|
||||
seg_col++; q++; prev_ch = ch; continue;
|
||||
}
|
||||
if (ch == '\t') {
|
||||
uint8_t tgt = (uint8_t)((seg_col & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP);
|
||||
if (tgt > SCREEN_W) tgt = SCREEN_W;
|
||||
seg_col = tgt; q++; prev_ch = ' '; continue;
|
||||
}
|
||||
if (ch == ' ') {
|
||||
last_space = q + 1;
|
||||
seg_col++; q++; prev_ch = ' ';
|
||||
} else {
|
||||
seg_col++; q++; prev_ch = ch;
|
||||
}
|
||||
if (seg_col >= SCREEN_W) {
|
||||
uint32_t wrap_at = (last_space != 0xFFFFFFFFu && last_space > line_offset[n_lines - 1])
|
||||
? last_space : q;
|
||||
if (wrap_at >= q_end || wrap_at == line_offset[n_lines - 1]) break;
|
||||
emit_seg(wrap_at, line_style, ckind, 1);
|
||||
seg_col = col; last_space = 0xFFFFFFFFu; prev_ch = ' '; q = wrap_at;
|
||||
}
|
||||
}
|
||||
return line_style;
|
||||
}
|
||||
|
||||
static void index_lines(void)
|
||||
{
|
||||
uint8_t line_style = INIT_STYLE_PLAIN;
|
||||
uint8_t in_block = 0;
|
||||
n_lines = 0;
|
||||
memset(in_code, 0, sizeof(in_code));
|
||||
memset(cont_flag, 0, sizeof(cont_flag));
|
||||
memset(nowrap_flag, 0, sizeof(nowrap_flag));
|
||||
memset(blank_flag, 0, sizeof(blank_flag));
|
||||
memset(line_kind, 0, sizeof(line_kind));
|
||||
memset(init_style, 0, sizeof(init_style));
|
||||
if (file_size == 0) return;
|
||||
|
||||
if (!wrap_mode) {
|
||||
/* Truncate: one entry per logical line. */
|
||||
line_offset[n_lines++] = 0;
|
||||
for (uint32_t p = 0; p < file_size; p++) {
|
||||
if (fb(p) == '\n' &&
|
||||
p + 1 < file_size && n_lines < MAX_LINES) {
|
||||
line_offset[n_lines++] = p + 1;
|
||||
if ((n_lines & 15) == 0) spinner_tick();
|
||||
uint32_t p = 0;
|
||||
while (p < file_size && n_lines < MAX_LINES) {
|
||||
if ((n_lines & 15) == 0) spinner_tick();
|
||||
|
||||
/* Blank line = paragraph separator. Emit one empty screen row
|
||||
* (only if the previous emitted line was not already blank). */
|
||||
if (is_line_blank(p)) {
|
||||
while (p < file_size && fb(p) != '\n') p++;
|
||||
if (p < file_size) p++;
|
||||
if (n_lines == 0 || line_offset[n_lines - 1] != p) {
|
||||
emit_seg(p, INIT_STYLE_PLAIN, CK_PLAIN, 0);
|
||||
set_blank(n_lines - 1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
/* Wrap: walk logical lines, split each into one or more segs. */
|
||||
uint32_t p = 0;
|
||||
uint8_t in_block = 0; /* inside fenced code block? */
|
||||
line_style = INIT_STYLE_PLAIN;
|
||||
while (p < file_size && n_lines < MAX_LINES) {
|
||||
if ((n_lines & 15) == 0) spinner_tick();
|
||||
/* Find end of logical line (\n or EOF). */
|
||||
uint32_t line_end = p;
|
||||
|
||||
/* ---- fenced code block (delimiter or body) ---- */
|
||||
if (is_fence_raw(p)) {
|
||||
in_block = !in_block;
|
||||
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
||||
set_nowrap(n_lines - 1);
|
||||
while (p < file_size && fb(p) != '\n') p++;
|
||||
if (p < file_size) p++;
|
||||
continue;
|
||||
}
|
||||
if (in_block) {
|
||||
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
||||
set_nowrap(n_lines - 1);
|
||||
in_code[(n_lines - 1) >> 3] |= (uint8_t)(1u << ((n_lines - 1) & 7));
|
||||
while (p < file_size && fb(p) != '\n') p++;
|
||||
if (p < file_size) p++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ---- horizontal rule ---- */
|
||||
if (is_hr_raw(p)) {
|
||||
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
||||
set_nowrap(n_lines - 1);
|
||||
while (p < file_size && fb(p) != '\n') p++;
|
||||
if (p < file_size) p++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ---- classify current line ---- */
|
||||
uint32_t content_off = p;
|
||||
uint8_t kind = classify_line(p, &content_off);
|
||||
|
||||
/* ---- Header: single line, not merged into paragraphs ---- */
|
||||
if (kind >= LK_H1 && kind <= LK_H4) {
|
||||
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++;
|
||||
(void)inline_scan(content_off, line_end, 0, CK_OTHER, INIT_STYLE_PLAIN);
|
||||
while (p < file_size && fb(p) != '\n') p++;
|
||||
if (p < file_size) p++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Detect fenced-code delimiter (``` at col 0). Toggle in_block
|
||||
* and reset emphasis tracking — code blocks never carry inline
|
||||
* style across their boundaries. */
|
||||
if (p + 2 < file_size &&
|
||||
fb(p) == '`' && fb(p + 1) == '`' && fb(p + 2) == '`') {
|
||||
in_block = (uint8_t)!in_block;
|
||||
line_style = INIT_STYLE_PLAIN;
|
||||
}
|
||||
/* ---- List item: single line, not merged ---- */
|
||||
if (kind == LK_ULIST || kind == LK_OLIST) {
|
||||
uint8_t col = marker_visible_col(kind, p, content_off);
|
||||
emit_seg(p, INIT_STYLE_PLAIN, CK_LIST, 0);
|
||||
uint32_t line_end = content_off;
|
||||
while (line_end < file_size && fb(line_end) != '\n') line_end++;
|
||||
(void)inline_scan(content_off, line_end, col, CK_LIST, INIT_STYLE_PLAIN);
|
||||
while (p < file_size && fb(p) != '\n') p++;
|
||||
if (p < file_size) p++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Emit the first seg of this logical line (no CONT). */
|
||||
set_init_style_raw(n_lines, in_block ? INIT_STYLE_PLAIN : line_style);
|
||||
line_offset[n_lines++] = p;
|
||||
/* ---- Quote line: single line, not merged ---- */
|
||||
if (kind == LK_QUOTE) {
|
||||
uint8_t col = marker_visible_col(kind, p, content_off);
|
||||
emit_seg(p, INIT_STYLE_PLAIN, CK_QUOTE, 0);
|
||||
uint32_t line_end = content_off;
|
||||
while (line_end < file_size && fb(line_end) != '\n') line_end++;
|
||||
(void)inline_scan(content_off, line_end, col, CK_QUOTE, INIT_STYLE_PLAIN);
|
||||
while (p < file_size && fb(p) != '\n') p++;
|
||||
if (p < file_size) p++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_nowrap_line(p)) {
|
||||
/* Determine marker prefix and starting visible column. */
|
||||
uint32_t content_off = p;
|
||||
uint8_t kind = classify_line(p, &content_off);
|
||||
uint8_t col = marker_visible_col(kind, p, content_off);
|
||||
uint32_t q;
|
||||
if (kind == LK_H1 || kind == LK_H2 || kind == LK_H3 || kind == LK_H4 ||
|
||||
kind == LK_ULIST || kind == LK_OLIST || kind == LK_QUOTE) {
|
||||
q = content_off;
|
||||
/* ---- Plain text paragraph: merge across soft breaks ---- */
|
||||
{
|
||||
uint8_t line_style = INIT_STYLE_PLAIN;
|
||||
emit_seg(p, line_style, CK_PLAIN, 0);
|
||||
|
||||
uint32_t q = p;
|
||||
uint32_t last_space = 0xFFFFFFFFu;
|
||||
char prev_ch = ' ';
|
||||
uint8_t seg_col = 0;
|
||||
|
||||
while (q < file_size && n_lines < MAX_LINES) {
|
||||
char ch = fb(q);
|
||||
uint8_t soft_break = 0;
|
||||
|
||||
if (ch == '\n') {
|
||||
/* Paragraph break? */
|
||||
if (q + 1 >= file_size) break;
|
||||
if (fb(q + 1) == '\n') break; /* blank line */
|
||||
uint32_t next = q + 1;
|
||||
if (is_fence_raw(next)) break;
|
||||
if (is_hr_raw(next)) break;
|
||||
uint32_t dummy;
|
||||
if (classify_line(next, &dummy) != LK_PLAIN) break;
|
||||
if (is_line_blank(next)) break;
|
||||
|
||||
/* Hard break? */
|
||||
uint8_t is_hard = 0;
|
||||
if (q >= p + 2) {
|
||||
char b1 = fb(q - 1);
|
||||
char b2 = fb(q - 2);
|
||||
if ((b1 == ' ' || b1 == '\t') && (b2 == ' ' || b2 == '\t'))
|
||||
is_hard = 1;
|
||||
}
|
||||
|
||||
if (is_hard) {
|
||||
q++;
|
||||
emit_seg(q, line_style, CK_PLAIN, 0);
|
||||
last_space = 0xFFFFFFFFu; prev_ch = ' '; seg_col = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Soft break — treat as a single space. */
|
||||
q++;
|
||||
soft_break = 1;
|
||||
ch = ' ';
|
||||
}
|
||||
|
||||
if (ch == '`') {
|
||||
if (line_style == INIT_STYLE_CODE) line_style = INIT_STYLE_PLAIN;
|
||||
else line_style = INIT_STYLE_CODE;
|
||||
if (!soft_break) q++;
|
||||
continue;
|
||||
}
|
||||
if (ch == '*' && (q + 1) < file_size && fb(q + 1) == '*') {
|
||||
char next_ch = ((q + 2) < file_size) ? fb(q + 2) : '\n';
|
||||
if (is_emph_flanked(prev_ch, next_ch)) {
|
||||
if (line_style == INIT_STYLE_PLAIN && ws_or_eol(prev_ch)) {
|
||||
line_style = INIT_STYLE_BOLD;
|
||||
if (!soft_break) q += 2; else q++;
|
||||
continue;
|
||||
} else if (line_style == INIT_STYLE_BOLD && ws_or_eol_or_delim(next_ch)) {
|
||||
line_style = INIT_STYLE_PLAIN;
|
||||
if (!soft_break) q += 2; else q++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
seg_col += 2;
|
||||
if (!soft_break) q += 2; else q++;
|
||||
prev_ch = '*'; continue;
|
||||
}
|
||||
if (ch == '*' || ch == '_') {
|
||||
char next_ch = ((q + 1) < file_size) ? fb(q + 1) : '\n';
|
||||
if (is_emph_flanked(prev_ch, next_ch)) {
|
||||
uint8_t new_style = (ch == '*') ? INIT_STYLE_ITALIC : INIT_STYLE_UNDER;
|
||||
if (line_style == INIT_STYLE_PLAIN && ws_or_eol(prev_ch)) {
|
||||
line_style = new_style;
|
||||
if (!soft_break) q++; else q++;
|
||||
continue;
|
||||
} else if (line_style == new_style && ws_or_eol_or_delim(next_ch)) {
|
||||
line_style = INIT_STYLE_PLAIN;
|
||||
if (!soft_break) q++; else q++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
seg_col++;
|
||||
if (!soft_break) q++; else q++;
|
||||
prev_ch = ch; continue;
|
||||
}
|
||||
if (ch == '\t') {
|
||||
uint8_t tgt = (uint8_t)((seg_col & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP);
|
||||
if (tgt > SCREEN_W) tgt = SCREEN_W;
|
||||
seg_col = tgt;
|
||||
if (!soft_break) q++; else q++;
|
||||
prev_ch = ' '; continue;
|
||||
}
|
||||
if (ch == ' ') {
|
||||
if (soft_break) last_space = q;
|
||||
else last_space = q + 1;
|
||||
seg_col++;
|
||||
if (!soft_break) q++; else q++;
|
||||
prev_ch = ' ';
|
||||
} else {
|
||||
q = p;
|
||||
seg_col++;
|
||||
if (!soft_break) q++; else q++;
|
||||
prev_ch = ch;
|
||||
}
|
||||
|
||||
uint32_t last_space = 0xFFFFFFFFu; /* byte pos AFTER last space */
|
||||
char prev_ch = ' '; /* for emphasis flanking */
|
||||
while (q < line_end && n_lines < MAX_LINES) {
|
||||
char ch = fb(q);
|
||||
/* Inside fenced code block: every byte is literal, no
|
||||
* emphasis markers, but wrap still applies. */
|
||||
if (in_block) {
|
||||
if (ch == '\t') {
|
||||
uint8_t tgt = (uint8_t)((col & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP);
|
||||
if (tgt > SCREEN_W) tgt = SCREEN_W;
|
||||
col = tgt;
|
||||
q++;
|
||||
prev_ch = ' ';
|
||||
} else {
|
||||
if (ch == ' ') last_space = q + 1;
|
||||
col++;
|
||||
q++;
|
||||
prev_ch = ch;
|
||||
}
|
||||
if (col >= SCREEN_W && q < line_end) {
|
||||
uint32_t wrap_at = (last_space != 0xFFFFFFFFu &&
|
||||
last_space > line_offset[n_lines - 1])
|
||||
? last_space : q;
|
||||
if (wrap_at >= line_end) break;
|
||||
line_offset[n_lines] = wrap_at;
|
||||
set_init_style_raw(n_lines, INIT_STYLE_PLAIN);
|
||||
set_cont(n_lines);
|
||||
n_lines++;
|
||||
col = 0;
|
||||
last_space = 0xFFFFFFFFu;
|
||||
prev_ch = ' ';
|
||||
q = wrap_at;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/* Backtick — always zero-width (inline code delimiter). */
|
||||
if (ch == '`') {
|
||||
q++;
|
||||
continue;
|
||||
}
|
||||
/* `**` — zero-width only when XOR-flanked; otherwise
|
||||
* both stars render as two literal columns. */
|
||||
if (ch == '*' && (q + 1) < line_end && fb(q + 1) == '*') {
|
||||
char next_ch = ((q + 2) < line_end) ? fb(q + 2) : '\n';
|
||||
prev_ch = fb(q - 1);
|
||||
if (is_emph_flanked(prev_ch, next_ch)) {
|
||||
if(line_style == INIT_STYLE_PLAIN && ws_or_eol(prev_ch)) {
|
||||
line_style = INIT_STYLE_BOLD;
|
||||
q += 2;
|
||||
continue;
|
||||
} else if(line_style == INIT_STYLE_BOLD && ws_or_eol_or_delim(next_ch)) {
|
||||
line_style = INIT_STYLE_PLAIN;
|
||||
q += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
col += 2;
|
||||
q += 2;
|
||||
prev_ch = '*';
|
||||
}
|
||||
/* Single `*` / `_` — zero-width only when XOR-flanked. */
|
||||
else if (ch == '*' || ch == '_') {
|
||||
prev_ch = fb(q - 1);
|
||||
char next_ch = ((q + 1) < line_end) ? fb(q + 1) : '\n';
|
||||
if (is_emph_flanked(prev_ch, next_ch)) {
|
||||
uint8_t new_style = (ch == '*') ? INIT_STYLE_ITALIC : INIT_STYLE_UNDER;
|
||||
if (line_style == INIT_STYLE_PLAIN && ws_or_eol(prev_ch)) {
|
||||
line_style = new_style;
|
||||
q++;
|
||||
continue;
|
||||
} else if (line_style == new_style && ws_or_eol_or_delim(next_ch)) {
|
||||
line_style = INIT_STYLE_PLAIN;
|
||||
q++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
col++;
|
||||
q++;
|
||||
prev_ch = ch;
|
||||
}
|
||||
else if (ch == '\t') {
|
||||
uint8_t tgt = (uint8_t)((col & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP);
|
||||
if (tgt > SCREEN_W) tgt = SCREEN_W;
|
||||
col = tgt;
|
||||
q++;
|
||||
prev_ch = ' ';
|
||||
} else {
|
||||
if (ch == ' ') last_space = q + 1;
|
||||
col++;
|
||||
q++;
|
||||
prev_ch = ch;
|
||||
}
|
||||
if (col >= SCREEN_W && q < line_end) {
|
||||
uint32_t wrap_at = (last_space != 0xFFFFFFFFu &&
|
||||
last_space > line_offset[n_lines - 1])
|
||||
? last_space : q;
|
||||
if (wrap_at >= line_end) break;
|
||||
line_offset[n_lines] = wrap_at;
|
||||
set_init_style_raw(n_lines, line_style); /* carry style across wrap */
|
||||
set_cont(n_lines);
|
||||
n_lines++;
|
||||
col = 0;
|
||||
last_space = 0xFFFFFFFFu;
|
||||
prev_ch = ' ';
|
||||
q = wrap_at;
|
||||
}
|
||||
if (seg_col >= SCREEN_W) {
|
||||
uint32_t wrap_at = (last_space != 0xFFFFFFFFu && last_space > line_offset[n_lines - 1])
|
||||
? last_space : q;
|
||||
if (wrap_at >= file_size || wrap_at == line_offset[n_lines - 1]) break;
|
||||
emit_seg(wrap_at, line_style, CK_PLAIN, 1);
|
||||
seg_col = 0; last_space = 0xFFFFFFFFu; prev_ch = ' '; q = wrap_at;
|
||||
}
|
||||
}
|
||||
|
||||
p = (line_end < file_size) ? (line_end + 1) : line_end;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fence/code-body bitmap pass — only non-CONT segs participate.
|
||||
* The wrap-pass above already tracked fenced blocks and set init_style
|
||||
* to PLAIN for every seg inside; this pass only fixes the in_code[]
|
||||
* bitmap (used by render_line to disable inline parsing). */
|
||||
{
|
||||
uint8_t in_block = 0;
|
||||
for (uint16_t i = 0; i < n_lines; i++) {
|
||||
if (is_cont(i)) {
|
||||
if (is_code_body(i - 1)) {
|
||||
in_code[i >> 3] |= (uint8_t)(1u << (i & 7));
|
||||
set_init_style_raw(i, INIT_STYLE_PLAIN);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (is_fence_delim(i)) {
|
||||
in_block = (uint8_t)!in_block;
|
||||
set_init_style_raw(i, INIT_STYLE_PLAIN);
|
||||
} else if (in_block) {
|
||||
in_code[i >> 3] |= (uint8_t)(1u << (i & 7));
|
||||
set_init_style_raw(i, INIT_STYLE_PLAIN);
|
||||
if (q >= file_size) {
|
||||
p = file_size;
|
||||
} else {
|
||||
while (q < file_size && fb(q) != '\n') q++;
|
||||
if (q + 1 < file_size && fb(q + 1) == '\n') q += 2;
|
||||
else if (q < file_size) q++;
|
||||
p = q;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ==================================================================
|
||||
* rendering
|
||||
* ================================================================== */
|
||||
|
||||
/* Inline emphasis tracking (one style active at a time, no nesting). */
|
||||
#define EM_NONE 0
|
||||
#define EM_BOLD 1
|
||||
#define EM_ITALIC 2
|
||||
@@ -743,18 +934,10 @@ static void render_line(uint16_t line_idx, uint8_t row)
|
||||
uint8_t emph = is_code_body(line_idx) ? EM_CODE : get_init_style_raw(line_idx);
|
||||
uint8_t parse_inline = 1; /* skip inline parsing for fenced code body */
|
||||
|
||||
uint8_t x = wherex();
|
||||
uint8_t y = wherey();
|
||||
uint16_t from = 342;
|
||||
if(line_idx >= from && line_idx <= from + 3) {
|
||||
gotoxy(18 + (line_idx - from) * 9, 31); dec16(line_idx);
|
||||
gotoxy(21 + (line_idx - from) * 9, 31); hex8(get_init_style_raw(line_idx));
|
||||
gotoxy(23 + (line_idx - from) * 9, 31); hex8(is_code_body(line_idx));
|
||||
if (is_blank(line_idx)) {
|
||||
while (col < SCREEN_W) wrchar(col++, row, ' ', ATTR_TEXT);
|
||||
return;
|
||||
}
|
||||
gotoxy(61, 31); dec16(line_idx);
|
||||
gotoxy(64, 31); hex8(get_init_style_raw(line_idx));
|
||||
gotoxy(66, 31); hex8(is_code_body(line_idx));
|
||||
gotoxy(x, y);
|
||||
|
||||
if (line_idx < n_lines) {
|
||||
uint32_t p = line_offset[line_idx];
|
||||
@@ -767,17 +950,34 @@ static void render_line(uint16_t line_idx, uint8_t row)
|
||||
if (seg_end > file_size) seg_end = file_size;
|
||||
uint8_t kind;
|
||||
|
||||
/* Continuation seg → render plain text from the seg's offset up to
|
||||
* either the next seg's offset or end-of-line. No markdown
|
||||
* classification, no marker prefixes, inline markers stay literal
|
||||
* (we drop them silently so the visible output matches the wrap
|
||||
* algorithm's column count). */
|
||||
/* Continuation seg → repeat prefix/indent from the first non-CONT
|
||||
* segment of this logical line, then render plain text. */
|
||||
if (cont) {
|
||||
// TODO -
|
||||
// Для cont можно сделать такое -
|
||||
// 1) если это продолжение quote - то отрисовать префикc QUOTE и дальше продолжить вывод
|
||||
// 2) если это продолжение пункта list - то посмотреть какой отступ был, повторить его же и продолжить вывод
|
||||
// 3) для обычного текста - продолжить вывод
|
||||
uint16_t first = line_idx;
|
||||
while (first > 0 && is_cont((uint16_t)(first - 1))) first--;
|
||||
uint32_t p_start = line_offset[first];
|
||||
uint32_t content_off = p_start;
|
||||
uint8_t kind = classify_line(p_start, &content_off);
|
||||
uint8_t indent = marker_visible_col(kind, p_start, content_off);
|
||||
if (kind == LK_QUOTE) {
|
||||
uint8_t q = 0;
|
||||
while (q + 2 < content_off && col < SCREEN_W && fb(p_start + q) == ' ') {
|
||||
wrchar(col++, row, ' ', ATTR_TEXT);
|
||||
q++;
|
||||
}
|
||||
if (col < SCREEN_W) wrchar(col++, row, 0xB3, ATTR_QUOTE_MARKER);
|
||||
if (col < SCREEN_W) wrchar(col++, row, ' ', ATTR_TEXT);
|
||||
} else if (kind == LK_ULIST || kind == LK_OLIST) {
|
||||
uint8_t q = 0;
|
||||
while (q < indent && col < SCREEN_W) {
|
||||
wrchar(col++, row, ' ', ATTR_TEXT);
|
||||
q++;
|
||||
}
|
||||
}
|
||||
/* start rendering from the wrapped segment offset */
|
||||
p = line_offset[line_idx];
|
||||
content_off = p;
|
||||
/* For cont segs, inline parsing continues normally. */
|
||||
}
|
||||
|
||||
/* Fenced code block: delimiter line → blank row; body lines render
|
||||
@@ -847,16 +1047,18 @@ static void render_line(uint16_t line_idx, uint8_t row)
|
||||
p = content_off;
|
||||
}
|
||||
|
||||
/* Effective horizontal offset: only nowrap lines honour viewport_x. */
|
||||
uint8_t effective_vx = is_nowrap(line_idx) ? viewport_x : 0;
|
||||
/* `cc` is the VISIBLE column inside the content area (the part
|
||||
* after any fixed marker). When viewport_x > 0 (only possible in
|
||||
* truncate mode), chars at cc < viewport_x are skipped silently
|
||||
* so the user can pan past the marker. The marker itself was
|
||||
* already rendered above at fixed screen cols 0..(col-1). */
|
||||
* after any fixed marker). For nowrap lines, chars at cc < viewport_x
|
||||
* are skipped so the user can pan horizontally. */
|
||||
uint8_t cc = 0;
|
||||
char prev_ch = ' '; /* for `_` flanking check */
|
||||
while (p < seg_end && col < SCREEN_W) {
|
||||
char ch = fb(p);
|
||||
if (ch == '\n' || ch == '\r') break;
|
||||
/* Soft break (newline inside a paragraph segment) — render as a
|
||||
* single space. Hard breaks are already separate segments. */
|
||||
if (ch == '\n' || ch == '\r') ch = ' ';
|
||||
|
||||
/* --- inline emphasis markers (consumed, not rendered) ---
|
||||
* Asterisk / double-asterisk / underscore are markers only
|
||||
@@ -883,9 +1085,9 @@ static void render_line(uint16_t line_idx, uint8_t row)
|
||||
/* literal `**` — render BOTH stars, do NOT fall
|
||||
* through to single-`*` handler. */
|
||||
p += 2;
|
||||
if (cc >= viewport_x && col < SCREEN_W) wrchar(col++, row, '*', cur_attr);
|
||||
if (cc >= effective_vx && col < SCREEN_W) wrchar(col++, row, '*', cur_attr);
|
||||
cc++;
|
||||
if (cc >= viewport_x && col < SCREEN_W) wrchar(col++, row, '*', cur_attr);
|
||||
if (cc >= effective_vx && col < SCREEN_W) wrchar(col++, row, '*', cur_attr);
|
||||
cc++;
|
||||
prev_ch = '*';
|
||||
continue;
|
||||
@@ -924,12 +1126,12 @@ static void render_line(uint16_t line_idx, uint8_t row)
|
||||
* space is then conditionally rendered through hpan. */
|
||||
uint8_t tgt = (uint8_t)((cc & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP);
|
||||
while (cc < tgt && col < SCREEN_W) {
|
||||
if (cc >= viewport_x) wrchar(col++, row, ' ', cur_attr);
|
||||
if (cc >= effective_vx) wrchar(col++, row, ' ', cur_attr);
|
||||
cc++;
|
||||
}
|
||||
prev_ch = ' ';
|
||||
} else {
|
||||
if (cc >= viewport_x && col < SCREEN_W) {
|
||||
if (cc >= effective_vx && col < SCREEN_W) {
|
||||
wrchar(col++, row, ch, cur_attr);
|
||||
}
|
||||
cc++;
|
||||
@@ -937,12 +1139,11 @@ static void render_line(uint16_t line_idx, uint8_t row)
|
||||
}
|
||||
}
|
||||
|
||||
/* Right-edge truncation indicator: when in truncate mode and the
|
||||
* main loop stopped because the screen ran out (col == SCREEN_W),
|
||||
* peek forward past any zero-width inline markers — if there's
|
||||
* still real visible content before the end of the line, overlay
|
||||
* the last screen cell with a bright '>'. */
|
||||
if (!wrap_mode && col >= SCREEN_W) {
|
||||
/* Right-edge truncation indicator for nowrap lines: when the screen
|
||||
* ran out (col == SCREEN_W) and this row is nowrap, peek forward
|
||||
* past any zero-width inline markers — if there's still real visible
|
||||
* content before the end of the line, overlay the last cell with '>'. */
|
||||
if (is_nowrap(line_idx) && col >= SCREEN_W) {
|
||||
uint32_t pp = p;
|
||||
uint32_t left_bound = line_offset[line_idx];
|
||||
while (pp < seg_end) {
|
||||
@@ -1024,9 +1225,7 @@ static void render_menu(void)
|
||||
fill_row(MENU_ROW, ATTR_MENU_T);
|
||||
put_str_attr(1, MENU_ROW, "F1", ATTR_MENU_K);
|
||||
put_str_attr(4, MENU_ROW, "Help", ATTR_MENU_T);
|
||||
put_str_attr(10, MENU_ROW, "F2", ATTR_MENU_K);
|
||||
/* Label reflects what F2 will DO: when wrap is on, F2 turns it off. */
|
||||
put_str_attr(13, MENU_ROW, wrap_mode ? "Unwrap" : "Wrap ", ATTR_MENU_T);
|
||||
/* F2 slot removed — no wrap/unwrap toggle any more. */
|
||||
put_str_attr(SCREEN_W - 10, MENU_ROW, "F10", ATTR_MENU_K);
|
||||
put_str_attr(SCREEN_W - 5, MENU_ROW, "Exit", ATTR_MENU_T);
|
||||
}
|
||||
@@ -1074,12 +1273,14 @@ static void scroll_down(uint16_t n)
|
||||
}
|
||||
}
|
||||
|
||||
/* Horizontal pan (truncate mode only). Each step is HPAN_STEP visible
|
||||
* columns; clamped to [0, 240] — enough for any real-world long line
|
||||
* within a 16 KB file. */
|
||||
/* Horizontal pan (only when viewport contains nowrap lines). */
|
||||
static void scroll_h(int8_t delta)
|
||||
{
|
||||
if (wrap_mode) return;
|
||||
uint8_t any_nowrap = 0;
|
||||
for (uint8_t i = 0; i < VIEW_H; i++) {
|
||||
if (is_nowrap((uint16_t)(top_line + i))) { any_nowrap = 1; break; }
|
||||
}
|
||||
if (!any_nowrap) return;
|
||||
int16_t nx = (int16_t)viewport_x + delta;
|
||||
if (nx < 0) nx = 0;
|
||||
if (nx > 240) nx = 240;
|
||||
@@ -1095,26 +1296,9 @@ static void scroll_h(int8_t delta)
|
||||
* the rebuild. Also resets horizontal pan — wrap mode doesn't use it. */
|
||||
static void toggle_wrap(void)
|
||||
{
|
||||
uint32_t top_off = (top_line < n_lines) ? line_offset[top_line] : 0;
|
||||
wrap_mode = wrap_mode ? 0u : 1u;
|
||||
viewport_x = 0;
|
||||
spinner_show(1);
|
||||
spinner_tick();
|
||||
index_lines();
|
||||
spinner_show(0);
|
||||
|
||||
/* Largest i such that the i-th seg's offset is ≤ top_off. */
|
||||
uint16_t i = 0;
|
||||
while ((uint16_t)(i + 1) < n_lines &&
|
||||
line_offset[i + 1] <= top_off) {
|
||||
i++;
|
||||
}
|
||||
top_line = i;
|
||||
clamp_top();
|
||||
|
||||
render_menu();
|
||||
render_full_status();
|
||||
render_viewport();
|
||||
/* Removed — no longer needed. Kept as stub to avoid
|
||||
* linker warnings if something still calls it. */
|
||||
(void)0;
|
||||
}
|
||||
|
||||
/* ==================================================================
|
||||
@@ -1131,11 +1315,11 @@ static const char * const help_lines[] = {
|
||||
" PgUp / PgDn - scroll one page",
|
||||
" Home - go to top of document",
|
||||
" End - go to bottom of document",
|
||||
" Left / Right - pan horizontally (Unwrap mode only)",
|
||||
" Left / Right - pan horizontally (nowrap lines only)",
|
||||
"",
|
||||
" Other:",
|
||||
" F1 - show this help",
|
||||
" F2 - toggle wrap / unwrap of long lines",
|
||||
" F2 - (reserved)",
|
||||
" F10 / Esc - exit",
|
||||
"",
|
||||
" Press any key to return...",
|
||||
@@ -1176,6 +1360,9 @@ int main(int argc, char **argv)
|
||||
memset(line_offset, 0, sizeof(line_offset));
|
||||
memset(cont_flag, 0, sizeof(cont_flag));
|
||||
memset(in_code, 0, sizeof(in_code));
|
||||
memset(nowrap_flag, 0, sizeof(nowrap_flag));
|
||||
memset(blank_flag, 0, sizeof(blank_flag));
|
||||
memset(line_kind, 0, sizeof(line_kind));
|
||||
memset(init_style, 0, sizeof(init_style));
|
||||
|
||||
const char *path;
|
||||
@@ -1240,8 +1427,6 @@ int main(int argc, char **argv)
|
||||
}
|
||||
switch (scan) {
|
||||
case KEY_F10: goto exit_loop;
|
||||
case KEY_F1: help_screen(); break;
|
||||
case KEY_F2: toggle_wrap(); break;
|
||||
case KEY_UP: scroll_up(1); break;
|
||||
case KEY_DOWN: scroll_down(1); break;
|
||||
case KEY_LEFT: scroll_h(-(int8_t)HPAN_STEP); break;
|
||||
|
||||
Reference in New Issue
Block a user