mdview: wide-break '\\', strikethrough, horizontal scroll bounds + '<'
- A trailing backslash before a newline now forces an in-paragraph line break (like two trailing spaces); render consumes the marker (non-code). - Add ~~strikethrough~~ inline style (INIT_STYLE_STRIKE / EM_STRIKE), parsed in inline_scan, the paragraph merger and render, mirroring **. - Horizontal pan is bounded by the widest nowrap segment on screen, and a '<' indicator marks hidden content off the left edge. Co-Authored-By: Oz <oz-agent@warp.dev>
This commit is contained in:
@@ -93,6 +93,7 @@ static const uint8_t md_pallete[16 * 4] = {
|
|||||||
#define ATTR_TEXT_ITALIC COLOR(COLOR_LIGHTGREEN, COLOR_BLUE)
|
#define ATTR_TEXT_ITALIC COLOR(COLOR_LIGHTGREEN, COLOR_BLUE)
|
||||||
#define ATTR_TEXT_UNDERSORE COLOR(COLOR_LIGHTMAGENTA, COLOR_BLUE)
|
#define ATTR_TEXT_UNDERSORE COLOR(COLOR_LIGHTMAGENTA, COLOR_BLUE)
|
||||||
#define ATTR_TEXT_CODE COLOR(COLOR_WHITE, COLOR_BLUE)
|
#define ATTR_TEXT_CODE COLOR(COLOR_WHITE, COLOR_BLUE)
|
||||||
|
#define ATTR_TEXT_STRIKE COLOR(COLOR_DARKGRAY, COLOR_BLUE)
|
||||||
#define ATTR_LIST_MARKER COLOR(COLOR_LIGHTCYAN, COLOR_BLUE)
|
#define ATTR_LIST_MARKER COLOR(COLOR_LIGHTCYAN, COLOR_BLUE)
|
||||||
#define ATTR_QUOTE_MARKER COLOR(COLOR_CYAN, COLOR_BLUE)
|
#define ATTR_QUOTE_MARKER COLOR(COLOR_CYAN, COLOR_BLUE)
|
||||||
#define ATTR_HR COLOR(COLOR_LIGHTGRAY, COLOR_BLUE)
|
#define ATTR_HR COLOR(COLOR_LIGHTGRAY, COLOR_BLUE)
|
||||||
@@ -435,9 +436,11 @@ static uint8_t is_fence_delim(uint16_t idx)
|
|||||||
#define INIT_STYLE_ITALIC 0x2
|
#define INIT_STYLE_ITALIC 0x2
|
||||||
#define INIT_STYLE_UNDER 0x3
|
#define INIT_STYLE_UNDER 0x3
|
||||||
#define INIT_STYLE_CODE 0x4
|
#define INIT_STYLE_CODE 0x4
|
||||||
|
#define INIT_STYLE_STRIKE 0x5
|
||||||
|
|
||||||
|
/* Indexed by init_style & 7; order must match INIT_STYLE_* / EM_* below. */
|
||||||
static const uint8_t styles_map[] = {
|
static const uint8_t styles_map[] = {
|
||||||
ATTR_TEXT, ATTR_TEXT_BOLD, ATTR_TEXT_ITALIC, ATTR_TEXT_UNDERSORE, ATTR_TEXT_CODE
|
ATTR_TEXT, ATTR_TEXT_BOLD, ATTR_TEXT_ITALIC, ATTR_TEXT_UNDERSORE, ATTR_TEXT_CODE, ATTR_TEXT_STRIKE
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint8_t get_init_style(uint16_t idx)
|
static uint8_t get_init_style(uint16_t idx)
|
||||||
@@ -705,6 +708,17 @@ static uint8_t inline_scan(uint32_t q, uint32_t q_end, uint8_t col,
|
|||||||
}
|
}
|
||||||
seg_col += 2; q += 2; prev_ch = '*'; continue;
|
seg_col += 2; q += 2; prev_ch = '*'; 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_STRIKE; q += 2; continue;
|
||||||
|
} else if (line_style == INIT_STYLE_STRIKE && 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 == '_') {
|
if (ch == '*' || ch == '_') {
|
||||||
char next_ch = ((q + 1) < q_end) ? fb(q + 1) : '\n';
|
char next_ch = ((q + 1) < q_end) ? fb(q + 1) : '\n';
|
||||||
if (is_emph_flanked(prev_ch, next_ch)) {
|
if (is_emph_flanked(prev_ch, next_ch)) {
|
||||||
@@ -854,14 +868,20 @@ static void index_lines(void)
|
|||||||
if (classify_line(next, &dummy) != LK_PLAIN) break;
|
if (classify_line(next, &dummy) != LK_PLAIN) break;
|
||||||
if (is_line_blank(next)) break;
|
if (is_line_blank(next)) break;
|
||||||
|
|
||||||
/* Hard break? */
|
/* Wide break? Two trailing spaces/tabs OR a trailing
|
||||||
|
* backslash before the newline force a line break inside
|
||||||
|
* the paragraph (the inline style is preserved). */
|
||||||
uint8_t is_hard = 0;
|
uint8_t is_hard = 0;
|
||||||
if (q >= p + 2) {
|
if (q >= p + 1) {
|
||||||
char b1 = fb(q - 1);
|
char b1 = fb(q - 1);
|
||||||
|
if (b1 == '\\') {
|
||||||
|
is_hard = 1;
|
||||||
|
} else if (q >= p + 2) {
|
||||||
char b2 = fb(q - 2);
|
char b2 = fb(q - 2);
|
||||||
if ((b1 == ' ' || b1 == '\t') && (b2 == ' ' || b2 == '\t'))
|
if ((b1 == ' ' || b1 == '\t') && (b2 == ' ' || b2 == '\t'))
|
||||||
is_hard = 1;
|
is_hard = 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (is_hard) {
|
if (is_hard) {
|
||||||
q++;
|
q++;
|
||||||
@@ -899,6 +919,23 @@ static void index_lines(void)
|
|||||||
if (!soft_break) q += 2; else q++;
|
if (!soft_break) q += 2; else q++;
|
||||||
prev_ch = '*'; continue;
|
prev_ch = '*'; 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_STRIKE;
|
||||||
|
if (!soft_break) q += 2; else q++;
|
||||||
|
continue;
|
||||||
|
} else if (line_style == INIT_STYLE_STRIKE && 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 == '_') {
|
if (ch == '*' || ch == '_') {
|
||||||
char next_ch = ((q + 1) < file_size) ? fb(q + 1) : '\n';
|
char next_ch = ((q + 1) < file_size) ? fb(q + 1) : '\n';
|
||||||
if (is_emph_flanked(prev_ch, next_ch)) {
|
if (is_emph_flanked(prev_ch, next_ch)) {
|
||||||
@@ -960,6 +997,7 @@ static void index_lines(void)
|
|||||||
#define EM_ITALIC 2
|
#define EM_ITALIC 2
|
||||||
#define EM_UNDER 3
|
#define EM_UNDER 3
|
||||||
#define EM_CODE 4
|
#define EM_CODE 4
|
||||||
|
#define EM_STRIKE 5
|
||||||
|
|
||||||
static void render_line(uint16_t line_idx, uint8_t row)
|
static void render_line(uint16_t line_idx, uint8_t row)
|
||||||
{
|
{
|
||||||
@@ -1089,6 +1127,13 @@ static void render_line(uint16_t line_idx, uint8_t row)
|
|||||||
char prev_ch = ' '; /* for `_` flanking check */
|
char prev_ch = ' '; /* for `_` flanking check */
|
||||||
while (p < seg_end && col < SCREEN_W) {
|
while (p < seg_end && col < SCREEN_W) {
|
||||||
char ch = fb(p);
|
char ch = fb(p);
|
||||||
|
/* A backslash immediately before this segment's end-of-line is a
|
||||||
|
* wide-break marker (index_lines split here) — consume it without
|
||||||
|
* drawing. Not applied to verbatim code bodies. */
|
||||||
|
if (parse_inline && ch == '\\' && (p + 1) < seg_end) {
|
||||||
|
char nb = fb(p + 1);
|
||||||
|
if (nb == '\n' || nb == '\r') { p++; continue; }
|
||||||
|
}
|
||||||
/* Soft break (newline inside a paragraph segment) — render as a
|
/* Soft break (newline inside a paragraph segment) — render as a
|
||||||
* single space. Hard breaks are already separate segments. */
|
* single space. Hard breaks are already separate segments. */
|
||||||
if (ch == '\n' || ch == '\r') ch = ' ';
|
if (ch == '\n' || ch == '\r') ch = ' ';
|
||||||
@@ -1125,6 +1170,27 @@ static void render_line(uint16_t line_idx, uint8_t row)
|
|||||||
prev_ch = '*';
|
prev_ch = '*';
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
/* `~~` (strikethrough) — same flanking rule as `**` */
|
||||||
|
if (ch == '~' && (p + 1) < seg_end && fb(p + 1) == '~') {
|
||||||
|
char next_ch = ((p + 2) < seg_end) ? fb(p + 2) : '\n';
|
||||||
|
if (is_emph_flanked(prev_ch, next_ch)) {
|
||||||
|
if (emph == EM_NONE && ws_or_eol(prev_ch)) {
|
||||||
|
emph = EM_STRIKE; cur_attr = ATTR_TEXT_STRIKE;
|
||||||
|
p += 2; continue;
|
||||||
|
} else if (emph == EM_STRIKE && ws_or_eol_or_delim(next_ch)) {
|
||||||
|
emph = EM_NONE; cur_attr = base_attr;
|
||||||
|
p += 2; continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* literal `~~` — render both tildes */
|
||||||
|
p += 2;
|
||||||
|
if (cc >= effective_vx && col < SCREEN_W) wrchar(col++, row, '~', cur_attr);
|
||||||
|
cc++;
|
||||||
|
if (cc >= effective_vx && col < SCREEN_W) wrchar(col++, row, '~', cur_attr);
|
||||||
|
cc++;
|
||||||
|
prev_ch = '~';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
/* `*` (italic) and `_` (underline) — same flanking rule */
|
/* `*` (italic) and `_` (underline) — same flanking rule */
|
||||||
if (ch == '*' || ch == '_') {
|
if (ch == '*' || ch == '_') {
|
||||||
char next_ch = ((p + 1) < seg_end) ? fb(p + 1) : '\n';
|
char next_ch = ((p + 1) < seg_end) ? fb(p + 1) : '\n';
|
||||||
@@ -1197,6 +1263,12 @@ static void render_line(uint16_t line_idx, uint8_t row)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Left-edge indicator: when panned right, show that nowrap content
|
||||||
|
* is hidden off the left side. Overlays column 0. */
|
||||||
|
if (is_nowrap(line_idx) && viewport_x > 0) {
|
||||||
|
wrchar(0, row, '<', ATTR_TRUNC);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* Pad rest of the row so previous render content is wiped. For code-
|
/* Pad rest of the row so previous render content is wiped. For code-
|
||||||
* body rows use the code attribute so the block extends to the right
|
* body rows use the code attribute so the block extends to the right
|
||||||
@@ -1306,17 +1378,32 @@ static void scroll_down(uint16_t n)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Horizontal pan (only when viewport contains nowrap lines). */
|
/* Horizontal pan (only when the viewport contains nowrap lines). The pan
|
||||||
|
* is bounded by the widest nowrap segment currently on screen, so the user
|
||||||
|
* cannot scroll past the end of the longest code/table line. */
|
||||||
static void scroll_h(int8_t delta)
|
static void scroll_h(int8_t delta)
|
||||||
{
|
{
|
||||||
uint8_t any_nowrap = 0;
|
uint16_t maxw = 0;
|
||||||
for (uint8_t i = 0; i < VIEW_H; i++) {
|
for (uint8_t i = 0; i < VIEW_H; i++) {
|
||||||
if (is_nowrap((uint16_t)(top_line + i))) { any_nowrap = 1; break; }
|
uint16_t li = (uint16_t)(top_line + i);
|
||||||
|
if (li >= n_lines) break;
|
||||||
|
if (!is_nowrap(li)) continue;
|
||||||
|
uint32_t a = seg_off(li);
|
||||||
|
uint32_t b = seg_off((uint16_t)(li + 1));
|
||||||
|
if (b > file_size) b = file_size;
|
||||||
|
uint16_t w = (b > a) ? (uint16_t)(b - a) : 0; /* ~visible width */
|
||||||
|
if (w > maxw) maxw = w;
|
||||||
}
|
}
|
||||||
if (!any_nowrap) return;
|
if (maxw == 0) return; /* no nowrap content in view */
|
||||||
|
|
||||||
|
/* Max pan = width beyond the screen, capped to the uint8 pan range. */
|
||||||
|
uint16_t over = (maxw > SCREEN_W) ? (uint16_t)(maxw - SCREEN_W) : 0;
|
||||||
|
if (over > 248u) over = 248u;
|
||||||
|
uint8_t max_vx = (uint8_t)over;
|
||||||
|
|
||||||
int16_t nx = (int16_t)viewport_x + delta;
|
int16_t nx = (int16_t)viewport_x + delta;
|
||||||
if (nx < 0) nx = 0;
|
if (nx < 0) nx = 0;
|
||||||
if (nx > 240) nx = 240;
|
if (nx > (int16_t)max_vx) nx = max_vx;
|
||||||
if ((uint8_t)nx != viewport_x) {
|
if ((uint8_t)nx != viewport_x) {
|
||||||
viewport_x = (uint8_t)nx;
|
viewport_x = (uint8_t)nx;
|
||||||
render_viewport();
|
render_viewport();
|
||||||
|
|||||||
Reference in New Issue
Block a user